Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/test/base/extHostContext/simulationExtHostToolsService.ts
13394 views
1
/*---------------------------------------------------------------------------------------------
2
* Copyright (c) Microsoft Corporation. All rights reserved.
3
* Licensed under the MIT License. See License.txt in the project root for license information.
4
*--------------------------------------------------------------------------------------------*/
5
6
import type { CancellationToken, ChatRequest, LanguageModelTool, LanguageModelToolInformation, LanguageModelToolInvocationOptions, LanguageModelToolResult } from 'vscode';
7
import { getToolName, ToolName } from '../../../src/extension/tools/common/toolNames';
8
import { ICopilotTool } from '../../../src/extension/tools/common/toolsRegistry';
9
import { BaseToolsService, IToolsService } from '../../../src/extension/tools/common/toolsService';
10
import { getPackagejsonToolsForTest } from '../../../src/extension/tools/node/test/testToolsService';
11
import { ToolsContribution } from '../../../src/extension/tools/vscode-node/tools';
12
import { ToolsService } from '../../../src/extension/tools/vscode-node/toolsService';
13
import { packageJson } from '../../../src/platform/env/common/packagejson';
14
import { ILogService } from '../../../src/platform/log/common/logService';
15
import { IChatEndpoint } from '../../../src/platform/networking/common/networking';
16
import { raceTimeout } from '../../../src/util/vs/base/common/async';
17
import { CancellationError } from '../../../src/util/vs/base/common/errors';
18
import { Iterable } from '../../../src/util/vs/base/common/iterator';
19
import { observableValue } from '../../../src/util/vs/base/common/observableInternal';
20
import { IInstantiationService } from '../../../src/util/vs/platform/instantiation/common/instantiation';
21
import { logger } from '../../simulationLogger';
22
23
export class SimulationExtHostToolsService extends BaseToolsService implements IToolsService {
24
declare readonly _serviceBrand: undefined;
25
26
private readonly _inner: IToolsService;
27
private readonly _overrides = new Map<ToolName | string, { info: LanguageModelToolInformation; tool: ICopilotTool<any> }>();
28
private _lmToolRegistration?: ToolsContribution;
29
30
override get onWillInvokeTool() {
31
return this._inner.onWillInvokeTool;
32
}
33
34
get tools() {
35
this.ensureToolsRegistered();
36
return [
37
...this._inner.tools.filter(t => !this._disabledTools.has(t.name) && !this._overrides.has(t.name)),
38
...Iterable.map(this._overrides.values(), i => i.info),
39
];
40
}
41
42
get copilotTools() {
43
const r = new Map([
44
...this._inner.copilotTools,
45
...Iterable.map(this._overrides, ([k, v]): [ToolName, ICopilotTool<any>] => [k as ToolName, v.tool]),
46
]);
47
for (const name of this._disabledTools) {
48
r.delete(name as ToolName);
49
}
50
return r;
51
}
52
53
constructor(
54
private readonly _disabledTools: Set<string>,
55
@ILogService logService: ILogService,
56
@IInstantiationService instantiationService: IInstantiationService,
57
) {
58
super(logService);
59
this._inner = instantiationService.createInstance(ToolsService);
60
61
// register the contribution so that our tools are on vscode.lm.tools
62
setImmediate(() => this.ensureToolsRegistered());
63
}
64
65
private ensureToolsRegistered() {
66
this._lmToolRegistration ??= new ToolsContribution(this, {} as any, { threshold: observableValue(this, 128) } as any, {} as any, {} as any, {} as any, {} as any);
67
}
68
69
getCopilotTool(name: string): ICopilotTool<any> | undefined {
70
return this._disabledTools.has(name) ? undefined : (this._overrides.get(name)?.tool || this._inner.getCopilotTool(name));
71
}
72
73
async invokeTool(name: string, options: LanguageModelToolInvocationOptions<unknown>, token: CancellationToken): Promise<LanguageModelToolResult> {
74
logger.debug('SimulationExtHostToolsService.invokeTool', name, JSON.stringify(options.input));
75
const start = Date.now();
76
let err: Error | undefined;
77
try {
78
const toolName = getToolName(name) as ToolName;
79
const tool = this._overrides.get(toolName)?.tool;
80
const invoke = tool?.invoke;
81
if (invoke) {
82
this._onWillInvokeTool.fire({ toolName });
83
const result = await invoke.call(tool, options, token);
84
if (!result) {
85
throw new CancellationError();
86
}
87
88
return result;
89
}
90
91
if (tool) {
92
throw new Error(`tool ${toolName} does not implement invoke`);
93
}
94
95
const r = await raceTimeout(Promise.resolve(this._inner.invokeTool(name, options, token)), 60_000);
96
if (!r) {
97
throw new Error(`Tool call timed out after 60 seconds`);
98
}
99
return r;
100
} catch (e) {
101
err = e;
102
throw e;
103
} finally {
104
logger.debug(`SimulationExtHostToolsService.invokeTool ${name} done in ${Date.now() - start}ms` + (err ? ` with error: ${err.message}` : ''));
105
}
106
}
107
108
getTool(name: string): LanguageModelToolInformation | undefined {
109
return this._disabledTools.has(name) ? undefined : (this._overrides.get(name)?.info || this._inner.getTool(name));
110
}
111
112
getToolByToolReferenceName(toolReferenceName: string): LanguageModelToolInformation | undefined {
113
const contributedTool = packageJson.contributes.languageModelTools.find(tool => tool.toolReferenceName === toolReferenceName && tool.canBeReferencedInPrompt);
114
if (contributedTool) {
115
return {
116
name: contributedTool.name,
117
description: contributedTool.modelDescription,
118
inputSchema: contributedTool.inputSchema,
119
source: undefined,
120
tags: []
121
};
122
}
123
124
return undefined;
125
}
126
127
getEnabledTools(request: ChatRequest, endpoint: IChatEndpoint, filter?: (tool: LanguageModelToolInformation) => boolean | undefined): LanguageModelToolInformation[] {
128
const packageJsonTools = getPackagejsonToolsForTest();
129
return this.tools
130
.map(tool => {
131
// Apply model-specific alternative if available via alternativeDefinition
132
const owned = this.copilotTools.get(getToolName(tool.name) as ToolName);
133
if (owned?.alternativeDefinition) {
134
const alternative = owned.alternativeDefinition(tool, endpoint);
135
if (alternative) {
136
return alternative;
137
}
138
}
139
return tool;
140
})
141
.filter(tool => filter?.(tool) ?? (!this._disabledTools.has(getToolName(tool.name)) && packageJsonTools.has(tool.name)));
142
}
143
144
addTestToolOverride(info: LanguageModelToolInformation, tool: LanguageModelTool<unknown>): void {
145
if (!this._disabledTools.has(info.name)) {
146
this._overrides.set(info.name as ToolName, { tool, info });
147
}
148
}
149
}
150
151