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}.