mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-07-09 17:29:12 +00:00
* feat: support multi-level thinking effort switching
- kimi provider: emit thinking.effort in the new wire format; keep reasoning_effort mirrored during the transition
- model catalog: thread support_efforts / default_effort from oauth through to /models
- config schema: add supportEfforts / defaultEffort on model aliases
- TUI: multi-segment thinking control in /model, new /effort command, footer effort display
- switch status uses displayName and distinguishes model vs effort-only changes
* docs: add thinking effort design plans
- thinking-effort-switching.md: implemented multi-level effort switching
- thinking-model-overhaul.md: follow-up refactor plan for the thinking state model
* docs: collapse thinking overhaul plan into a single PR
* refactor!: overhaul thinking config and effort resolution
Replace default_thinking and thinking.mode with a single [thinking] enabled/effort table. ThinkingEffort is now an open string ('off' | 'on' | model-declared effort); effort levels come from each model's support_efforts instead of a fixed enum.
Centralize default and always_thinking clamp logic in resolveThinkingEffort/defaultThinkingEffortFor, and honor an explicitly configured effort when an always_thinking model is forced back on.
TUI keeps a single thinkingEffort field instead of the boolean + level pair; 'on' is normalized to the model default at the UI boundary.
BREAKING CHANGE: default_thinking and thinking.mode are removed from config; migrate to [thinking] enabled/effort.
* refactor: rename residual thinking level wording to effort
Rename comments, error messages, parameter names, the SetThinkingPayload wire field (level -> effort), and TUI local variables so the thinking effort naming is consistent throughout. No behavior change.
* refactor: rename remaining camelCase thinking level identifiers to effort
Rename liveLevel/prevLevel/levelChanged/commitLevel/effectiveLevel to liveEffort/prevEffort/effortChanged/commitEffort/effectiveEffort in the TUI model picker and config commands.
* refactor: eliminate remaining thinking level wording in comments and tests
Rename levelLabel -> effortLabel, EffortSelectorOptions.levels -> efforts, and 'effort level(s)' / 'default level' / 'requested level' wording in comments, error messages, slash-command description, and test titles to effort. Also restore the withThinking(effort) parameter rename in the Kimi provider that was accidentally reverted.
* fix: address codex review feedback on thinking effort handling
- OpenAI thinkingEffortToReasoningEffort and Anthropic clampEffort now normalize 'on' / unrecognized efforts instead of throwing, so boolean non-Kimi models no longer crash on session start.
- ACP resolveCurrentThinkingEnabled treats a non-empty thinking.effort as enabled, matching agent-core's resolveThinkingEffort.
- REST promptThinkingSchema accepts any non-empty effort string so model-declared efforts are not rejected at the API boundary.
* test: align kimi e2e expectations with supportEfforts-gated reasoning_effort
The kimi provider now sends reasoning_effort only when the model declares support_efforts; boolean models (no support_efforts) send only thinking.type. Update the kimi e2e tests to drop the stale reasoning_effort expectation for the boolean test model.
* test: cover [thinking] effort parsing in config.test
Add effort = "high" to the documented [thinking] table in the config parse test and assert config.thinking.effort is resolved, so the new [thinking] effort field has direct parse coverage.
* docs: add thinking test coverage gap analysis
Capture the explore agent's test coverage review for the thinking overhaul PR, including P1/P2 gaps and the two open design questions, for follow-up test additions.
* feat(oauth): parse nested think_efforts from /models response
The /models endpoint now returns effort levels under a nested think_efforts object ({ support, valid_efforts, default_effort }). Parse it preferentially in both managed-kimi-code and open-platform model parsing, falling back to the legacy flat support_efforts / default_effort fields for older servers.
* refactor(oauth): only read nested think_efforts; gate on support=true
Drop the legacy flat support_efforts / default_effort fallback. The think_efforts object is now the single source, and its support flag gates the whole object — when support is not true, valid_efforts and default_effort are ignored entirely.
* chore: remove unused parseStringArray import in open-platform
* docs: finalize thinking effort release notes
Downgrade the changeset to minor with an English summary, drop the version-specific 'added in 1.0.0' info block, and present the deprecated config fields as a table (field / deprecated in 0.21.0 / description).
* refactor: drop temporary refresh toggles and kimi reasoning_effort mirror
Remove the always-true REFRESH_MODELS_ON_PICKER_OPEN / REFRESH_PROVIDER_MODELS_ON_STARTUP toggles and their stale re-enable TODOs, and stop sending reasoning_effort from the kimi provider (thinking.effort is the only wire field now).
* fix(tui): avoid persisting "on" as thinking effort
* fix: preserve persisted thinking effort across login and provider setup
* fix(tui): show actual thinking effort in /status and footer
* test(tui): align message-flow expectations with effort persistence and /status display
* fix(vis): rename thinkingLevel to thinkingEffort in config.update analysis
12 KiB
12 KiB
Thinking 模型重构 — 测试覆盖审查报告
PR #1132(分支
support-effort)的测试缺口分析。生成于 2026-06-26,供后续补测试用。总体结论:核心逻辑(resolveThinkingEffort、kimi/anthropic provider、ACP toggle、TUI commitEffort)覆盖较扎实;本次重构引入的若干"归一/兼容"分支存在行为契约未锁定的缺口。没有发现会导致运行时崩溃(P0)的缺口;所有缺口均为行为契约(P1)或 nice-to-have(P2)。
两个待确认的设计问题
1. default_thinking / thinking.mode 是「拒绝」还是「静默忽略」?
- 当前实现:静默忽略 + 下次写入时剥离(schema 非 strict,
packages/agent-core/src/config/toml.ts写入时delete out['mode']/delete out['default_thinking'])。 - 计划意图:「直接删除,不做兼容读取」,breaking change。
- 建议:按"静默忽略 + 写入剥离"补测试锁定(P1 第 4 项)。如果要 fail-fast,需把 schema 改为
.strict()并补 fail-fast 测试。
2. commitEffort('on') 当 defaultEffort 不在 supportEfforts 内时返回 defaultEffort,是否有意?
- 当前实现:
defaultThinkingEffortFor(agent-core 和 TUI 内联)都是model.defaultEffort ?? middleOf(supportEfforts),不校验defaultEffort是否在supportEfforts内。 - 为什么合理:provider 端会归一(声明外的 effort 不下发 wire effort),所以即使
defaultEffort不在声明里也不会导致 wire 错误。 - 建议:按"返回 defaultEffort,由 provider 归一"补测试锁定(P1 第 12 项)。
P1 缺口(行为契约未锁定,建议本 PR 或 follow-up 尽快补)
kosong
openai-common.ts — thinkingEffortToReasoningEffort
- 现有:
off/low/medium/high/xhigh/max+ 未知'extreme'→ undefined(openai-common-errors.test.ts:314-337)。 - 缺口:
'on'没有显式断言(注释明确把'on'列为归一对象,但测试用'extreme')。 - 建议:把
it('normalizes unknown effort to undefined')改成it.each(['on', 'extreme', 'foo'])。
anthropic.ts — clampEffort
- 现有:xhigh/max 在 opus-4-5/4-6/4-7/4-8/fable/sonnet/haiku 上的 clamp 与透传、adaptive vs budget、
off、low/medium 透传(anthropic.test.ts:1084-1400)。 - 缺口:
'on'/ 未知 effort(如'foo')→ clamp 到'high'(anthropic.ts:342-350的最后 if 分支)完全未覆盖。 - 建议:
it.each(['on', 'foo'])在 adaptive 模型(如claude-opus-4-7)上 →output_config={effort:'high'};在非 adaptive 模型(如claude-sonnet-4-5)上 →thinking={type:'enabled',budget_tokens:32000}且无output_config。
google-genai.ts — withThinking
- 现有:非 gemini-3 的
high/off;gemini-3 的off/low/medium/high;getter 反射(google-genai.test.ts:729-836)。 - 缺口:
'on'/ 未知 effort('foo')在 gemini-3 上 → 只include_thoughts:true、不设thinking_level(google-genai.ts:829-852)。未覆盖。'on'/ 未知 effort 在 非 gemini-3 上 → 只include_thoughts:true、不设thinking_budget(google-genai.ts:853-873)。未覆盖。'xhigh'/'max'在 gemini-3 →HIGH(fall-through)。未覆盖。
- 建议:
it.each(['on','foo'])在gemini-3-pro-preview上 →thinking_config={include_thoughts:true}(无thinking_level);在gemini-2.5-flash上 →{include_thoughts:true}(无thinking_budget)。it.each(['xhigh','max'])在 gemini-3 上 →thinking_level:'HIGH'。
kimi.ts — withThinking
- 现有:非 effort 模型 / effort 模型 /
max/off/xhigh/on/foo不在 supportEfforts / getter / 重复调用(kimi.test.ts:708-799)。 - 缺口:空
supportEfforts: [](与undefined等价)未显式覆盖。 - 建议:加
createProvider(false, []).withThinking('high')→thinking={type:'enabled'}、无reasoning_effort。
agent-core
config/schema.ts — 删除 default_thinking / thinking.mode
- 现有:
[thinking] enabled/effort解析;patch merge thinking;default_yolo的 drop-deprecated 范式(configs.test.ts)。 - 缺口:
default_thinking(顶层)被忽略/剥离无测试;thinking.mode被忽略/剥离无测试。 - 建议:
parseConfigString('default_thinking = true\n[thinking]\nmode = "always"\neffort="high"\n')→config.thinking不含mode、顶层无defaultThinking,且writeConfigFile后文本不含default_thinking/mode。
config/env-model.ts — 删除 KIMI_MODEL_THINKING_MODE / KIMI_MODEL_DEFAULT_THINKING
- 现有:
KIMI_MODEL_THINKING_EFFORT映射;KIMI_MODEL_ADAPTIVE_THINKING;write-back 隔离(env-model.test.ts)。 - 缺口:
KIMI_MODEL_THINKING_MODE/KIMI_MODEL_DEFAULT_THINKING被忽略无测试(回归保护,防止未来被误加回)。 - 建议:
applyEnvModelConfig(MIN, { KIMI_MODEL_THINKING_MODE:'always', KIMI_MODEL_DEFAULT_THINKING:'high' })→config.thinking为undefined(或不含 effort/mode)。
agent/config/index.ts — update() clamp 集成
- 现有:always_thinking
'off'→'on';provider 构建 enabled;toggleable'off'保持;切到 always_thinking re-clamp 旧'off'(config-state.test.ts:163-228)。 - 缺口:
modelAlias变化 + 旧 effort='off'+ 新 always_thinking 模型 +config.effort='max'→ 应 clamp 到'max'(而非defaultEffort)。update()在 modelAlias 分支把this._thinkingEffort作为 requested 传入,clamp 时读config?.effort——这条路径未在 ConfigState 集成层测。 - 建议:先
update({modelAlias: toggleable, thinkingEffort:'off'}),再update({modelAlias: deep})且 kimiConfig 带thinking:{effort:'max'}→ 期望'max'。
session/provider-manager.ts — supportEfforts 透传给 kimi provider
- 现有:无直接测试。kimi provider 单元覆盖了 supportEfforts 行为,但接线层(provider-manager 把 supportEfforts 写进 kimi provider config)未测。
- 缺口:effort-capable alias 经
ProviderManager.resolveProviderConfig→ kimiProviderConfig应携带supportEfforts。 - 建议:构造
supportEfforts:['low','high','max']的 kimi alias,断言resolveProviderConfig(...).provider的 supportEfforts 透传(或经config.provider.thinkingEffort/modelParameters验证)。
acp-adapter
server.ts — resolveCurrentThinkingEnabled
- 现有:无直接测试。仅经
newSession间接走getConfig无thinking字段 →false分支。 - 缺口(全 P1):
getConfig缺失(partial stub)→falsethinking.enabled === true→true;=== false→falsethinking.effort为非空 string(无enabled)→true(本次新增核心分支)getConfig抛错 →falsethinking缺失 /effort:''→false
- 建议:抽一个可直接调用
resolveCurrentThinkingEnabled的测试(或经newSession+ 不同getConfig返回值断言configOptions.thinking.currentValue),覆盖以上 5 条。
server.ts — setupSessionFromExisting resumedThinkingEffort 投影
- 现有:
thinkingEffort='high'→ toggleon(session-resume.test.ts:142-189)。 - 缺口:
thinkingEffort='off'→currentValue='off'未测;thinkingEffort=''(空串)→off未测。 - 建议:加
thinkingEffort:'off'与thinkingEffort:''两条,断言thinking.currentValue==='off'。
session.ts — thinkingOnEffort
- 现有:默认返回
'on'(fixture model 无supportEfforts/defaultEffort)。 - 缺口:effort-capable model(
defaultEffort='high'或 middlesupportEfforts)→ 返回对应 effort,而非'on';harness缺失(undefined)→'on'。 - 建议:
makeHarness加 effort-capable model(supportEfforts:['low','high','max']、defaultEffort:'high'),断言setThinkingCalls收到'high';另加无 harness 的AcpSession.setThinking(true)→'on'。
apps/kimi-code(TUI)
tui/utils/thinking-config.ts — thinkingEffortToConfig / isThinkingOn
- 现有:无直接单元测试。仅经
cli/provider.test.ts与kimi-tui-message-flow.test.ts间接覆盖。 - 缺口:
thinkingEffortToConfig('off')→{enabled:false};('low')→{enabled:true,effort:'low'};('on')→{enabled:true,effort:'on'}无直接单测。 - 建议:新建
test/tui/utils/thinking-config.test.ts,对thinkingEffortToConfig/isThinkingOn做参数化断言。
tui/components/dialogs/model-selector.ts — commitEffort
- 现有:
'on'→effort 模型defaultEffort;'on'→middle;'on'→boolean'on'(model-selector.test.ts)。 - 缺口:
commitEffort('on')当defaultEffort不在supportEfforts内 → 返回defaultEffort(即使模型未声明支持)。该行为与 agent-core 一致,但 TUI 层未锁定(见"待确认问题 2")。 - 建议:加
effortModel(..., ['low','high'], 'max')非当前模型 → Enter →onSelect收到thinking:'max'(锁定现状)。
tui/commands/config.ts — persistModelSelection short-circuit
- 现有:runtime 不变但
defaultModel不同 → 仍写入;Alt+S 不持久化(kimi-tui-message-flow.test.ts)。 - 缺口:
defaultModel、thinking.enabled、thinking.effort三者完全相同 → 返回false、不调用setConfig(short-circuitconfig.ts:467-473)。未直接测。 - 建议:加一条
getConfig返回defaultModel:'k2', thinking:{enabled:true,effort:'on'},选择k2+on→setConfig未被调用,且状态提示 "Already using ..."。
P2 缺口(nice-to-have)
provider.ts:type-level 开放字符串断言(const e: ThinkingEffort = 'any-custom-effort')。kimi.ts:keep字段在withThinking后保留(withExtraBody({thinking:{keep}}).withThinking(...))。anthropic.ts:budgetTokensForEffort对'off'/'xhigh'/'max'的throw分支(生产路径被 clampEffort 保护)。google-genai.ts:非 gemini-3 的low/medium/xhigh/maxbudget 矩阵;gemini-3 getter 反射。thinking.ts:defaultEffort不在supportEfforts内的语义命名;requested='off'+ always_thinking ±config.effort组合。config-state.test.ts:modelAlias 切换保留非'off'effort;thinkingEffort + modelAlias 同时变化。schema.ts:thinking.enabled非 boolean、thinking.effort非 string → 报错的类型校验。env-model.ts:KIMI_MODEL_THINKING_EFFORT与已有enabled的合并(base config 带thinking:{enabled:true},env 设 effort →{enabled:true, effort})。session-resume.test.ts:thinkingEffort大写/带空格;mainConfig 缺thinkingEffort的 fallback 断言。session.tsthinkingOnEffort:currentModelId 不在 catalog →'on'。promptThinkingSchema:非 string(如number/boolean)→ 拒绝。thinking-config.test.ts:isThinkingOn各分支。commitEffort:非'on'draft 透传。persistModelSelection:defaultModel 相同但 effort 不同 → 写入。
建议的补充顺序
本 PR 内补(简单 + 高价值)
- openai-common:
it.each(['on', 'extreme', 'foo'])显式含'on' - anthropic
clampEffort:it.each(['on', 'foo'])adaptive + budget 两条 - schema:
default_thinking/thinking.mode静默忽略 + 写入剥离 - env-model:
KIMI_MODEL_THINKING_MODE/KIMI_MODEL_DEFAULT_THINKING被忽略 - ACP
resolveCurrentThinkingEnabled:5 条分支 - TUI
thinkingEffortToConfig:直接单元测试(off/on/low) - kimi
withThinking:空supportEfforts: [] - google-genai
withThinking:'on'/ 未知(gemini-3 + 非 gemini-3)
follow-up(避免本 PR 继续膨胀)
- provider-manager
supportEfforts透传 - ConfigState.update() modelAlias 切换 + config.effort 集成
- ACP resume
thinkingEffort='off'/'' - commitEffort defaultEffort 不在声明内
- persistModelSelection short-circuit
- 全部 P2