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
21 changes: 0 additions & 21 deletions extensions/copilot/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3196,27 +3196,6 @@
"preview",
"onExp"
]
},
"github.copilot.chat.anthropic.toolSearchTool.enabled": {
"type": "boolean",
"default": true,
"markdownDescription": "%github.copilot.config.anthropic.toolSearchTool.enabled%",
"tags": [
"preview"
]
},
"github.copilot.chat.anthropic.toolSearchTool.mode": {
"type": "string",
"enum": [
"server",
"client"
],
"default": "server",
"markdownDescription": "%github.copilot.config.anthropic.toolSearchTool.mode%",
"tags": [
"preview",
"onExp"
]
}
}
},
Expand Down
2 changes: 0 additions & 2 deletions extensions/copilot/package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,6 @@
"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.",
"github.copilot.config.anthropic.contextEditing.mode": "Select the context editing mode for Anthropic models. Automatically manages conversation context as it grows, helping optimize costs and stay within context window limits.\n\n- `off`: Context editing is disabled.\n- `clear-thinking`: Clears thinking blocks while preserving tool uses.\n- `clear-tooluse`: Clears tool uses while preserving thinking blocks.\n- `clear-both`: Clears both thinking blocks and tool uses.\n\n**Note**: This is an experimental feature. Context editing may cause additional cache rewrites. Enable with caution.",
"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.",
"github.copilot.config.anthropic.toolSearchTool.mode": "Controls how tool search works for Anthropic models. 'server' uses Anthropic's built-in regex-based tool search. 'client' uses local embeddings-based semantic search for more accurate tool discovery.",
"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.responsesApiReasoningSummary": "Sets the reasoning summary style used for the Responses API. Requires `#github.copilot.chat.useResponsesApi#`.",
"github.copilot.config.responsesApiContextManagement.enabled": "Enables context management for the Responses API. Requires `#github.copilot.chat.useResponsesApi#`.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import { CancellationToken, LanguageModelChatInformation, LanguageModelChatMessa
import { ChatFetchResponseType, ChatLocation } from '../../../platform/chat/common/commonTypes';
import { ConfigKey, IConfigurationService } from '../../../platform/configuration/common/configurationService';
import { CustomDataPartMimeTypes } from '../../../platform/endpoint/common/endpointTypes';
import { modelSupportsToolSearch } from '../../../platform/endpoint/common/chatModelCapabilities';
import { buildToolInputSchema } from '../../../platform/endpoint/node/messagesApi';
import { ILogService } from '../../../platform/log/common/logService';
import { ContextManagementResponse, getContextManagementFromConfig, isAnthropicContextEditingEnabled, isAnthropicMemoryToolEnabled, isAnthropicToolSearchEnabled, TOOL_SEARCH_TOOL_NAME, TOOL_SEARCH_TOOL_TYPE, ToolSearchToolResult, ToolSearchToolSearchResult } from '../../../platform/networking/common/anthropic';
import { ContextManagementResponse, CUSTOM_TOOL_SEARCH_NAME, getContextManagementFromConfig, isAnthropicContextEditingEnabled, isAnthropicMemoryToolEnabled } from '../../../platform/networking/common/anthropic';
import { IToolDeferralService } from '../../../platform/networking/common/toolDeferralService';
import { IResponseDelta, OpenAiFunctionTool } from '../../../platform/networking/common/fetch';
import { APIUsage } from '../../../platform/networking/common/openai';
Expand Down Expand Up @@ -146,19 +147,14 @@ export class AnthropicLMProvider extends AbstractLanguageModelChatProvider {

const memoryToolEnabled = isAnthropicMemoryToolEnabled(model.id, this._configurationService, this._experimentationService);

const toolSearchEnabled = isAnthropicToolSearchEnabled(model.id.replace(/-/g, '.'), this._configurationService);
// Requires the client-side tool_search tool in the request: without it, defer-loaded tools can't be retrieved.
// If the user disables tool_search in the tool picker, it won't be present here and tool search is skipped.
const toolSearchEnabled = modelSupportsToolSearch(model.id)
&& !!options.tools?.some(t => t.name === CUSTOM_TOOL_SEARCH_NAME);

// Build tools array, handling both standard tools and native Anthropic tools
const tools: Anthropic.Beta.BetaToolUnion[] = [];

// Add tool search tool if enabled (must be first in the array)
if (toolSearchEnabled) {
tools.push({
name: TOOL_SEARCH_TOOL_NAME,
type: TOOL_SEARCH_TOOL_TYPE,
defer_loading: false
} as Anthropic.Beta.BetaToolUnion);
}
let hasMemoryTool = false;
for (const tool of (options.tools ?? [])) {
// Handle native Anthropic memory tool (only for models that support it)
Expand Down Expand Up @@ -646,33 +642,6 @@ export class AnthropicLMProvider extends AbstractLanguageModelChatProvider {
[new LanguageModelTextPart(searchResults)]
));
pendingServerToolCall = undefined;
} else if ('content_block' in chunk && chunk.content_block.type === 'tool_search_tool_result') {
const toolSearchResult = chunk.content_block as unknown as ToolSearchToolResult;
if (toolSearchResult.content.type === 'tool_search_tool_search_result') {
const searchResult = toolSearchResult.content as ToolSearchToolSearchResult;
const toolNames = searchResult.tool_references.map(ref => ref.tool_name);

this._logService.trace(`Tool search discovered ${toolNames.length} tools: ${toolNames.join(', ')}`);

let query: string | undefined;
if (pendingServerToolCall) {
try {
const parsed = JSON.parse(pendingServerToolCall.jsonInput || '{}');
query = parsed.query;
} catch {
// Ignore parse errors
}
}

progress.report(new LanguageModelToolResultPart(
toolSearchResult.tool_use_id,
[new LanguageModelTextPart(JSON.stringify({ query, discovered_tools: toolNames }))]
));
pendingServerToolCall = undefined;
} else if (toolSearchResult.content.type === 'tool_search_tool_result_error') {
this._logService.warn(`Tool search error: ${toolSearchResult.content.error_code}`);
pendingServerToolCall = undefined;
}
}
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import { CopilotToken } from '../../../platform/authentication/common/copilotTok
import { IBlockedExtensionService } from '../../../platform/chat/common/blockedExtensionService';
import { ChatFetchResponseType, ChatLocation, getErrorDetailsFromChatFetchError } from '../../../platform/chat/common/commonTypes';
import { getTextPart } from '../../../platform/chat/common/globalStringUtils';
import { IConfigurationService } from '../../../platform/configuration/common/configurationService';
import { EmbeddingType, getWellKnownEmbeddingTypeInfo, IEmbeddingsComputer } from '../../../platform/embeddings/common/embeddingsComputer';
import { IEndpointProvider } from '../../../platform/endpoint/common/endpointProvider';
import { CustomDataPartMimeTypes } from '../../../platform/endpoint/common/endpointTypes';
Expand All @@ -23,7 +22,6 @@ import { IEnvService, isScenarioAutomation } from '../../../platform/env/common/
import { IVSCodeExtensionContext } from '../../../platform/extContext/common/extensionContext';
import { IOctoKitService } from '../../../platform/github/common/githubService';
import { ILogService } from '../../../platform/log/common/logService';
import { isAnthropicToolSearchEnabled } from '../../../platform/networking/common/anthropic';
import { FinishedCallback, OpenAiFunctionTool, OptionalChatRequestParams } from '../../../platform/networking/common/fetch';
import { IChatEndpoint, IEndpoint } from '../../../platform/networking/common/networking';
import { IOTelService, type OTelModelOptions } from '../../../platform/otel/common/otelService';
Expand Down Expand Up @@ -495,7 +493,6 @@ export class CopilotLanguageModelWrapper extends Disposable {
@ILogService private readonly _logService: ILogService,
@IAuthenticationService private readonly _authenticationService: IAuthenticationService,
@IEnvService private readonly _envService: IEnvService,
@IConfigurationService private readonly _configurationService: IConfigurationService,
@IOTelService private readonly _otelService: IOTelService,
@IOctoKitService private readonly _octoKitService: IOctoKitService,
) {
Expand Down Expand Up @@ -561,7 +558,7 @@ export class CopilotLanguageModelWrapper extends Disposable {
throw new Error('Message exceeds token limit.');
}

if (_options.tools && _options.tools.length > 128 && !isAnthropicToolSearchEnabled(_endpoint, this._configurationService)) {
if (_options.tools && _options.tools.length > 128 && !_endpoint.supportsToolSearch) {
Comment thread
bhavyaus marked this conversation as resolved.
throw new Error('Cannot have more than 128 tools per request.');
}

Expand Down
8 changes: 4 additions & 4 deletions extensions/copilot/src/extension/intents/node/agentIntent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { IAutomodeService } from '../../../platform/endpoint/node/automodeServic
import { IEnvService } from '../../../platform/env/common/envService';
import { ILogService } from '../../../platform/log/common/logService';
import { IEditLogService } from '../../../platform/multiFileEdit/common/editLogService';
import { CUSTOM_TOOL_SEARCH_NAME, isAnthropicCustomToolSearchEnabled, isAnthropicContextEditingEnabled, isAnthropicToolSearchEnabled } from '../../../platform/networking/common/anthropic';
import { CUSTOM_TOOL_SEARCH_NAME, isAnthropicContextEditingEnabled } from '../../../platform/networking/common/anthropic';
import { IChatEndpoint } from '../../../platform/networking/common/networking';
import { modelsWithoutResponsesContextManagement } from '../../../platform/networking/common/openai';
import { INotebookService } from '../../../platform/notebook/common/notebookService';
Expand Down Expand Up @@ -139,7 +139,7 @@ export const getAgentTools = async (accessor: ServicesAccessor, request: vscode.
allowTools[ToolName.MultiReplaceString] = true;
}

allowTools[CUSTOM_TOOL_SEARCH_NAME] = isAnthropicCustomToolSearchEnabled(model, configurationService, experimentationService);
allowTools[CUSTOM_TOOL_SEARCH_NAME] = !!model.supportsToolSearch;

const tools = toolsService.getEnabledTools(request, model, tool => {
if (typeof allowTools[tool.name] === 'boolean') {
Expand Down Expand Up @@ -406,7 +406,7 @@ export class AgentIntentInvocation extends EditCodeIntentInvocation implements I
}

const tools = promptContext.tools?.availableTools;
const toolSearchEnabled = isAnthropicToolSearchEnabled(this.endpoint, this.configurationService);
const toolSearchEnabled = !!this.endpoint.supportsToolSearch;
const toolTokens = tools?.length ? await this.endpoint.acquireTokenizer().countToolTokens(tools) : 0;

const summarizeThresholdOverride = this.configurationService.getConfig<number | undefined>(ConfigKey.Advanced.SummarizeAgentConversationHistoryThreshold);
Expand Down Expand Up @@ -674,7 +674,7 @@ export class AgentIntentInvocation extends EditCodeIntentInvocation implements I
this._lastModelCapabilities = {
enableThinking: !isAnthropicFamily(this.endpoint) || ToolCallingLoop.messagesContainThinking(strippedMessages),
reasoningEffort: typeof rawEffort === 'string' ? rawEffort : undefined,
enableToolSearch: !isSubagent && isAnthropicToolSearchEnabled(this.endpoint, this.configurationService),
enableToolSearch: !isSubagent && !!this.endpoint.supportsToolSearch,
enableContextEditing: !isSubagent && isAnthropicContextEditingEnabled(this.endpoint, this.configurationService, this.expService),
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { ICAPIClientService } from '../../../platform/endpoint/common/capiClient
import { isAutoModel } from '../../../platform/endpoint/node/autoChatEndpoint';
import { getResponsesApiCompactionThresholdFromBody, OpenAIResponsesProcessor, responseApiInputToRawMessagesForLogging, sendCompletionOutputTelemetry } from '../../../platform/endpoint/node/responsesApi';
import { collectSingleLineErrorMessage, ILogService } from '../../../platform/log/common/logService';
import { isAnthropicToolSearchEnabled } from '../../../platform/networking/common/anthropic';
import { FinishedCallback, getRequestId, IResponseDelta, OptionalChatRequestParams, RequestId } from '../../../platform/networking/common/fetch';
import { FetcherId, IFetcherService, Response } from '../../../platform/networking/common/fetcherService';
import { IBackgroundRequestOptions, IChatEndpoint, IEndpointBody, ISubagentRequestOptions, postRequest, stringifyUrlOrRequestMetadata } from '../../../platform/networking/common/networking';
Expand Down Expand Up @@ -2172,7 +2171,7 @@ function isValidChatPayload(messages: Raw.ChatMessage[], postOptions: OptionalCh
return { isValid: false, reason: asUnexpected('Function names must match ^[a-zA-Z0-9_-]+$') };
}

if (postOptions?.tools && postOptions.tools.length > HARD_TOOL_LIMIT && !isAnthropicToolSearchEnabled(endpoint, configurationService)) {
if (postOptions?.tools && postOptions.tools.length > HARD_TOOL_LIMIT && !endpoint.supportsToolSearch) {
Comment thread
bhavyaus marked this conversation as resolved.
return { isValid: false, reason: `Tool limit exceeded (${postOptions.tools.length}/${HARD_TOOL_LIMIT}). Click "Configure Tools" in the chat input to disable ${postOptions.tools.length - HARD_TOOL_LIMIT} tools and retry.` };
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import { IGitService } from '../../../platform/git/common/gitService';
import { IOctoKitService } from '../../../platform/github/common/githubService';
import { HAS_IGNORED_FILES_MESSAGE } from '../../../platform/ignore/common/ignoreService';
import { ILogService } from '../../../platform/log/common/logService';
import { isAnthropicContextEditingEnabled, isAnthropicToolSearchEnabled } from '../../../platform/networking/common/anthropic';
import { isAnthropicContextEditingEnabled } from '../../../platform/networking/common/anthropic';
import { FilterReason } from '../../../platform/networking/common/openai';
import { IOTelService } from '../../../platform/otel/common/otelService';
import { CapturingToken } from '../../../platform/requestLogger/common/capturingToken';
Expand Down Expand Up @@ -701,7 +701,7 @@ class DefaultToolCallingLoop extends ToolCallingLoop<IDefaultToolLoopOptions> {
...opts.modelCapabilities,
enableThinking: isThinkingLocation && opts.modelCapabilities?.enableThinking,
reasoningEffort,
enableToolSearch: !isSubagent && isAnthropicToolSearchEnabled(this.options.invocation.endpoint, this._configurationService),
enableToolSearch: !isSubagent && !!this.options.invocation.endpoint.supportsToolSearch,
enableContextEditing: !isSubagent && isAnthropicContextEditingEnabled(this.options.invocation.endpoint, this._configurationService, this._experimentationService),
},
debugName,
Expand Down Expand Up @@ -741,7 +741,7 @@ class DefaultToolCallingLoop extends ToolCallingLoop<IDefaultToolLoopOptions> {
const tools = await this.options.invocation.getAvailableTools?.() ?? [];

// Skip tool grouping when Anthropic tool search is enabled
if (isAnthropicFamily(this.options.invocation.endpoint) && isAnthropicToolSearchEnabled(this.options.invocation.endpoint, this._configurationService)) {
if (isAnthropicFamily(this.options.invocation.endpoint) && this.options.invocation.endpoint.supportsToolSearch) {
return tools;
}

Expand Down
Loading
Loading