mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-18 17:34:45 +00:00
fix: keep music generation timeouts internal
This commit is contained in:
@@ -28,6 +28,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Providers/OpenAI Codex: include base `gpt-5.5` and `gpt-5.4` reasoning metadata in the bundled Codex catalog so `/think xhigh` remains available for those models. Fixes #82744.
|
||||
- Providers/MiniMax: declare CN endpoint auth aliases in the plugin manifest so `minimax-cn` and `minimax-portal-cn` reuse the correct base auth profiles instead of falling back to unrelated models after 401s. Fixes #63823. Thanks @kamusis.
|
||||
- Agents/model fallback: suppress fallback notices when the active OpenAI Codex runtime reports the same canonical OpenAI model.
|
||||
- Agents/music generation: remove model-controlled request timeouts while keeping configured timeouts at a 120-second floor.
|
||||
- Agents/edit tool: honor `file_path` and related path aliases when resolving edit-recovery targets, so post-write errors no longer surface false edit failures after the file actually changed. Fixes #81909. Thanks @giodl73-repo.
|
||||
- QQBot: treat only explicit truthy `QQBOT_DEBUG` values as enabling debug logs, so false-like values such as `0` no longer expose debug output. Fixes #82644. (#82697) Thanks @leno23.
|
||||
- Agents/session_status: resolve implicit no-arg status lookups against the live run session, so `/think` changes report the current thinking level instead of stale sandbox state. Fixes #82669. (#82696) Thanks @leno23.
|
||||
|
||||
@@ -161,7 +161,6 @@ Direct generation example:
|
||||
Output format hint when the provider supports it.
|
||||
</ParamField>
|
||||
<ParamField path="filename" type="string">Output filename hint.</ParamField>
|
||||
<ParamField path="timeoutMs" type="number">Optional provider request timeout in milliseconds. When omitted, OpenClaw uses `agents.defaults.musicGenerationModel.timeoutMs` if configured. Values below 10000ms are raised to 10000ms and reported in the tool result.</ParamField>
|
||||
|
||||
<Note>
|
||||
Not all providers support all parameters. OpenClaw still validates hard
|
||||
@@ -173,6 +172,10 @@ them. Tool results report applied settings; `details.normalization`
|
||||
captures any requested-to-applied mapping.
|
||||
</Note>
|
||||
|
||||
Provider request timeouts are operator configuration only. OpenClaw uses
|
||||
`agents.defaults.musicGenerationModel.timeoutMs` when configured and raises
|
||||
values below 120000ms to 120000ms.
|
||||
|
||||
## Async behavior
|
||||
|
||||
Session-backed music generation runs as a background task:
|
||||
|
||||
@@ -421,7 +421,10 @@ describe("createMusicGenerateTool", () => {
|
||||
config: asConfig({
|
||||
agents: {
|
||||
defaults: {
|
||||
musicGenerationModel: { primary: "google/lyria-3-clip-preview" },
|
||||
musicGenerationModel: {
|
||||
primary: "google/lyria-3-clip-preview",
|
||||
timeoutMs: 1000,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
@@ -432,25 +435,24 @@ describe("createMusicGenerateTool", () => {
|
||||
|
||||
const result = await tool.execute("call-1", {
|
||||
prompt: "night-drive synthwave",
|
||||
timeoutMs: 1000,
|
||||
});
|
||||
const text = (result.content?.[0] as { text: string } | undefined)?.text ?? "";
|
||||
|
||||
expect(generateSpy).toHaveBeenCalledTimes(1);
|
||||
expect(generateMusicOptions().autoProviderFallback).toBe(false);
|
||||
expect(generateMusicOptions().timeoutMs).toBe(10_000);
|
||||
expect(text).toContain("Timeout normalized: requested 1000ms; used 10000ms.");
|
||||
expect(generateMusicOptions().timeoutMs).toBe(120_000);
|
||||
expect(text).toContain("Timeout normalized: requested 1000ms; used 120000ms.");
|
||||
const details = detailsOf(result);
|
||||
expect(details.timeoutMs).toBe(10_000);
|
||||
expect(details.timeoutMs).toBe(120_000);
|
||||
expect(details.requestedTimeoutMs).toBe(1000);
|
||||
expect(details.timeoutNormalization).toEqual({
|
||||
requested: 1000,
|
||||
applied: 10_000,
|
||||
minimum: 10_000,
|
||||
applied: 120_000,
|
||||
minimum: 120_000,
|
||||
});
|
||||
});
|
||||
|
||||
it("uses configured timeoutMs for music generation and lets calls override it", async () => {
|
||||
it("uses configured timeoutMs for music generation and ignores call-provided timeoutMs", async () => {
|
||||
vi.spyOn(musicGenerationRuntime, "generateMusic").mockResolvedValue({
|
||||
provider: "google",
|
||||
model: "lyria-3-clip-preview",
|
||||
@@ -492,13 +494,13 @@ describe("createMusicGenerateTool", () => {
|
||||
});
|
||||
const overrideResult = await tool.execute("call-timeout-override", {
|
||||
prompt: "night-drive synthwave",
|
||||
timeoutMs: 12_345,
|
||||
timeoutMs: 240_000,
|
||||
});
|
||||
|
||||
expect(generateMusicOptions(0).timeoutMs).toBe(180_000);
|
||||
expect(generateMusicOptions(1).timeoutMs).toBe(12_345);
|
||||
expect(generateMusicOptions(1).timeoutMs).toBe(180_000);
|
||||
expect(detailsOf(defaultResult).timeoutMs).toBe(180_000);
|
||||
expect(detailsOf(overrideResult).timeoutMs).toBe(12_345);
|
||||
expect(detailsOf(overrideResult).timeoutMs).toBe(180_000);
|
||||
});
|
||||
|
||||
it("starts background generation and wakes the session with MEDIA lines", async () => {
|
||||
@@ -543,7 +545,10 @@ describe("createMusicGenerateTool", () => {
|
||||
config: asConfig({
|
||||
agents: {
|
||||
defaults: {
|
||||
musicGenerationModel: { primary: "google/lyria-3-clip-preview" },
|
||||
musicGenerationModel: {
|
||||
primary: "google/lyria-3-clip-preview",
|
||||
timeoutMs: 1000,
|
||||
},
|
||||
},
|
||||
},
|
||||
}),
|
||||
@@ -563,31 +568,30 @@ describe("createMusicGenerateTool", () => {
|
||||
const result = await tool.execute("call-1", {
|
||||
prompt: "night-drive synthwave",
|
||||
instrumental: true,
|
||||
timeoutMs: 1000,
|
||||
});
|
||||
const text = (result.content?.[0] as { text: string } | undefined)?.text ?? "";
|
||||
|
||||
expect(text).toContain("Background task started for music generation (task-123).");
|
||||
expect(text).toContain("Do not call music_generate again for this request.");
|
||||
expect(text).toContain("Timeout normalized: requested 1000ms; used 10000ms.");
|
||||
expect(text).toContain("Timeout normalized: requested 1000ms; used 120000ms.");
|
||||
const details = detailsOf(result);
|
||||
expect(details.async).toBe(true);
|
||||
expect(details.status).toBe("started");
|
||||
expect((details.task as { taskId?: unknown }).taskId).toBe("task-123");
|
||||
expect(details.instrumental).toBe(true);
|
||||
expect(details.timeoutMs).toBe(10_000);
|
||||
expect(details.timeoutMs).toBe(120_000);
|
||||
expect(details.requestedTimeoutMs).toBe(1000);
|
||||
expect(details.timeoutNormalization).toEqual({
|
||||
requested: 1000,
|
||||
applied: 10_000,
|
||||
minimum: 10_000,
|
||||
applied: 120_000,
|
||||
minimum: 120_000,
|
||||
});
|
||||
if (!scheduledWork) {
|
||||
throw new Error("expected scheduled music generation work");
|
||||
}
|
||||
await scheduledWork();
|
||||
expect(generateMusicOptions().autoProviderFallback).toBe(false);
|
||||
expect(generateMusicOptions().timeoutMs).toBe(10_000);
|
||||
expect(generateMusicOptions().timeoutMs).toBe(120_000);
|
||||
const progress = taskProgressCall();
|
||||
expect(String(progress.runId)).toMatch(/^tool:music_generate:/);
|
||||
expect(progress.progressSummary).toBe("Generating music");
|
||||
|
||||
@@ -45,7 +45,6 @@ import {
|
||||
hasGenerationToolAvailability,
|
||||
normalizeMediaReferenceInputs,
|
||||
readBooleanToolParam,
|
||||
readGenerationTimeoutMs,
|
||||
resolveCapabilityModelConfigForTool,
|
||||
resolveGenerateAction,
|
||||
resolveMediaToolLocalRoots,
|
||||
@@ -82,7 +81,7 @@ const log = createSubsystemLogger("agents/tools/music-generate");
|
||||
const MAX_INPUT_IMAGES = 10;
|
||||
const SUPPORTED_OUTPUT_FORMATS = new Set<MusicGenerationOutputFormat>(["mp3", "wav"]);
|
||||
const DEFAULT_REFERENCE_FETCH_TIMEOUT_MS = 30_000;
|
||||
const MIN_MUSIC_GENERATION_TIMEOUT_MS = 10_000;
|
||||
const MIN_MUSIC_GENERATION_TIMEOUT_MS = 120_000;
|
||||
|
||||
const MusicGenerateToolSchema = Type.Object({
|
||||
action: Type.Optional(
|
||||
@@ -123,13 +122,6 @@ const MusicGenerateToolSchema = Type.Object({
|
||||
minimum: 1,
|
||||
}),
|
||||
),
|
||||
timeoutMs: Type.Optional(
|
||||
Type.Number({
|
||||
description:
|
||||
"Optional provider request timeout in milliseconds. Values below 10000ms are raised to 10000ms.",
|
||||
minimum: 1,
|
||||
}),
|
||||
),
|
||||
format: Type.Optional(
|
||||
Type.String({
|
||||
description: 'Optional output format hint: "mp3" or "wav" when the provider supports it.',
|
||||
@@ -653,12 +645,8 @@ export function createMusicGenerateTool(options?: {
|
||||
});
|
||||
const format = normalizeOutputFormat(readStringParam(args, "format"));
|
||||
const filename = readStringParam(args, "filename");
|
||||
const requestedTimeoutMs = readGenerationTimeoutMs(args);
|
||||
const requestedGenerationTimeoutMs =
|
||||
requestedTimeoutMs ?? musicGenerationModelConfig.timeoutMs;
|
||||
const timeout = normalizeMusicGenerationTimeoutMs(requestedGenerationTimeoutMs);
|
||||
const timeout = normalizeMusicGenerationTimeoutMs(musicGenerationModelConfig.timeoutMs);
|
||||
const timeoutMs = timeout.timeoutMs;
|
||||
const referenceFetchTimeoutMs = requestedTimeoutMs === undefined ? undefined : timeoutMs;
|
||||
const imageInputs = normalizeReferenceImageInputs(args);
|
||||
const selectedModelRef =
|
||||
parseMusicGenerationModelRef(model) ??
|
||||
@@ -677,7 +665,6 @@ export function createMusicGenerateTool(options?: {
|
||||
workspaceDir: options?.workspaceDir,
|
||||
sandboxConfig,
|
||||
ssrfPolicy: remoteMediaSsrfPolicy,
|
||||
timeoutMs: referenceFetchTimeoutMs,
|
||||
});
|
||||
validateMusicGenerationCapabilities({
|
||||
provider: selectedProvider,
|
||||
|
||||
Reference in New Issue
Block a user