-
Notifications
You must be signed in to change notification settings - Fork 138
feat(tui): add /effort command to set model thinking effort #253
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@moonshot-ai/kimi-code": minor | ||
| --- | ||
|
|
||
| Add a `/effort` slash command (alias `/thinking`) to adjust the model's thinking effort at runtime. `/effort <level>` sets one of `off`, `low`, `medium`, `high`, `xhigh`, `max` directly; `/effort` with no argument opens a picker highlighting the current level. Invalid levels are rejected with the list of valid values. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,12 @@ | ||
| import type { PermissionMode, Session } from '@moonshot-ai/kimi-code-sdk'; | ||
|
|
||
| import { EditorSelectorComponent } from '../components/dialogs/editor-selector'; | ||
| import { | ||
| EffortSelectorComponent, | ||
| isThinkingEffortLevel, | ||
| THINKING_EFFORT_LEVELS, | ||
| type ThinkingEffortLevel, | ||
| } from '../components/dialogs/effort-selector'; | ||
| import { ModelSelectorComponent } from '../components/dialogs/model-selector'; | ||
| import { PermissionSelectorComponent } from '../components/dialogs/permission-selector'; | ||
| import { SettingsSelectorComponent, type SettingsSelection } from '../components/dialogs/settings-selector'; | ||
|
|
@@ -195,10 +201,64 @@ export function handleModelCommand(host: SlashCommandHost, args: string): void { | |
| showModelPicker(host, alias); | ||
| } | ||
|
|
||
| export async function handleEffortCommand(host: SlashCommandHost, args: string): Promise<void> { | ||
| if (host.session === undefined) { | ||
| host.showError(NO_ACTIVE_SESSION_MESSAGE); | ||
| return; | ||
| } | ||
|
|
||
| const requested = args.trim().toLowerCase(); | ||
| if (requested.length === 0) { | ||
| showEffortPicker(host); | ||
| return; | ||
| } | ||
| if (!isThinkingEffortLevel(requested)) { | ||
| host.showError( | ||
| `Unknown thinking effort: ${requested}. Valid levels: ${THINKING_EFFORT_LEVELS.join(', ')}.`, | ||
| ); | ||
| return; | ||
| } | ||
| await applyEffortChoice(host, requested); | ||
| } | ||
|
|
||
| // --------------------------------------------------------------------------- | ||
| // Pickers & config apply | ||
| // --------------------------------------------------------------------------- | ||
|
|
||
| function showEffortPicker(host: SlashCommandHost): void { | ||
| host.mountEditorReplacement( | ||
| new EffortSelectorComponent({ | ||
| currentValue: host.state.appState.thinkingLevel, | ||
| colors: host.state.theme.colors, | ||
| onSelect: (level) => { | ||
| host.restoreEditor(); | ||
| void applyEffortChoice(host, level); | ||
| }, | ||
| onCancel: () => { | ||
| host.restoreEditor(); | ||
| }, | ||
| }), | ||
| ); | ||
| } | ||
|
|
||
| async function applyEffortChoice(host: SlashCommandHost, level: ThinkingEffortLevel): Promise<void> { | ||
| if (level === host.state.appState.thinkingLevel) { | ||
| host.showStatus(`Thinking effort unchanged: ${level}.`); | ||
| return; | ||
| } | ||
|
Comment on lines
+245
to
+248
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
If the user sets Useful? React with 👍 / 👎. |
||
|
|
||
| try { | ||
| await host.requireSession().setThinking(level); | ||
| } catch (error) { | ||
| host.showError(`Failed to set thinking effort: ${formatErrorMessage(error)}`); | ||
| return; | ||
| } | ||
|
|
||
| host.setAppState({ thinkingLevel: level, thinking: level !== 'off' }); | ||
| host.track('thinking_toggle', { enabled: level !== 'off' }); | ||
| host.showStatus(`Thinking effort set to ${level}.`, host.state.theme.colors.success); | ||
| } | ||
|
|
||
| function showEditorPicker(host: SlashCommandHost): void { | ||
| const currentValue = host.state.appState.editorCommand ?? ''; | ||
| host.mountEditorReplacement( | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| import { ChoicePickerComponent, type ChoiceOption } from './choice-picker'; | ||
|
|
||
| import type { ColorPalette } from '#/tui/theme/colors'; | ||
|
|
||
| /** Thinking effort levels exposed to the user, in ascending order. */ | ||
| export const THINKING_EFFORT_LEVELS = [ | ||
| 'off', | ||
| 'low', | ||
| 'medium', | ||
| 'high', | ||
| 'xhigh', | ||
| 'max', | ||
| ] as const; | ||
|
|
||
| export type ThinkingEffortLevel = (typeof THINKING_EFFORT_LEVELS)[number]; | ||
|
|
||
| export function isThinkingEffortLevel(value: string): value is ThinkingEffortLevel { | ||
| return (THINKING_EFFORT_LEVELS as readonly string[]).includes(value); | ||
| } | ||
|
|
||
| const EFFORT_OPTIONS: readonly ChoiceOption[] = [ | ||
| { value: 'off', label: 'Off', description: 'Disable extended thinking — respond directly.' }, | ||
| { value: 'low', label: 'Low', description: 'Brief reasoning before responding.' }, | ||
| { value: 'medium', label: 'Medium', description: 'Moderate reasoning for everyday tasks.' }, | ||
| { value: 'high', label: 'High', description: 'Thorough reasoning. Recommended default.' }, | ||
| { | ||
| value: 'xhigh', | ||
| label: 'Extra high', | ||
| description: 'Extended reasoning. Provider/model-specific; clamps to high when unsupported.', | ||
| }, | ||
| { | ||
| value: 'max', | ||
| label: 'Max', | ||
| description: 'Maximum reasoning. Provider/model-specific; clamps to high when unsupported.', | ||
| }, | ||
| ]; | ||
|
|
||
| export interface EffortSelectorOptions { | ||
| readonly currentValue: string; | ||
| readonly colors: ColorPalette; | ||
| readonly onSelect: (level: ThinkingEffortLevel) => void; | ||
| readonly onCancel: () => void; | ||
| } | ||
|
|
||
| export class EffortSelectorComponent extends ChoicePickerComponent { | ||
| constructor(opts: EffortSelectorOptions) { | ||
| super({ | ||
| title: 'Set thinking effort', | ||
| options: [...EFFORT_OPTIONS], | ||
| currentValue: opts.currentValue, | ||
| colors: opts.colors, | ||
| onSelect: (value) => { | ||
| if (isThinkingEffortLevel(value)) opts.onSelect(value); | ||
| }, | ||
| onCancel: opts.onCancel, | ||
| }); | ||
| } | ||
| } |
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.
For aliases whose capabilities do not include
thinking/always_thinkingand do not setadaptiveThinking, the existing/modelselector forces thinking off, but this command accepts any non-offeffort for the same model. That lets users enable reasoning parameters on a model the app already knows is unsupported, so the next prompt can fail at the provider instead of being rejected in the TUI. Please reuse the model capability check before applying non-off effort levels.Useful? React with 👍 / 👎.