Skip to content

Commit ee173b0

Browse files
authored
Tools API tweaks, merge into lmTools (#216750)
* Tools API tweaks, merge into lmTools * Rename more id -> name * Fix * Add lmTools API version
1 parent 5f330d3 commit ee173b0

11 files changed

+99
-83
lines changed

extensions/vscode-api-tests/package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,8 @@
5353
"treeViewActiveItem",
5454
"treeViewReveal",
5555
"workspaceTrust",
56-
"telemetry"
56+
"telemetry",
57+
"lmTools"
5758
],
5859
"private": true,
5960
"activationEvents": [],

src/vs/platform/extensions/common/extensionsApiProposals.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,6 @@ const _allApiProposals = {
4242
chatTab: {
4343
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatTab.d.ts',
4444
},
45-
chatTools: {
46-
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatTools.d.ts',
47-
},
4845
chatVariableResolver: {
4946
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatVariableResolver.d.ts',
5047
},
@@ -227,6 +224,7 @@ const _allApiProposals = {
227224
},
228225
lmTools: {
229226
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.lmTools.d.ts',
227+
version: 2
230228
},
231229
mappedEditsProvider: {
232230
proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts',

src/vs/workbench/api/browser/mainThreadLanguageModelTools.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -33,18 +33,18 @@ export class MainThreadLanguageModelTools extends Disposable implements MainThre
3333
return this._languageModelToolsService.invokeTool(name, parameters, token);
3434
}
3535

36-
$registerTool(id: string): void {
36+
$registerTool(name: string): void {
3737
const disposable = this._languageModelToolsService.registerToolImplementation(
38-
id,
38+
name,
3939
{
4040
invoke: async (parameters, token) => {
41-
return await this._proxy.$invokeTool(id, parameters, token);
41+
return await this._proxy.$invokeTool(name, parameters, token);
4242
},
4343
});
44-
this._tools.set(id, disposable);
44+
this._tools.set(name, disposable);
4545
}
4646

47-
$unregisterTool(id: string): void {
48-
this._tools.deleteAndDispose(id);
47+
$unregisterTool(name: string): void {
48+
this._tools.deleteAndDispose(name);
4949
}
5050
}

src/vs/workbench/api/common/extHost.api.impl.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1483,15 +1483,15 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
14831483
}
14841484
},
14851485
registerTool(toolId: string, tool: vscode.LanguageModelTool) {
1486-
checkProposedApiEnabled(extension, 'chatVariableResolver');
1486+
checkProposedApiEnabled(extension, 'lmTools');
14871487
return extHostLanguageModelTools.registerTool(extension, toolId, tool);
14881488
},
14891489
invokeTool(toolId: string, parameters: Object, token: vscode.CancellationToken) {
1490-
checkProposedApiEnabled(extension, 'chatVariableResolver');
1490+
checkProposedApiEnabled(extension, 'lmTools');
14911491
return extHostLanguageModelTools.invokeTool(toolId, parameters, token);
14921492
},
14931493
get tools() {
1494-
checkProposedApiEnabled(extension, 'chatVariableResolver');
1494+
checkProposedApiEnabled(extension, 'lmTools');
14951495
return extHostLanguageModelTools.tools;
14961496
},
14971497
};

