diff --git a/src/extension/extension/vscode-node/services.ts b/src/extension/extension/vscode-node/services.ts index 04fab1d78..e7afa20a5 100644 --- a/src/extension/extension/vscode-node/services.ts +++ b/src/extension/extension/vscode-node/services.ts @@ -24,6 +24,7 @@ import { IEndpointProvider } from '../../../platform/endpoint/common/endpointPro import { CAPIClientImpl } from '../../../platform/endpoint/node/capiClientImpl'; import { DomainService } from '../../../platform/endpoint/node/domainServiceImpl'; import { INativeEnvService, isScenarioAutomation } from '../../../platform/env/common/envService'; +import { NativeEnvServiceImpl } from '../../../platform/env/vscode-node/nativeEnvServiceImpl'; import { IGitCommitMessageService } from '../../../platform/git/common/gitCommitMessageService'; import { IGitDiffService } from '../../../platform/git/common/gitDiffService'; import { IGithubRepositoryService } from '../../../platform/github/common/githubService'; @@ -102,7 +103,6 @@ import { LanguageContextServiceImpl } from '../../typescriptContext/vscode-node/ import { IWorkspaceListenerService } from '../../workspaceRecorder/common/workspaceListenerService'; import { WorkspacListenerService } from '../../workspaceRecorder/vscode-node/workspaceListenerService'; import { registerServices as registerCommonServices } from '../vscode/services'; -import { NativeEnvServiceImpl } from '../../../platform/env/vscode-node/nativeEnvServiceImpl'; // ########################################################################################### // ### ### diff --git a/src/extension/prompt/node/defaultIntentRequestHandler.ts b/src/extension/prompt/node/defaultIntentRequestHandler.ts index fed176d23..5605e83c9 100644 --- a/src/extension/prompt/node/defaultIntentRequestHandler.ts +++ b/src/extension/prompt/node/defaultIntentRequestHandler.ts @@ -42,6 +42,7 @@ import { SummarizedConversationHistoryMetadata } from '../../prompts/node/agent/ import { normalizeToolSchema } from '../../tools/common/toolSchemaNormalizer'; import { ToolCallCancelledError } from '../../tools/common/toolsService'; import { IToolGrouping, IToolGroupingService } from '../../tools/common/virtualTools/virtualToolTypes'; +import { ChatVariablesCollection } from '../common/chatVariablesCollection'; import { Conversation, getUniqueReferences, GlobalContextMessageMetadata, IResultMetadata, RenderedUserMessageMetadata, RequestDebugInformation, ResponseStreamParticipant, Turn, TurnStatus } from '../common/conversation'; import { IBuildPromptContext, IToolCallRound } from '../common/intents'; import { ChatTelemetry, ChatTelemetryBuilder } from './chatParticipantTelemetry'; @@ -49,7 +50,6 @@ import { IntentInvocationMetadata } from './conversation'; import { IDocumentContext } from './documentContext'; import { IBuildPromptResult, IIntent, IIntentInvocation, IResponseProcessor } from './intents'; import { ConversationalBaseTelemetryData, createTelemetryWithId, sendModelMessageTelemetry } from './telemetry'; -import { ChatVariablesCollection } from '../common/chatVariablesCollection'; export interface IDefaultIntentRequestHandlerOptions { maxToolCallIterations: number; @@ -595,7 +595,7 @@ class DefaultToolCallingLoop extends ToolCallingLoop { const token = CancellationToken.None; const allTools = await this.options.invocation.getAvailableTools?.() ?? []; const grouping = this.toolGroupingService.create(this.options.conversation.sessionId, allTools); - const computed = await grouping.compute(this.options.request.prompt, token); + const computed = await grouping.compute(this.options.request.prompt, token, this.options.invocation.endpoint); const container = grouping.getContainerFor(candidateCall.name); @@ -722,7 +722,7 @@ class DefaultToolCallingLoop extends ToolCallingLoop { return tools; } - const computePromise = this.toolGrouping.compute(this.options.request.prompt, token); + const computePromise = this.toolGrouping.compute(this.options.request.prompt, token, this.options.invocation.endpoint); // Show progress if this takes a moment... const timeout = setTimeout(() => { diff --git a/src/extension/tools/common/virtualTools/builtInToolGroupHandler.ts b/src/extension/tools/common/virtualTools/builtInToolGroupHandler.ts new file mode 100644 index 000000000..b36616599 --- /dev/null +++ b/src/extension/tools/common/virtualTools/builtInToolGroupHandler.ts @@ -0,0 +1,149 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { LanguageModelToolInformation } from 'vscode'; +import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry'; +import { VIRTUAL_TOOL_NAME_PREFIX, VirtualTool } from './virtualTool'; +import * as Constant from './virtualToolsConstants'; + +const BUILT_IN_GROUP = 'builtin'; +const SUMMARY_PREFIX = 'Call this tool when you need access to a new category of tools. The category of tools is described as follows:\n\n'; +const SUMMARY_SUFFIX = '\n\nBe sure to call this tool if you need a capability related to the above.'; + +// categorize all tools except for the 12 default tools +// 12 default tools = semantic_search, grep_search, read_file, create_file, apply_patch, replace_string_in_file, +// insert_edit_into_file, run_in_terminal, list_dir, think, get_terminal_output, manage_todo_list +const BUILT_IN_TOOL_GROUPS = { + 'Jupyter Notebook Tools': { + summary: 'Call tools from this group when you need to work with Jupyter notebooks - creating, editing, running cells, and managing notebook operations.', + tools: [ + 'create_new_jupyter_notebook', + 'edit_notebook_file', + 'run_notebook_cell', + 'copilot_getNotebookSummary', + 'read_notebook_cell_output', + 'configure_notebook', + 'notebook_install_packages', + 'notebook_list_packages', + 'configure_python_environment', + 'get_python_environment_details', + 'get_python_executable_details' + ] + }, + 'Web Interaction': { + summary: 'Call tools from this group when you need to interact with web content, browse websites, or access external resources.', + tools: [ + 'fetch_webpage', + 'open_simple_browser', + 'github_repo' + ] + }, + 'VS Code Interaction': { + summary: 'Call tools from this group when you need to interact with the VS Code workspace and access VS Code features.', + tools: [ + 'search_workspace_symbols', + 'list_code_usages', + 'get_errors', + 'get_vscode_api', + 'get_changed_files', + 'create_new_workspace', + 'install_extension', + 'get_project_setup_info', + 'create_and_run_task', + 'run_task', + 'get_task_output', + 'run_vscode_command', + 'install_python_packages', + 'get_search_view_results', + 'vscode_searchExtensions_internal', + 'read_project_structure' + ] + }, + 'Testing': { + summary: 'Call tools from this group when you need to run tests, analyze test failures, and manage test workflows.', + tools: [ + 'run_tests', + 'test_failure', + 'test_search', + 'runTests' + ] + }, + 'Redundant but Specific': { + summary: 'These tools have overlapping functionalities but are highly specialized for certain tasks. \nTools: file_search, terminal_selection, terminal_last_command, create_directory, get_doc_info, multi_replace_string_in_file, edit_files', + tools: [ + 'file_search', + 'terminal_selection', + 'terminal_last_command', + 'create_directory', + 'get_doc_info', + 'edit_files', + 'multi_replace_string_in_file' + ] + } +} as const; + +export class BuiltInToolGroupHandler { + constructor( + private readonly _telemetryService: ITelemetryService, + ) { } + + /** Creates groups for built-in tools based on the pre-defined enum above*/ + createBuiltInToolGroups(tools: LanguageModelToolInformation[]): (VirtualTool | LanguageModelToolInformation)[] { + // If there are too few tools, don't group them + if (tools.length <= Constant.MIN_TOOLSET_SIZE_TO_GROUP) { + return tools; + } + + const toolMap = new Map(tools.map(tool => [tool.name, tool])); + + const virtualTools: VirtualTool[] = []; + const usedTools = new Set(); + + // Create virtual tools for each predefined group + for (const [groupName, groupDef] of Object.entries(BUILT_IN_TOOL_GROUPS)) { + const groupTools = groupDef.tools + .map(toolName => toolMap.get(toolName)) + .filter((tool): tool is LanguageModelToolInformation => tool !== undefined); + + if (groupTools.length > 0) { + // Mark each tool that has already been added to a group + groupTools.forEach(tool => usedTools.add(tool.name)); + + const virtualTool = new VirtualTool( + VIRTUAL_TOOL_NAME_PREFIX + groupName.toLowerCase().replace(/\s+/g, '_'), + SUMMARY_PREFIX + groupDef.summary + SUMMARY_SUFFIX, + 0, + { + toolsetKey: BUILT_IN_GROUP, + groups: [], + possiblePrefix: 'builtin_' + }, + groupTools + ); + virtualTools.push(virtualTool); + } + } + + // Add any remaining uncategorized tools individually + const uncategorizedTools = tools.filter(tool => !usedTools.has(tool.name)); + + // Send telemetry for built-in tool grouping + this._telemetryService.sendMSFTTelemetryEvent('virtualTools.generate', { + groupKey: BUILT_IN_GROUP, + }, { + uncategorized: uncategorizedTools.length, + toolsBefore: tools.length, + toolsAfter: virtualTools.length, + retries: 0, // No retries for predefined groups + durationMs: 0, // Instant for predefined groups + }); + + return [...virtualTools, ...uncategorizedTools]; + } + + static get BUILT_IN_GROUP_KEY(): string { + return BUILT_IN_GROUP; + } +} \ No newline at end of file diff --git a/src/extension/tools/common/virtualTools/toolGrouping.ts b/src/extension/tools/common/virtualTools/toolGrouping.ts index d0d920801..8725bf201 100644 --- a/src/extension/tools/common/virtualTools/toolGrouping.ts +++ b/src/extension/tools/common/virtualTools/toolGrouping.ts @@ -5,6 +5,7 @@ import type { LanguageModelToolInformation } from 'vscode'; import { ConfigKey, HARD_TOOL_LIMIT, IConfigurationService } from '../../../../platform/configuration/common/configurationService'; +import { IChatEndpoint } from '../../../../platform/networking/common/networking'; import { IExperimentationService } from '../../../../platform/telemetry/common/nullExperimentationService'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry'; import { equals as arraysEqual, uniqueFilter } from '../../../../util/vs/base/common/arrays'; @@ -47,7 +48,13 @@ export class ToolGrouping implements IToolGrouping { } public get isEnabled() { - return this._tools.length >= computeToolGroupingMinThreshold(this._experimentationService, this._configurationService).get(); + // Match the logic from VirtualToolGrouper.addGroups() + // Enable if we could potentially trigger built-in grouping (when GPT model is used) + const defaultToolGroupingEnabled = this._configurationService.getExperimentBasedConfig(ConfigKey.Internal.DefaultToolsGrouped, this._experimentationService); + const couldTriggerBuiltInGrouping = this._tools.length > Constant.START_BUILTIN_GROUPING_AFTER_TOOL_COUNT && defaultToolGroupingEnabled; + + // Or if we meet the standard threshold for all tool types + return couldTriggerBuiltInGrouping || this._tools.length >= Constant.START_GROUPING_AFTER_TOOL_COUNT; } constructor( @@ -126,19 +133,19 @@ export class ToolGrouping implements IToolGrouping { this._expandOnNext.add(toolName); } - async compute(query: string, token: CancellationToken): Promise { - await this._doCompute(query, token); + async compute(query: string, token: CancellationToken, endpoint?: IChatEndpoint): Promise { + await this._doCompute(query, token, endpoint); return [...this._root.tools()].filter(uniqueFilter(t => t.name)); } - async computeAll(query: string, token: CancellationToken): Promise<(LanguageModelToolInformation | VirtualTool)[]> { - await this._doCompute(query, token); + async computeAll(query: string, token: CancellationToken, endpoint?: IChatEndpoint): Promise<(LanguageModelToolInformation | VirtualTool)[]> { + await this._doCompute(query, token, endpoint); return this._root.contents; } - private async _doCompute(query: string, token: CancellationToken) { + private async _doCompute(query: string, token: CancellationToken, endpoint?: IChatEndpoint) { if (this._didToolsChange) { - await this._grouper.addGroups(query, this._root, this._tools.slice(), token); + await this._grouper.addGroups(query, this._root, this._tools.slice(), token, endpoint); this._didToolsChange = false; } diff --git a/src/extension/tools/common/virtualTools/virtualToolGrouper.ts b/src/extension/tools/common/virtualTools/virtualToolGrouper.ts index 0ed8fd49e..78e32d00c 100644 --- a/src/extension/tools/common/virtualTools/virtualToolGrouper.ts +++ b/src/extension/tools/common/virtualTools/virtualToolGrouper.ts @@ -8,6 +8,7 @@ import { CHAT_MODEL, ConfigKey, HARD_TOOL_LIMIT, IConfigurationService } from '. import { IEmbeddingsComputer } from '../../../../platform/embeddings/common/embeddingsComputer'; import { IEndpointProvider } from '../../../../platform/endpoint/common/endpointProvider'; import { ILogService } from '../../../../platform/log/common/logService'; +import { IChatEndpoint } from '../../../../platform/networking/common/networking'; import { IExperimentationService } from '../../../../platform/telemetry/common/nullExperimentationService'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry'; import { TelemetryCorrelationId } from '../../../../util/common/telemetryCorrelationId'; @@ -17,19 +18,21 @@ import { Iterable } from '../../../../util/vs/base/common/iterator'; import { StopWatch } from '../../../../util/vs/base/common/stopwatch'; import { IInstantiationService } from '../../../../util/vs/platform/instantiation/common/instantiation'; import { LanguageModelToolExtensionSource, LanguageModelToolMCPSource } from '../../../../vscodeTypes'; +import { BuiltInToolGroupHandler } from './builtInToolGroupHandler'; import { EMBEDDING_TYPE_FOR_TOOL_GROUPING, ToolEmbeddingsComputer } from './toolEmbeddingsCache'; import { EMBEDDINGS_GROUP_NAME, VIRTUAL_TOOL_NAME_PREFIX, VirtualTool } from './virtualTool'; import { divideToolsIntoExistingGroups, divideToolsIntoGroups, summarizeToolGroup } from './virtualToolSummarizer'; import { ISummarizedToolCategory, IToolCategorization, IToolGroupingCache } from './virtualToolTypes'; import * as Constant from './virtualToolsConstants'; -const BUILT_IN_GROUP = 'builtin'; const CATEGORIZATION_ENDPOINT = CHAT_MODEL.GPT4OMINI; const SUMMARY_PREFIX = 'Call this tool when you need access to a new category of tools. The category of tools is described as follows:\n\n'; const SUMMARY_SUFFIX = '\n\nBe sure to call this tool if you need a capability related to the above.'; export class VirtualToolGrouper implements IToolCategorization { private readonly toolEmbeddingsComputer: ToolEmbeddingsComputer; + private readonly builtInToolGroupHandler: BuiltInToolGroupHandler; + private _hasGroupedDefaultTools = false; constructor( @IEndpointProvider private readonly _endpointProvider: IEndpointProvider, @@ -42,15 +45,26 @@ export class VirtualToolGrouper implements IToolCategorization { @IInstantiationService _instantiationService: IInstantiationService, ) { this.toolEmbeddingsComputer = _instantiationService.createInstance(ToolEmbeddingsComputer); + this.builtInToolGroupHandler = new BuiltInToolGroupHandler(_telemetryService); } private get virtualToolEmbeddingRankingEnabled() { return this._configurationService.getExperimentBasedConfig(ConfigKey.Internal.VirtualToolEmbeddingRanking, this._expService); } - async addGroups(query: string, root: VirtualTool, tools: LanguageModelToolInformation[], token: CancellationToken): Promise { + async addGroups(query: string, root: VirtualTool, tools: LanguageModelToolInformation[], token: CancellationToken, endpoint?: IChatEndpoint): Promise { // If there's no need to group tools, just add them all directly; - if (tools.length < Constant.START_GROUPING_AFTER_TOOL_COUNT) { + + // if the model is gpt 4.1 or gpt-5 and there are more than START_BUILTIN_GROUPING_AFTER_TOOL_COUNT tools, we should group built-in tools + // otherwise, follow the existing logic of grouping all tools together + const currentEndpoint = endpoint ?? (await this._endpointProvider.getAllChatEndpoints()).find(e => e.isDefault) ?? await this._endpointProvider.getChatEndpoint('gpt-4.1'); + const modelFamily = currentEndpoint?.family; + const isGpt = modelFamily?.startsWith('gpt-4.1') || modelFamily?.startsWith('gpt-5'); + const defaultToolGroupingEnabled = this._configurationService.getExperimentBasedConfig(ConfigKey.Internal.DefaultToolsGrouped, this._expService); + + const triggerBuiltInGrouping = isGpt && tools.length > Constant.START_BUILTIN_GROUPING_AFTER_TOOL_COUNT && defaultToolGroupingEnabled; + + if (!triggerBuiltInGrouping && tools.length < Constant.START_GROUPING_AFTER_TOOL_COUNT) { root.contents = tools; return; } @@ -61,7 +75,7 @@ export class VirtualToolGrouper implements IToolCategorization { } else if (t.source instanceof LanguageModelToolMCPSource) { return 'mcp_' + t.source.label; } else { - return BUILT_IN_GROUP; + return BuiltInToolGroupHandler.BUILT_IN_GROUP_KEY; } }); @@ -79,12 +93,9 @@ export class VirtualToolGrouper implements IToolCategorization { const predictedToolsSw = new StopWatch(); const predictedToolsPromise = this.virtualToolEmbeddingRankingEnabled && this._getPredictedTools(query, tools, token).then(tools => ({ tools, durationMs: predictedToolsSw.elapsed() })); + // Now all toolsets (including built-in) go through _generateGroupsFromToolset const grouped = await Promise.all(Object.entries(byToolset).map(([key, tools]) => { - if (key === BUILT_IN_GROUP) { - return tools; - } else { - return this._generateGroupsFromToolset(key, tools, previousCategorizations.get(key), token); - } + return this._generateGroupsFromToolset(key, tools, previousCategorizations.get(key), token, endpoint); })); this._cache.flush(); @@ -215,8 +226,18 @@ export class VirtualToolGrouper implements IToolCategorization { } // Get unexpanded virtual tools, sorted by the ranker function (ascending order). + // If we've grouped default tools, exclude built-in tool groups from expansion const expandable = root.contents - .filter((t): t is VirtualTool => t instanceof VirtualTool && !t.isExpanded) + .filter((t): t is VirtualTool => { + if (!(t instanceof VirtualTool) || t.isExpanded) { + return false; + } + // Skip built-in tool groups if we've grouped default tools for GPT models + if (this._hasGroupedDefaultTools && t.metadata.toolsetKey === BuiltInToolGroupHandler.BUILT_IN_GROUP_KEY) { + return false; + } + return true; + }) .sort((a, b) => ranker(a) - ranker(b)); // Expand them until we hit the target limit @@ -237,7 +258,33 @@ export class VirtualToolGrouper implements IToolCategorization { } /** Top-level request to categorize a group of tools from a single source. */ - private async _generateGroupsFromToolset(key: string, tools: LanguageModelToolInformation[], previous: ISummarizedToolCategory[] | undefined, token: CancellationToken): Promise<(VirtualTool | LanguageModelToolInformation)[]> { + private async _generateGroupsFromToolset(key: string, tools: LanguageModelToolInformation[], previous: ISummarizedToolCategory[] | undefined, token: CancellationToken, endpoint?: IChatEndpoint): Promise<(VirtualTool | LanguageModelToolInformation)[]> { + // Handle built-in tools with predefined groups only if experimental setting is enabled + if (key === BuiltInToolGroupHandler.BUILT_IN_GROUP_KEY) { + const defaultToolGroupingEnabled = this._configurationService.getExperimentBasedConfig(ConfigKey.Internal.DefaultToolsGrouped, this._expService); + if (defaultToolGroupingEnabled) { + + // Get the model family from the current chat endpoint to check if grouping should apply + // Use the passed endpoint if available, otherwise fallback to default endpoint + const currentEndpoint = endpoint ?? (await this._endpointProvider.getAllChatEndpoints()).find(e => e.isDefault) ?? await this._endpointProvider.getChatEndpoint('gpt-4.1'); + const modelFamily = currentEndpoint?.family; + + // Only apply grouping for GPT-4.1 or GPT-5 + // For other models (like Claude/Sonnet), do not group default tools - let them expand to the limit + if (!modelFamily || + !(modelFamily === 'gpt-4.1' || + modelFamily.startsWith('gpt-5'))) { + return tools; + } + + // Mark that we've grouped default tools so we don't aggressively expand later + this._hasGroupedDefaultTools = true; + return this.builtInToolGroupHandler.createBuiltInToolGroups(tools); + } else { + return tools; + } + } + if (tools.length <= Constant.MIN_TOOLSET_SIZE_TO_GROUP) { return tools; } @@ -313,6 +360,7 @@ export class VirtualToolGrouper implements IToolCategorization { return virtualTools.concat(uncategorized); } + private async _getPredictedTools(query: string, tools: LanguageModelToolInformation[], token: CancellationToken): Promise { // compute the embeddings for the query const queryEmbedding = await this.embeddingsComputer.computeEmbeddings(EMBEDDING_TYPE_FOR_TOOL_GROUPING, [query], {}, new TelemetryCorrelationId('VirtualToolGrouper::_getPredictedTools'), token); diff --git a/src/extension/tools/common/virtualTools/virtualToolTypes.ts b/src/extension/tools/common/virtualTools/virtualToolTypes.ts index 788a12649..880eaef8e 100644 --- a/src/extension/tools/common/virtualTools/virtualToolTypes.ts +++ b/src/extension/tools/common/virtualTools/virtualToolTypes.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import type { LanguageModelToolInformation, LanguageModelToolResult } from 'vscode'; +import { IChatEndpoint } from '../../../../platform/networking/common/networking'; import { createServiceIdentifier } from '../../../../util/common/services'; import { CancellationToken } from '../../../../util/vs/base/common/cancellation'; import { IObservable } from '../../../../util/vs/base/common/observableInternal'; @@ -53,12 +54,12 @@ export interface IToolGrouping { * Returns a list of tools that should be used for the given request. * Internally re-reads the request and conversation state. */ - compute(query: string, token: CancellationToken): Promise; + compute(query: string, token: CancellationToken, endpoint?: IChatEndpoint): Promise; /** * Returns the complete tree of tools, used for diagnostic purposes. */ - computeAll(query: string, token: CancellationToken): Promise<(LanguageModelToolInformation | VirtualTool)[]>; + computeAll(query: string, token: CancellationToken, endpoint?: IChatEndpoint): Promise<(LanguageModelToolInformation | VirtualTool)[]>; } export interface IToolGroupingService { @@ -103,7 +104,7 @@ export interface IToolCategorization { * Called whenever new tools are added. The function should add each tool into * the appropriate virtual tool or top-level tool in the `root`. */ - addGroups(query: string, root: VirtualTool, tools: LanguageModelToolInformation[], token: CancellationToken): Promise; + addGroups(query: string, root: VirtualTool, tools: LanguageModelToolInformation[], token: CancellationToken, endpoint?: IChatEndpoint): Promise; /** * Recalculates the "embeddings" group, when enabled, so relevant tools diff --git a/src/extension/tools/common/virtualTools/virtualToolsConstants.ts b/src/extension/tools/common/virtualTools/virtualToolsConstants.ts index 6e7716367..a00b46849 100644 --- a/src/extension/tools/common/virtualTools/virtualToolsConstants.ts +++ b/src/extension/tools/common/virtualTools/virtualToolsConstants.ts @@ -8,6 +8,8 @@ import { HARD_TOOL_LIMIT } from '../../../../platform/configuration/common/confi /** Point after which we'll start grouping tools */ export const START_GROUPING_AFTER_TOOL_COUNT = HARD_TOOL_LIMIT / 2; // 64, currently +export const START_BUILTIN_GROUPING_AFTER_TOOL_COUNT = 20; // Lower bound above which we trigger built-in tool grouping + /** Re-expand groups until we have at least this many tools. */ export const EXPAND_UNTIL_COUNT = START_GROUPING_AFTER_TOOL_COUNT; /** diff --git a/src/platform/configuration/common/configurationService.ts b/src/platform/configuration/common/configurationService.ts index 9a9d165bb..74f920c3c 100644 --- a/src/platform/configuration/common/configurationService.ts +++ b/src/platform/configuration/common/configurationService.ts @@ -735,6 +735,7 @@ export namespace ConfigKey { export const PromptFileContext = defineExpSetting('chat.advanced.promptFileContextProvider.enabled', true); export const MultiReplaceString = defineExpSetting('chat.advanced.multiReplaceString.enabled', false, INTERNAL); + export const DefaultToolsGrouped = defineExpSetting('chat.advanced.tools.defaultToolsGrouped', true, INTERNAL); export const VirtualToolEmbeddingRanking = defineExpSetting('chat.advanced.virtualTools.embeddingRanking', false, INTERNAL); export const MultiReplaceStringGrok = defineExpSetting('chat.advanced.multiReplaceStringGrok.enabled', false, INTERNAL);