Merge pull request #231145 from microsoft/roblou/tools-api-updates

lmTools API updates
This commit is contained in:
Rob Lourens
2024-10-12 18:58:08 -07:00
committed by GitHub
17 changed files with 160 additions and 157 deletions

View File

@@ -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',

View File

@@ -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);
}

View File

@@ -1491,9 +1491,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
return extHostEmbeddings.computeEmbeddings(embeddingsModel, input, token);
}
},
registerTool<T>(toolId: string, tool: vscode.LanguageModelTool<T>) {
registerTool<T>(name: string, tool: vscode.LanguageModelTool<T>) {
checkProposedApiEnabled(extension, 'lmTools');
return extHostLanguageModelTools.registerTool(extension, toolId, tool);
return extHostLanguageModelTools.registerTool(extension, name, tool);
},
invokeTool<T>(toolId: string, parameters: vscode.LanguageModelToolInvocationOptions<T>, token: vscode.CancellationToken) {
checkProposedApiEnabled(extension, 'lmTools');

View File

@@ -1369,7 +1369,7 @@ export interface ExtHostLanguageModelToolsShape {
$invokeTool(dto: IToolInvocation, token: CancellationToken): Promise<IToolResult>;
$countTokensForInvocation(callId: string, input: string, token: CancellationToken): Promise<number>;
$prepareToolInvocation(toolId: string, participantName: string, parameters: any, token: CancellationToken): Promise<IPreparedToolInvocation | undefined>;
$prepareToolInvocation(toolId: string, parameters: any, token: CancellationToken): Promise<IPreparedToolInvocation | undefined>;
}
export interface MainThreadUrlsShape extends IDisposable {

View File

@@ -121,7 +121,7 @@ export class ExtHostLanguageModelTools implements ExtHostLanguageModelToolsShape
return extensionResult;
}
async $prepareToolInvocation(toolId: string, participantName: string, parameters: any, token: CancellationToken): Promise<IPreparedToolInvocation | undefined> {
async $prepareToolInvocation(toolId: string, parameters: any, token: CancellationToken): Promise<IPreparedToolInvocation | undefined> {
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;
}

View File

@@ -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 ?? [],
};

View File

@@ -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);
}
}

View File

@@ -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(

View File

@@ -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]

View File

@@ -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 {

View File

@@ -77,7 +77,6 @@ export interface IChatAgentData {
slashCommands: IChatAgentCommand[];
locations: ChatAgentLocation[];
disambiguation: { category: string; description: string; examples: string[] }[];
supportsToolReferences?: boolean;
}
export interface IChatWelcomeMessageContent {

View File

@@ -29,7 +29,6 @@ export interface IRawChatParticipantContribution {
defaultImplicitVariables?: string[];
locations?: RawChatParticipantLocation[];
disambiguation?: { category: string; categoryName?: string /** Deprecated */; description: string; examples: string[] }[];
supportsToolReferences?: boolean;
}
/**

View File

@@ -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);
}

View File

@@ -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<IToolResult>;
prepareToolInvocation?(participantName: string, parameters: any, token: CancellationToken): Promise<IPreparedToolInvocation | undefined>;
prepareToolInvocation?(parameters: any, token: CancellationToken): Promise<IPreparedToolInvocation | undefined>;
}
export const ILanguageModelToolsService = createDecorator<ILanguageModelToolsService>('ILanguageModelToolsService');

View File

@@ -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<IRawToolContribution[]>({
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));
}
}
});

View File

@@ -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));

View File

@@ -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<LanguageModelTextPart | LanguageModelToolCallPart>;
}
// 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<T>(id: string, tool: LanguageModelTool<T>): Disposable;
export function registerTool<T>(name: string, tool: LanguageModelTool<T>): Disposable;
/**
* A list of all available tools.
@@ -103,7 +145,7 @@ declare module 'vscode' {
/**
* Invoke a tool with the given parameters.
*/
export function invokeTool<T>(id: string, options: LanguageModelToolInvocationOptions<T>, token: CancellationToken): Thenable<LanguageModelToolResult>;
export function invokeTool(id: string, options: LanguageModelToolInvocationOptions<object>, token: CancellationToken): Thenable<LanguageModelToolResult>;
}
/**
@@ -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<T> {
/**
* 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<T>, token: CancellationToken): ProviderResult<LanguageModelToolResult>;
/**
* 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<T>, token: CancellationToken): ProviderResult<PreparedToolInvocation>;
}
@@ -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;
}