diff --git a/.changeset/openai-max-output-size.md b/.changeset/openai-max-output-size.md new file mode 100644 index 000000000..8ccf53567 --- /dev/null +++ b/.changeset/openai-max-output-size.md @@ -0,0 +1,6 @@ +--- +"@moonshot-ai/agent-core": patch +"@moonshot-ai/kimi-code": patch +--- + +Fix 400 "Invalid max_tokens value" error when using DeepSeek and other OpenAI-compatible providers with `max_output_size` configured diff --git a/packages/agent-core/src/session/provider-manager.ts b/packages/agent-core/src/session/provider-manager.ts index 38d3e1cd1..dd6de62c9 100644 --- a/packages/agent-core/src/session/provider-manager.ts +++ b/packages/agent-core/src/session/provider-manager.ts @@ -236,6 +236,7 @@ function toKosongProviderConfig( baseUrl: providerValue(provider.baseUrl, provider.env, 'OPENAI_BASE_URL'), apiKey: providerApiKey(provider), reasoningKey, + ...(maxOutputSize !== undefined ? { maxTokens: maxOutputSize } : {}), ...defaultHeadersField(provider.customHeaders), }; case 'kimi': @@ -259,6 +260,7 @@ function toKosongProviderConfig( model, baseUrl: providerValue(provider.baseUrl, provider.env, 'OPENAI_BASE_URL'), apiKey: providerApiKey(provider), + ...(maxOutputSize !== undefined ? { maxOutputTokens: maxOutputSize } : {}), ...defaultHeadersField(provider.customHeaders), }; case 'vertexai': { diff --git a/packages/agent-core/test/harness/runtime-provider.test.ts b/packages/agent-core/test/harness/runtime-provider.test.ts index 986357bf8..de3e5b843 100644 --- a/packages/agent-core/test/harness/runtime-provider.test.ts +++ b/packages/agent-core/test/harness/runtime-provider.test.ts @@ -338,6 +338,101 @@ describe('resolveRuntimeProvider maxOutputSize forwarding', () => { }); }); + it('forwards alias.maxOutputSize to the openai provider config as maxTokens', () => { + const resolved = resolveRuntimeProvider({ + config: { + ...BASE_CONFIG, + providers: { + ...BASE_CONFIG.providers, + openai: { + type: 'openai', + apiKey: 'sk-openai', + baseUrl: 'https://openai.example/v1', + }, + }, + models: { + ...BASE_CONFIG.models!, + 'deepseek-alias': { + provider: 'openai', + model: 'deepseek-chat', + maxContextSize: 1_000_000, + maxOutputSize: 393216, + }, + }, + }, + model: 'deepseek-alias', + }); + + expect(resolved.provider).toMatchObject({ + type: 'openai', + model: 'deepseek-chat', + maxTokens: 393216, + }); + }); + + it('omits maxTokens from the openai provider config when alias.maxOutputSize is unset', () => { + const resolved = resolveRuntimeProvider({ + config: { + ...BASE_CONFIG, + providers: { + ...BASE_CONFIG.providers, + openai: { + type: 'openai', + apiKey: 'sk-openai', + baseUrl: 'https://openai.example/v1', + }, + }, + models: { + ...BASE_CONFIG.models!, + 'gpt-alias': { + provider: 'openai', + model: 'gpt-4o', + maxContextSize: 128000, + }, + }, + }, + model: 'gpt-alias', + }); + + expect(resolved.provider).toMatchObject({ + type: 'openai', + model: 'gpt-4o', + }); + expect('maxTokens' in resolved.provider).toBe(false); + }); + + it('forwards alias.maxOutputSize to the openai_responses provider config as maxOutputTokens', () => { + const resolved = resolveRuntimeProvider({ + config: { + ...BASE_CONFIG, + providers: { + ...BASE_CONFIG.providers, + openai_resp: { + type: 'openai_responses', + apiKey: 'sk-openai', + baseUrl: 'https://openai.example/v1', + }, + }, + models: { + ...BASE_CONFIG.models!, + 'o3-alias': { + provider: 'openai_resp', + model: 'o3', + maxContextSize: 200000, + maxOutputSize: 100000, + }, + }, + }, + model: 'o3-alias', + }); + + expect(resolved.provider).toMatchObject({ + type: 'openai_responses', + model: 'o3', + maxOutputTokens: 100000, + }); + }); + it('omits adaptiveThinking when alias.adaptiveThinking is unset', () => { const resolved = resolveRuntimeProvider({ config: {