From df946f379d110caa86963ad22a054efcb3c267e6 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 13 Aug 2024 15:24:22 -0700 Subject: [PATCH] API update --- chat-sample/package.json | 10 ++- chat-sample/src/extension.ts | 29 ++++---- ...ode.proposed.chatParticipantAdditions.d.ts | 35 +--------- chat-sample/vscode.proposed.lmTools.d.ts | 68 ++++++++++++++++--- 4 files changed, 81 insertions(+), 61 deletions(-) diff --git a/chat-sample/package.json b/chat-sample/package.json index 39b1414e..cb17cc52 100644 --- a/chat-sample/package.json +++ b/chat-sample/package.json @@ -33,9 +33,13 @@ ], "languageModelTools": [ { - "name": "chat-sample.activeTabCount", - "description": "The number of active tabs in the editor", - "displayName": "Text tab count" + "id": "chat-sample_tabCount", + "name": "tabCount", + "displayName": "Tab Count", + "modelDescription": "The number of active tabs in the editor", + "userDescription": "The number of active tabs in the editor", + "icon": "$(files)", + "canBeInvokedManually": true } ] }, diff --git a/chat-sample/src/extension.ts b/chat-sample/src/extension.ts index d009bacf..53911267 100644 --- a/chat-sample/src/extension.ts +++ b/chat-sample/src/extension.ts @@ -6,7 +6,7 @@ export function activate(context: vscode.ExtensionContext) { } function registerChatTool() { - return vscode.lm.registerTool('chat-sample.activeTabCount', { + return vscode.lm.registerTool('chat-sample_tabCount', { async invoke(parameters, token) { return { toString() { @@ -26,12 +26,12 @@ function registerChatParticipant() { }); const model = models[0]; - stream.markdown(`Available tools: ${vscode.lm.tools.map(tool => tool.name).join(', ')}\n\n`); + stream.markdown(`Available tools: ${vscode.lm.tools.map(tool => tool.id).join(', ')}\n\n`); const allTools = vscode.lm.tools.map((tool): vscode.LanguageModelChatFunction => { return { - name: tool.name.replace(/\./g, '_'), - description: tool.description, + name: tool.id, + description: tool.modelDescription, parametersSchema: tool.parametersSchema ?? {} }; }); @@ -44,11 +44,12 @@ function registerChatParticipant() { vscode.LanguageModelChatMessage.User(`There is a selection of tools that may give helpful context to answer the user's query. If you aren't sure which tool is relevant, you can call multiple tools.`), vscode.LanguageModelChatMessage.User(request.prompt), ]; + const toolReferences = [...request.toolReferences]; const runWithFunctions = async () => { - const requestedTool = request.requestedTools?.shift(); + const requestedTool = toolReferences.shift(); if (requestedTool) { - options.toolChoice = requestedTool; - options.tools = allTools.filter(tool => tool.name === requestedTool); + options.toolChoice = requestedTool.id; + options.tools = allTools.filter(tool => tool.name === requestedTool.id); } else { options.toolChoice = undefined; options.tools = allTools; @@ -62,7 +63,7 @@ function registerChatParticipant() { if (part instanceof vscode.LanguageModelChatResponseTextPart) { stream.markdown(part.value); } else if (part instanceof vscode.LanguageModelChatResponseFunctionUsePart) { - const tool = vscode.lm.tools.find(tool => tool.name.replace(/\./g, '_') === part.name); + const tool = vscode.lm.tools.find(tool => tool.id === part.name); if (!tool) { // BAD tool choice? continue; @@ -75,22 +76,18 @@ function registerChatParticipant() { throw new Error(`Got invalid tool use parameters: "${part.parameters}". (${(err as Error).message})`); } - const resultPromise = vscode.lm.invokeTool(tool.name, JSON.parse(part.parameters), token); - stream.progress(`FUNCTION_CALL: ${tool.name} with ${part.parameters}`, async () => { - await resultPromise; - }); - - const result = await resultPromise; + stream.progress(`Calling tool: ${tool.id} with ${part.parameters}`); + const result = await vscode.lm.invokeTool(tool.id, JSON.parse(part.parameters), token); // NOTE that the result of calling a function is a special content type of a USER-message let message = vscode.LanguageModelChatMessage.User(''); - message.content2 = new vscode.LanguageModelChatMessageFunctionResultPart(tool.name.replace(/\./g, '_'), result.toString()); + message.content2 = new vscode.LanguageModelChatMessageFunctionResultPart(tool.id.replace(/\./g, '_'), result.toString()); messages.push(message); // IMPORTANT // IMPORTANT working around CAPI always wanting to end with a `User`-message // IMPORTANT - messages.push(vscode.LanguageModelChatMessage.User(`Above is the result of calling the function ${tool.name}. The user cannot see this result, so you should explain it to the user if referencing it in your answer.`)); + messages.push(vscode.LanguageModelChatMessage.User(`Above is the result of calling the function ${tool.id}. The user cannot see this result, so you should explain it to the user if referencing it in your answer.`)); didReceiveFunctionUse = true; } } diff --git a/chat-sample/vscode.proposed.chatParticipantAdditions.d.ts b/chat-sample/vscode.proposed.chatParticipantAdditions.d.ts index 05db1eb1..5f489925 100644 --- a/chat-sample/vscode.proposed.chatParticipantAdditions.d.ts +++ b/chat-sample/vscode.proposed.chatParticipantAdditions.d.ts @@ -188,8 +188,6 @@ declare module 'vscode' { * The `data` for any confirmations that were rejected */ rejectedConfirmationData?: any[]; - - requestedTools?: string[]; } // TODO@API fit this into the stream @@ -197,31 +195,6 @@ declare module 'vscode' { documents: ChatDocumentContext[]; } - export interface ChatParticipant { - /** - * Provide a set of variables that can only be used with this participant. - */ - participantVariableProvider?: { provider: ChatParticipantCompletionItemProvider; triggerCharacters: string[] }; - } - - export interface ChatParticipantCompletionItemProvider { - provideCompletionItems(query: string, token: CancellationToken): ProviderResult; - } - - export class ChatCompletionItem { - id: string; - label: string | CompletionItemLabel; - values: ChatVariableValue[]; - fullName?: string; - icon?: ThemeIcon; - insertText?: string; - detail?: string; - documentation?: string | MarkdownString; - command?: Command; - - constructor(id: string, label: string | CompletionItemLabel, values: ChatVariableValue[]); - } - export type ChatExtendedRequestHandler = (request: ChatRequest, context: ChatContext, response: ChatResponseStream, token: CancellationToken) => ProviderResult; export interface ChatResult { @@ -245,14 +218,12 @@ declare module 'vscode' { * The chat extension should not activate if it doesn't support the current version. */ export const _version: 1 | number; - - export function registerChatParticipantDetectionProvider(participantDetectionProvider: ChatParticipantDetectionProvider): Disposable; } export interface ChatParticipantMetadata { participant: string; command?: string; - description?: string; + disambiguation: { categoryName: string; description: string; examples: string[] }[]; } export interface ChatParticipantDetectionResult { @@ -260,10 +231,6 @@ declare module 'vscode' { command?: string; } - export interface ChatParticipantDetectionProvider { - provideParticipantDetection(chatRequest: ChatRequest, context: ChatContext, options: { participants?: ChatParticipantMetadata[]; location: ChatLocation }, token: CancellationToken): ProviderResult; - } - /* * User action events */ diff --git a/chat-sample/vscode.proposed.lmTools.d.ts b/chat-sample/vscode.proposed.lmTools.d.ts index 1956eafe..9dc0d909 100644 --- a/chat-sample/vscode.proposed.lmTools.d.ts +++ b/chat-sample/vscode.proposed.lmTools.d.ts @@ -3,15 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// version: 3 +// version: 4 // https://github.com/microsoft/vscode/issues/213274 declare module 'vscode' { // TODO@API capabilities - export type JSONSchema = object; - // API -> LM: an tool/function that is available to the language model export interface LanguageModelChatFunction { name: string; @@ -91,19 +89,73 @@ declare module 'vscode' { /** * Invoke a tool with the given parameters. - * TODO Could request a set of contentTypes to be returned so they don't all need to be computed? + * TODO@API Could request a set of contentTypes to be returned so they don't all need to be computed? */ - export function invokeTool(name: string, parameters: Object, token: CancellationToken): Thenable; + export function invokeTool(id: string, parameters: Object, token: CancellationToken): Thenable; } - // Is the same as LanguageModelChatFunction now, but could have more details in the future + export type JSONSchema = object; + export interface LanguageModelToolDescription { - name: string; - description: string; + /** + * A unique identifier for the tool. + */ + id: string; + + /** + * A human-readable name for this tool that may be used to describe it in the UI. + */ + displayName: string | undefined; + + /** + * A description of this tool that may be passed to a language model. + */ + modelDescription: string; + + /** + * A JSON schema for the parameters this tool accepts. + */ parametersSchema?: JSONSchema; } export interface LanguageModelTool { + // TODO@API should it be LanguageModelToolResult | string? invoke(parameters: any, token: CancellationToken): Thenable; } + + export interface ChatLanguageModelToolReference { + /** + * The tool's ID. Refers to a tool listed in {@link lm.tools}. + */ + readonly id: string; + + /** + * The start and end index of the reference in the {@link ChatRequest.prompt prompt}. When undefined, the reference was not part of the prompt text. + * + * *Note* that the indices take the leading `#`-character into account which means they can + * used to modify the prompt as-is. + */ + readonly range?: [start: number, end: number]; + } + + export interface ChatRequest { + /** + * The list of tools that the user attached to their request. + * + * *Note* that if tools are referenced in the text of the prompt, using `#`, the prompt contains + * references as authored and that it is up to the participant + * to further modify the prompt, for instance by inlining reference values or creating links to + * headings which contain the resolved values. References are sorted in reverse by their range + * in the prompt. That means the last reference in the prompt is the first in this list. This simplifies + * string-manipulation of the prompt. + */ + readonly toolReferences: readonly ChatLanguageModelToolReference[]; + } + + export interface ChatRequestTurn { + /** + * The list of tools were attached to this request. + */ + readonly toolReferences?: readonly ChatLanguageModelToolReference[]; + } }