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
41 changes: 41 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3631,6 +3631,47 @@
{
"id": "advanced",
"properties": {
"github.copilot.chat.anthropic.contextEditing.config": {
"type": "object",
"default": null,
"markdownDescription": "%github.copilot.config.anthropic.contextEditing.config%",
"tags": [
"advanced",
"experimental"
],
"properties": {
"triggerType": {
"type": "string",
"enum": ["input_tokens", "tool_uses"],
"description": "Type of trigger for context editing"
},
"triggerValue": {
"type": "number",
"description": "Threshold value for the trigger (default: 80000 tokens)"
},
"keepCount": {
"type": "number",
"description": "Number of recent tool uses to keep (default: 3)"
},
"clearAtLeastTokens": {
"type": "number",
"description": "Minimum tokens to clear per edit (default: 10000)"
},
"excludeTools": {
"type": "array",
"items": { "type": "string" },
"description": "Tool names to exclude from clearing"
},
"clearInputs": {
"type": "boolean",
"description": "Whether to clear tool inputs (default: true)"
},
"thinkingKeepTurns": {
"type": "number",
"description": "Number of thinking turns to keep (default: 1)"
}
}
},
"github.copilot.chat.debug.overrideChatEngine": {
"type": [
"string",
Expand Down
1 change: 1 addition & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@
"copilot.toolSet.web.description": "Fetch information from the web",
"github.copilot.config.useMessagesApi": "Use the Messages API instead of the Chat Completions API when supported.\n\n**Note**: This is an experimental feature that is not yet activated for all users.",
"github.copilot.config.anthropic.contextEditing.enabled": "Enable context editing for Anthropic models. Automatically manages conversation context as it grows, helping optimize costs and stay within context window limits.\n\n**Note**: This is an experimental feature. Context editing may cause additional cache rewrites. Enable with caution.",
"github.copilot.config.anthropic.contextEditing.config": "Advanced configuration for Anthropic context editing. Customize trigger thresholds and behavior for automatic context management.\n\n- **triggerType**: `input_tokens` or `tool_uses`\n- **triggerValue**: Threshold to trigger editing (default: 80000)\n- **keepCount**: Number of recent tool uses to keep (default: 3)\n- **clearAtLeastTokens**: Minimum tokens to clear (default: 10000)\n- **excludeTools**: Tool names to exclude from clearing\n- **clearInputs**: Whether to clear tool inputs (default: true)\n- **thinkingKeepTurns**: Number of thinking turns to keep (default: 1)",
"github.copilot.config.anthropic.toolSearchTool.enabled": "Enable tool search tool for Anthropic models. When enabled, tools are dynamically discovered and loaded on-demand using natural language search, reducing context window usage when many tools are available.\n\n**Note**: This is an experimental feature.",
"github.copilot.config.useResponsesApi": "Use the Responses API instead of the Chat Completions API when supported. Enables reasoning and reasoning summaries.\n\n**Note**: This is an experimental feature that is not yet activated for all users.\n\n**Important**: URL API path resolution for custom OpenAI-compatible and Azure models is independent of this setting and fully determined by `url` property of `#github.copilot.chat.customOAIModels#` or `#github.copilot.chat.azureModels#` respectively.",
"github.copilot.config.responsesApiReasoningEffort": "Sets the reasoning effort used for the Responses API. Requires `#github.copilot.chat.useResponsesApi#`.",
Expand Down
5 changes: 2 additions & 3 deletions src/extension/byok/vscode-node/anthropicProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,8 @@ export class AnthropicLMProvider extends AbstractLanguageModelChatProvider {

// Build context management configuration
const contextManagement = isAnthropicContextEditingEnabled(model.id, this._configurationService, this._experimentationService) ? getContextManagementFromConfig(
this._experimentationService,
thinkingBudget,
model.maxInputTokens
this._configurationService,
(thinkingBudget ?? 0) > 0
) : undefined;

// Build betas array for beta API features
Expand Down
11 changes: 11 additions & 0 deletions src/platform/configuration/common/configurationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -698,6 +698,17 @@ export namespace ConfigKey {
export const DiagnosticsContextProvider = defineAndMigrateExpSetting<boolean>('chat.advanced.inlineEdits.diagnosticsContextProvider.enabled', 'chat.inlineEdits.diagnosticsContextProvider.enabled', false);
export const Gemini3MultiReplaceString = defineSetting<boolean>('chat.edits.gemini3MultiReplaceString', ConfigType.ExperimentBased, false);
export const AgentOmitFileAttachmentContents = defineSetting<boolean>('chat.agent.omitFileAttachmentContents', ConfigType.ExperimentBased, false);

/** Context editing configuration for Anthropic Messages API */
export const AnthropicContextEditingConfig = defineSetting<{
triggerType?: 'input_tokens' | 'tool_uses';
triggerValue?: number;
keepCount?: number;
clearAtLeastTokens?: number;
excludeTools?: string[];
clearInputs?: boolean;
thinkingKeepTurns?: number;
} | null>('chat.anthropic.contextEditing.config', ConfigType.Simple, null);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/platform/endpoint/node/messagesApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export function createMessagesRequestBody(accessor: ServicesAccessor, options: I

// Build context management configuration
const contextManagement = isAnthropicContextEditingEnabled(endpoint, configurationService, experimentationService)
? getContextManagementFromConfig(experimentationService, thinkingBudget, endpoint.modelMaxPromptTokens)
? getContextManagementFromConfig(configurationService, (thinkingBudget ?? 0) > 0)
: undefined;

return {
Expand Down
57 changes: 35 additions & 22 deletions src/platform/networking/common/anthropic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,16 +162,16 @@ export function modelSupportsContextEditing(modelId: string): boolean {

/**
* Tool search is only supported by:
* - Claude Sonnet 4.5 (claude-sonnet-4-5-* or claude-sonnet-4.5-*)
* - Claude Opus 4.5 (claude-opus-4-5-* or claude-opus-4.5-*)
* @param modelId The model ID to check
* @returns true if the model supports tool search
*/
export function modelSupportsToolSearch(modelId: string): boolean {
// Normalize: lowercase and replace dots with dashes so "4.5" matches "4-5"
const normalized = modelId.toLowerCase().replace(/\./g, '-');
return normalized.startsWith('claude-sonnet-4-5') ||
normalized.startsWith('claude-opus-4-5');
// TODO: Enable sonnet tool search when supported by all providers
// return normalized.startsWith('claude-sonnet-4-5') ||
return normalized.startsWith('claude-opus-4-5');
}

/**
Expand Down Expand Up @@ -250,19 +250,17 @@ export interface ContextEditingConfig {
/**
* Builds the context_management configuration object for the Messages API request.
* @param config The context editing configuration from individual settings
* @param hasThinking Whether extended thinking is enabled (the thinking budget value)
* @param modelMaxTokens The maximum input tokens supported by the model
* @param thinkingEnabled Whether extended thinking is enabled
* @returns The context_management object to include in the request, or undefined if no edits
*/
export function buildContextManagement(
config: ContextEditingConfig,
hasThinking: number | undefined,
modelMaxTokens: number
thinkingEnabled: boolean
): ContextManagement | undefined {
const edits: ContextManagementEdit[] = [];

// Add thinking block clearing if extended thinking is enabled
if (hasThinking) {
if (thinkingEnabled) {
const thinkingKeepTurns = config.thinkingKeepTurns;
edits.push({
type: 'clear_thinking_20251015',
Expand All @@ -289,30 +287,45 @@ export function buildContextManagement(
return edits.length > 0 ? { edits } : undefined;
}

/**
* Default values for context editing configuration.
*/
export const CONTEXT_EDITING_DEFAULTS: ContextEditingConfig = {
triggerType: 'input_tokens',
triggerValue: 80000,
keepCount: 3,
clearAtLeastTokens: 10000,
excludeTools: [],
clearInputs: true,
thinkingKeepTurns: 1,
};

/**
* Reads context editing configuration from settings and builds the context_management object.
* This is a convenience function that combines reading configuration with buildContextManagement.
* @param configurationService The configuration service to read settings from
* @param experimentationService The experimentation service for experiment-based config
* @param thinkingBudget The thinking budget value (undefined if thinking is disabled)
* @param modelMaxInputTokens The maximum input tokens supported by the model
* @param thinkingEnabled Whether extended thinking is enabled
* @returns The context_management object to include in the request, or undefined if disabled
*/
export function getContextManagementFromConfig(
experimentationService: IExperimentationService,
thinkingBudget: number | undefined,
modelMaxInputTokens: number
configurationService: IConfigurationService,
thinkingEnabled: boolean,
): ContextManagement | undefined {

const userConfig = configurationService.getConfig(ConfigKey.Advanced.AnthropicContextEditingConfig);
if (!userConfig) {
return buildContextManagement(CONTEXT_EDITING_DEFAULTS, thinkingEnabled);
}

const contextEditingConfig: ContextEditingConfig = {
triggerType: (experimentationService.getTreatmentVariable<string>('copilotchat.anthropic.contextEditing.toolResult.triggerType') ?? 'input_tokens') as 'input_tokens' | 'tool_uses',
triggerValue: experimentationService.getTreatmentVariable<number>('copilotchat.anthropic.contextEditing.toolResult.triggerValue') ?? 75000,
keepCount: experimentationService.getTreatmentVariable<number>('copilotchat.anthropic.contextEditing.toolResult.keepCount') ?? 5,
clearAtLeastTokens: experimentationService.getTreatmentVariable<number>('copilotchat.anthropic.contextEditing.toolResult.clearAtLeastTokens') ?? 5000,
excludeTools: [],
clearInputs: experimentationService.getTreatmentVariable<boolean>('copilotchat.anthropic.contextEditing.toolResult.clearInputs') ?? true,
thinkingKeepTurns: experimentationService.getTreatmentVariable<number>('copilotchat.anthropic.contextEditing.thinking.keepTurns') ?? 1,
triggerType: userConfig.triggerType ?? CONTEXT_EDITING_DEFAULTS.triggerType,
triggerValue: userConfig.triggerValue ?? CONTEXT_EDITING_DEFAULTS.triggerValue,
keepCount: userConfig.keepCount ?? CONTEXT_EDITING_DEFAULTS.keepCount,
clearAtLeastTokens: userConfig.clearAtLeastTokens ?? CONTEXT_EDITING_DEFAULTS.clearAtLeastTokens,
excludeTools: userConfig.excludeTools ?? CONTEXT_EDITING_DEFAULTS.excludeTools,
clearInputs: userConfig.clearInputs ?? CONTEXT_EDITING_DEFAULTS.clearInputs,
thinkingKeepTurns: userConfig.thinkingKeepTurns ?? CONTEXT_EDITING_DEFAULTS.thinkingKeepTurns,
};

return buildContextManagement(contextEditingConfig, thinkingBudget, modelMaxInputTokens);
return buildContextManagement(contextEditingConfig, thinkingEnabled);
}