From bb885aff9424c71e8c53f2eeb4b091dcdb25c955 Mon Sep 17 00:00:00 2001 From: BeniBenj Date: Tue, 20 Jan 2026 13:01:11 +0100 Subject: [PATCH] Add telemetry for tools picker --- .../chat/browser/actions/chatToolActions.ts | 2 +- .../chat/browser/actions/chatToolPicker.ts | 37 +++++++++++++++++++ .../promptToolsCodeLensProvider.ts | 2 +- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatToolActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatToolActions.ts index a8ad2887cac2..f37633f0f941 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatToolActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatToolActions.ts @@ -188,7 +188,7 @@ class ConfigureToolsAction extends Action2 { }); try { - const result = await instaService.invokeFunction(showToolsPicker, placeholder, description, () => entriesMap.get(), cts.token); + const result = await instaService.invokeFunction(showToolsPicker, placeholder, 'chatInput', description, () => entriesMap.get(), cts.token); if (result) { widget.input.selectedToolsModel.set(result, false); } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatToolPicker.ts b/src/vs/workbench/contrib/chat/browser/actions/chatToolPicker.ts index 75a41db3f6e9..2c17502c21b6 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatToolPicker.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatToolPicker.ts @@ -17,6 +17,7 @@ import { IContextKeyService } from '../../../../../platform/contextkey/common/co import { ExtensionIdentifier } from '../../../../../platform/extensions/common/extensions.js'; import { ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; import { IQuickInputButton, IQuickInputService, IQuickPickItem, IQuickTreeItem } from '../../../../../platform/quickinput/common/quickInput.js'; +import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js'; import { IEditorService } from '../../../../services/editor/common/editorService.js'; import { ExtensionEditorTab, IExtensionsWorkbenchService } from '../../../extensions/common/extensions.js'; import { McpCommandIds } from '../../../mcp/common/mcpCommandIds.js'; @@ -190,6 +191,7 @@ function createToolSetTreeItem(toolset: ToolSet, checked: boolean, editorService export async function showToolsPicker( accessor: ServicesAccessor, placeHolder: string, + source: string, description?: string, getToolsEntries?: () => ReadonlyMap, token?: CancellationToken @@ -203,6 +205,7 @@ export async function showToolsPicker( const editorService = accessor.get(IEditorService); const mcpWorkbenchService = accessor.get(IMcpWorkbenchService); const toolsService = accessor.get(ILanguageModelToolsService); + const telemetryService = accessor.get(ITelemetryService); const toolLimit = accessor.get(IContextKeyService).getContextKeyValue(ChatContextKeys.chatToolGroupingThreshold.key); const mcpServerByTool = new Map(); @@ -593,11 +596,45 @@ export async function showToolsPicker( })); } + // Capture initial state for telemetry comparison + const initialStateString = serializeToolsState(collectResults()); + treePicker.show(); await Promise.race([Event.toPromise(Event.any(treePicker.onDidHide, didAcceptFinalItem.event), store)]); + // Send telemetry whether the tool selection changed + sendDidChangeEvent(source, telemetryService, initialStateString !== serializeToolsState(collectResults())); + store.dispose(); return didAccept ? collectResults() : undefined; } + +function serializeToolsState(state: ReadonlyMap): string { + const entries: [string, boolean][] = []; + state.forEach((value, key) => { + entries.push([key.id, value]); + }); + entries.sort((a, b) => a[0].localeCompare(b[0])); + return JSON.stringify(entries); +} + +function sendDidChangeEvent(source: string, telemetryService: ITelemetryService, changed: boolean): void { + type ToolPickerClosedEvent = { + changed: boolean; + source: string; + }; + + type ToolPickerClosedClassification = { + changed: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the user changed the tool selection from the initial state.' }; + source: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The source of the tool picker event.' }; + owner: 'benibenj'; + comment: 'Tracks whether users modify tool selection in the tool picker.'; + }; + + telemetryService.publicLog2('chatToolPickerClosed', { + source, + changed, + }); +} diff --git a/src/vs/workbench/contrib/chat/browser/promptSyntax/promptToolsCodeLensProvider.ts b/src/vs/workbench/contrib/chat/browser/promptSyntax/promptToolsCodeLensProvider.ts index 4c9a0c632039..37a96a89d103 100644 --- a/src/vs/workbench/contrib/chat/browser/promptSyntax/promptToolsCodeLensProvider.ts +++ b/src/vs/workbench/contrib/chat/browser/promptSyntax/promptToolsCodeLensProvider.ts @@ -85,7 +85,7 @@ class PromptToolsCodeLensProvider extends Disposable implements CodeLensProvider private async updateTools(model: ITextModel, range: Range, selectedTools: readonly string[], target: string | undefined): Promise { const selectedToolsNow = () => this.languageModelToolsService.toToolAndToolSetEnablementMap(selectedTools, target); - const newSelectedAfter = await this.instantiationService.invokeFunction(showToolsPicker, localize('placeholder', "Select tools"), undefined, selectedToolsNow); + const newSelectedAfter = await this.instantiationService.invokeFunction(showToolsPicker, localize('placeholder', "Select tools"), 'codeLens', undefined, selectedToolsNow); if (!newSelectedAfter) { return; }