From 399d7f61783f77081da0286fc52eb525f54f695d Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 3 May 2026 14:09:49 +0100 Subject: [PATCH] fix(agents): forward model maxTokens by default --- CHANGELOG.md | 1 + src/agents/openai-transport-stream.test.ts | 76 ++++++++++++++++++++++ src/agents/openai-transport-stream.ts | 18 +++-- 3 files changed, 88 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 713bbb6590c..ed4b2bc8a6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ Docs: https://docs.openclaw.ai ### Fixes +- Agents/models: forward model `maxTokens` as the default output-token limit for OpenAI-compatible Responses and Completions transports when no runtime override is provided, preventing provider defaults from silently truncating larger outputs. (#76645) Thanks @joeyfrasier. - Gateway/update: run `doctor --non-interactive --fix` after Control UI global package updates before reporting success, so legacy config is migrated before the gateway restart. Thanks @stevenchouai. - Gateway/cron: stop a lazy cron startup that loses a hot-reload race, preventing the old cron service from starting after reload has already replaced cron state. - CLI/plugins: warn when npm plugin installs remain shadowed by a failing config-selected source and surface the repair path in `plugins doctor`. Thanks @LindalyX-Lee. diff --git a/src/agents/openai-transport-stream.test.ts b/src/agents/openai-transport-stream.test.ts index 28d09495a46..73dc31415f9 100644 --- a/src/agents/openai-transport-stream.test.ts +++ b/src/agents/openai-transport-stream.test.ts @@ -995,6 +995,31 @@ describe("openai transport stream", () => { expect(params.input?.[0]).toMatchObject({ role: "developer" }); }); + it("uses model maxTokens for Responses params when runtime maxTokens is omitted", () => { + const params = buildOpenAIResponsesParams( + { + id: "gpt-5.4", + name: "GPT-5.4", + api: "openai-responses", + provider: "openai", + baseUrl: "https://api.openai.com/v1", + reasoning: true, + input: ["text"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 200000, + maxTokens: 65_536, + } satisfies Model<"openai-responses">, + { + systemPrompt: "system", + messages: [], + tools: [], + } as never, + undefined, + ) as { max_output_tokens?: unknown }; + + expect(params.max_output_tokens).toBe(65_536); + }); + it("uses top-level instructions for Codex responses and strips unsupported ChatGPT params", () => { const params = buildOpenAIResponsesParams( { @@ -2373,6 +2398,57 @@ describe("openai transport stream", () => { expect(params).not.toHaveProperty("max_completion_tokens"); }); + it("uses model maxTokens for OpenAI completions params when runtime maxTokens is omitted", () => { + const params = buildOpenAICompletionsParams( + { + id: "gpt-5.4", + name: "GPT-5.4", + api: "openai-completions", + provider: "openai", + baseUrl: "https://api.openai.com/v1", + reasoning: true, + input: ["text"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 200000, + maxTokens: 65_536, + } satisfies Model<"openai-completions">, + { + systemPrompt: "system", + messages: [], + tools: [], + } as never, + undefined, + ); + + expect(params.max_completion_tokens).toBe(65_536); + expect(params).not.toHaveProperty("max_tokens"); + }); + + it("uses model maxTokens with max_tokens completions compat when runtime maxTokens is omitted", () => { + const params = buildOpenAICompletionsParams( + { + id: "zai-org/GLM-4.7-TEE", + name: "GLM 4.7 TEE", + api: "openai-completions", + provider: "chutes", + reasoning: true, + input: ["text"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 200000, + maxTokens: 65_536, + } as never, + { + systemPrompt: "system", + messages: [], + tools: [], + } as never, + undefined, + ); + + expect(params.max_tokens).toBe(65_536); + expect(params).not.toHaveProperty("max_completion_tokens"); + }); + it("omits strict tool shaping for Z.ai default-route completions providers", () => { const params = buildOpenAICompletionsParams( { diff --git a/src/agents/openai-transport-stream.ts b/src/agents/openai-transport-stream.ts index a660b98b1c3..d1c9f543e88 100644 --- a/src/agents/openai-transport-stream.ts +++ b/src/agents/openai-transport-stream.ts @@ -981,8 +981,9 @@ export function buildOpenAIResponsesParams( ...(isCodexResponses ? { instructions: buildOpenAICodexResponsesInstructions(context) } : {}), ...(metadata ? { metadata } : {}), }; - if (options?.maxTokens) { - params.max_output_tokens = options.maxTokens; + const effectiveMaxTokens = options?.maxTokens || model.maxTokens; + if (effectiveMaxTokens) { + params.max_output_tokens = effectiveMaxTokens; } if (options?.temperature !== undefined) { params.temperature = options.temperature; @@ -1863,11 +1864,14 @@ export function buildOpenAICompletionsParams( if (compat.supportsPromptCacheKey && cacheRetention !== "none" && options?.sessionId) { params.prompt_cache_key = options.sessionId; } - if (options?.maxTokens) { - if (compat.maxTokensField === "max_tokens") { - params.max_tokens = options.maxTokens; - } else { - params.max_completion_tokens = options.maxTokens; + { + const effectiveMaxTokens = options?.maxTokens || model.maxTokens; + if (effectiveMaxTokens) { + if (compat.maxTokensField === "max_tokens") { + params.max_tokens = effectiveMaxTokens; + } else { + params.max_completion_tokens = effectiveMaxTokens; + } } } if (options?.temperature !== undefined) {