Unify reasoning effort strings for agent host (#321674)

This commit is contained in:
Paul
2026-06-16 16:41:29 -07:00
committed by GitHub
parent e364243a0d
commit cb3e67df04
7 changed files with 95 additions and 35 deletions
@@ -48,6 +48,24 @@ export function getReasoningEffortDescription(level: string): string {
}
}
/**
* Returns the localized, title-cased picker label for a given
* reasoning-effort `level`. Centralised here so every provider surfaces the
* same wording. Falls back to capitalizing an unknown value.
*/
export function getReasoningEffortLabel(level: string): string {
switch (level) {
case 'none': return l10n.t('None');
case 'minimal': return l10n.t('Minimal');
case 'low': return l10n.t('Low');
case 'medium': return l10n.t('Medium');
case 'high': return l10n.t('High');
case 'xhigh': return l10n.t('Extra High');
case 'max': return l10n.t('Max');
default: return level.charAt(0).toUpperCase() + level.slice(1);
}
}
/**
* Builds the `reasoningEffort` property descriptor for a model's
* {@link LanguageModelConfigurationSchema}. Centralises the default-selection
@@ -59,7 +77,7 @@ export function buildReasoningEffortSchemaProperty(effortLevels: readonly string
type: 'string',
title: l10n.t('Thinking Effort'),
enum: effortLevels,
enumItemLabels: effortLevels.map(level => level.charAt(0).toUpperCase() + level.slice(1)),
enumItemLabels: effortLevels.map(getReasoningEffortLabel),
enumDescriptions: effortLevels.map(getReasoningEffortDescription),
default: pickDefaultReasoningEffort(effortLevels, family),
group: 'navigation',
@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { localize } from '../../../nls.js';
import { getReasoningEffortDescription, getReasoningEffortLabel } from './reasoningEffort.js';
import type { ConfigSchema, ModelSelection } from './state/protocol/state.js';
/**
@@ -80,16 +81,6 @@ export function isClaudeEffortLevel(value: string): value is ClaudeEffortLevel {
return (CLAUDE_EFFORT_LEVELS as readonly string[]).includes(value);
}
function labelForClaudeEffort(level: ClaudeEffortLevel): string {
switch (level) {
case 'low': return localize('claude.modelThinkingLevel.low', "Low");
case 'medium': return localize('claude.modelThinkingLevel.medium', "Medium");
case 'high': return localize('claude.modelThinkingLevel.high', "High");
case 'xhigh': return localize('claude.modelThinkingLevel.xhigh', "Extra High");
case 'max': return localize('claude.modelThinkingLevel.max', "Max");
}
}
/**
* Synthesize the per-model `configSchema` advertising the `thinkingLevel`
* picker entry on Claude models that support adaptive thinking. Mirror of
@@ -124,7 +115,8 @@ export function createClaudeThinkingLevelSchema(supportedEfforts: readonly Claud
title: localize('claude.modelThinkingLevel.title', "Thinking Level"),
description: localize('claude.modelThinkingLevel.description', "Controls how much reasoning effort Claude uses."),
enum: [...supportedEfforts],
enumLabels: supportedEfforts.map(labelForClaudeEffort),
enumLabels: supportedEfforts.map(getReasoningEffortLabel),
enumDescriptions: supportedEfforts.map(effort => getReasoningEffortDescription(effort) ?? ''),
...(defaultEffort !== undefined ? { default: defaultEffort } : {}),
},
},
@@ -0,0 +1,58 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { localize } from '../../../nls.js';
/**
* Union of every reasoning-effort / thinking-level value surfaced by any
* agent-host provider. Individual providers expose a subset:
* - Codex: `'minimal' | 'low' | 'medium' | 'high'`
* - Copilot / Claude: `'low' | 'medium' | 'high' | 'xhigh' | 'max'`
*
* The label/description helpers below are the single source of truth for
* the localized picker strings so every provider renders the same value
* consistently.
*/
export type ReasoningEffortLevel = 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
/**
* Localized, title-cased picker label for a reasoning-effort value.
* Falls back to capitalizing an unrecognized value so a newly-introduced
* effort tier never surfaces raw (e.g. lowercase `'max'`).
*/
export function getReasoningEffortLabel(level: string): string {
switch (level) {
case 'none': return localize('reasoningEffort.none', "None");
case 'minimal': return localize('reasoningEffort.minimal', "Minimal");
case 'low': return localize('reasoningEffort.low', "Low");
case 'medium': return localize('reasoningEffort.medium', "Medium");
case 'high': return localize('reasoningEffort.high', "High");
case 'xhigh': return localize('reasoningEffort.xhigh', "Extra High");
case 'max': return localize('reasoningEffort.max', "Max");
default: return level.charAt(0).toUpperCase() + level.slice(1);
}
}
/**
* Localized description for a reasoning-effort value, shown beneath the
* label in the picker. Returns `undefined` for an unrecognized value so
* callers can omit the description rather than show an empty string.
*
* Wording mirrors the canonical extension helper `getReasoningEffortDescription`
* in `extensions/copilot/src/extension/conversation/common/languageModelAccess.ts`
* so every provider surfaces the same descriptions.
*/
export function getReasoningEffortDescription(level: string): string | undefined {
switch (level) {
case 'none': return localize('reasoningEffort.noneDescription', "No reasoning applied");
case 'minimal': return localize('reasoningEffort.minimalDescription', "Minimal reasoning for fastest responses");
case 'low': return localize('reasoningEffort.lowDescription', "Faster responses with less reasoning");
case 'medium': return localize('reasoningEffort.mediumDescription', "Balanced reasoning and speed");
case 'high': return localize('reasoningEffort.highDescription', "Greater reasoning depth but slower");
case 'xhigh': return localize('reasoningEffort.xhighDescription', "Highest reasoning depth but slowest");
case 'max': return localize('reasoningEffort.maxDescription', "Absolute maximum capability with no constraints");
default: return undefined;
}
}
@@ -16,6 +16,7 @@ import { IInstantiationService } from '../../../instantiation/common/instantiati
import { localize } from '../../../../nls.js';
import { ILogService } from '../../../log/common/log.js';
import { createSchema, platformSessionSchema, schemaProperty } from '../../common/agentHostSchema.js';
import { getReasoningEffortDescription, getReasoningEffortLabel } from '../../common/reasoningEffort.js';
import { AgentHostCodexAgentBinaryArgsEnvVar, AgentHostCodexAgentCodexHomeEnvVar, AgentHostCodexAgentSdkRootEnvVar, AgentSession, AgentSignal, GITHUB_COPILOT_PROTECTED_RESOURCE, IAgent, IAgentCreateSessionConfig, IAgentCreateSessionResult, IAgentDescriptor, IAgentMaterializeSessionEvent, IAgentModelInfo, IAgentResolveSessionConfigParams, IAgentSessionConfigCompletionsParams, IAgentSessionMetadata, type AgentProvider } from '../../common/agentService.js';
import { SessionConfigKey } from '../../common/sessionConfigKeys.js';
import { AHP_AUTH_REQUIRED, ProtocolError } from '../../common/state/sessionProtocol.js';
@@ -122,12 +123,8 @@ const codexSessionConfigSchema = createSchema({
title: localize('codex.sessionConfig.modelReasoningEffort', "Reasoning Effort"),
description: localize('codex.sessionConfig.modelReasoningEffortDescription', "Controls how much reasoning effort Codex uses."),
enum: [...CODEX_REASONING_EFFORTS],
enumLabels: [
localize('codex.sessionConfig.modelReasoningEffort.minimal', "Minimal"),
localize('codex.sessionConfig.modelReasoningEffort.low', "Low"),
localize('codex.sessionConfig.modelReasoningEffort.medium', "Medium"),
localize('codex.sessionConfig.modelReasoningEffort.high', "High"),
],
enumLabels: CODEX_REASONING_EFFORTS.map(getReasoningEffortLabel),
enumDescriptions: CODEX_REASONING_EFFORTS.map(effort => getReasoningEffortDescription(effort) ?? ''),
default: 'medium',
sessionMutable: true,
}),
@@ -404,12 +401,8 @@ export class CodexAgent extends Disposable implements IAgent {
description: localize('codex.modelThinkingLevel.description', "Controls how much reasoning effort Codex uses."),
default: 'medium',
enum: [...CODEX_REASONING_EFFORTS],
enumLabels: [
localize('codex.modelThinkingLevel.minimal', "Minimal"),
localize('codex.modelThinkingLevel.low', "Low"),
localize('codex.modelThinkingLevel.medium', "Medium"),
localize('codex.modelThinkingLevel.high', "High"),
],
enumLabels: CODEX_REASONING_EFFORTS.map(getReasoningEffortLabel),
enumDescriptions: CODEX_REASONING_EFFORTS.map(effort => getReasoningEffortDescription(effort) ?? ''),
},
},
};
@@ -34,6 +34,7 @@ import { AgentHostSessionSyncEnabledConfigKey, AutoApproveLevel, ISchemaProperty
import { IAgentPluginManager, ISyncedCustomization } from '../../common/agentPluginManager.js';
import { AgentSession, AgentSignal, GITHUB_COPILOT_PROTECTED_RESOURCE, IAgent, IAgentCreateSessionConfig, IAgentCreateSessionResult, IAgentDescriptor, IAgentMaterializeSessionEvent, IAgentModelInfo, IAgentResolveSessionConfigParams, IAgentSessionConfigCompletionsParams, IAgentSessionMetadata, IAgentSessionProjectInfo, IMcpNotification } from '../../common/agentService.js';
import { getEffectiveAgents } from '../../common/customAgents.js';
import { getReasoningEffortDescription, getReasoningEffortLabel } from '../../common/reasoningEffort.js';
import type { IAgentServerToolHost } from '../../common/agentServerTools.js';
import { IAgentHostOTelService } from '../../common/otel/agentHostOTelService.js';
import { SessionConfigKey } from '../../common/sessionConfigKeys.js';
@@ -674,23 +675,14 @@ export class CopilotAgent extends Disposable implements IAgent {
return undefined;
}
const enumLabels = supportedReasoningEfforts.map(value => {
switch (value) {
case 'low': return localize('copilot.modelThinkingLevel.low', "Low");
case 'medium': return localize('copilot.modelThinkingLevel.medium', "Medium");
case 'high': return localize('copilot.modelThinkingLevel.high', "High");
case 'xhigh': return localize('copilot.modelThinkingLevel.xhigh', "Extra High");
default: return value;
}
});
return {
type: 'string',
title: localize('copilot.modelThinkingLevel.title', "Thinking Level"),
description: localize('copilot.modelThinkingLevel.description', "Controls how much reasoning effort the model uses."),
default: defaultReasoningEffort,
enum: [...supportedReasoningEfforts],
enumLabels,
enumLabels: supportedReasoningEfforts.map(getReasoningEffortLabel),
enumDescriptions: supportedReasoningEfforts.map(value => getReasoningEffortDescription(value) ?? ''),
};
}
@@ -109,6 +109,7 @@ suite('createClaudeThinkingLevelSchema (Phase 6.1 / Cycle D3)', () => {
description: 'Controls how much reasoning effort Claude uses.',
enum: ['low', 'medium', 'high', 'xhigh', 'max'],
enumLabels: ['Low', 'Medium', 'High', 'Extra High', 'Max'],
enumDescriptions: ['Faster responses with less reasoning', 'Balanced reasoning and speed', 'Greater reasoning depth but slower', 'Highest reasoning depth but slowest', 'Absolute maximum capability with no constraints'],
default: 'high',
},
},
@@ -122,6 +123,7 @@ suite('createClaudeThinkingLevelSchema (Phase 6.1 / Cycle D3)', () => {
description: 'Controls how much reasoning effort Claude uses.',
enum: ['low', 'medium', 'high'],
enumLabels: ['Low', 'Medium', 'High'],
enumDescriptions: ['Faster responses with less reasoning', 'Balanced reasoning and speed', 'Greater reasoning depth but slower'],
default: 'high',
},
},
@@ -135,6 +137,7 @@ suite('createClaudeThinkingLevelSchema (Phase 6.1 / Cycle D3)', () => {
description: 'Controls how much reasoning effort Claude uses.',
enum: ['high'],
enumLabels: ['High'],
enumDescriptions: ['Greater reasoning depth but slower'],
default: 'high',
},
},
@@ -148,6 +151,7 @@ suite('createClaudeThinkingLevelSchema (Phase 6.1 / Cycle D3)', () => {
description: 'Controls how much reasoning effort Claude uses.',
enum: ['max', 'low'],
enumLabels: ['Max', 'Low'],
enumDescriptions: ['Absolute maximum capability with no constraints', 'Faster responses with less reasoning'],
},
},
},
@@ -821,6 +821,7 @@ suite('ClaudeAgent', () => {
description: 'Controls how much reasoning effort Claude uses.',
enum: ['low', 'medium', 'high'],
enumLabels: ['Low', 'Medium', 'High'],
enumDescriptions: ['Faster responses with less reasoning', 'Balanced reasoning and speed', 'Greater reasoning depth but slower'],
default: 'high',
},
},
@@ -834,6 +835,7 @@ suite('ClaudeAgent', () => {
description: 'Controls how much reasoning effort Claude uses.',
enum: ['high'],
enumLabels: ['High'],
enumDescriptions: ['Greater reasoning depth but slower'],
default: 'high',
},
},
@@ -848,6 +850,7 @@ suite('ClaudeAgent', () => {
description: 'Controls how much reasoning effort Claude uses.',
enum: ['low', 'high'],
enumLabels: ['Low', 'High'],
enumDescriptions: ['Faster responses with less reasoning', 'Greater reasoning depth but slower'],
default: 'high',
},
},