From 279f0d150f234b4102f07c9e75a17acb97d60dcc Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sun, 10 May 2026 15:03:01 +0900 Subject: [PATCH] fix(chat-params): guard non-positive max output tokens --- src/plugin/chat-params.test.ts | 32 +++++++++++++++++++ src/plugin/chat-params.ts | 11 +++++-- .../model-settings-compatibility.test.ts | 12 +++++++ src/shared/model-settings-compatibility.ts | 4 +++ 4 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/plugin/chat-params.test.ts b/src/plugin/chat-params.test.ts index 5886b7204..de7acb5df 100644 --- a/src/plugin/chat-params.test.ts +++ b/src/plugin/chat-params.test.ts @@ -253,4 +253,36 @@ describe("createChatParamsHandler", () => { options: {}, }) }) + + test("falls back to default maxOutputTokens when stored and compatibility tokens are non-positive", async () => { + //#given + setSessionPromptParams("ses_chat_params", { + maxOutputTokens: 0, + }) + + const handler = createChatParamsHandler({ + anthropicEffort: null, + }) + + const input = { + sessionID: "ses_chat_params", + agent: { name: "oracle" }, + model: { providerID: "custom-provider", modelID: "custom-model" }, + provider: { id: "custom-provider" }, + message: {}, + } + + const output: ChatParamsOutput = { + topP: 1, + topK: 1, + maxOutputTokens: 0, + options: {}, + } + + //#when + await handler(input, output) + + //#then + expect(output.maxOutputTokens).toBe(4096) + }) }) diff --git a/src/plugin/chat-params.ts b/src/plugin/chat-params.ts index 41e4a0200..ac665b62b 100644 --- a/src/plugin/chat-params.ts +++ b/src/plugin/chat-params.ts @@ -96,7 +96,10 @@ export function createChatParamsHandler(args: { if (storedPromptParams.topP !== undefined) { output.topP = storedPromptParams.topP } - if (storedPromptParams.maxOutputTokens !== undefined) { + if ( + typeof storedPromptParams.maxOutputTokens === "number" && + storedPromptParams.maxOutputTokens > 0 + ) { (output as Record).maxOutputTokens = storedPromptParams.maxOutputTokens } if (storedPromptParams.options) { @@ -162,10 +165,12 @@ export function createChatParamsHandler(args: { } if ("maxTokens" in compatibility) { - if (compatibility.maxTokens !== undefined) { + if (compatibility.maxTokens !== undefined && compatibility.maxTokens > 0) { output.maxOutputTokens = compatibility.maxTokens } else { - delete output.maxOutputTokens + const capabilitiesLimit = capabilities?.maxOutputTokens + output.maxOutputTokens = + typeof capabilitiesLimit === "number" && capabilitiesLimit > 0 ? capabilitiesLimit : 4096 } } diff --git a/src/shared/model-settings-compatibility.test.ts b/src/shared/model-settings-compatibility.test.ts index 725b2a54c..a653f7259 100644 --- a/src/shared/model-settings-compatibility.test.ts +++ b/src/shared/model-settings-compatibility.test.ts @@ -553,6 +553,18 @@ describe("resolveCompatibleModelSettings", () => { expect(result.changes).toEqual([]) }) + test("#given desired.maxTokens is 0 #then maxTokens is dropped", () => { + const result = resolveCompatibleModelSettings({ + providerID: "openai", + modelID: "gpt-5.4", + desired: { maxTokens: 0 }, + capabilities: { maxOutputTokens: 128_000 }, + }) + + expect(result.maxTokens).toBeUndefined() + expect(result.changes).toEqual([]) + }) + // Passthrough: undefined desired values produce no changes test("no-op when desired settings are empty", () => { const result = resolveCompatibleModelSettings({ diff --git a/src/shared/model-settings-compatibility.ts b/src/shared/model-settings-compatibility.ts index c2354038e..4e9145192 100644 --- a/src/shared/model-settings-compatibility.ts +++ b/src/shared/model-settings-compatibility.ts @@ -162,6 +162,10 @@ export function resolveCompatibleModelSettings( } let maxTokens = input.desired.maxTokens + if (maxTokens !== undefined && maxTokens <= 0) { + maxTokens = undefined + } + if ( maxTokens !== undefined && input.capabilities?.maxOutputTokens !== undefined &&