From cd24be84d5891d33b79e1820f18d03666a9f602b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 11 Oct 2024 10:37:41 -0700 Subject: [PATCH 01/10] Rename tool id to 'name' --- .../common/extensionsApiProposals.ts | 2 +- .../workbench/api/common/extHost.api.impl.ts | 4 +-- .../api/common/extHostTypeConverters.ts | 3 +- .../browser/actions/chatContextActions.ts | 2 +- .../browser/contrib/chatInputCompletions.ts | 4 +-- .../chat/browser/languageModelToolsService.ts | 2 +- .../chat/common/languageModelToolsService.ts | 2 +- .../tools/languageModelToolsContribution.ts | 29 ++++++++++--------- src/vscode-dts/vscode.proposed.lmTools.d.ts | 19 ++++-------- 9 files changed, 31 insertions(+), 36 deletions(-) diff --git a/src/vs/platform/extensions/common/extensionsApiProposals.ts b/src/vs/platform/extensions/common/extensionsApiProposals.ts index 7d844383e91..4267b5062af 100644 --- a/src/vs/platform/extensions/common/extensionsApiProposals.ts +++ b/src/vs/platform/extensions/common/extensionsApiProposals.ts @@ -244,7 +244,7 @@ const _allApiProposals = { }, lmTools: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.lmTools.d.ts', - version: 8 + version: 9 }, mappedEditsProvider: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts', diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index d604b1d151d..bfce6627972 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1491,9 +1491,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I return extHostEmbeddings.computeEmbeddings(embeddingsModel, input, token); } }, - registerTool(toolId: string, tool: vscode.LanguageModelTool) { + registerTool(name: string, tool: vscode.LanguageModelTool) { checkProposedApiEnabled(extension, 'lmTools'); - return extHostLanguageModelTools.registerTool(extension, toolId, tool); + return extHostLanguageModelTools.registerTool(extension, name, tool); }, invokeTool(toolId: string, parameters: vscode.LanguageModelToolInvocationOptions, token: vscode.CancellationToken) { checkProposedApiEnabled(extension, 'lmTools'); diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 426f4128330..f2ad51684bb 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -2927,7 +2927,8 @@ export namespace DebugTreeItem { export namespace LanguageModelToolDescription { export function to(item: IToolData): vscode.LanguageModelToolDescription { return { - id: item.id, + // Note- the reason this is a unique 'name' is just to avoid confusion with the toolCallId + name: item.id, description: item.modelDescription, parametersSchema: item.parametersSchema, displayName: item.displayName, diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts index ab4664ef5e5..b4eaba6e8a4 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts @@ -459,7 +459,7 @@ export class AttachContextAction extends Action2 { if (tool.canBeInvokedManually) { const item: IToolQuickPickItem = { kind: 'tool', - label: tool.displayName ?? tool.name ?? '', + label: tool.displayName ?? tool.name2 ?? '', id: tool.id, icon: ThemeIcon.isThemeIcon(tool.icon) ? tool.icon : undefined // TODO need to support icon path? }; diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts index d54d953b3c4..66f4c84f833 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts @@ -578,9 +578,9 @@ class VariableCompletions extends Disposable { if (!usedAgent || usedAgent.agent.supportsToolReferences) { toolItems.push(...Array.from(toolsService.getTools()) .filter(t => t.canBeInvokedManually) - .filter(t => !usedToolNames.has(t.name ?? '')) + .filter(t => !usedToolNames.has(t.name2 ?? '')) .map((t): CompletionItem => { - const withLeader = `${chatVariableLeader}${t.name}`; + const withLeader = `${chatVariableLeader}${t.name2}`; return { label: withLeader, range, diff --git a/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts index 226acd4f4a4..c83111b8d1a 100644 --- a/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts @@ -112,7 +112,7 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo getToolByName(name: string): IToolData | undefined { for (const toolData of this.getTools()) { - if (toolData.name === name) { + if (toolData.name2 === name) { return toolData; } } diff --git a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts index ac0c2a4ef1f..d232f2733c0 100644 --- a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts @@ -15,7 +15,7 @@ import { createDecorator } from '../../../../platform/instantiation/common/insta export interface IToolData { id: string; - name?: string; + name2?: string; icon?: { dark: URI; light?: URI } | ThemeIcon; when?: ContextKeyExpression; tags?: string[]; diff --git a/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts b/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts index 2bb02dd9ced..391b8116838 100644 --- a/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts +++ b/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts @@ -17,8 +17,8 @@ import { ILanguageModelToolsService, IToolData } from '../languageModelToolsServ import * as extensionsRegistry from '../../../../services/extensions/common/extensionsRegistry.js'; interface IRawToolContribution { - id: string; - name?: string; + name: string; + name2?: string; icon?: string | { light: string; dark: string }; when?: string; tags?: string[]; @@ -35,7 +35,7 @@ const languageModelToolsExtensionPoint = extensionsRegistry.ExtensionsRegistry.r extensionPoint: 'languageModelTools', activationEventsGenerator: (contributions: IRawToolContribution[], result) => { for (const contrib of contributions) { - result.push(`onLanguageModelTool:${contrib.id}`); + result.push(`onLanguageModelTool:${contrib.name}`); } }, jsonSchema: { @@ -45,16 +45,16 @@ const languageModelToolsExtensionPoint = extensionsRegistry.ExtensionsRegistry.r additionalProperties: false, type: 'object', defaultSnippets: [{ body: { name: '', description: '' } }], - required: ['id', 'modelDescription'], + required: ['name', 'modelDescription'], properties: { - id: { - description: localize('toolId', "A unique id for this tool."), + name: { + description: localize('toolName', "A unique name for this tool. This name must be a globally unique identifier, and is also used as a name when presenting this tool to an LLM."), type: 'string', // Borrow OpenAI's requirement for tool names pattern: '^[\\w-]+$' }, - name: { - markdownDescription: localize('toolName', "If {0} is enabled for this tool, the user may use '#' with this name to invoke the tool in a query. Otherwise, the name is not required. Name must not contain whitespace.", '`canBeInvokedManually`'), + name2: { + markdownDescription: localize('toolName2', "If {0} is enabled for this tool, the user may use '#' with this name to invoke the tool in a query. Otherwise, the name is not required. Name must not contain whitespace.", '`canBeInvokedManually`'), type: 'string', pattern: '^[\\w-]+$' }, @@ -140,17 +140,17 @@ export class LanguageModelToolsExtensionPointHandler implements IWorkbenchContri languageModelToolsExtensionPoint.setHandler((extensions, delta) => { for (const extension of delta.added) { for (const rawTool of extension.value) { - if (!rawTool.id || !rawTool.modelDescription) { + if (!rawTool.name || !rawTool.modelDescription) { logService.error(`Extension '${extension.description.identifier.value}' CANNOT register tool without name and modelDescription: ${JSON.stringify(rawTool)}`); continue; } - if (!rawTool.id.match(/^[\w-]+$/)) { - logService.error(`Extension '${extension.description.identifier.value}' CANNOT register tool with invalid id: ${rawTool.id}. The id must match /^[\\w-]+$/.`); + if (!rawTool.name.match(/^[\w-]+$/)) { + logService.error(`Extension '${extension.description.identifier.value}' CANNOT register tool with invalid id: ${rawTool.name}. The id must match /^[\\w-]+$/.`); continue; } - if (rawTool.canBeInvokedManually && !rawTool.name) { + if (rawTool.canBeInvokedManually && !rawTool.name2) { logService.error(`Extension '${extension.description.identifier.value}' CANNOT register tool with 'canBeInvokedManually' set without a name: ${JSON.stringify(rawTool)}`); continue; } @@ -171,18 +171,19 @@ export class LanguageModelToolsExtensionPointHandler implements IWorkbenchContri const tool: IToolData = { ...rawTool, + id: rawTool.name, icon, when: rawTool.when ? ContextKeyExpr.deserialize(rawTool.when) : undefined, supportedContentTypes: rawTool.supportedContentTypes ? rawTool.supportedContentTypes : [], }; const disposable = languageModelToolsService.registerToolData(tool); - this._registrationDisposables.set(toToolKey(extension.description.identifier, rawTool.id), disposable); + this._registrationDisposables.set(toToolKey(extension.description.identifier, rawTool.name), disposable); } } for (const extension of delta.removed) { for (const tool of extension.value) { - this._registrationDisposables.deleteAndDispose(toToolKey(extension.description.identifier, tool.id)); + this._registrationDisposables.deleteAndDispose(toToolKey(extension.description.identifier, tool.name)); } } }); diff --git a/src/vscode-dts/vscode.proposed.lmTools.d.ts b/src/vscode-dts/vscode.proposed.lmTools.d.ts index c5ecb486a82..1bdfaaca092 100644 --- a/src/vscode-dts/vscode.proposed.lmTools.d.ts +++ b/src/vscode-dts/vscode.proposed.lmTools.d.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// version: 8 +// version: 9 // https://github.com/microsoft/vscode/issues/213274 declare module 'vscode' { @@ -12,10 +12,9 @@ declare module 'vscode' { // API -> LM: an tool/function that is available to the language model export interface LanguageModelChatTool { - // TODO@API should use "id" here to match vscode tools, or keep name to match OpenAI? Align everything. name: string; description: string; - parametersSchema?: JSONSchema; + parametersSchema?: Object; } // API -> LM: add tools as request option @@ -93,7 +92,7 @@ declare module 'vscode' { * point. A registered tool is available in the {@link lm.tools} list for any extension to see. But in order for it to * be seen by a language model, it must be passed in the list of available tools in {@link LanguageModelChatRequestOptions.tools}. */ - export function registerTool(id: string, tool: LanguageModelTool): Disposable; + export function registerTool(name: string, tool: LanguageModelTool): Disposable; /** * A list of all available tools. @@ -154,20 +153,14 @@ declare module 'vscode' { }; } - /** - * Represents a JSON Schema. - * TODO@API - is this worth it? - */ - export type JSONSchema = Object; - /** * A description of an available tool. */ export interface LanguageModelToolDescription { /** - * A unique identifier for the tool. + * A unique name for the tool. */ - readonly id: string; + readonly name: string; /** * A human-readable name for this tool that may be used to describe it in the UI. @@ -183,7 +176,7 @@ declare module 'vscode' { /** * A JSON schema for the parameters this tool accepts. */ - readonly parametersSchema?: JSONSchema; + readonly parametersSchema?: Object; /** * The list of content types that the tool has declared support for. See {@link LanguageModelToolResult}. From be8ca55b808a94d898bbce19268071b3bb4131c2 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 11 Oct 2024 11:05:46 -0700 Subject: [PATCH 02/10] Simplify chat confirmation, remove the static contrib flag --- .../chat/browser/languageModelToolsService.ts | 21 ++++++------------- .../chat/common/languageModelToolsService.ts | 1 - .../tools/languageModelToolsContribution.ts | 4 ---- src/vscode-dts/vscode.proposed.lmTools.d.ts | 21 ++++++------------- 4 files changed, 12 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts index c83111b8d1a..b1777aa620a 100644 --- a/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts @@ -147,37 +147,28 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo const prepared = tool.impl.prepareToolInvocation ? await tool.impl.prepareToolInvocation(participantName, dto.parameters, token) : undefined; - const confirmationMessages = tool.data.requiresConfirmation ? - prepared?.confirmationMessages ?? { - title: localize('toolConfirmTitle', "Use {0}?", `"${tool.data.displayName ?? tool.data.id}"`), - message: localize('toolConfirmMessage', "{0} will use {1}.", participantName, `"${tool.data.displayName ?? tool.data.id}"`), - } - : undefined; const defaultMessage = localize('toolInvocationMessage', "Using {0}", `"${tool.data.displayName ?? tool.data.id}"`); const invocationMessage = prepared?.invocationMessage ?? defaultMessage; - toolInvocation = new ChatToolInvocation(invocationMessage, confirmationMessages); + toolInvocation = new ChatToolInvocation(invocationMessage, prepared?.confirmationMessages); token.onCancellationRequested(() => { toolInvocation!.confirmed.complete(false); }); model.acceptResponseProgress(request, toolInvocation); - if (tool.data.requiresConfirmation) { + if (prepared?.confirmationMessages) { const userConfirmed = await toolInvocation.confirmed.p; if (!userConfirmed) { throw new CancellationError(); } } - } else if (tool.data.requiresConfirmation) { + } else { const prepared = tool.impl.prepareToolInvocation ? await tool.impl.prepareToolInvocation('Some Extension', dto.parameters, token) : undefined; - const confirmationMessages = prepared?.confirmationMessages ?? { - title: localize('toolConfirmTitle', "Use {0}?", `"${tool.data.displayName ?? tool.data.id}"`), - message: localize('toolConfirmMessage', "{0} will use {1}.", 'Some Extension', `"${tool.data.displayName ?? tool.data.id}"`), - }; - - await this._dialogService.confirm({ message: confirmationMessages.title, detail: renderStringAsPlaintext(confirmationMessages.message) }); + if (prepared?.confirmationMessages) { + await this._dialogService.confirm({ message: prepared.confirmationMessages.title, detail: renderStringAsPlaintext(prepared.confirmationMessages.message) }); + } } try { diff --git a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts index d232f2733c0..40bfcff40fb 100644 --- a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts @@ -25,7 +25,6 @@ export interface IToolData { parametersSchema?: IJSONSchema; canBeInvokedManually?: boolean; supportedContentTypes: string[]; - requiresConfirmation?: boolean; } export interface IToolInvocation { diff --git a/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts b/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts index 391b8116838..fb8d8e99572 100644 --- a/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts +++ b/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts @@ -28,7 +28,6 @@ interface IRawToolContribution { parametersSchema?: IJSONSchema; canBeInvokedManually?: boolean; supportedContentTypes?: string[]; - requiresConfirmation?: boolean; } const languageModelToolsExtensionPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint({ @@ -109,9 +108,6 @@ const languageModelToolsExtensionPoint = extensionsRegistry.ExtensionsRegistry.r type: 'string' } }, - requiresConfirmation: { - description: localize('requiresConfirmation', "Whether this tool requires user confirmation before being executed."), - }, tags: { description: localize('toolTags', "A set of tags that roughly describe the tool's capabilities. A tool user may use these to filter the set of tools to just ones that are relevant for the task at hand."), type: 'array', diff --git a/src/vscode-dts/vscode.proposed.lmTools.d.ts b/src/vscode-dts/vscode.proposed.lmTools.d.ts index 1bdfaaca092..be94f801d1d 100644 --- a/src/vscode-dts/vscode.proposed.lmTools.d.ts +++ b/src/vscode-dts/vscode.proposed.lmTools.d.ts @@ -191,8 +191,8 @@ declare module 'vscode' { } /** - * Messages shown in the chat view when a tool needs confirmation from the user to run. These messages will be shown with - * buttons that say Continue and Cancel. + * When this is returned in {@link PreparedToolInvocation}, the user will be asked to confirm before running the tool. These + * messages will be shown with buttons that say "Continue" and "Cancel". */ export interface LanguageModelToolConfirmationMessages { /** @@ -201,10 +201,7 @@ declare module 'vscode' { title: string; /** - * The body of the confirmation message. This should be phrased as an action of the participant that is invoking the tool - * from {@link LanguageModelToolInvocationPrepareOptions.participantName}. An example of a good message would be - * `${participantName} will run the command ${echo 'hello world'} in the terminal.` - * TODO@API keep this? + * The body of the confirmation message. */ message: string | MarkdownString; } @@ -213,12 +210,6 @@ declare module 'vscode' { * Options for {@link LanguageModelTool.prepareToolInvocation}. */ export interface LanguageModelToolInvocationPrepareOptions { - /** - * The name of the participant invoking the tool. - * TODO@API keep this? - */ - participantName: string; - /** * The parameters that the tool is being invoked with. */ @@ -235,8 +226,8 @@ declare module 'vscode' { invoke(options: LanguageModelToolInvocationOptions, token: CancellationToken): ProviderResult; /** - * Called once before a tool is invoked. May be implemented to customize the progress message that appears while the tool - * is running, and the messages that appear when the tool needs confirmation. + * Called once before a tool is invoked. May be implemented to signal that a tool needs user confirmation before running, + * and to customize the progress message that appears while the tool is running. */ prepareToolInvocation?(options: LanguageModelToolInvocationPrepareOptions, token: CancellationToken): ProviderResult; } @@ -251,7 +242,7 @@ declare module 'vscode' { invocationMessage?: string; /** - * Customized messages to show when asking for user confirmation to run the tool. + * The presence of this property indicates that the user should be asked to confirm before running the tool. */ confirmationMessages?: LanguageModelToolConfirmationMessages; } From 7e8a0d6d48e9ae257be5745aadfdeb3929bebaf7 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 11 Oct 2024 11:08:39 -0700 Subject: [PATCH 03/10] Clean up participantName --- .../workbench/api/browser/mainThreadLanguageModelTools.ts | 2 +- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- src/vs/workbench/api/common/extHostLanguageModelTools.ts | 4 ++-- .../contrib/chat/browser/languageModelToolsService.ts | 6 ++---- .../contrib/chat/common/languageModelToolsService.ts | 2 +- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadLanguageModelTools.ts b/src/vs/workbench/api/browser/mainThreadLanguageModelTools.ts index 257a59aa9ed..f59b6248d47 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageModelTools.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageModelTools.ts @@ -59,7 +59,7 @@ export class MainThreadLanguageModelTools extends Disposable implements MainThre this._countTokenCallbacks.delete(dto.callId); } }, - prepareToolInvocation: (participantName, parameters, token) => this._proxy.$prepareToolInvocation(id, participantName, parameters, token), + prepareToolInvocation: (parameters, token) => this._proxy.$prepareToolInvocation(id, parameters, token), }); this._tools.set(id, disposable); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 9613c48a722..2c40ab4707c 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1369,7 +1369,7 @@ export interface ExtHostLanguageModelToolsShape { $invokeTool(dto: IToolInvocation, token: CancellationToken): Promise; $countTokensForInvocation(callId: string, input: string, token: CancellationToken): Promise; - $prepareToolInvocation(toolId: string, participantName: string, parameters: any, token: CancellationToken): Promise; + $prepareToolInvocation(toolId: string, parameters: any, token: CancellationToken): Promise; } export interface MainThreadUrlsShape extends IDisposable { diff --git a/src/vs/workbench/api/common/extHostLanguageModelTools.ts b/src/vs/workbench/api/common/extHostLanguageModelTools.ts index 21240737888..1fd511758bf 100644 --- a/src/vs/workbench/api/common/extHostLanguageModelTools.ts +++ b/src/vs/workbench/api/common/extHostLanguageModelTools.ts @@ -121,7 +121,7 @@ export class ExtHostLanguageModelTools implements ExtHostLanguageModelToolsShape return extensionResult; } - async $prepareToolInvocation(toolId: string, participantName: string, parameters: any, token: CancellationToken): Promise { + async $prepareToolInvocation(toolId: string, parameters: any, token: CancellationToken): Promise { const item = this._registeredTools.get(toolId); if (!item) { throw new Error(`Unknown tool ${toolId}`); @@ -131,7 +131,7 @@ export class ExtHostLanguageModelTools implements ExtHostLanguageModelToolsShape return undefined; } - const result = await item.tool.prepareToolInvocation({ participantName, parameters }, token); + const result = await item.tool.prepareToolInvocation({ parameters }, token); if (!result) { return undefined; } diff --git a/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts index b1777aa620a..760480ea6ed 100644 --- a/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts @@ -142,10 +142,8 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo const model = this._chatService.getSession(dto.context?.sessionId) as ChatModel; const request = model.getRequests().at(-1)!; - const participantName = request.response?.agent?.fullName ?? ''; // This should always be set in this scenario with a new live request - const prepared = tool.impl.prepareToolInvocation ? - await tool.impl.prepareToolInvocation(participantName, dto.parameters, token) + await tool.impl.prepareToolInvocation(dto.parameters, token) : undefined; const defaultMessage = localize('toolInvocationMessage', "Using {0}", `"${tool.data.displayName ?? tool.data.id}"`); @@ -163,7 +161,7 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo } } else { const prepared = tool.impl.prepareToolInvocation ? - await tool.impl.prepareToolInvocation('Some Extension', dto.parameters, token) + await tool.impl.prepareToolInvocation(dto.parameters, token) : undefined; if (prepared?.confirmationMessages) { diff --git a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts index 40bfcff40fb..9cbafa99f66 100644 --- a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts @@ -56,7 +56,7 @@ export interface IPreparedToolInvocation { export interface IToolImpl { invoke(invocation: IToolInvocation, countTokens: CountTokensCallback, token: CancellationToken): Promise; - prepareToolInvocation?(participantName: string, parameters: any, token: CancellationToken): Promise; + prepareToolInvocation?(parameters: any, token: CancellationToken): Promise; } export const ILanguageModelToolsService = createDecorator('ILanguageModelToolsService'); From 1f1ac2830878dd6ff8a2fc4bab16bd043722db92 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 11 Oct 2024 11:11:20 -0700 Subject: [PATCH 04/10] Remove generic parameter on invokeTool --- src/vscode-dts/vscode.proposed.lmTools.d.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vscode-dts/vscode.proposed.lmTools.d.ts b/src/vscode-dts/vscode.proposed.lmTools.d.ts index be94f801d1d..d21d4bb5964 100644 --- a/src/vscode-dts/vscode.proposed.lmTools.d.ts +++ b/src/vscode-dts/vscode.proposed.lmTools.d.ts @@ -102,7 +102,7 @@ declare module 'vscode' { /** * Invoke a tool with the given parameters. */ - export function invokeTool(id: string, options: LanguageModelToolInvocationOptions, token: CancellationToken): Thenable; + export function invokeTool(id: string, options: LanguageModelToolInvocationOptions, token: CancellationToken): Thenable; } /** @@ -119,6 +119,8 @@ declare module 'vscode' { * {@link ChatRequest.toolInvocationToken}. In that case, a progress bar will be automatically shown for the tool * invocation in the chat response view, and if the tool requires user confirmation, it will show up inline in the chat * view. If the tool is being invoked outside of a chat request, `undefined` should be passed instead. + * + * If a tool invokes another tool during its invocation, it can pass along the `toolInvocationToken` that it received. */ toolInvocationToken: ChatParticipantToolToken | undefined; From 5d736a45d1a37a7eb1267d015ec6f774ebea3c60 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 11 Oct 2024 11:14:29 -0700 Subject: [PATCH 05/10] Remove tool description displayName --- src/vs/workbench/api/common/extHostTypeConverters.ts | 1 - src/vscode-dts/vscode.proposed.lmTools.d.ts | 6 ------ 2 files changed, 7 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index f2ad51684bb..8b84d0ad41c 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -2931,7 +2931,6 @@ export namespace LanguageModelToolDescription { name: item.id, description: item.modelDescription, parametersSchema: item.parametersSchema, - displayName: item.displayName, supportedContentTypes: item.supportedContentTypes, tags: item.tags ?? [], }; diff --git a/src/vscode-dts/vscode.proposed.lmTools.d.ts b/src/vscode-dts/vscode.proposed.lmTools.d.ts index d21d4bb5964..00792d5484c 100644 --- a/src/vscode-dts/vscode.proposed.lmTools.d.ts +++ b/src/vscode-dts/vscode.proposed.lmTools.d.ts @@ -164,12 +164,6 @@ declare module 'vscode' { */ readonly name: string; - /** - * A human-readable name for this tool that may be used to describe it in the UI. - * TODO@API keep? - */ - readonly displayName: string | undefined; - /** * A description of this tool that may be passed to a language model. */ From 91a92a7c6b9d6f636b3753d75a23e80b6b9c2e82 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 11 Oct 2024 11:21:00 -0700 Subject: [PATCH 06/10] Tool type polish --- src/vscode-dts/vscode.proposed.lmTools.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vscode-dts/vscode.proposed.lmTools.d.ts b/src/vscode-dts/vscode.proposed.lmTools.d.ts index 00792d5484c..6ea098fb4c8 100644 --- a/src/vscode-dts/vscode.proposed.lmTools.d.ts +++ b/src/vscode-dts/vscode.proposed.lmTools.d.ts @@ -32,9 +32,9 @@ declare module 'vscode' { export class LanguageModelToolCallPart { name: string; toolCallId: string; - parameters: any; + parameters: Object; - constructor(name: string, toolCallId: string, parameters: any); + constructor(name: string, toolCallId: string, parameters: Object); } // LM -> USER: text chunk @@ -102,7 +102,7 @@ declare module 'vscode' { /** * Invoke a tool with the given parameters. */ - export function invokeTool(id: string, options: LanguageModelToolInvocationOptions, token: CancellationToken): Thenable; + export function invokeTool(id: string, options: LanguageModelToolInvocationOptions, token: CancellationToken): Thenable; } /** From 251bd001350ac99362d5620835bc06d66183f0cd Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 11 Oct 2024 13:03:08 -0700 Subject: [PATCH 07/10] canBeInvokedManually -> canBeReferencedInPrompt name --- .../browser/actions/chatContextActions.ts | 4 +-- .../browser/contrib/chatInputCompletions.ts | 6 ++-- .../chat/browser/languageModelToolsService.ts | 4 +-- .../contrib/chat/common/chatRequestParser.ts | 2 +- .../chat/common/languageModelToolsService.ts | 6 ++-- .../tools/languageModelToolsContribution.ts | 30 +++++++++---------- .../browser/languageModelToolsService.test.ts | 18 +++++++---- src/vscode-dts/vscode.proposed.lmTools.d.ts | 2 ++ 8 files changed, 40 insertions(+), 32 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts index b4eaba6e8a4..71b0c584243 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts @@ -456,10 +456,10 @@ export class AttachContextAction extends Action2 { if (!usedAgent || usedAgent.agent.supportsToolReferences) { for (const tool of languageModelToolsService.getTools()) { - if (tool.canBeInvokedManually) { + if (tool.canBeReferencedInPrompt) { const item: IToolQuickPickItem = { kind: 'tool', - label: tool.displayName ?? tool.name2 ?? '', + label: tool.displayName ?? '', id: tool.id, icon: ThemeIcon.isThemeIcon(tool.icon) ? tool.icon : undefined // TODO need to support icon path? }; diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts index 66f4c84f833..c3601430638 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts @@ -577,10 +577,10 @@ class VariableCompletions extends Disposable { const toolItems: CompletionItem[] = []; if (!usedAgent || usedAgent.agent.supportsToolReferences) { toolItems.push(...Array.from(toolsService.getTools()) - .filter(t => t.canBeInvokedManually) - .filter(t => !usedToolNames.has(t.name2 ?? '')) + .filter(t => t.canBeReferencedInPrompt) + .filter(t => !usedToolNames.has(t.toolReferenceName ?? '')) .map((t): CompletionItem => { - const withLeader = `${chatVariableLeader}${t.name2}`; + const withLeader = `${chatVariableLeader}${t.toolReferenceName}`; return { label: withLeader, range, diff --git a/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts index 760480ea6ed..045e854c429 100644 --- a/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts @@ -112,7 +112,7 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo getToolByName(name: string): IToolData | undefined { for (const toolData of this.getTools()) { - if (toolData.name2 === name) { + if (toolData.toolReferenceName === name) { return toolData; } } @@ -146,7 +146,7 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo await tool.impl.prepareToolInvocation(dto.parameters, token) : undefined; - const defaultMessage = localize('toolInvocationMessage', "Using {0}", `"${tool.data.displayName ?? tool.data.id}"`); + const defaultMessage = localize('toolInvocationMessage', "Using {0}", `"${tool.data.displayName}"`); const invocationMessage = prepared?.invocationMessage ?? defaultMessage; toolInvocation = new ChatToolInvocation(invocationMessage, prepared?.confirmationMessages); token.onCancellationRequested(() => { diff --git a/src/vs/workbench/contrib/chat/common/chatRequestParser.ts b/src/vs/workbench/contrib/chat/common/chatRequestParser.ts index 1cbf2e81622..29d94ce5d89 100644 --- a/src/vs/workbench/contrib/chat/common/chatRequestParser.ts +++ b/src/vs/workbench/contrib/chat/common/chatRequestParser.ts @@ -159,7 +159,7 @@ export class ChatRequestParser { } const tool = this.toolsService.getToolByName(name); - if (tool && tool.canBeInvokedManually && (!usedAgent || usedAgent.agent.supportsToolReferences)) { + if (tool && tool.canBeReferencedInPrompt && (!usedAgent || usedAgent.agent.supportsToolReferences)) { return new ChatRequestToolPart(varRange, varEditorRange, name, tool.id, tool.displayName, tool.icon); } diff --git a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts index 9cbafa99f66..ab5f6d1ae23 100644 --- a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts @@ -15,15 +15,15 @@ import { createDecorator } from '../../../../platform/instantiation/common/insta export interface IToolData { id: string; - name2?: string; + toolReferenceName?: string; icon?: { dark: URI; light?: URI } | ThemeIcon; when?: ContextKeyExpression; tags?: string[]; - displayName?: string; + displayName: string; userDescription?: string; modelDescription: string; parametersSchema?: IJSONSchema; - canBeInvokedManually?: boolean; + canBeReferencedInPrompt?: boolean; supportedContentTypes: string[]; } diff --git a/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts b/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts index fb8d8e99572..36df5b60125 100644 --- a/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts +++ b/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts @@ -18,15 +18,15 @@ import * as extensionsRegistry from '../../../../services/extensions/common/exte interface IRawToolContribution { name: string; - name2?: string; + displayName: string; + modelDescription: string; + toolReferenceName?: string; icon?: string | { light: string; dark: string }; when?: string; tags?: string[]; - displayName?: string; userDescription?: string; - modelDescription: string; parametersSchema?: IJSONSchema; - canBeInvokedManually?: boolean; + canBeReferencedInPrompt?: boolean; supportedContentTypes?: string[]; } @@ -44,16 +44,16 @@ const languageModelToolsExtensionPoint = extensionsRegistry.ExtensionsRegistry.r additionalProperties: false, type: 'object', defaultSnippets: [{ body: { name: '', description: '' } }], - required: ['name', 'modelDescription'], + required: ['name', 'displayName', 'modelDescription'], properties: { name: { - description: localize('toolName', "A unique name for this tool. This name must be a globally unique identifier, and is also used as a name when presenting this tool to an LLM."), + description: localize('toolName', "A unique name for this tool. This name must be a globally unique identifier, and is also used as a name when presenting this tool to a language model."), type: 'string', // Borrow OpenAI's requirement for tool names pattern: '^[\\w-]+$' }, - name2: { - markdownDescription: localize('toolName2', "If {0} is enabled for this tool, the user may use '#' with this name to invoke the tool in a query. Otherwise, the name is not required. Name must not contain whitespace.", '`canBeInvokedManually`'), + toolReferenceName: { + markdownDescription: localize('toolName2', "If {0} is enabled for this tool, the user may use '#' with this name to invoke the tool in a query. Otherwise, the name is not required. Name must not contain whitespace.", '`canBeReferencedInPrompt`'), type: 'string', pattern: '^[\\w-]+$' }, @@ -66,7 +66,7 @@ const languageModelToolsExtensionPoint = extensionsRegistry.ExtensionsRegistry.r type: 'string' }, modelDescription: { - description: localize('toolModelDescription', "A description of this tool that may be passed to a language model."), + description: localize('toolModelDescription', "A description of this tool that may be used by a language model to select it."), type: 'string' }, parametersSchema: { @@ -74,8 +74,8 @@ const languageModelToolsExtensionPoint = extensionsRegistry.ExtensionsRegistry.r type: 'object', $ref: 'http://json-schema.org/draft-07/schema#' }, - canBeInvokedManually: { - description: localize('canBeInvokedManually', "Whether this tool can be invoked manually by the user through the chat UX."), + canBeReferencedInPrompt: { + markdownDescription: localize('canBeReferencedInPrompt', "If true, this tool shows up as an attachment that the user can add manually to their request. Chat participants will receive the tool in {0}.", '`ChatRequest#toolReferences`'), type: 'boolean' }, icon: { @@ -136,8 +136,8 @@ export class LanguageModelToolsExtensionPointHandler implements IWorkbenchContri languageModelToolsExtensionPoint.setHandler((extensions, delta) => { for (const extension of delta.added) { for (const rawTool of extension.value) { - if (!rawTool.name || !rawTool.modelDescription) { - logService.error(`Extension '${extension.description.identifier.value}' CANNOT register tool without name and modelDescription: ${JSON.stringify(rawTool)}`); + if (!rawTool.name || !rawTool.modelDescription || !rawTool.displayName) { + logService.error(`Extension '${extension.description.identifier.value}' CANNOT register tool without name, modelDescription, and displayName: ${JSON.stringify(rawTool)}`); continue; } @@ -146,8 +146,8 @@ export class LanguageModelToolsExtensionPointHandler implements IWorkbenchContri continue; } - if (rawTool.canBeInvokedManually && !rawTool.name2) { - logService.error(`Extension '${extension.description.identifier.value}' CANNOT register tool with 'canBeInvokedManually' set without a name: ${JSON.stringify(rawTool)}`); + if (rawTool.canBeReferencedInPrompt && !rawTool.toolReferenceName) { + logService.error(`Extension '${extension.description.identifier.value}' CANNOT register tool with 'canBeReferencedInPrompt' set without a 'toolReferenceName': ${JSON.stringify(rawTool)}`); continue; } diff --git a/src/vs/workbench/contrib/chat/test/browser/languageModelToolsService.test.ts b/src/vs/workbench/contrib/chat/test/browser/languageModelToolsService.test.ts index da7a5f3e5a1..2bd4519971f 100644 --- a/src/vs/workbench/contrib/chat/test/browser/languageModelToolsService.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/languageModelToolsService.test.ts @@ -31,7 +31,8 @@ suite('LanguageModelToolsService', () => { const toolData: IToolData = { id: 'testTool', modelDescription: 'Test Tool', - supportedContentTypes: [] + supportedContentTypes: [], + displayName: 'Test Tool' }; const disposable = service.registerToolData(toolData); @@ -44,7 +45,8 @@ suite('LanguageModelToolsService', () => { const toolData: IToolData = { id: 'testTool', modelDescription: 'Test Tool', - supportedContentTypes: [] + supportedContentTypes: [], + displayName: 'Test Tool' }; store.add(service.registerToolData(toolData)); @@ -63,20 +65,23 @@ suite('LanguageModelToolsService', () => { id: 'testTool1', modelDescription: 'Test Tool 1', when: ContextKeyEqualsExpr.create('testKey', false), - supportedContentTypes: [] + supportedContentTypes: [], + displayName: 'Test Tool' }; const toolData2: IToolData = { id: 'testTool2', modelDescription: 'Test Tool 2', when: ContextKeyEqualsExpr.create('testKey', true), - supportedContentTypes: [] + supportedContentTypes: [], + displayName: 'Test Tool' }; const toolData3: IToolData = { id: 'testTool3', modelDescription: 'Test Tool 3', - supportedContentTypes: [] + supportedContentTypes: [], + displayName: 'Test Tool' }; store.add(service.registerToolData(toolData1)); @@ -93,7 +98,8 @@ suite('LanguageModelToolsService', () => { const toolData: IToolData = { id: 'testTool', modelDescription: 'Test Tool', - supportedContentTypes: [] + supportedContentTypes: [], + displayName: 'Test Tool' }; store.add(service.registerToolData(toolData)); diff --git a/src/vscode-dts/vscode.proposed.lmTools.d.ts b/src/vscode-dts/vscode.proposed.lmTools.d.ts index 6ea098fb4c8..bcea9d8975d 100644 --- a/src/vscode-dts/vscode.proposed.lmTools.d.ts +++ b/src/vscode-dts/vscode.proposed.lmTools.d.ts @@ -283,6 +283,8 @@ declare module 'vscode' { export interface ChatRequestTurn { /** * The list of tools were attached to this request. + * TODO@API- do we keep `supportsToolReferences`, or accept that some participants won't implement them? + * This is hard to implement properly anyway, with participant detection etc. */ readonly toolReferences?: readonly ChatLanguageModelToolReference[]; } From fdb6b456e27da44f12f001ffc087a2ca66b001e8 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 11 Oct 2024 13:06:05 -0700 Subject: [PATCH 08/10] All participants get tool references --- .../browser/actions/chatContextActions.ts | 30 +++++++++---------- .../browser/chatParticipantContributions.ts | 5 ---- .../browser/contrib/chatInputCompletions.ts | 30 +++++++++---------- .../contrib/chat/common/chatAgents.ts | 1 - .../common/chatParticipantContribTypes.ts | 1 - .../contrib/chat/common/chatRequestParser.ts | 2 +- src/vscode-dts/vscode.proposed.lmTools.d.ts | 2 -- 7 files changed, 29 insertions(+), 42 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts index 71b0c584243..60fec86715b 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts @@ -454,23 +454,21 @@ export class AttachContextAction extends Action2 { } } - if (!usedAgent || usedAgent.agent.supportsToolReferences) { - for (const tool of languageModelToolsService.getTools()) { - if (tool.canBeReferencedInPrompt) { - const item: IToolQuickPickItem = { - kind: 'tool', - label: tool.displayName ?? '', - id: tool.id, - icon: ThemeIcon.isThemeIcon(tool.icon) ? tool.icon : undefined // TODO need to support icon path? - }; - if (ThemeIcon.isThemeIcon(tool.icon)) { - item.iconClass = ThemeIcon.asClassName(tool.icon); - } else if (tool.icon) { - item.iconPath = tool.icon; - } - - quickPickItems.push(item); + for (const tool of languageModelToolsService.getTools()) { + if (tool.canBeReferencedInPrompt) { + const item: IToolQuickPickItem = { + kind: 'tool', + label: tool.displayName ?? '', + id: tool.id, + icon: ThemeIcon.isThemeIcon(tool.icon) ? tool.icon : undefined // TODO need to support icon path? + }; + if (ThemeIcon.isThemeIcon(tool.icon)) { + item.iconClass = ThemeIcon.asClassName(tool.icon); + } else if (tool.icon) { + item.iconPath = tool.icon; } + + quickPickItems.push(item); } } diff --git a/src/vs/workbench/contrib/chat/browser/chatParticipantContributions.ts b/src/vs/workbench/contrib/chat/browser/chatParticipantContributions.ts index c5135968d7d..23ae079a8ec 100644 --- a/src/vs/workbench/contrib/chat/browser/chatParticipantContributions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatParticipantContributions.ts @@ -149,10 +149,6 @@ const chatParticipantExtensionPoint = extensionsRegistry.ExtensionsRegistry.regi } } }, - supportsToolReferences: { - description: localize('chatParticipantSupportsToolReferences', "Whether this participant supports {0}.", 'ChatRequest#toolReferences'), - type: 'boolean' - } } } }, @@ -261,7 +257,6 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution { [ChatAgentLocation.Panel], slashCommands: providerDescriptor.commands ?? [], disambiguation: coalesce(participantsAndCommandsDisambiguation.flat()), - supportsToolReferences: providerDescriptor.supportsToolReferences, } satisfies IChatAgentData)); this._participantRegistrationDisposables.set( diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts index c3601430638..9cb42912214 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts @@ -575,22 +575,20 @@ class VariableCompletions extends Disposable { const usedTools = widget.parsedInput.parts.filter((p): p is ChatRequestToolPart => p instanceof ChatRequestToolPart); const usedToolNames = new Set(usedTools.map(v => v.toolName)); const toolItems: CompletionItem[] = []; - if (!usedAgent || usedAgent.agent.supportsToolReferences) { - toolItems.push(...Array.from(toolsService.getTools()) - .filter(t => t.canBeReferencedInPrompt) - .filter(t => !usedToolNames.has(t.toolReferenceName ?? '')) - .map((t): CompletionItem => { - const withLeader = `${chatVariableLeader}${t.toolReferenceName}`; - return { - label: withLeader, - range, - insertText: withLeader + ' ', - detail: t.userDescription, - kind: CompletionItemKind.Text, - sortText: 'z' - }; - })); - } + toolItems.push(...Array.from(toolsService.getTools()) + .filter(t => t.canBeReferencedInPrompt) + .filter(t => !usedToolNames.has(t.toolReferenceName ?? '')) + .map((t): CompletionItem => { + const withLeader = `${chatVariableLeader}${t.toolReferenceName}`; + return { + label: withLeader, + range, + insertText: withLeader + ' ', + detail: t.userDescription, + kind: CompletionItemKind.Text, + sortText: 'z' + }; + })); return { suggestions: [...variableItems, ...toolItems] diff --git a/src/vs/workbench/contrib/chat/common/chatAgents.ts b/src/vs/workbench/contrib/chat/common/chatAgents.ts index e1201ed93b6..667969f2322 100644 --- a/src/vs/workbench/contrib/chat/common/chatAgents.ts +++ b/src/vs/workbench/contrib/chat/common/chatAgents.ts @@ -77,7 +77,6 @@ export interface IChatAgentData { slashCommands: IChatAgentCommand[]; locations: ChatAgentLocation[]; disambiguation: { category: string; description: string; examples: string[] }[]; - supportsToolReferences?: boolean; } export interface IChatWelcomeMessageContent { diff --git a/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts b/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts index c2eea41c05c..b898b6d37d6 100644 --- a/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts +++ b/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts @@ -29,7 +29,6 @@ export interface IRawChatParticipantContribution { defaultImplicitVariables?: string[]; locations?: RawChatParticipantLocation[]; disambiguation?: { category: string; categoryName?: string /** Deprecated */; description: string; examples: string[] }[]; - supportsToolReferences?: boolean; } /** diff --git a/src/vs/workbench/contrib/chat/common/chatRequestParser.ts b/src/vs/workbench/contrib/chat/common/chatRequestParser.ts index 29d94ce5d89..61bc911ed5b 100644 --- a/src/vs/workbench/contrib/chat/common/chatRequestParser.ts +++ b/src/vs/workbench/contrib/chat/common/chatRequestParser.ts @@ -159,7 +159,7 @@ export class ChatRequestParser { } const tool = this.toolsService.getToolByName(name); - if (tool && tool.canBeReferencedInPrompt && (!usedAgent || usedAgent.agent.supportsToolReferences)) { + if (tool && tool.canBeReferencedInPrompt) { return new ChatRequestToolPart(varRange, varEditorRange, name, tool.id, tool.displayName, tool.icon); } diff --git a/src/vscode-dts/vscode.proposed.lmTools.d.ts b/src/vscode-dts/vscode.proposed.lmTools.d.ts index bcea9d8975d..6ea098fb4c8 100644 --- a/src/vscode-dts/vscode.proposed.lmTools.d.ts +++ b/src/vscode-dts/vscode.proposed.lmTools.d.ts @@ -283,8 +283,6 @@ declare module 'vscode' { export interface ChatRequestTurn { /** * The list of tools were attached to this request. - * TODO@API- do we keep `supportsToolReferences`, or accept that some participants won't implement them? - * This is hard to implement properly anyway, with participant detection etc. */ readonly toolReferences?: readonly ChatLanguageModelToolReference[]; } From 7d2705acd855cfcf8777d071f32955e4a35ba788 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 11 Oct 2024 13:22:37 -0700 Subject: [PATCH 09/10] Object -> object --- src/vscode-dts/vscode.proposed.lmTools.d.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vscode-dts/vscode.proposed.lmTools.d.ts b/src/vscode-dts/vscode.proposed.lmTools.d.ts index 6ea098fb4c8..3d3aec273e9 100644 --- a/src/vscode-dts/vscode.proposed.lmTools.d.ts +++ b/src/vscode-dts/vscode.proposed.lmTools.d.ts @@ -14,7 +14,7 @@ declare module 'vscode' { export interface LanguageModelChatTool { name: string; description: string; - parametersSchema?: Object; + parametersSchema?: object; } // API -> LM: add tools as request option @@ -32,9 +32,9 @@ declare module 'vscode' { export class LanguageModelToolCallPart { name: string; toolCallId: string; - parameters: Object; + parameters: object; - constructor(name: string, toolCallId: string, parameters: Object); + constructor(name: string, toolCallId: string, parameters: object); } // LM -> USER: text chunk @@ -102,7 +102,7 @@ declare module 'vscode' { /** * Invoke a tool with the given parameters. */ - export function invokeTool(id: string, options: LanguageModelToolInvocationOptions, token: CancellationToken): Thenable; + export function invokeTool(id: string, options: LanguageModelToolInvocationOptions, token: CancellationToken): Thenable; } /** @@ -172,7 +172,7 @@ declare module 'vscode' { /** * A JSON schema for the parameters this tool accepts. */ - readonly parametersSchema?: Object; + readonly parametersSchema?: object; /** * The list of content types that the tool has declared support for. See {@link LanguageModelToolResult}. From 6d0865fe55576c76cbcbf8255bd2c14726b3c142 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sat, 12 Oct 2024 15:19:48 -0700 Subject: [PATCH 10/10] tsdoc --- src/vscode-dts/vscode.proposed.lmTools.d.ts | 67 +++++++++++++++++---- 1 file changed, 55 insertions(+), 12 deletions(-) diff --git a/src/vscode-dts/vscode.proposed.lmTools.d.ts b/src/vscode-dts/vscode.proposed.lmTools.d.ts index 3d3aec273e9..72b8c6c79a5 100644 --- a/src/vscode-dts/vscode.proposed.lmTools.d.ts +++ b/src/vscode-dts/vscode.proposed.lmTools.d.ts @@ -10,16 +10,31 @@ declare module 'vscode' { // TODO@API capabilities - // API -> LM: an tool/function that is available to the language model + /** + * A tool that is available to the language model via {@link LanguageModelChatRequestOptions}. + */ export interface LanguageModelChatTool { + /** + * The name of the tool. + */ name: string; + + /** + * The description of the tool. + */ description: string; + + /** + * A JSON schema for the parameters this tool accepts. + */ parametersSchema?: object; } - // API -> LM: add tools as request option export interface LanguageModelChatRequestOptions { // TODO@API this will be a heterogeneous array of different types of tools + /** + * An optional list of tools that are available to the language model. + */ tools?: LanguageModelChatTool[]; /** @@ -28,30 +43,62 @@ declare module 'vscode' { toolChoice?: string; } - // LM -> USER: function that should be used + /** + * A language model response part indicating a tool call, returned from a {@link LanguageModelChatResponse}, and also can be + * included as a content part on a {@link LanguageModelChatMessage}, to represent a previous tool call in a + * chat request. + */ export class LanguageModelToolCallPart { + /** + * The name of the tool to call. + */ name: string; + + /** + * The ID of the tool call. This is a unique identifier for the tool call within the chat request. + */ toolCallId: string; + + /** + * The parameters with which to call the tool. + */ parameters: object; constructor(name: string, toolCallId: string, parameters: object); } - // LM -> USER: text chunk + /** + * A language model response part containing a piece of text, returned from a {@link LanguageModelChatResponse}. + */ export class LanguageModelTextPart { + /** + * The text content of the part. + */ value: string; constructor(value: string); } export interface LanguageModelChatResponse { + /** + * A stream of parts that make up the response. Could be extended with more types in the future. + * TODO@API add "| unknown"? + */ stream: AsyncIterable; } - - // USER -> LM: the result of a function call + /** + * The result of a tool call. Can only be included in the content of a User message. + */ export class LanguageModelToolResultPart { + /** + * The ID of the tool call. + */ toolCallId: string; + + /** + * The content of the tool result. + */ content: string; constructor(toolCallId: string, content: string); @@ -59,12 +106,8 @@ declare module 'vscode' { export interface LanguageModelChatMessage { /** - * A heterogeneous array of other things that a message can contain as content. - * Some parts would be message-type specific for some models and wouldn't go together, - * but it's up to the chat provider to decide what to do about that. - * Can drop parts that are not valid for the message type. - * LanguageModelToolResultPart: only on User messages - * LanguageModelToolCallPart: only on Assistant messages + * A heterogeneous array of other things that a message can contain as content. Some parts may be message-type specific + * for some models. */ content2: (string | LanguageModelToolResultPart | LanguageModelToolCallPart)[]; }