From 1ad9a25543bea6a3681557e2052fb6ef3a18c4ca Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Fri, 27 Jun 2025 09:04:52 +1000 Subject: [PATCH 1/2] Capture invocation and failure reasons for tools --- src/client/chat/baseTool.ts | 75 +++++++++++++++++++++++ src/client/chat/configurePythonEnvTool.ts | 17 +++-- src/client/chat/createVirtualEnvTool.ts | 20 +++--- src/client/chat/getExecutableTool.ts | 24 +++----- src/client/chat/getPythonEnvTool.ts | 31 +++++----- src/client/chat/installPackagesTool.ts | 40 +++++++----- src/client/chat/selectEnvTool.ts | 19 +++--- src/client/chat/utils.ts | 22 ++++--- src/client/common/errors/errorUtils.ts | 11 +--- src/client/telemetry/constants.ts | 1 + src/client/telemetry/index.ts | 26 ++++++++ 11 files changed, 194 insertions(+), 92 deletions(-) create mode 100644 src/client/chat/baseTool.ts diff --git a/src/client/chat/baseTool.ts b/src/client/chat/baseTool.ts new file mode 100644 index 000000000000..2eedbbe226e3 --- /dev/null +++ b/src/client/chat/baseTool.ts @@ -0,0 +1,75 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import { + CancellationToken, + LanguageModelTextPart, + LanguageModelTool, + LanguageModelToolInvocationOptions, + LanguageModelToolInvocationPrepareOptions, + LanguageModelToolResult, + PreparedToolInvocation, + Uri, + workspace, +} from 'vscode'; +import { IResourceReference, isCancellationError, resolveFilePath } from './utils'; +import { ErrorWithTelemetrySafeReason } from '../common/errors/errorUtils'; +import { sendTelemetryEvent } from '../telemetry'; +import { EventName } from '../telemetry/constants'; + +export abstract class BaseTool implements LanguageModelTool { + constructor(private readonly toolName: string) {} + + async invoke( + options: LanguageModelToolInvocationOptions, + token: CancellationToken, + ): Promise { + if (!workspace.isTrusted) { + return new LanguageModelToolResult([ + new LanguageModelTextPart('Cannot use this tool in an untrusted workspace.'), + ]); + } + let error: Error | undefined; + const resource = resolveFilePath(options.input.resourcePath); + try { + return await this.invokeImpl(options, resource, token); + } catch (ex) { + error = ex as any; + throw ex; + } finally { + const isCancelled = token.isCancellationRequested || (error ? isCancellationError(error) : false); + const failed = !!error || isCancelled; + const failureCategory = isCancelled + ? 'cancelled' + : error + ? error instanceof ErrorWithTelemetrySafeReason + ? error.telemetrySafeReason + : 'error' + : undefined; + sendTelemetryEvent(EventName.INVOKE_TOOL, undefined, { + toolName: this.toolName, + failed, + failureCategory, + }); + } + } + protected abstract invokeImpl( + options: LanguageModelToolInvocationOptions, + resource: Uri | undefined, + token: CancellationToken, + ): Promise; + + async prepareInvocation( + options: LanguageModelToolInvocationPrepareOptions, + token: CancellationToken, + ): Promise { + const resource = resolveFilePath(options.input.resourcePath); + return this.prepareInvocationImpl(options, resource, token); + } + + protected abstract prepareInvocationImpl( + options: LanguageModelToolInvocationPrepareOptions, + resource: Uri | undefined, + token: CancellationToken, + ): Promise; +} diff --git a/src/client/chat/configurePythonEnvTool.ts b/src/client/chat/configurePythonEnvTool.ts index e80347914a4d..0634b9c9ac34 100644 --- a/src/client/chat/configurePythonEnvTool.ts +++ b/src/client/chat/configurePythonEnvTool.ts @@ -19,18 +19,18 @@ import { TerminalCodeExecutionProvider } from '../terminals/codeExecution/termin import { getEnvDetailsForResponse, getToolResponseIfNotebook, - getUntrustedWorkspaceResponse, IResourceReference, isCancellationError, raceCancellationError, } from './utils'; -import { resolveFilePath } from './utils'; import { ITerminalHelper } from '../common/terminal/types'; import { IRecommendedEnvironmentService } from '../interpreter/configuration/types'; import { CreateVirtualEnvTool } from './createVirtualEnvTool'; import { ISelectPythonEnvToolArguments, SelectPythonEnvTool } from './selectEnvTool'; +import { BaseTool } from './baseTool'; -export class ConfigurePythonEnvTool implements LanguageModelTool { +export class ConfigurePythonEnvTool extends BaseTool + implements LanguageModelTool { private readonly terminalExecutionService: TerminalCodeExecutionProvider; private readonly terminalHelper: ITerminalHelper; private readonly recommendedEnvService: IRecommendedEnvironmentService; @@ -40,6 +40,7 @@ export class ConfigurePythonEnvTool implements LanguageModelTool( ICodeExecutionService, 'standard', @@ -50,14 +51,11 @@ export class ConfigurePythonEnvTool implements LanguageModelTool, + resource: Uri | undefined, token: CancellationToken, ): Promise { - if (!workspace.isTrusted) { - return getUntrustedWorkspaceResponse(); - } - const resource = resolveFilePath(options.input.resourcePath); const notebookResponse = getToolResponseIfNotebook(resource); if (notebookResponse) { return notebookResponse; @@ -101,8 +99,9 @@ export class ConfigurePythonEnvTool implements LanguageModelTool, + _resource: Uri | undefined, _token: CancellationToken, ): Promise { return { diff --git a/src/client/chat/createVirtualEnvTool.ts b/src/client/chat/createVirtualEnvTool.ts index 9bbcc466fe28..56760d2b4bef 100644 --- a/src/client/chat/createVirtualEnvTool.ts +++ b/src/client/chat/createVirtualEnvTool.ts @@ -22,12 +22,10 @@ import { doesWorkspaceHaveVenvOrCondaEnv, getDisplayVersion, getEnvDetailsForResponse, - getUntrustedWorkspaceResponse, IResourceReference, isCancellationError, raceCancellationError, } from './utils'; -import { resolveFilePath } from './utils'; import { ITerminalHelper } from '../common/terminal/types'; import { raceTimeout, sleep } from '../common/utils/async'; import { IInterpreterPathService } from '../common/types'; @@ -44,12 +42,14 @@ import { StopWatch } from '../common/utils/stopWatch'; import { useEnvExtension } from '../envExt/api.internal'; import { PythonEnvironment } from '../envExt/types'; import { hideEnvCreation } from '../pythonEnvironments/creation/provider/hideEnvCreation'; +import { BaseTool } from './baseTool'; interface ICreateVirtualEnvToolParams extends IResourceReference { packageList?: string[]; // Added only becausewe have ability to create a virtual env with list of packages same tool within the in Python Env extension. } -export class CreateVirtualEnvTool implements LanguageModelTool { +export class CreateVirtualEnvTool extends BaseTool + implements LanguageModelTool { private readonly terminalExecutionService: TerminalCodeExecutionProvider; private readonly terminalHelper: ITerminalHelper; private readonly recommendedEnvService: IRecommendedEnvironmentService; @@ -60,6 +60,7 @@ export class CreateVirtualEnvTool implements LanguageModelTool( ICodeExecutionService, 'standard', @@ -70,14 +71,11 @@ export class CreateVirtualEnvTool implements LanguageModelTool, + resource: Uri | undefined, token: CancellationToken, ): Promise { - if (!workspace.isTrusted) { - return getUntrustedWorkspaceResponse(); - } - const resource = resolveFilePath(options.input.resourcePath); let info = await this.getPreferredEnvForCreation(resource); if (!info) { traceWarn(`Called ${CreateVirtualEnvTool.toolName} tool not invoked, no preferred environment found.`); @@ -170,11 +168,11 @@ export class CreateVirtualEnvTool implements LanguageModelTool, + async prepareInvocationImpl( + _options: LanguageModelToolInvocationPrepareOptions, + resource: Uri | undefined, token: CancellationToken, ): Promise { - const resource = resolveFilePath(options.input.resourcePath); const info = await raceCancellationError(this.getPreferredEnvForCreation(resource), token); if (!info) { return {}; diff --git a/src/client/chat/getExecutableTool.ts b/src/client/chat/getExecutableTool.ts index 8c1bed632384..746a540d14f8 100644 --- a/src/client/chat/getExecutableTool.ts +++ b/src/client/chat/getExecutableTool.ts @@ -10,7 +10,7 @@ import { LanguageModelToolInvocationPrepareOptions, LanguageModelToolResult, PreparedToolInvocation, - workspace, + Uri, } from 'vscode'; import { PythonExtension } from '../api/types'; import { IServiceContainer } from '../ioc/types'; @@ -20,15 +20,14 @@ import { getEnvDisplayName, getEnvironmentDetails, getToolResponseIfNotebook, - getUntrustedWorkspaceResponse, IResourceReference, raceCancellationError, } from './utils'; -import { resolveFilePath } from './utils'; import { ITerminalHelper } from '../common/terminal/types'; import { IDiscoveryAPI } from '../pythonEnvironments/base/locator'; +import { BaseTool } from './baseTool'; -export class GetExecutableTool implements LanguageModelTool { +export class GetExecutableTool extends BaseTool implements LanguageModelTool { private readonly terminalExecutionService: TerminalCodeExecutionProvider; private readonly terminalHelper: ITerminalHelper; public static readonly toolName = 'get_python_executable_details'; @@ -37,21 +36,18 @@ export class GetExecutableTool implements LanguageModelTool private readonly serviceContainer: IServiceContainer, private readonly discovery: IDiscoveryAPI, ) { + super(GetExecutableTool.toolName); this.terminalExecutionService = this.serviceContainer.get( ICodeExecutionService, 'standard', ); this.terminalHelper = this.serviceContainer.get(ITerminalHelper); } - async invoke( - options: LanguageModelToolInvocationOptions, + async invokeImpl( + _options: LanguageModelToolInvocationOptions, + resourcePath: Uri | undefined, token: CancellationToken, ): Promise { - if (!workspace.isTrusted) { - return getUntrustedWorkspaceResponse(); - } - - const resourcePath = resolveFilePath(options.input.resourcePath); const notebookResponse = getToolResponseIfNotebook(resourcePath); if (notebookResponse) { return notebookResponse; @@ -68,11 +64,11 @@ export class GetExecutableTool implements LanguageModelTool return new LanguageModelToolResult([new LanguageModelTextPart(message)]); } - async prepareInvocation?( - options: LanguageModelToolInvocationPrepareOptions, + async prepareInvocationImpl( + _options: LanguageModelToolInvocationPrepareOptions, + resourcePath: Uri | undefined, token: CancellationToken, ): Promise { - const resourcePath = resolveFilePath(options.input.resourcePath); if (getToolResponseIfNotebook(resourcePath)) { return {}; } diff --git a/src/client/chat/getPythonEnvTool.ts b/src/client/chat/getPythonEnvTool.ts index 91184d8e4ef2..ffa30068df07 100644 --- a/src/client/chat/getPythonEnvTool.ts +++ b/src/client/chat/getPythonEnvTool.ts @@ -10,7 +10,7 @@ import { LanguageModelToolInvocationPrepareOptions, LanguageModelToolResult, PreparedToolInvocation, - workspace, + Uri, } from 'vscode'; import { PythonExtension } from '../api/types'; import { IServiceContainer } from '../ioc/types'; @@ -20,16 +20,17 @@ import { IProcessServiceFactory, IPythonExecutionFactory } from '../common/proce import { getEnvironmentDetails, getToolResponseIfNotebook, - getUntrustedWorkspaceResponse, IResourceReference, raceCancellationError, } from './utils'; -import { resolveFilePath } from './utils'; import { getPythonPackagesResponse } from './listPackagesTool'; import { ITerminalHelper } from '../common/terminal/types'; import { getEnvExtApi, useEnvExtension } from '../envExt/api.internal'; +import { ErrorWithTelemetrySafeReason } from '../common/errors/errorUtils'; +import { BaseTool } from './baseTool'; -export class GetEnvironmentInfoTool implements LanguageModelTool { +export class GetEnvironmentInfoTool extends BaseTool + implements LanguageModelTool { private readonly terminalExecutionService: TerminalCodeExecutionProvider; private readonly pythonExecFactory: IPythonExecutionFactory; private readonly processServiceFactory: IProcessServiceFactory; @@ -39,6 +40,7 @@ export class GetEnvironmentInfoTool implements LanguageModelTool( ICodeExecutionService, 'standard', @@ -48,15 +50,11 @@ export class GetEnvironmentInfoTool implements LanguageModelTool(ITerminalHelper); } - async invoke( - options: LanguageModelToolInvocationOptions, + async invokeImpl( + _options: LanguageModelToolInvocationOptions, + resourcePath: Uri | undefined, token: CancellationToken, ): Promise { - if (!workspace.isTrusted) { - return getUntrustedWorkspaceResponse(); - } - - const resourcePath = resolveFilePath(options.input.resourcePath); const notebookResponse = getToolResponseIfNotebook(resourcePath); if (notebookResponse) { return notebookResponse; @@ -66,7 +64,10 @@ export class GetEnvironmentInfoTool implements LanguageModelTool, + async prepareInvocationImpl( + _options: LanguageModelToolInvocationPrepareOptions, + resourcePath: Uri | undefined, _token: CancellationToken, ): Promise { - const resourcePath = resolveFilePath(options.input.resourcePath); if (getToolResponseIfNotebook(resourcePath)) { return {}; } diff --git a/src/client/chat/installPackagesTool.ts b/src/client/chat/installPackagesTool.ts index e359fce110db..f7795620cf13 100644 --- a/src/client/chat/installPackagesTool.ts +++ b/src/client/chat/installPackagesTool.ts @@ -10,46 +10,45 @@ import { LanguageModelToolInvocationPrepareOptions, LanguageModelToolResult, PreparedToolInvocation, - workspace, + Uri, } from 'vscode'; import { PythonExtension } from '../api/types'; import { IServiceContainer } from '../ioc/types'; import { getEnvDisplayName, getToolResponseIfNotebook, - getUntrustedWorkspaceResponse, IResourceReference, isCancellationError, isCondaEnv, raceCancellationError, } from './utils'; -import { resolveFilePath } from './utils'; import { IModuleInstaller } from '../common/installer/types'; import { ModuleInstallerType } from '../pythonEnvironments/info'; import { IDiscoveryAPI } from '../pythonEnvironments/base/locator'; import { getEnvExtApi, useEnvExtension } from '../envExt/api.internal'; +import { ErrorWithTelemetrySafeReason } from '../common/errors/errorUtils'; +import { BaseTool } from './baseTool'; export interface IInstallPackageArgs extends IResourceReference { packageList: string[]; } -export class InstallPackagesTool implements LanguageModelTool { +export class InstallPackagesTool extends BaseTool + implements LanguageModelTool { public static readonly toolName = 'install_python_packages'; constructor( private readonly api: PythonExtension['environments'], private readonly serviceContainer: IServiceContainer, private readonly discovery: IDiscoveryAPI, - ) {} + ) { + super(InstallPackagesTool.toolName); + } - async invoke( + async invokeImpl( options: LanguageModelToolInvocationOptions, + resourcePath: Uri | undefined, token: CancellationToken, ): Promise { - if (!workspace.isTrusted) { - return getUntrustedWorkspaceResponse(); - } - - const resourcePath = resolveFilePath(options.input.resourcePath); const packageCount = options.input.packageList.length; const packagePlurality = packageCount === 1 ? 'package' : 'packages'; const notebookResponse = getToolResponseIfNotebook(resourcePath); @@ -80,17 +79,26 @@ export class InstallPackagesTool implements LanguageModelTool(IModuleInstaller); const installerType = isConda ? ModuleInstallerType.Conda : ModuleInstallerType.Pip; const installer = installers.find((i) => i.type === installerType); if (!installer) { - throw new Error(`No installer found for the environment type: ${installerType}`); + throw new ErrorWithTelemetrySafeReason( + `No installer found for the environment type: ${installerType}`, + 'noInstallerFound', + ); } if (!installer.isSupported(resourcePath)) { - throw new Error(`Installer ${installerType} not supported for the environment type: ${installerType}`); + throw new ErrorWithTelemetrySafeReason( + `Installer ${installerType} not supported for the environment type: ${installerType}`, + 'installerNotSupported', + ); } for (const packageName of options.input.packageList) { await installer.installModule(packageName, resourcePath, token, undefined, { @@ -110,11 +118,11 @@ export class InstallPackagesTool implements LanguageModelTool, + resourcePath: Uri | undefined, token: CancellationToken, ): Promise { - const resourcePath = resolveFilePath(options.input.resourcePath); const packageCount = options.input.packageList.length; if (getToolResponseIfNotebook(resourcePath)) { return {}; diff --git a/src/client/chat/selectEnvTool.ts b/src/client/chat/selectEnvTool.ts index 5f1c2f6b7c36..9eeebdfc1b56 100644 --- a/src/client/chat/selectEnvTool.ts +++ b/src/client/chat/selectEnvTool.ts @@ -24,10 +24,8 @@ import { doesWorkspaceHaveVenvOrCondaEnv, getEnvDetailsForResponse, getToolResponseIfNotebook, - getUntrustedWorkspaceResponse, IResourceReference, } from './utils'; -import { resolveFilePath } from './utils'; import { ITerminalHelper } from '../common/terminal/types'; import { raceTimeout } from '../common/utils/async'; import { Commands, Octicons } from '../common/constants'; @@ -38,12 +36,14 @@ import { Common, InterpreterQuickPickList } from '../common/utils/localize'; import { showQuickPick } from '../common/vscodeApis/windowApis'; import { DisposableStore } from '../common/utils/resourceLifecycle'; import { traceError, traceVerbose, traceWarn } from '../logging'; +import { BaseTool } from './baseTool'; export interface ISelectPythonEnvToolArguments extends IResourceReference { reason?: 'cancelled'; } -export class SelectPythonEnvTool implements LanguageModelTool { +export class SelectPythonEnvTool extends BaseTool + implements LanguageModelTool { private readonly terminalExecutionService: TerminalCodeExecutionProvider; private readonly terminalHelper: ITerminalHelper; public static readonly toolName = 'selectEnvironment'; @@ -51,6 +51,7 @@ export class SelectPythonEnvTool implements LanguageModelTool( ICodeExecutionService, 'standard', @@ -58,15 +59,11 @@ export class SelectPythonEnvTool implements LanguageModelTool(ITerminalHelper); } - async invoke( + async invokeImpl( options: LanguageModelToolInvocationOptions, + resource: Uri | undefined, token: CancellationToken, ): Promise { - if (!workspace.isTrusted) { - return getUntrustedWorkspaceResponse(); - } - - const resource = resolveFilePath(options.input.resourcePath); let selected: boolean | undefined = false; const hasVenvOrCondaEnvInWorkspaceFolder = doesWorkspaceHaveVenvOrCondaEnv(resource, this.api); if (options.input.reason === 'cancelled' || hasVenvOrCondaEnvInWorkspaceFolder) { @@ -115,11 +112,11 @@ export class SelectPythonEnvTool implements LanguageModelTool, + resource: Uri | undefined, _token: CancellationToken, ): Promise { - const resource = resolveFilePath(options.input.resourcePath); if (getToolResponseIfNotebook(resource)) { return {}; } diff --git a/src/client/chat/utils.ts b/src/client/chat/utils.ts index 7c5a4dd9725f..bddd26049668 100644 --- a/src/client/chat/utils.ts +++ b/src/client/chat/utils.ts @@ -18,6 +18,7 @@ import { Conda } from '../pythonEnvironments/common/environmentManagers/conda'; import { JUPYTER_EXTENSION_ID, NotebookCellScheme } from '../common/constants'; import { dirname, join } from 'path'; import { resolveEnvironment, useEnvExtension } from '../envExt/api.internal'; +import { ErrorWithTelemetrySafeReason } from '../common/errors/errorUtils'; export interface IResourceReference { resourcePath?: string; @@ -85,7 +86,10 @@ export async function getEnvironmentDetails( (await raceCancellationError(resolveEnvironment(envPath.id), token)) || (await raceCancellationError(resolveEnvironment(envPath.path), token)); if (!environment || !environment.version) { - throw new Error('No environment found for the provided resource path: ' + resourcePath?.fsPath); + throw new ErrorWithTelemetrySafeReason( + 'No environment found for the provided resource path: ' + resourcePath?.fsPath, + 'noEnvFound', + ); } envVersion = environment.version; try { @@ -104,7 +108,10 @@ export async function getEnvironmentDetails( } else { const environment = await raceCancellationError(api.resolveEnvironment(envPath), token); if (!environment || !environment.version) { - throw new Error('No environment found for the provided resource path: ' + resourcePath?.fsPath); + throw new ErrorWithTelemetrySafeReason( + 'No environment found for the provided resource path: ' + resourcePath?.fsPath, + 'noEnvFound', + ); } envType = environment.environment?.type || 'unknown'; envVersion = environment.version.sysVersion || 'unknown'; @@ -126,10 +133,6 @@ export async function getEnvironmentDetails( return message.join('\n'); } -export function getUntrustedWorkspaceResponse() { - return new LanguageModelToolResult([new LanguageModelTextPart('Cannot use this tool in an untrusted workspace.')]); -} - export async function getTerminalCommand( environment: ResolvedEnvironment, resource: Uri | undefined, @@ -244,12 +247,15 @@ export async function getEnvDetailsForResponse( token: CancellationToken, ): Promise { if (!workspace.isTrusted) { - throw new Error('Cannot use this tool in an untrusted workspace.'); + throw new ErrorWithTelemetrySafeReason('Cannot use this tool in an untrusted workspace.', 'untrustedWorkspace'); } const envPath = api.getActiveEnvironmentPath(resource); environment = environment || (await raceCancellationError(api.resolveEnvironment(envPath), token)); if (!environment || !environment.version) { - throw new Error('No environment found for the provided resource path: ' + resource?.fsPath); + throw new ErrorWithTelemetrySafeReason( + 'No environment found for the provided resource path: ' + resource?.fsPath, + 'noEnvFound', + ); } const message = await getEnvironmentDetails( resource, diff --git a/src/client/common/errors/errorUtils.ts b/src/client/common/errors/errorUtils.ts index 2c666acb105b..7867d5ccfe30 100644 --- a/src/client/common/errors/errorUtils.ts +++ b/src/client/common/errors/errorUtils.ts @@ -1,8 +1,6 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -import { EOL } from 'os'; - export class ErrorUtils { public static outputHasModuleNotInstalledError(moduleName: string, content?: string): boolean { return content && @@ -14,13 +12,10 @@ export class ErrorUtils { } /** - * Wraps an error with a custom error message, retaining the call stack information. + * An error class that contains a telemetry safe reason. */ -export class WrappedError extends Error { - constructor(message: string, originalException: Error) { +export class ErrorWithTelemetrySafeReason extends Error { + constructor(message: string, public readonly telemetrySafeReason: string) { super(message); - // Retain call stack that trapped the error and rethrows this error. - // Also retain the call stack of the original error. - this.stack = `${new Error('').stack}${EOL}${EOL}${originalException.stack}`; } } diff --git a/src/client/telemetry/constants.ts b/src/client/telemetry/constants.ts index ecc44177338a..eff32a6e3299 100644 --- a/src/client/telemetry/constants.ts +++ b/src/client/telemetry/constants.ts @@ -7,6 +7,7 @@ export enum EventName { FORMAT_ON_TYPE = 'FORMAT.FORMAT_ON_TYPE', EDITOR_LOAD = 'EDITOR.LOAD', REPL = 'REPL', + INVOKE_TOOL = 'INVOKE_TOOL', CREATE_NEW_FILE_COMMAND = 'CREATE_NEW_FILE_COMMAND', SELECT_INTERPRETER = 'SELECT_INTERPRETER', SELECT_INTERPRETER_ENTER_BUTTON = 'SELECT_INTERPRETER_ENTER_BUTTON', diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index 6c97bd083d96..a387e45d694a 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -1980,6 +1980,32 @@ export interface IEventNamePropertyMapping { */ replType: 'Terminal' | 'Native' | 'manualTerminal' | `runningScript`; }; + /** + * Telemetry event sent when invoking a Tool + */ + /* __GDPR__ + "invokeTool" : { + "duration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true, "owner": "donjayamanne" }, + "toolName" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "owner": "donjayamanne" }, + "failed": {"classification":"SystemMetaData","purpose":"FeatureInsight","comment":"Whether there was a failure. Common to most of the events.", "owner": "donjayamanne" }, + "failureCategory": {"classification":"SystemMetaData","purpose":"FeatureInsight","comment":"A reason that we generate (e.g. kerneldied, noipykernel, etc), more like a category of the error. Common to most of the events.", "owner": "donjayamanne" } + } + */ + [EventName.INVOKE_TOOL]: { + /** + * Tool name. + */ + toolName: string; + /** + * Whether there was a failure. + * Common to most of the events. + */ + failed: boolean; + /** + * A reason the error was thrown. + */ + failureCategory?: string; + }; /** * Telemetry event sent if and when user configure tests command. This command can be trigerred from multiple places in the extension. (Command palette, prompt etc.) */ From 39fb1d79976a35b180d72ec15100a0c682cf1a04 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Fri, 27 Jun 2025 09:07:10 +1000 Subject: [PATCH 2/2] Updates --- src/client/chat/getPythonEnvTool.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/client/chat/getPythonEnvTool.ts b/src/client/chat/getPythonEnvTool.ts index ffa30068df07..ed1dd0374424 100644 --- a/src/client/chat/getPythonEnvTool.ts +++ b/src/client/chat/getPythonEnvTool.ts @@ -17,12 +17,7 @@ import { IServiceContainer } from '../ioc/types'; import { ICodeExecutionService } from '../terminals/types'; import { TerminalCodeExecutionProvider } from '../terminals/codeExecution/terminalCodeExecution'; import { IProcessServiceFactory, IPythonExecutionFactory } from '../common/process/types'; -import { - getEnvironmentDetails, - getToolResponseIfNotebook, - IResourceReference, - raceCancellationError, -} from './utils'; +import { getEnvironmentDetails, getToolResponseIfNotebook, IResourceReference, raceCancellationError } from './utils'; import { getPythonPackagesResponse } from './listPackagesTool'; import { ITerminalHelper } from '../common/terminal/types'; import { getEnvExtApi, useEnvExtension } from '../envExt/api.internal';