Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 0 additions & 20 deletions extensions/copilot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4406,17 +4406,6 @@
"advanced"
]
},
"github.copilot.chat.autoModeTierOverride": {
"type": [
"string",
"null"
],
"default": null,
"markdownDescription": "Overrides the routing tier that the `Auto` model requests, ignoring both the tier picked in the model picker and the tier inline chat defaults to. Accepts `efficiency`, `balance`, `intelligence`, or `fast`. Used by evals.\n\n**Note**: This is an advanced debugging setting.",
"tags": [
"advanced"
]
},
"github.copilot.chat.anthropic.promptCaching.extendedTtl": {
"type": "boolean",
"default": false,
Expand Down Expand Up @@ -5089,15 +5078,6 @@
"advanced"
]
},
"github.copilot.chat.autoMode.tiers.enabled": {
"type": "boolean",
"default": false,
"markdownDescription": "%github.copilot.config.chat.autoMode.tiers.enabled%",
"tags": [
"advanced",
"onExp"
]
},
"github.copilot.chat.agent.modelDetails.enabled": {
"type": "boolean",
"default": true,
Expand Down
1 change: 0 additions & 1 deletion extensions/copilot/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,6 @@
"github.copilot.config.cli.forkSessions.enabled": "Enable forking sessions in Copilot CLI.",
"github.copilot.config.cli.planExitMode.enabled": "Enable Plan Mode exit handling in Copilot CLI.",
"github.copilot.config.cli.autoModel.enabled": "Enable the Auto model option in Copilot CLI, which automatically selects the best model for each request. Requires VS Code reload.",
"github.copilot.config.chat.autoMode.tiers.enabled": "Choose a routing tier for the Auto model, biasing model selection toward cost, capability, or speed. When disabled, the service picks the routing profile.",
"github.copilot.config.chat.agent.modelDetails.enabled": "Show model details (model name and request multiplier) on Copilot CLI agent chat responses. Requires VS Code reload to update already loaded sessions.",
"github.copilot.config.cli.planCommand.enabled": "Enable the /plan command in Copilot CLI to create implementation plans before coding.",
"github.copilot.config.cli.lazyLoadSessionItem.enabled": "Enable lazy loading of session items in Copilot CLI. Requires VS Code reload.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -698,11 +698,7 @@ export const XTabProviderId = 'XtabProvider';

export namespace ConfigKey {

/**
* These settings are defined in the completions extensions and shared.
*
* We should not change the names of these settings without coordinating with Completions extension.
*/
/** Settings owned by VS Code or the completions extension; coordinate identifier changes with their owner. */
export namespace Shared {
/** Allows for overriding the base domain we use for making requests to the CAPI. This helps CAPI devs develop against a local instance. */
export const DebugOverrideProxyUrl = defineSetting<string | undefined>('advanced.debug.overrideProxyUrl', ConfigType.Simple, undefined, undefined, { userScopeOnly: true });
Expand All @@ -715,6 +711,10 @@ export namespace ConfigKey {
export const DebugNodeFetchCache = defineSetting<'off' | 'memory' | 'persistent'>('advanced.debug.nodeFetchCache', ConfigType.Simple, 'memory');
export const AuthProvider = defineSetting<AuthProviderId>('advanced.authProvider', ConfigType.Simple, AuthProviderId.GitHub);
export const AuthPermissions = defineSetting<AuthPermissionMode>('advanced.authPermissions', ConfigType.Simple, AuthPermissionMode.Default);
/** The workbench owns the experiment default so all Auto pickers read the same effective setting. */
export const AutoModeTiersEnabled = defineSetting<boolean>('chat.autoMode.tiers.enabled', ConfigType.Simple, false);
/** Override Auto's "Optimize for" preference in both harnesses; the extension additionally accepts `fast`. */
export const AutoModeTierOverride = defineSetting<string | null>('chat.autoModeTierOverride', ConfigType.Simple, null);
}

/**
Expand Down Expand Up @@ -746,11 +746,6 @@ export namespace ConfigKey {
export const OmitBaseAgentInstructions = defineAndMigrateSetting<boolean>('chat.advanced.omitBaseAgentInstructions', 'chat.omitBaseAgentInstructions', false);
export const CLIPlanExitModeEnabled = defineSetting<boolean>('chat.cli.planExitMode.enabled', ConfigType.Simple, true);
export const CLIAutoModelEnabled = defineSetting<boolean>('chat.cli.autoModel.enabled', ConfigType.Simple, true);
/**
* Offer routing tiers on the Auto model. Off by default: while disabled
* no tier is sent and the server picks its own routing profile.
*/
export const AutoModeTiersEnabled = defineSetting<boolean>('chat.autoMode.tiers.enabled', ConfigType.ExperimentBased, false, undefined, undefined, { experimentName: 'copilotchat.autoModeTiersEnabled' });
export const CLIModelDetailsEnabled = defineSetting<boolean>('chat.agent.modelDetails.enabled', ConfigType.Simple, true);
export const CLIPlanCommandEnabled = defineSetting<boolean>('chat.cli.planCommand.enabled', ConfigType.Simple, true);
export const CLIChatLazyLoadSessionItem = defineSetting<boolean>('chat.cli.lazyLoadSessionItem.enabled', ConfigType.Simple, true);
Expand Down Expand Up @@ -871,13 +866,6 @@ export namespace ConfigKey {
export const ReasoningEffortOverride = defineSetting<string | null>('chat.reasoningEffortOverride', ConfigType.Simple, null);
export const ChatCompletionsTokenParameter = defineSetting('chat.chatCompletionsTokenParameter', ConfigType.ExperimentBased, 'max_tokens', vEnum('max_completion_tokens', 'max_tokens'));

/**
* Internal: override the routing tier sent to `POST /auto`, ignoring both the
* model picker and the tier inline chat defaults to. Unlike the picker this
* accepts `fast`, so evals can exercise every profile.
*/
export const AutoModeTierOverride = defineSetting<string | null>('chat.autoModeTierOverride', ConfigType.Simple, null);

/**
* When enabled, periodic keep-alive probes are sent during long-running tool calls
* to keep the server-side prompt cache warm.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export type AutoModeTier = typeof autoModeTiers[number];
* The tiers offered in the model picker. `fast` is excluded: it is the profile
* inline chat falls back to when the user has not picked a tier, and is not
* offered as a choice. It remains reachable through the internal
* {@link ConfigKey.Advanced.AutoModeTierOverride} setting.
* {@link ConfigKey.Shared.AutoModeTierOverride} setting.
*/
export const selectableAutoModeTiers: readonly AutoModeTier[] = ['efficiency', 'balance', 'intelligence'];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { ILogService } from '../../log/common/logService';
import { isAbortError } from '../../networking/common/fetcherService';
import { IChatEndpoint } from '../../networking/common/networking';
import { IRequestLogger } from '../../requestLogger/common/requestLogger';
import { IExperimentationService } from '../../telemetry/common/nullExperimentationService';
import { ITelemetryService } from '../../telemetry/common/telemetry';
import { AUTO_MODE_TIER_PROPERTY, autoModeTiers, defaultAutoModeTier, inlineChatAutoModeTier, isSelectableAutoModeTier, normalizeAutoModeTier, type AutoModeTier } from '../common/autoModeTiers';
import { ICAPIClientService } from '../common/capiClient';
Expand Down Expand Up @@ -162,7 +161,6 @@ export class AutomodeService extends Disposable implements IAutomodeService {
@IAuthenticationService private readonly _authService: IAuthenticationService,
@ILogService private readonly _logService: ILogService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IExperimentationService private readonly _expService: IExperimentationService,
@ITelemetryService private readonly _telemetryService: ITelemetryService,
@IRequestLogger private readonly _requestLogger: IRequestLogger,
@IConfigurationService private readonly _configurationService: IConfigurationService,
Expand Down Expand Up @@ -364,7 +362,7 @@ export class AutomodeService extends Disposable implements IAutomodeService {
}

areAutoModeTiersSupported(): boolean {
return this._configurationService.getExperimentBasedConfig(ConfigKey.Advanced.AutoModeTiersEnabled, this._expService);
return this._configurationService.getConfig(ConfigKey.Shared.AutoModeTiersEnabled);
}

/**
Expand Down Expand Up @@ -402,7 +400,7 @@ export class AutomodeService extends Disposable implements IAutomodeService {
* pin below unreachable.
*/
private _resolveTier(chatRequest: IAutoModeRoutingRequest | undefined): AutoModeTier | undefined {
const override = this._configurationService.getConfig(ConfigKey.Advanced.AutoModeTierOverride);
const override = this._configurationService.getConfig(ConfigKey.Shared.AutoModeTierOverride);
if (override) {
const normalized = normalizeAutoModeTier(override);
// The override is internal, so unlike the picker it may select `fast`.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import { createPngBytes } from '../../../image/common/test/testImageData';
import { ILogService } from '../../../log/common/logService';
import { IChatEndpoint } from '../../../networking/common/networking';
import { NullRequestLogger } from '../../../requestLogger/node/nullRequestLogger';
import { IExperimentationService, NullExperimentationService } from '../../../telemetry/common/nullExperimentationService';
import { ITelemetryService } from '../../../telemetry/common/telemetry';
import { defaultAutoModeTier } from '../../common/autoModeTiers';
import { ICAPIClientService } from '../../common/capiClient';
Expand All @@ -37,7 +36,6 @@ describe('AutomodeService', () => {
let mockAuthService: IAuthenticationService;
let mockLogService: ILogService;
let mockInstantiationService: IInstantiationService;
let mockExpService: IExperimentationService;
let mockChatEndpoint: IChatEndpoint;
let configurationService: IConfigurationService;
let onDidAuthenticationChangeEmitter: Emitter<void>;
Expand Down Expand Up @@ -66,7 +64,6 @@ describe('AutomodeService', () => {
mockAuthService,
mockLogService,
mockInstantiationService,
mockExpService,
mockTelemetryService,
new NullRequestLogger(),
configurationService
Expand All @@ -79,11 +76,11 @@ describe('AutomodeService', () => {

/** Tiers are experiment-gated and off by default, so tier tests opt in. */
function enableTiers(): void {
configure(new Map<BaseConfig<unknown>, unknown>([[ConfigKey.Advanced.AutoModeTiersEnabled, true]]));
configure(new Map<BaseConfig<unknown>, unknown>([[ConfigKey.Shared.AutoModeTiersEnabled, true]]));
}

function setTierOverride(override: string): void {
configure(new Map<BaseConfig<unknown>, unknown>([[ConfigKey.Advanced.AutoModeTierOverride, override]]));
configure(new Map<BaseConfig<unknown>, unknown>([[ConfigKey.Shared.AutoModeTierOverride, override]]));
}

function makeAutoResponse(body: unknown, status = 200) {
Expand Down Expand Up @@ -158,7 +155,6 @@ describe('AutomodeService', () => {
)
} as unknown as IInstantiationService;

mockExpService = new NullExperimentationService();
configure();
mockTelemetryService = {
sendTelemetryEvent: vi.fn(),
Expand Down Expand Up @@ -733,8 +729,8 @@ describe('AutomodeService', () => {
mockAuto(autoResponse('gpt-4o'));

configure(new Map<BaseConfig<unknown>, unknown>([
[ConfigKey.Advanced.AutoModeTiersEnabled, true],
[ConfigKey.Advanced.AutoModeTierOverride, 'turbo'],
[ConfigKey.Shared.AutoModeTiersEnabled, true],
[ConfigKey.Shared.AutoModeTierOverride, 'turbo'],
]));
automodeService = createService();
await automodeService.resolveAutoModeEndpoint({
Expand All @@ -747,18 +743,24 @@ describe('AutomodeService', () => {
expect(autoRequestBodies()).toEqual([{ prompt: 'panel turn', tier: 'intelligence' }]);
});

it('announces tier support when the setting changes', async () => {
it('announces tier support from the effective setting, not a separate experiment lookup', async () => {
const getExperimentBasedConfig = vi.spyOn(configurationService, 'getExperimentBasedConfig');
automodeService = createService();
expect(automodeService.areAutoModeTiersSupported()).toBe(false);

let announced = 0;
const listener = automodeService.onDidChangeAutoModeTierSupport(() => announced++);
await configurationService.setConfig(ConfigKey.Advanced.AutoModeTiersEnabled, true);
await configurationService.setConfig(ConfigKey.Shared.AutoModeTiersEnabled, true);
// An unrelated change must not re-announce.
await configurationService.setConfig(ConfigKey.Advanced.AutoModeTierOverride, 'intelligence');
await configurationService.setConfig(ConfigKey.Shared.AutoModeTierOverride, 'intelligence');
await configurationService.setConfig(ConfigKey.Shared.AutoModeTiersEnabled, false);
listener.dispose();

expect({ announced, supported: automodeService.areAutoModeTiersSupported() }).toEqual({ announced: 1, supported: true });
expect({
announced,
supported: automodeService.areAutoModeTiersSupported(),
experimentLookups: getExperimentBasedConfig.mock.calls.length,
}).toEqual({ announced: 2, supported: false, experimentLookups: 0 });
});

it('does not reuse a cached endpoint from a different tier when /auto fails', async () => {
Expand Down
10 changes: 10 additions & 0 deletions src/vs/platform/agentHost/common/autoModeTiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,16 @@ export function isAutoModeTier(value: unknown): value is AutoModeTier {
return autoModeTiers.some(tier => tier === value);
}

/** Maps retired picker and override values to the current runtime names, matching the Copilot extension. */
export function normalizeAutoModeTier(value: unknown): unknown {
switch (value) {
case 'eco': return 'efficiency';
case 'balanced': return 'balance';
case 'max': return 'intelligence';
default: return value;
}
}

/**
* Localized picker label for a routing profile, capitalizing an unrecognized value so a new profile
* never surfaces raw. Wording matches the extension's `getAutoModeTierLabel`, which cannot be imported here.
Expand Down
31 changes: 16 additions & 15 deletions src/vs/platform/agentHost/common/copilotCliConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,10 @@ export const enum CopilotCliConfigKey {
ReasoningSummary = 'reasoningSummary',
/** Let the Auto router score prior turns instead of the latest message alone. Off by default. */
MultiTurnContextRouting = 'multiTurnContextRouting',
/**
* Offer the Auto model's routing-profile ("Optimize for") picker. Off by default. Separate from
* the extension's gate, which tracks CAPI readiness rather than the bundled runtime's support.
*/
/** Offer the Auto model's "Optimize for" picker. Shares the Copilot extension's setting and experiment. */
AutoModeTiers = 'autoModeTiers',
/** Override Auto's "Optimize for" preference, even when the picker is disabled. */
AutoModeTierOverride = 'autoModeTierOverride',
/** Tell the model to keep subagents on their default model unless the user asks otherwise. Off by default. */
SubagentModelGuidance = 'subagentModelGuidance',
/** Per-model capability overrides (family aliases) keyed by model id. */
Expand All @@ -47,10 +46,7 @@ export const enum CopilotCliConfigKey {

export const CopilotCliVSCodeAssignmentContextKey = 'copilotCliVSCodeAssignmentContext';

// VS Code `chat.agentHost.*` / `chat.copilot.*` setting IDs that feed the root-config
// keys above, kept beside the keys they forward to. Registered in `chat.shared.contribution.ts`
// and forwarded into the host's root config by `AgentHostCopilotCliSettingsContribution`
// (and, for the terminal-tool toggle, `AgentHostTerminalContribution`).
// Client setting IDs forwarded into the matching provider-owned root-config keys.

export const AgentHostCustomTerminalToolEnabledSettingId = 'chat.agentHost.customTerminalTool.enabled';

Expand All @@ -71,12 +67,11 @@ export const AgentHostReasoningSummaryEnabledSettingId = 'chat.agentHost.copilot

export const AgentHostMultiTurnContextRoutingEnabledSettingId = 'chat.agentHost.copilot.multiTurnContextRouting.enabled';

export const AgentHostAutoModeTiersEnabledSettingId = 'chat.agentHost.copilot.autoModeTiers.enabled';
export const CopilotAutoModeTiersEnabledSettingId = 'github.copilot.chat.autoMode.tiers.enabled';

/**
* ExP treatment gating the Auto routing-profile picker. The Copilot extension gates its own picker
* on this same name, so one assignment turns tiers on for both harnesses.
*/
export const CopilotAutoModeTierOverrideSettingId = 'github.copilot.chat.autoModeTierOverride';

/** Applied to the shared setting's default by the workbench configuration service. */
export const AutoModeTiersExperimentName = 'copilotchat.autoModeTiersEnabled';

export const CopilotSubagentModelGuidanceEnabledSettingId = 'chat.copilot.subagentModelGuidance.enabled';
Expand Down Expand Up @@ -208,10 +203,16 @@ export const copilotCliConfigSchema = createSchema({
}),
[CopilotCliConfigKey.AutoModeTiers]: schemaProperty<boolean>({
type: 'boolean',
title: localize('agentHost.config.autoModeTiers.title', "Auto Routing Profiles"),
description: localize('agentHost.config.autoModeTiers.description', "When enabled, the Auto model offers an \"Optimize for\" picker that biases routing toward efficiency, balance, or intelligence. The profile is chosen before a session starts and applies for its lifetime."),
title: localize('agentHost.config.autoModeTiers.title', "Auto Optimize for"),
description: localize('agentHost.config.autoModeTiers.description', "When enabled, the Auto model offers an \"Optimize for\" picker with Efficiency, Balance, and Intelligence options. You can change the preference during a session. When disabled, the service chooses how to route unless an override is configured."),
default: false,
}),
[CopilotCliConfigKey.AutoModeTierOverride]: schemaProperty<string>({
type: 'string',
title: localize('agentHost.config.autoModeTierOverride.title', "Auto Optimize for Override"),
description: localize('agentHost.config.autoModeTierOverride.description', "Overrides Auto's \"Optimize for\" preference, even when the picker is disabled. Accepts efficiency, balance, or intelligence. Applied when a session is created or resumed and when its model changes. Empty or unsupported values use the picker or service defaults."),
default: '',
}),
[CopilotCliConfigKey.SubagentModelGuidance]: schemaProperty<boolean>({
type: 'boolean',
title: localize('agentHost.config.subagentModelGuidance.title', "Subagent Model Guidance"),
Expand Down
Loading