src/vs/workbench/api/common/extHostChatAgents2.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extH
2222
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
2323
import * as extHostTypes from 'vs/workbench/api/common/extHostTypes';
2424
import { ChatAgentLocation, IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents';
25-
import { IChatContentReference, IChatFollowup, IChatUserActionEvent, ChatAgentVoteDirection, IChatResponseErrorDetails } from 'vs/workbench/contrib/chat/common/chatService';
25+
import { ChatAgentVoteDirection, IChatContentReference, IChatFollowup, IChatResponseErrorDetails, IChatUserActionEvent } from 'vs/workbench/contrib/chat/common/chatService';
2626
import { checkProposedApiEnabled, isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions';
2727
import { Dto } from 'vs/workbench/services/extensions/common/proxyIdentifier';
2828
import type * as vscode from 'vscode';

src/vs/workbench/api/common/extHostLanguageModelTools.ts

+13-11
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { CancellationToken } from 'vs/base/common/cancellation';
77
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
88
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
99
import { ExtHostLanguageModelToolsShape, IMainContext, MainContext, MainThreadLanguageModelToolsShape } from 'vs/workbench/api/common/extHost.protocol';
10+
import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters';
1011
import { IToolData, IToolDelta } from 'vs/workbench/contrib/chat/common/languageModelToolsService';
1112
import type * as vscode from 'vscode';
1213

@@ -23,7 +24,7 @@ export class ExtHostLanguageModelTools implements ExtHostLanguageModelToolsShape
2324

2425
this._proxy.$getTools().then(tools => {
2526
for (const tool of tools) {
26-
this._allTools.set(tool.id, tool);
27+
this._allTools.set(tool.name, tool);
2728
}
2829
});
2930
}
@@ -35,7 +36,7 @@ export class ExtHostLanguageModelTools implements ExtHostLanguageModelToolsShape
3536

3637
async $acceptToolDelta(delta: IToolDelta): Promise<void> {
3738
if (delta.added) {
38-
this._allTools.set(delta.added.id, delta.added);
39+
this._allTools.set(delta.added.name, delta.added);
3940
}
4041

4142
if (delta.removed) {
@@ -44,25 +45,26 @@ export class ExtHostLanguageModelTools implements ExtHostLanguageModelToolsShape
4445
}
4546

4647
get tools(): vscode.LanguageModelToolDescription[] {
47-
return Array.from(this._allTools.values());
48+
return Array.from(this._allTools.values())
49+
.map(tool => typeConvert.LanguageModelToolDescription.to(tool));
4850
}
4951

50-
async $invokeTool(id: string, parameters: any, token: CancellationToken): Promise<string> {
51-
const item = this._registeredTools.get(id);
52+
async $invokeTool(name: string, parameters: any, token: CancellationToken): Promise<string> {
53+
const item = this._registeredTools.get(name);
5254
if (!item) {
53-
throw new Error(`Unknown tool ${id}`);
55+
throw new Error(`Unknown tool ${name}`);
5456
}
5557

5658
return await item.tool.invoke(parameters, token);
5759
}
5860

59-
registerTool(extension: IExtensionDescription, id: string, tool: vscode.LanguageModelTool): IDisposable {
60-
this._registeredTools.set(id, { extension, tool });
61-
this._proxy.$registerTool(id);
61+
registerTool(extension: IExtensionDescription, name: string, tool: vscode.LanguageModelTool): IDisposable {
62+
this._registeredTools.set(name, { extension, tool });
63+
this._proxy.$registerTool(name);
6264

6365
return toDisposable(() => {
64-
this._registeredTools.delete(id);
65-
this._proxy.$unregisterTool(id);
66+
this._registeredTools.delete(name);
67+
this._proxy.$unregisterTool(name);
6668
});
6769
}
6870
}

src/vs/workbench/api/common/extHostTypeConverters.ts

+11
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import { ACTIVE_GROUP, SIDE_GROUP } from 'vs/workbench/services/editor/common/ed
5353
import { Dto } from 'vs/workbench/services/extensions/common/proxyIdentifier';
5454
import type * as vscode from 'vscode';
5555
import * as types from './extHostTypes';
56+
import { IToolData } from 'vs/workbench/contrib/chat/common/languageModelToolsService';
5657

5758
export namespace Command {
5859

@@ -2743,3 +2744,13 @@ export namespace DebugTreeItem {
27432744
};
27442745
}
27452746
}
2747+
2748+
export namespace LanguageModelToolDescription {
2749+
export function to(item: IToolData): vscode.LanguageModelToolDescription {
2750+
return {
2751+
name: item.name,
2752+
description: item.description,
2753+
parametersSchema: item.parametersSchema,
2754+
};
2755+
}
2756+
}

src/vs/workbench/contrib/chat/common/languageModelToolsService.ts

+11-11
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { Iterable } from 'vs/base/common/iterator';
1111
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
1212

1313
export interface IToolData {
14-
id: string;
14+
name: string;
1515
displayName?: string;
1616
description: string;
1717
parametersSchema?: Object;
@@ -37,7 +37,7 @@ export interface ILanguageModelToolsService {
3737
_serviceBrand: undefined;
3838
onDidChangeTools: Event<IToolDelta>;
3939
registerToolData(toolData: IToolData): IDisposable;
40-
registerToolImplementation(id: string, tool: IToolImpl): IDisposable;
40+
registerToolImplementation(name: string, tool: IToolImpl): IDisposable;
4141
getTools(): Iterable<Readonly<IToolData>>;
4242
invokeTool(name: string, parameters: any, token: CancellationToken): Promise<string>;
4343
}
@@ -55,28 +55,28 @@ export class LanguageModelToolsService implements ILanguageModelToolsService {
5555
) { }
5656

5757
registerToolData(toolData: IToolData): IDisposable {
58-
if (this._tools.has(toolData.id)) {
59-
throw new Error(`Tool "${toolData.id}" is already registered.`);
58+
if (this._tools.has(toolData.name)) {
59+
throw new Error(`Tool "${toolData.name}" is already registered.`);
6060
}
6161

62-
this._tools.set(toolData.id, { data: toolData });
62+
this._tools.set(toolData.name, { data: toolData });
6363
this._onDidChangeTools.fire({ added: toolData });
6464

6565
return toDisposable(() => {
66-
this._tools.delete(toolData.id);
67-
this._onDidChangeTools.fire({ removed: toolData.id });
66+
this._tools.delete(toolData.name);
67+
this._onDidChangeTools.fire({ removed: toolData.name });
6868
});
6969

7070
}
7171

72-
registerToolImplementation(id: string, tool: IToolImpl): IDisposable {
73-
const entry = this._tools.get(id);
72+
registerToolImplementation(name: string, tool: IToolImpl): IDisposable {
73+
const entry = this._tools.get(name);
7474
if (!entry) {
75-
throw new Error(`Tool "${id}" was not contributed.`);
75+
throw new Error(`Tool "${name}" was not contributed.`);
7676
}
7777

7878
if (entry.impl) {
79-
throw new Error(`Tool "${id}" already has an implementation.`);
79+
throw new Error(`Tool "${name}" already has an implementation.`);
8080
}
8181

8282
entry.impl = tool;

src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts

+18-11
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,13 @@ import { IJSONSchema } from 'vs/base/common/jsonSchema';
88
import { DisposableMap } from 'vs/base/common/lifecycle';
99
import { localize } from 'vs/nls';
1010
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
11+
import { ILogService } from 'vs/platform/log/common/log';
1112
import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
1213
import { ILanguageModelToolsService } from 'vs/workbench/contrib/chat/common/languageModelToolsService';
1314
import * as extensionsRegistry from 'vs/workbench/services/extensions/common/extensionsRegistry';
1415

1516
interface IRawToolContribution {
16-
id: string;
17+
name: string;
1718
displayName?: string;
1819
description: string;
1920
parametersSchema?: IJSONSchema;
@@ -23,7 +24,7 @@ const languageModelToolsExtensionPoint = extensionsRegistry.ExtensionsRegistry.r
2324
extensionPoint: 'languageModelTools',
2425
activationEventsGenerator: (contributions: IRawToolContribution[], result) => {
2526
for (const contrib of contributions) {
26-
result.push(`onLanguageModelTool:${contrib.id}`);
27+
result.push(`onLanguageModelTool:${contrib.name}`);
2728
}
2829
},
2930
jsonSchema: {
@@ -32,11 +33,11 @@ const languageModelToolsExtensionPoint = extensionsRegistry.ExtensionsRegistry.r
3233
items: {
3334
additionalProperties: false,
3435
type: 'object',
35-
defaultSnippets: [{ body: { id: '', description: '' } }],
36-
required: ['id', 'description'],
36+
defaultSnippets: [{ body: { name: '', description: '' } }],
37+
required: ['name', 'description'],
3738
properties: {
38-
id: {
39-
description: localize('toolId', "A unique id for this tool."),
39+
name: {
40+
description: localize('toolname', "A name for this tool which must be unique across all tools."),
4041
type: 'string'
4142
},
4243
description: {
@@ -57,8 +58,8 @@ const languageModelToolsExtensionPoint = extensionsRegistry.ExtensionsRegistry.r
5758
}
5859
});
5960

60-
function toToolKey(extensionIdentifier: ExtensionIdentifier, toolId: string) {
61-
return `${extensionIdentifier.value}/${toolId}`;
61+
function toToolKey(extensionIdentifier: ExtensionIdentifier, toolName: string) {
62+
return `${extensionIdentifier.value}/${toolName}`;
6263
}
6364

6465
export class LanguageModelToolsExtensionPointHandler implements IWorkbenchContribution {
@@ -67,19 +68,25 @@ export class LanguageModelToolsExtensionPointHandler implements IWorkbenchContri
6768
private _registrationDisposables = new DisposableMap<string>();
6869

6970
constructor(
70-
@ILanguageModelToolsService languageModelToolsService: ILanguageModelToolsService
71+
@ILanguageModelToolsService languageModelToolsService: ILanguageModelToolsService,
72+
@ILogService logService: ILogService,
7173
) {
7274
languageModelToolsExtensionPoint.setHandler((extensions, delta) => {
7375
for (const extension of delta.added) {
7476
for (const tool of extension.value) {
77+
if (!tool.name || !tool.description) {
78+
logService.warn(`Invalid tool contribution from ${extension.description.identifier.value}: ${JSON.stringify(tool)}`);
79+
continue;
80+
}
81+
7582
const disposable = languageModelToolsService.registerToolData(tool);
76-
this._registrationDisposables.set(toToolKey(extension.description.identifier, tool.id), disposable);
83+
this._registrationDisposables.set(toToolKey(extension.description.identifier, tool.name), disposable);
7784
}
7885
}
7986

8087
for (const extension of delta.removed) {
8188
for (const tool of extension.value) {
82-
this._registrationDisposables.deleteAndDispose(toToolKey(extension.description.identifier, tool.id));
89+
this._registrationDisposables.deleteAndDispose(toToolKey(extension.description.identifier, tool.name));
8390
}
8491
}
8592
});

src/vscode-dts/vscode.proposed.chatTools.d.ts

-35
This file was deleted.

src/vscode-dts/vscode.proposed.lmTools.d.ts

+33-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
* Licensed under the MIT License. See License.txt in the project root for license information.
44
*--------------------------------------------------------------------------------------------*/
55

6+
// version: 2
7+
68
declare module 'vscode' {
79

810
// TODO@API capabilities
@@ -13,7 +15,7 @@ declare module 'vscode' {
1315
export interface LanguageModelChatFunction {
1416
name: string;
1517
description: string;
16-
parametersSchema: JSONSchema;
18+
parametersSchema?: JSONSchema;
1719
}
1820

1921
// API -> LM: add tools as request option
@@ -55,4 +57,34 @@ declare module 'vscode' {
5557
export interface LanguageModelChatMessage {
5658
content2: string | LanguageModelChatMessageFunctionResultPart;
5759
}
60+
61+
// Tool registration/invoking between extensions
62+
63+
export namespace lm {
64+
/**
65+
* Register a LanguageModelTool. The tool must also be registered in the package.json `languageModelTools` contribution point.
66+
*/
67+
export function registerTool(name: string, tool: LanguageModelTool): Disposable;
68+
69+
/**
70+
* A list of all available tools.
71+
*/
72+
export const tools: ReadonlyArray<LanguageModelToolDescription>;
73+
74+
/**
75+
* Invoke a tool with the given parameters.
76+
*/
77+
export function invokeTool(name: string, parameters: Object, token: CancellationToken): Thenable<string>;
78+
}
79+
80+
// Is the same as LanguageModelChatFunction now, but could have more details in the future
81+
export interface LanguageModelToolDescription {
82+
name: string;
83+
description: string;
84+
parametersSchema?: JSONSchema;
85+
}
86+
87+
export interface LanguageModelTool {
88+
invoke(parameters: any, token: CancellationToken): Thenable<string>;
89+
}
5890
}

0 commit comments

Comments
 (0)