-
Notifications
You must be signed in to change notification settings - Fork 665
feat(agent-core): dual-model-routing — separate subagent model and thinking effort #1996
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
kassieclaire
wants to merge
8
commits into
MoonshotAI:main
Choose a base branch
from
kassieclaire:feat/dual-model-routing
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
3f47332
feat(agent-core): dual-model-routing experimental feature for separat…
kassieclaire 6a07fe8
feat(agent-core): subagent thinking-effort routing + dual-model-routi…
kassieclaire 86e052d
docs(zh): document dual-model-routing fields in zh configuration docs
kassieclaire 142292e
fix(agent-core): address dual-model-routing review findings
kassieclaire 36c6f97
test(tui): use real context sizes for kimi-k3 and glm-5.2
kassieclaire f9473d4
fix(tui): show subagent badge when effort or provider differs
kassieclaire 3ebed81
Merge remote-tracking branch 'origin/main' into feat/dual-model-routing
kassieclaire 30f7418
feat(agent-core-v2): dual-model-routing — separate subagent model and…
kassieclaire File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| --- | ||
| "@moonshot-ai/kimi-code": minor | ||
| "@moonshot-ai/kimi-code-sdk": minor | ||
| --- | ||
|
|
||
| Add the `dual-model-routing` experimental feature: route the main agent and its subagents to different models. | ||
|
|
||
| When the feature is enabled (via `/experiments`, `KIMI_CODE_EXPERIMENTAL_DUAL_MODEL_ROUTING`, or the master `KIMI_CODE_EXPERIMENTAL_FLAG` switch), subagents use a dedicated subagent model instead of inheriting the main agent's model. The subagent model defaults to the new `default_subagent_model` config field and can be switched live via `/model`, which now opens a scope picker (Main agent / Subagents) when the feature is active. The footer status bar shows both the main and subagent models while the feature is on. Subagent thinking effort is also separable: it defaults to the new `default_subagent_thinking_effort` config field and can be switched live via the same `/model` scope picker (the chosen effort is persisted alongside the model). Note that the dedicated model and effort apply to delegated subagents only — the side-question (`/btw`) agent always inherits the main agent's model and effort, because it replays the main agent's prompt prefix and shares its prefix cache, so routing it elsewhere would re-process the whole conversation on a cold cache. | ||
|
|
||
| When the feature is disabled, all UI and runtime behavior is unchanged — subagents inherit the parent model as before, the footer shows only the main model, and `/model` keeps its singular behavior. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
78 changes: 78 additions & 0 deletions
78
apps/kimi-code/src/tui/components/dialogs/model-scope-selector.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,78 @@ | ||
| /** | ||
| * ModelScopeSelectorComponent — a small picker that lets the user choose | ||
| * which model scope to configure when the `dual-model-routing` experimental | ||
| * feature is active: | ||
| * | ||
| * - "main" → configure the main agent's model | ||
| * - "subagent" → configure the subagent model (used by spawned subagents) | ||
| * | ||
| * It is a thin wrapper around ChoicePickerComponent, mirroring | ||
| * PermissionSelectorComponent. Mounted via `mountEditorReplacement`. | ||
| */ | ||
|
|
||
| import type { ModelAlias } from '@moonshot-ai/kimi-code-sdk'; | ||
|
|
||
| import { ChoicePickerComponent, type ChoiceOption } from './choice-picker'; | ||
| import { modelDisplayName } from './model-selector'; | ||
|
|
||
| export type ModelScope = 'main' | 'subagent'; | ||
|
|
||
| export interface ModelScopeSelectorOptions { | ||
| /** Current main-agent model alias. */ | ||
| readonly currentModel: string; | ||
| /** Current subagent model alias, or undefined when unset. */ | ||
| readonly currentSubagentModel: string | undefined; | ||
| /** Current subagent thinking effort, or undefined when unset. */ | ||
| readonly currentSubagentThinkingEffort: string | undefined; | ||
| /** Catalog of available models (alias → definition) for display names. */ | ||
| readonly availableModels: Record<string, ModelAlias>; | ||
| readonly onSelect: (scope: ModelScope) => void; | ||
| readonly onCancel: () => void; | ||
| } | ||
|
|
||
| function isModelScope(value: string): value is ModelScope { | ||
| return value === 'main' || value === 'subagent'; | ||
| } | ||
|
|
||
| export class ModelScopeSelectorComponent extends ChoicePickerComponent { | ||
| constructor(opts: ModelScopeSelectorOptions) { | ||
| const mainName = modelDisplayName(opts.currentModel, opts.availableModels[opts.currentModel]); | ||
| const effort = opts.currentSubagentThinkingEffort; | ||
| const hasEffort = effort !== undefined && effort.length > 0; | ||
| let subagentName: string; | ||
| if (opts.currentSubagentModel !== undefined && opts.currentSubagentModel.length > 0) { | ||
| const model = modelDisplayName( | ||
| opts.currentSubagentModel, | ||
| opts.availableModels[opts.currentSubagentModel], | ||
| ); | ||
| subagentName = hasEffort ? `${model} (effort: ${effort})` : model; | ||
| } else { | ||
| // The model is inherited, but a configured subagent effort still | ||
| // applies — show it so the label reflects the effective settings. | ||
| subagentName = hasEffort ? `(inherits main model, effort: ${effort})` : '(inherits main model)'; | ||
| } | ||
|
|
||
| const options: readonly ChoiceOption[] = [ | ||
| { | ||
| value: 'main', | ||
| label: `Main agent — ${mainName}`, | ||
| description: 'The model that runs your conversation and owns the main turn.', | ||
| }, | ||
| { | ||
| value: 'subagent', | ||
| label: `Subagents — ${subagentName}`, | ||
| description: | ||
| 'The model used by delegated subagents (task, explore, swarm). When set, subagents run on this model instead of the main model.', | ||
| }, | ||
| ]; | ||
|
|
||
| super({ | ||
| title: 'Select model scope', | ||
| options, | ||
| onSelect: (value) => { | ||
| if (isModelScope(value)) opts.onSelect(value); | ||
| }, | ||
| onCancel: opts.onCancel, | ||
| }); | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
KIMI_MODEL_*is active, the picker can include the synthetic__kimi_env_model__alias, and this new persistence path writes that alias directly intodefault_subagent_model. UnlikedefaultModel, the write path does not restore/drop env-injected subagent defaults, so choosing the env model here commitsdefault_subagent_model = "__kimi_env_model__"toconfig.toml; after restarting without those env vars, delegated subagents resolve an unconfigured alias and fail. Please handle the env alias the same way the main default is stripped/restored before saving.Useful? React with 👍 / 👎.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in 142292e.
stripEnvModelConfignow mirrors thedefaultModelhandling fordefaultSubagentModel: when it points at the env alias, the value is restored fromconfig.raw['default_subagent_model'](or dropped if raw has none) instead of being persisted. Covered by three new tests inenv-model.test.ts(restore-from-raw, drop-when-no-raw, keep-non-alias).