diff --git a/.changeset/honor-explicit-anthropic-max-output.md b/.changeset/honor-explicit-anthropic-max-output.md new file mode 100644 index 000000000..48f84cd09 --- /dev/null +++ b/.changeset/honor-explicit-anthropic-max-output.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Honor explicit Anthropic `max_output_size` settings instead of clamping them to built-in ceilings. diff --git a/docs/en/configuration/config-files.md b/docs/en/configuration/config-files.md index 055b59182..f7e893555 100644 --- a/docs/en/configuration/config-files.md +++ b/docs/en/configuration/config-files.md @@ -125,7 +125,7 @@ Each entry in the `models` table defines a model alias (the name used in `defaul | `provider` | `string` | Yes | Name of the provider to use; must be defined in `providers` | | `model` | `string` | Yes | Model identifier sent to the server when calling the API | | `max_context_size` | `integer` | Yes | Maximum context length in tokens; must be at least 1 | -| `max_output_size` | `integer` | No | Per-request output token cap (maps to `max_tokens`). Currently only the `anthropic` provider honors it; recognized Claude models are automatically clamped to the server-side maximum | +| `max_output_size` | `integer` | No | Per-request output token cap (maps to `max_tokens`). Currently only the `anthropic` provider honors it. When set for a Claude model, this explicit value overrides the built-in server-side maximum | | `capabilities` | `array` | No | Capability tags to add explicitly: `thinking`, `image_in`, `video_in`, `audio_in`, `tool_use`. Unioned with the capabilities auto-detected by the provider — entries can only be added, never removed | | `support_efforts` | `array` | No | Thinking effort levels declared by the model catalog. Managed and open-platform refreshes may rewrite this field; to pin it manually, set `[models."".overrides] support_efforts` instead | | `default_effort` | `string` | No | Default thinking effort for the model. Managed and open-platform refreshes may rewrite this field; to pin it manually, set `[models."".overrides] default_effort` instead | diff --git a/docs/en/configuration/env-vars.md b/docs/en/configuration/env-vars.md index 29d4247b2..dc0ab2626 100644 --- a/docs/en/configuration/env-vars.md +++ b/docs/en/configuration/env-vars.md @@ -107,7 +107,7 @@ Complete variable list: | `KIMI_MODEL_MAX_CONTEXT_SIZE` | No | Maximum context length (tokens) | `262144` (256 K) | | `KIMI_MODEL_CAPABILITIES` | No | Comma-separated capability tags, unioned with auto-detected capabilities | `image_in,thinking` | | `KIMI_MODEL_DISPLAY_NAME` | No | Name shown in `/model` | Falls back to `KIMI_MODEL_NAME` | -| `KIMI_MODEL_MAX_OUTPUT_SIZE` | No | Per-request output cap (`anthropic` only) | Model default | +| `KIMI_MODEL_MAX_OUTPUT_SIZE` | No | Per-request output cap (`anthropic` only); when set, overrides the built-in Claude ceiling | Model default | | `KIMI_MODEL_REASONING_KEY` | No | Reasoning field name override (`openai` only) | Auto-detected | | `KIMI_MODEL_THINKING_EFFORT` | No | Thinking effort level: `low`/`medium`/`high`/`xhigh`/`max` | — | | `KIMI_MODEL_ADAPTIVE_THINKING` | No | Force adaptive thinking on or off (`anthropic` only) | Inferred from model name | diff --git a/docs/zh/configuration/config-files.md b/docs/zh/configuration/config-files.md index 144dc6aac..cf3633891 100644 --- a/docs/zh/configuration/config-files.md +++ b/docs/zh/configuration/config-files.md @@ -125,7 +125,7 @@ KIMI_BASE_URL = "https://api.moonshot.ai/v1" | `provider` | `string` | 是 | 使用的供应商名称,必须在 `providers` 中定义 | | `model` | `string` | 是 | 调用 API 时实际传给服务端的模型 ID | | `max_context_size` | `integer` | 是 | 最大上下文长度(token 数),必须 ≥ 1 | -| `max_output_size` | `integer` | 否 | 单次请求的输出 token 上限(对应 `max_tokens`)。目前仅 `anthropic` 供应商读取;已识别的 Claude 系列会自动限制在服务端允许的最大值内 | +| `max_output_size` | `integer` | 否 | 单次请求的输出 token 上限(对应 `max_tokens`)。目前仅 `anthropic` 供应商读取。为 Claude 模型设置后,这个显式值会覆盖内置的服务端最大值 | | `capabilities` | `array` | 否 | 显式追加的能力标签:`thinking`、`image_in`、`video_in`、`audio_in`、`tool_use`。与供应商自动识别的能力取并集,只能追加不能移除 | | `support_efforts` | `array` | 否 | 模型目录声明的 Thinking 档位。managed 和 open-platform 刷新可能会改写该字段;如需手动固定,请改用 `[models."".overrides] support_efforts` | | `default_effort` | `string` | 否 | 模型的默认 Thinking 档位。managed 和 open-platform 刷新可能会改写该字段;如需手动固定,请改用 `[models."".overrides] default_effort` | diff --git a/docs/zh/configuration/env-vars.md b/docs/zh/configuration/env-vars.md index 0a4a59e9b..f381eefd6 100644 --- a/docs/zh/configuration/env-vars.md +++ b/docs/zh/configuration/env-vars.md @@ -107,7 +107,7 @@ kimi | `KIMI_MODEL_MAX_CONTEXT_SIZE` | 否 | 最大上下文长度(token 数) | `262144`(256K) | | `KIMI_MODEL_CAPABILITIES` | 否 | 逗号分隔的能力标签,与自动探测的能力取并集 | `image_in,thinking` | | `KIMI_MODEL_DISPLAY_NAME` | 否 | 在 `/model` 中显示的名称 | 回退到 `KIMI_MODEL_NAME` | -| `KIMI_MODEL_MAX_OUTPUT_SIZE` | 否 | 单次输出上限(仅 `anthropic`) | 模型默认值 | +| `KIMI_MODEL_MAX_OUTPUT_SIZE` | 否 | 单次输出上限(仅 `anthropic`);设置后会覆盖内置的 Claude 上限 | 模型默认值 | | `KIMI_MODEL_REASONING_KEY` | 否 | 推理字段名覆盖(仅 `openai`) | 自动探测 | | `KIMI_MODEL_THINKING_EFFORT` | 否 | Thinking 强度:`low`/`medium`/`high`/`xhigh`/`max` | — | | `KIMI_MODEL_ADAPTIVE_THINKING` | 否 | 强制开启或关闭 adaptive thinking(仅 `anthropic`) | 按模型名推断 | diff --git a/packages/kosong/src/providers/anthropic.ts b/packages/kosong/src/providers/anthropic.ts index 791175e12..9d38d9e3f 100644 --- a/packages/kosong/src/providers/anthropic.ts +++ b/packages/kosong/src/providers/anthropic.ts @@ -985,7 +985,7 @@ export class AnthropicChatProvider implements ChatProvider { this._client = this._apiKey === undefined ? undefined : this._buildClient(this._apiKey); this._explicitMaxTokens = options.defaultMaxTokens !== undefined; this._generationKwargs = { - max_tokens: resolveDefaultMaxTokens(options.model, options.defaultMaxTokens), + max_tokens: options.defaultMaxTokens ?? resolveDefaultMaxTokens(options.model), betaFeatures: options.betaFeatures ?? [INTERLEAVED_THINKING_BETA], }; } diff --git a/packages/kosong/test/anthropic.test.ts b/packages/kosong/test/anthropic.test.ts index 494ea94d1..3a3940495 100644 --- a/packages/kosong/test/anthropic.test.ts +++ b/packages/kosong/test/anthropic.test.ts @@ -2759,8 +2759,8 @@ describe('AnthropicChatProvider constructor max_tokens', () => { expect(await maxTokensFor('claude-opus-4-7', { defaultMaxTokens: 200 })).toBe(200); }); - it('clamps defaultMaxTokens above the documented ceiling for known models', async () => { - expect(await maxTokensFor('claude-opus-4-7', { defaultMaxTokens: 999999 })).toBe(128000); + it('honors explicit defaultMaxTokens above the ceiling for known models', async () => { + expect(await maxTokensFor('claude-opus-4-7', { defaultMaxTokens: 999999 })).toBe(999999); }); it('withMaxCompletionTokens sets max_tokens when no existing cap is present', async () => { @@ -2847,6 +2847,21 @@ describe('AnthropicChatProvider constructor max_tokens', () => { expect(body['max_tokens']).toBe(128000); }); + it('withMaxCompletionTokens preserves explicit defaultMaxTokens above the ceiling for known models', async () => { + const provider = new AnthropicChatProvider({ + model: 'claude-opus-4-7', + apiKey: 'test-key', + stream: false, + defaultMaxTokens: 999999, + }).withMaxCompletionTokens(1024); + const history: Message[] = [ + { role: 'user', content: [{ type: 'text', text: 'hi' }], toolCalls: [] }, + ]; + const body = await captureRequestBody(provider, '', [], history); + + expect(body['max_tokens']).toBe(999999); + }); + it('withMaxCompletionTokens clamps above the documented ceiling for known models', async () => { const provider = new AnthropicChatProvider({ model: 'claude-opus-4-7',