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/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.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/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/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 426f4128330..8b84d0ad41c 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -2927,10 +2927,10 @@ 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, supportedContentTypes: item.supportedContentTypes, tags: item.tags ?? [], }; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts index 83fa8df35c8..4f5cecd66db 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts @@ -482,23 +482,21 @@ export class AttachContextAction extends Action2 { } } - if (!usedAgent || usedAgent.agent.supportsToolReferences) { - for (const tool of languageModelToolsService.getTools()) { - if (tool.canBeInvokedManually) { - const item: IToolQuickPickItem = { - kind: 'tool', - label: tool.displayName ?? tool.name ?? '', - 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 d54d953b3c4..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.canBeInvokedManually) - .filter(t => !usedToolNames.has(t.name ?? '')) - .map((t): CompletionItem => { - const withLeader = `${chatVariableLeader}${t.name}`; - 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/browser/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts index 226acd4f4a4..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.name === name) { + if (toolData.toolReferenceName === name) { return toolData; } } @@ -142,42 +142,31 @@ 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) - : 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}"`), - } + 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, 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) + await tool.impl.prepareToolInvocation(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/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 1cbf2e81622..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.canBeInvokedManually && (!usedAgent || usedAgent.agent.supportsToolReferences)) { + if (tool && tool.canBeReferencedInPrompt) { 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 ac0c2a4ef1f..ab5f6d1ae23 100644 --- a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts @@ -15,17 +15,16 @@ import { createDecorator } from '../../../../platform/instantiation/common/insta export interface IToolData { id: string; - name?: 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[]; - requiresConfirmation?: boolean; } export interface IToolInvocation { @@ -57,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'); diff --git a/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts b/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts index 2bb02dd9ced..36df5b60125 100644 --- a/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts +++ b/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts @@ -17,25 +17,24 @@ import { ILanguageModelToolsService, IToolData } from '../languageModelToolsServ import * as extensionsRegistry from '../../../../services/extensions/common/extensionsRegistry.js'; interface IRawToolContribution { - id: string; - name?: string; + name: 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[]; - requiresConfirmation?: boolean; } const languageModelToolsExtensionPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'languageModelTools', activationEventsGenerator: (contributions: IRawToolContribution[], result) => { for (const contrib of contributions) { - result.push(`onLanguageModelTool:${contrib.id}`); + result.push(`onLanguageModelTool:${contrib.name}`); } }, jsonSchema: { @@ -45,16 +44,16 @@ const languageModelToolsExtensionPoint = extensionsRegistry.ExtensionsRegistry.r additionalProperties: false, type: 'object', defaultSnippets: [{ body: { name: '', description: '' } }], - required: ['id', 'modelDescription'], + required: ['name', 'displayName', '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 a language model."), 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`'), + 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-]+$' }, @@ -67,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: { @@ -75,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: { @@ -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', @@ -140,18 +136,18 @@ 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) { - 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; } - 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) { - 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; } @@ -171,18 +167,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/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 c5ecb486a82..72b8c6c79a5 100644 --- a/src/vscode-dts/vscode.proposed.lmTools.d.ts +++ b/src/vscode-dts/vscode.proposed.lmTools.d.ts @@ -3,24 +3,38 @@ * 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' { // 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 { - // TODO@API should use "id" here to match vscode tools, or keep name to match OpenAI? Align everything. + /** + * The name of the tool. + */ name: string; + + /** + * The description of the tool. + */ description: string; - parametersSchema?: JSONSchema; + + /** + * 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[]; /** @@ -29,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; - toolCallId: string; - parameters: any; - constructor(name: string, toolCallId: string, parameters: any); + /** + * 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); @@ -60,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)[]; } @@ -93,7 +135,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. @@ -103,7 +145,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; } /** @@ -120,6 +162,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; @@ -154,26 +198,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; - - /** - * A human-readable name for this tool that may be used to describe it in the UI. - * TODO@API keep? - */ - readonly displayName: string | undefined; + readonly name: string; /** * A description of this tool that may be passed to a language model. @@ -183,7 +215,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}. @@ -198,8 +230,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 { /** @@ -208,10 +240,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; } @@ -220,12 +249,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. */ @@ -242,8 +265,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; } @@ -258,7 +281,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; }