Revert "Add context size picker for Claude Agent models" (#320614)

Revert "Add context size picker for Claude Agent models (#320166)"

This reverts commit 99d6bf7676.
This commit is contained in:
Tyler James Leonhardt
2026-06-09 11:07:37 -07:00
committed by GitHub
parent 055061c84e
commit 5c9d3ecb5c
7 changed files with 27 additions and 268 deletions
@@ -24,7 +24,6 @@ export interface SessionState {
folderInfo: ClaudeFolderInfo | undefined;
usageHandler: UsageHandler | undefined;
reasoningEffort: EffortLevel | undefined;
contextSize: number | undefined;
traceContext: TraceContext | undefined;
turnId: string | undefined;
}
@@ -107,18 +106,6 @@ export interface IClaudeSessionStateService {
*/
setReasoningEffortForSession(sessionId: string, effort: EffortLevel | undefined): void;
/**
* Gets the context size for a session (user's per-request selection from the model picker).
* When set, the proxy reports this value as the endpoint's modelMaxPromptTokens so internal
* accounting reflects the chosen tier.
*/
getContextSizeForSession(sessionId: string): number | undefined;
/**
* Sets the context size for a session.
*/
setContextSizeForSession(sessionId: string, contextSize: number | undefined): void;
/**
* Gets the OTel trace context for a session (used to parent chat spans to invoke_agent).
*/
@@ -8,7 +8,7 @@ import type * as vscode from 'vscode';
import { IEndpointProvider } from '../../../../platform/endpoint/common/endpointProvider';
import { ILogService } from '../../../../platform/log/common/logService';
import { IChatEndpoint } from '../../../../platform/networking/common/networking';
import { formatPricingLabel, formatTokenCount, getModelCapabilitiesDescription } from '../../../conversation/common/languageModelAccess';
import { formatPricingLabel, getModelCapabilitiesDescription } from '../../../conversation/common/languageModelAccess';
import { createServiceIdentifier } from '../../../../util/common/services';
import { Emitter } from '../../../../util/vs/base/common/event';
import { Disposable } from '../../../../util/vs/base/common/lifecycle';
@@ -17,7 +17,6 @@ import { tryParseClaudeModelId } from './claudeModelId';
import type { EffortLevel } from '@anthropic-ai/claude-agent-sdk';
export const CLAUDE_REASONING_EFFORT_PROPERTY = 'reasoningEffort';
export const CLAUDE_CONTEXT_SIZE_PROPERTY = 'contextSize';
export interface IClaudeCodeModels {
readonly _serviceBrand: undefined;
@@ -215,57 +214,33 @@ export function pickReasoningEffort(endpoint: IChatEndpoint | undefined, request
}
function buildConfigurationSchema(endpoint: IChatEndpoint): vscode.LanguageModelConfigurationSchema | undefined {
const properties: Record<string, NonNullable<vscode.LanguageModelConfigurationSchema['properties']>[string]> = {};
// Thinking effort
const effortLevels = endpoint.supportsReasoningEffort?.filter(
(level): level is typeof SUPPORTED_EFFORT_LEVELS[number] =>
(SUPPORTED_EFFORT_LEVELS as readonly string[]).includes(level)
);
if (effortLevels && effortLevels.length > 0) {
const defaultEffort = effortLevels.includes('high') ? 'high' : undefined;
properties[CLAUDE_REASONING_EFFORT_PROPERTY] = {
type: 'string',
title: l10n.t('Thinking Effort'),
enum: effortLevels,
enumItemLabels: effortLevels.map(level => level.charAt(0).toUpperCase() + level.slice(1)),
enumDescriptions: effortLevels.map(level => {
switch (level) {
case 'low': return l10n.t('Faster responses with less reasoning');
case 'medium': return l10n.t('Balanced reasoning and speed');
case 'high': return l10n.t('Greater reasoning depth but slower');
}
}),
default: defaultEffort,
group: 'navigation',
};
if (!effortLevels) {
return;
}
// Context size — only when CAPI provides a default context max, indicating
// a meaningful distinction between default and long context tiers.
const pricing = endpoint.tokenPricing;
const defaultContextMax = pricing?.default.contextMax;
const fullMax = endpoint.modelMaxPromptTokens;
if (defaultContextMax && defaultContextMax < fullMax) {
const hasLongContextSurcharge = !!pricing?.longContext;
properties[CLAUDE_CONTEXT_SIZE_PROPERTY] = {
type: 'number',
title: l10n.t('Context Size'),
enum: [defaultContextMax, fullMax],
enumItemLabels: [formatTokenCount(defaultContextMax), formatTokenCount(fullMax)],
enumDescriptions: [
l10n.t('Default'),
hasLongContextSurcharge
? l10n.t('Longer sessions')
: l10n.t('Longer sessions without compaction'),
],
default: defaultContextMax,
group: 'tokens',
};
}
const defaultEffort = effortLevels.includes('high') ? 'high' : undefined;
if (Object.keys(properties).length === 0) {
return undefined;
}
return { properties };
return {
properties: {
[CLAUDE_REASONING_EFFORT_PROPERTY]: {
type: 'string',
title: l10n.t('Thinking Effort'),
enum: effortLevels,
enumItemLabels: effortLevels.map(level => level.charAt(0).toUpperCase() + level.slice(1)),
enumDescriptions: effortLevels.map(level => {
switch (level) {
case 'low': return l10n.t('Faster responses with less reasoning');
case 'medium': return l10n.t('Balanced reasoning and speed');
case 'high': return l10n.t('Greater reasoning depth but slower');
}
}),
default: defaultEffort,
group: 'navigation',
}
}
};
}
@@ -191,17 +191,6 @@ export class ClaudeLanguageModelServer extends Disposable {
tokenSource.cancel();
});
// If the user picked a context tier in the model picker, surface that
// value directly as the prompt token budget. CAPI's `tokenPricing.default.contextMax`
// is documented as a prompt-token limit (matching `endpoint.modelMaxPromptTokens`),
// and CAPI bills the tier based on actual prompt size, so no other signaling is
// required. Clamp to the default budget so the override never lowers it.
const sessionContextSize = sessionId ? this.sessionStateService.getContextSizeForSession(sessionId) : undefined;
const defaultPromptBudget = DEFAULT_MAX_TOKENS - DEFAULT_MAX_OUTPUT_TOKENS;
const modelMaxPromptTokens = sessionContextSize !== undefined
? Math.max(defaultPromptBudget, sessionContextSize)
: defaultPromptBudget;
const endpointRequestBody = requestBody as IEndpointBody;
const streamingEndpoint = this.instantiationService.createInstance(
ClaudeStreamingPassThroughEndpoint,
@@ -211,7 +200,7 @@ export class ClaudeLanguageModelServer extends Disposable {
headers,
'vscode_claude_code',
{
modelMaxPromptTokens,
modelMaxPromptTokens: DEFAULT_MAX_TOKENS - DEFAULT_MAX_OUTPUT_TOKENS,
maxOutputTokens: DEFAULT_MAX_OUTPUT_TOKENS
},
sessionId
@@ -47,7 +47,6 @@ export class ClaudeSessionStateService extends Disposable implements IClaudeSess
folderInfo: existing?.folderInfo,
usageHandler: existing?.usageHandler,
reasoningEffort: existing?.reasoningEffort,
contextSize: existing?.contextSize,
traceContext: existing?.traceContext,
turnId: existing?.turnId,
});
@@ -70,7 +69,6 @@ export class ClaudeSessionStateService extends Disposable implements IClaudeSess
folderInfo: existing?.folderInfo,
usageHandler: existing?.usageHandler,
reasoningEffort: existing?.reasoningEffort,
contextSize: existing?.contextSize,
traceContext: existing?.traceContext,
turnId: existing?.turnId,
});
@@ -90,7 +88,6 @@ export class ClaudeSessionStateService extends Disposable implements IClaudeSess
folderInfo: existing?.folderInfo,
usageHandler: existing?.usageHandler,
reasoningEffort: existing?.reasoningEffort,
contextSize: existing?.contextSize,
traceContext: existing?.traceContext,
turnId: existing?.turnId,
});
@@ -112,7 +109,6 @@ export class ClaudeSessionStateService extends Disposable implements IClaudeSess
folderInfo,
usageHandler: existing?.usageHandler,
reasoningEffort: existing?.reasoningEffort,
contextSize: existing?.contextSize,
traceContext: existing?.traceContext,
turnId: existing?.turnId,
});
@@ -132,7 +128,6 @@ export class ClaudeSessionStateService extends Disposable implements IClaudeSess
folderInfo: existing?.folderInfo,
usageHandler: handler,
reasoningEffort: existing?.reasoningEffort,
contextSize: existing?.contextSize,
traceContext: existing?.traceContext,
turnId: existing?.turnId,
});
@@ -154,29 +149,6 @@ export class ClaudeSessionStateService extends Disposable implements IClaudeSess
folderInfo: existing?.folderInfo,
usageHandler: existing?.usageHandler,
reasoningEffort: effort,
contextSize: existing?.contextSize,
traceContext: existing?.traceContext,
turnId: existing?.turnId,
});
}
getContextSizeForSession(sessionId: string): number | undefined {
return this._sessionState.get(sessionId)?.contextSize;
}
setContextSizeForSession(sessionId: string, contextSize: number | undefined): void {
const existing = this._sessionState.get(sessionId);
if (existing?.contextSize === contextSize) {
return;
}
this._sessionState.set(sessionId, {
modelId: existing?.modelId,
permissionMode: existing?.permissionMode ?? 'acceptEdits',
capturingToken: existing?.capturingToken,
folderInfo: existing?.folderInfo,
usageHandler: existing?.usageHandler,
reasoningEffort: existing?.reasoningEffort,
contextSize,
traceContext: existing?.traceContext,
turnId: existing?.turnId,
});
@@ -195,7 +167,6 @@ export class ClaudeSessionStateService extends Disposable implements IClaudeSess
folderInfo: existing?.folderInfo,
usageHandler: existing?.usageHandler,
reasoningEffort: existing?.reasoningEffort,
contextSize: existing?.contextSize,
traceContext,
turnId: existing?.turnId,
});
@@ -214,7 +185,6 @@ export class ClaudeSessionStateService extends Disposable implements IClaudeSess
folderInfo: existing?.folderInfo,
usageHandler: existing?.usageHandler,
reasoningEffort: existing?.reasoningEffort,
contextSize: existing?.contextSize,
traceContext: existing?.traceContext,
turnId,
});
@@ -6,7 +6,7 @@
import { afterEach, describe, expect, it } from 'vitest';
import type * as vscode from 'vscode';
import { IEndpointProvider } from '../../../../../platform/endpoint/common/endpointProvider';
import { IChatEndpoint, IChatEndpointTokenPricing } from '../../../../../platform/networking/common/networking';
import { IChatEndpoint } from '../../../../../platform/networking/common/networking';
import { Emitter } from '../../../../../util/vs/base/common/event';
import { DisposableStore } from '../../../../../util/vs/base/common/lifecycle';
import { IInstantiationService } from '../../../../../util/vs/platform/instantiation/common/instantiation';
@@ -26,8 +26,6 @@ function createMockEndpoint(overrides: {
apiType?: string;
modelProvider?: string;
supportsReasoningEffort?: string[];
tokenPricing?: IChatEndpointTokenPricing;
modelMaxPromptTokens?: number;
}): IChatEndpoint {
const isAnthropic = overrides.modelProvider === undefined || overrides.modelProvider === 'Anthropic';
return {
@@ -49,8 +47,7 @@ function createMockEndpoint(overrides: {
isFallback: false,
policy: 'enabled',
urlOrRequestMetadata: 'mock://endpoint',
modelMaxPromptTokens: overrides.modelMaxPromptTokens ?? 128000,
tokenPricing: overrides.tokenPricing,
modelMaxPromptTokens: 128000,
tokenizer: 'cl100k_base',
acquireTokenizer: () => ({ encode: () => [], free: () => { } }) as any,
processResponseFromChatEndpoint: () => Promise.resolve({} as any),
@@ -328,110 +325,6 @@ describe('ClaudeCodeModels', () => {
expect(schema.properties?.['reasoningEffort'].enum).toEqual(['high']);
expect(schema.properties!['reasoningEffort'].default).toBe('high');
});
it('includes contextSize when endpoint pricing exposes a default context max below modelMaxPromptTokens', async () => {
const { service } = createServiceWithRefreshableEndpoints([
createMockEndpoint({
model: 'claude-sonnet-4-model',
name: 'Claude Sonnet 4',
family: 'claude-sonnet-4',
modelMaxPromptTokens: 1_000_000,
tokenPricing: {
default: { inputPrice: 3, outputPrice: 15, cacheReadTokenPrice: 0.3, contextMax: 200_000 },
longContext: { inputPrice: 6, outputPrice: 22.5, cacheReadTokenPrice: 0.6 },
},
}),
]);
const { lm, getCapturedProvider } = createMockLm();
const info = await getProviderInfo(service, lm, getCapturedProvider);
const schema = info[0].configurationSchema!;
expect(schema.properties?.['contextSize']).toEqual({
type: 'number',
title: 'Context Size',
enum: [200_000, 1_000_000],
enumItemLabels: ['200K', '1M'],
enumDescriptions: ['Default', 'Longer sessions'],
default: 200_000,
group: 'tokens',
});
});
it('omits contextSize when pricing has no default context max', async () => {
const { service } = createServiceWithRefreshableEndpoints([
createMockEndpoint({
model: 'claude-sonnet-4-model',
name: 'Claude Sonnet 4',
family: 'claude-sonnet-4',
modelMaxPromptTokens: 1_000_000,
tokenPricing: {
default: { inputPrice: 3, outputPrice: 15, cacheReadTokenPrice: 0.3 },
},
}),
]);
const { lm, getCapturedProvider } = createMockLm();
const info = await getProviderInfo(service, lm, getCapturedProvider);
expect(info[0].configurationSchema).toBeUndefined();
});
it('omits contextSize when default context max equals modelMaxPromptTokens', async () => {
const { service } = createServiceWithRefreshableEndpoints([
createMockEndpoint({
model: 'claude-sonnet-4-model',
name: 'Claude Sonnet 4',
family: 'claude-sonnet-4',
modelMaxPromptTokens: 200_000,
tokenPricing: {
default: { inputPrice: 3, outputPrice: 15, cacheReadTokenPrice: 0.3, contextMax: 200_000 },
},
}),
]);
const { lm, getCapturedProvider } = createMockLm();
const info = await getProviderInfo(service, lm, getCapturedProvider);
expect(info[0].configurationSchema).toBeUndefined();
});
it('describes the long-context option without surcharge when there is no long-context pricing tier', async () => {
const { service } = createServiceWithRefreshableEndpoints([
createMockEndpoint({
model: 'claude-sonnet-4-model',
name: 'Claude Sonnet 4',
family: 'claude-sonnet-4',
modelMaxPromptTokens: 1_000_000,
tokenPricing: {
default: { inputPrice: 3, outputPrice: 15, cacheReadTokenPrice: 0.3, contextMax: 200_000 },
},
}),
]);
const { lm, getCapturedProvider } = createMockLm();
const info = await getProviderInfo(service, lm, getCapturedProvider);
const schema = info[0].configurationSchema!;
expect(schema.properties?.['contextSize'].enumDescriptions).toEqual(['Default', 'Longer sessions without compaction']);
});
it('includes both reasoningEffort and contextSize when supported', async () => {
const { service } = createServiceWithRefreshableEndpoints([
createMockEndpoint({
model: 'claude-sonnet-4-model',
name: 'Claude Sonnet 4',
family: 'claude-sonnet-4',
supportsReasoningEffort: ['low', 'medium', 'high'],
modelMaxPromptTokens: 1_000_000,
tokenPricing: {
default: { inputPrice: 3, outputPrice: 15, cacheReadTokenPrice: 0.3, contextMax: 200_000 },
longContext: { inputPrice: 6, outputPrice: 22.5, cacheReadTokenPrice: 0.6 },
},
}),
]);
const { lm, getCapturedProvider } = createMockLm();
const info = await getProviderInfo(service, lm, getCapturedProvider);
const schema = info[0].configurationSchema!;
expect(Object.keys(schema.properties ?? {})).toEqual(['reasoningEffort', 'contextSize']);
});
});
describe('resolveEndpoint with ParsedClaudeModelId', () => {
@@ -268,57 +268,6 @@ describe('ClaudeSessionStateService', () => {
});
});
describe('getContextSizeForSession', () => {
it('should return undefined when no context size is set', () => {
assert.strictEqual(service.getContextSizeForSession('session-1'), undefined);
});
it('should return the set context size', () => {
service.setContextSizeForSession('session-1', 200_000);
assert.strictEqual(service.getContextSizeForSession('session-1'), 200_000);
});
it('should return different sizes for different sessions', () => {
service.setContextSizeForSession('session-1', 200_000);
service.setContextSizeForSession('session-2', 1_000_000);
assert.strictEqual(service.getContextSizeForSession('session-1'), 200_000);
assert.strictEqual(service.getContextSizeForSession('session-2'), 1_000_000);
});
});
describe('setContextSizeForSession', () => {
it('should allow setting a context size', () => {
service.setContextSizeForSession('session-1', 1_000_000);
assert.strictEqual(service.getContextSizeForSession('session-1'), 1_000_000);
});
it('should allow clearing a context size', () => {
service.setContextSizeForSession('session-1', 1_000_000);
service.setContextSizeForSession('session-1', undefined);
assert.strictEqual(service.getContextSizeForSession('session-1'), undefined);
});
it('should preserve other state when setting context size', () => {
service.setModelIdForSession('session-1', OPUS_4);
service.setReasoningEffortForSession('session-1', 'high');
service.setContextSizeForSession('session-1', 1_000_000);
assert.strictEqual(service.getModelIdForSession('session-1'), OPUS_4);
assert.strictEqual(service.getReasoningEffortForSession('session-1'), 'high');
});
it('should not fire onDidChangeSessionState event', () => {
const events: SessionStateChangeEvent[] = [];
service.onDidChangeSessionState(e => events.push(e));
service.setContextSizeForSession('session-1', 1_000_000);
assert.strictEqual(events.length, 0);
});
});
describe('dispose', () => {
it('should clear session state on dispose', () => {
service.setModelIdForSession('session-1', OPUS_4);
@@ -26,7 +26,7 @@ import { IInstantiationService } from '../../../util/vs/platform/instantiation/c
import { ClaudeFolderInfo } from '../claude/common/claudeFolderInfo';
import { ClaudeSessionUri } from '../claude/common/claudeSessionUri';
import { ClaudeAgentManager } from '../claude/node/claudeCodeAgent';
import { CLAUDE_CONTEXT_SIZE_PROPERTY, CLAUDE_REASONING_EFFORT_PROPERTY, IClaudeCodeModels, pickReasoningEffort } from '../claude/node/claudeCodeModels';
import { CLAUDE_REASONING_EFFORT_PROPERTY, IClaudeCodeModels, pickReasoningEffort } from '../claude/node/claudeCodeModels';
import { IClaudeCodeSdkService } from '../claude/node/claudeCodeSdkService';
import { parseClaudeModelId } from '../claude/node/claudeModelId';
import { IClaudeSessionStateService } from '../claude/common/claudeSessionStateService';
@@ -177,10 +177,6 @@ export class ClaudeChatSessionContentProvider extends Disposable implements vsco
const reasoningEffort = pickReasoningEffort(endpoint, typeof rawReasoningEffort === 'string' ? rawReasoningEffort : undefined);
this.sessionStateService.setReasoningEffortForSession(effectiveSessionId, reasoningEffort);
const rawContextSize = request.modelConfiguration?.[CLAUDE_CONTEXT_SIZE_PROPERTY];
const contextSize = typeof rawContextSize === 'number' && rawContextSize > 0 ? rawContextSize : undefined;
this.sessionStateService.setContextSizeForSession(effectiveSessionId, contextSize);
// Set usage handler to report token usage for context window widget
this.sessionStateService.setUsageHandlerForSession(effectiveSessionId, (usage) => {
stream.usage(usage);