From cda583114f4c3accfeaf2409be45437b865c541b Mon Sep 17 00:00:00 2001 From: Abhishek Divekar Date: Sun, 21 Jun 2026 21:03:25 +0530 Subject: [PATCH] fix(command-code): cap max_tokens per model using registry maxOutputTokens (#4518) clampMaxTokens now uses the per-model maxOutputTokens from REGISTRY['command-code'] as the upper bound (falls back to MAX_COMMAND_CODE_TOKENS), so GLM-5.x stops being rejected for max_tokens > 131072. Rebuilt onto release/v3.8.33 (passthrough block from the PR base is already present via #2986); 3 tests added. Integrated into release/v3.8.33. --- open-sse/executors/commandCode.ts | 25 ++++++++++-- tests/unit/command-code-executor.test.ts | 52 ++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 4 deletions(-) diff --git a/open-sse/executors/commandCode.ts b/open-sse/executors/commandCode.ts index 8906c01e03..dbb3115252 100644 --- a/open-sse/executors/commandCode.ts +++ b/open-sse/executors/commandCode.ts @@ -140,9 +140,23 @@ function convertMessages(messages: unknown): { system: string; messages: unknown return { system: system.join("\n\n"), messages: out }; } -function clampMaxTokens(value: unknown): number { - const numeric = numberValue(value) ?? MAX_COMMAND_CODE_TOKENS; - return Math.max(1, Math.min(Math.floor(numeric), MAX_COMMAND_CODE_TOKENS)); +function clampMaxTokens(value: unknown, cap: number = MAX_COMMAND_CODE_TOKENS): number { + const numeric = numberValue(value) ?? cap; + return Math.max(1, Math.min(Math.floor(numeric), cap)); +} + +// Resolve the per-model max_tokens cap for a given CommandCode model id. +// Falls back to MAX_COMMAND_CODE_TOKENS when the model isn't registered or +// when the registry entry omits maxOutputTokens. Without this, GLM-5.x +// requests get capped at 200_000 and rejected with "限制数值范围[1,131072]". +function getModelMaxTokensCap(modelId: string): number { + const entry = REGISTRY["command-code"]; + if (!entry) return MAX_COMMAND_CODE_TOKENS; + const model = entry.models?.find((m: { id: string }) => m.id === modelId); + const registryCap = (model as { maxOutputTokens?: number } | undefined)?.maxOutputTokens; + return typeof registryCap === "number" && registryCap > 0 + ? registryCap + : MAX_COMMAND_CODE_TOKENS; } // Reasoning/thinking fields that payload rules or clients may inject and that @@ -174,7 +188,10 @@ function buildCommandCodeBody(model: string, body: unknown, stream = false): Jso messages: converted.messages, tools: convertTools(input.tools), system, - max_tokens: clampMaxTokens(input.max_tokens ?? input.max_completion_tokens), + max_tokens: clampMaxTokens( + input.max_tokens ?? input.max_completion_tokens, + getModelMaxTokensCap(resolvedModel) + ), stream: true, }; diff --git a/tests/unit/command-code-executor.test.ts b/tests/unit/command-code-executor.test.ts index 71d00f74cd..a52f59b62b 100644 --- a/tests/unit/command-code-executor.test.ts +++ b/tests/unit/command-code-executor.test.ts @@ -294,6 +294,58 @@ test("Command Code executor surfaces upstream and streamed errors", async () => }, /boom/); }); +test("Command Code executor caps max_tokens to the registered per-model limit (GLM-5.x)", async () => { + const calls: FetchCall[] = []; + globalThis.fetch = async (url, init = {}) => { + calls.push({ url: String(url), init, body: JSON.parse(String(init.body)) }); + return commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }]); + }; + + // GLM-5 and GLM-5.1 are registered with maxOutputTokens: 131072. + // Without per-model capping, the upstream rejects with + // "限制数值范围[1,131072]". + await getExecutor("command-code").execute({ + model: "zai-org/GLM-5.1", + stream: false, + credentials: { apiKey: "cc_test_key" }, + body: { messages: [{ role: "user", content: "Hi" }] }, + }); + assert.equal(calls[0].body.params.max_tokens, 131072); +}); + +test("Command Code executor caps max_tokens to the registered per-model limit (DeepSeek v4)", async () => { + const calls: FetchCall[] = []; + globalThis.fetch = async (url, init = {}) => { + calls.push({ url: String(url), init, body: JSON.parse(String(init.body)) }); + return commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }]); + }; + + // DeepSeek v4 pro is registered with maxOutputTokens: 384000. + await getExecutor("command-code").execute({ + model: "deepseek/deepseek-v4-pro", + stream: false, + credentials: { apiKey: "cc_test_key" }, + body: { messages: [{ role: "user", content: "Hi" }] }, + }); + assert.equal(calls[0].body.params.max_tokens, 384000); +}); + +test("Command Code executor honors a smaller client-provided max_tokens under the per-model cap", async () => { + const calls: FetchCall[] = []; + globalThis.fetch = async (url, init = {}) => { + calls.push({ url: String(url), init, body: JSON.parse(String(init.body)) }); + return commandCodeStream([{ type: "text-delta", text: "ok" }, { type: "finish" }]); + }; + + await getExecutor("command-code").execute({ + model: "zai-org/GLM-5.1", + stream: false, + credentials: { apiKey: "cc_test_key" }, + body: { messages: [{ role: "user", content: "Hi" }], max_tokens: 2048 }, + }); + assert.equal(calls[0].body.params.max_tokens, 2048); +}); + test("Command Code non-stream aggregation throws when the final error event lacks a trailing newline", async () => { globalThis.fetch = async () => new Response(