From 7abae15a6bd44dfa53363ec90049580264da4891 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 16 May 2026 23:40:34 +0100 Subject: [PATCH] fix: keep music generation timeouts internal --- CHANGELOG.md | 1 + docs/tools/music-generation.md | 5 ++- src/agents/tools/music-generate-tool.test.ts | 40 +++++++++++--------- src/agents/tools/music-generate-tool.ts | 17 +-------- 4 files changed, 29 insertions(+), 34 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5b0ba0abe49..d4a8ce30e72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/docs/tools/music-generation.md b/docs/tools/music-generation.md index 6a3068849b0..eb5afd5f9d0 100644 --- a/docs/tools/music-generation.md +++ b/docs/tools/music-generation.md @@ -161,7 +161,6 @@ Direct generation example: Output format hint when the provider supports it. Output filename hint. -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. 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. +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: diff --git a/src/agents/tools/music-generate-tool.test.ts b/src/agents/tools/music-generate-tool.test.ts index 11940d7c56f..fe4936b0b5a 100644 --- a/src/agents/tools/music-generate-tool.test.ts +++ b/src/agents/tools/music-generate-tool.test.ts @@ -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"); diff --git a/src/agents/tools/music-generate-tool.ts b/src/agents/tools/music-generate-tool.ts index cf14cd01d0e..1cde08a1917 100644 --- a/src/agents/tools/music-generate-tool.ts +++ b/src/agents/tools/music-generate-tool.ts @@ -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(["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,