Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/chat/test/browser/chatSelectedTools.test.ts
3296 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 assert from 'assert';
7
import { TestConfigurationService } from '../../../../../platform/configuration/test/common/testConfigurationService.js';
8
import { ContextKeyService } from '../../../../../platform/contextkey/browser/contextKeyService.js';
9
import { workbenchInstantiationService } from '../../../../test/browser/workbenchTestServices.js';
10
import { LanguageModelToolsService } from '../../browser/languageModelToolsService.js';
11
import { IChatService } from '../../common/chatService.js';
12
import { ILanguageModelToolsService, IToolData, ToolDataSource, ToolSet } from '../../common/languageModelToolsService.js';
13
import { MockChatService } from '../common/mockChatService.js';
14
import { ChatSelectedTools } from '../../browser/chatSelectedTools.js';
15
import { constObservable } from '../../../../../base/common/observable.js';
16
import { Iterable } from '../../../../../base/common/iterator.js';
17
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
18
import { runWithFakedTimers } from '../../../../../base/test/common/timeTravelScheduler.js';
19
import { timeout } from '../../../../../base/common/async.js';
20
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
21
import { URI } from '../../../../../base/common/uri.js';
22
import { ChatMode } from '../../common/chatModes.js';
23
24
suite('ChatSelectedTools', () => {
25
26
let store: DisposableStore;
27
28
let toolsService: ILanguageModelToolsService;
29
let selectedTools: ChatSelectedTools;
30
31
setup(() => {
32
33
store = new DisposableStore();
34
35
const instaService = workbenchInstantiationService({
36
contextKeyService: () => store.add(new ContextKeyService(new TestConfigurationService)),
37
}, store);
38
instaService.stub(IChatService, new MockChatService());
39
instaService.stub(ILanguageModelToolsService, instaService.createInstance(LanguageModelToolsService));
40
41
store.add(instaService);
42
toolsService = instaService.get(ILanguageModelToolsService);
43
selectedTools = store.add(instaService.createInstance(ChatSelectedTools, constObservable(ChatMode.Agent)));
44
});
45
46
teardown(function () {
47
store.dispose();
48
});
49
50
ensureNoDisposablesAreLeakedInTestSuite();
51
52
const mcpSource: ToolDataSource = { type: 'mcp', label: 'MCP', collectionId: '', definitionId: '', instructions: '', serverLabel: '' };
53
test('Can\'t enable/disable MCP tools directly #18161', () => {
54
55
return runWithFakedTimers({}, async () => {
56
57
const toolData1: IToolData = {
58
id: 'testTool1',
59
modelDescription: 'Test Tool 1',
60
displayName: 'Test Tool 1',
61
canBeReferencedInPrompt: true,
62
toolReferenceName: 't1',
63
source: mcpSource,
64
};
65
66
const toolData2: IToolData = {
67
id: 'testTool2',
68
modelDescription: 'Test Tool 2',
69
displayName: 'Test Tool 2',
70
source: mcpSource,
71
canBeReferencedInPrompt: true,
72
toolReferenceName: 't2',
73
};
74
75
const toolData3: IToolData = {
76
id: 'testTool3',
77
modelDescription: 'Test Tool 3',
78
displayName: 'Test Tool 3',
79
source: mcpSource,
80
canBeReferencedInPrompt: true,
81
toolReferenceName: 't3',
82
};
83
84
const toolset = toolsService.createToolSet(
85
mcpSource,
86
'mcp', 'mcp'
87
);
88
89
store.add(toolsService.registerToolData(toolData1));
90
store.add(toolsService.registerToolData(toolData2));
91
store.add(toolsService.registerToolData(toolData3));
92
93
store.add(toolset);
94
store.add(toolset.addTool(toolData1));
95
store.add(toolset.addTool(toolData2));
96
store.add(toolset.addTool(toolData3));
97
98
assert.strictEqual(Iterable.length(toolsService.getTools()), 3);
99
100
const size = Iterable.length(toolset.getTools());
101
assert.strictEqual(size, 3);
102
103
await timeout(1000); // UGLY the tools service updates its state sync but emits the event async (750ms) delay. This affects the observable that depends on the event
104
105
assert.strictEqual(selectedTools.entriesMap.get().size, 4); // 1 toolset, 3 tools
106
107
const toSet = new Map<IToolData | ToolSet, boolean>([[toolData1, true], [toolData2, false], [toolData3, false], [toolset, false]]);
108
selectedTools.set(toSet, false);
109
110
const userSelectedTools = selectedTools.userSelectedTools.get();
111
assert.strictEqual(Object.keys(userSelectedTools).length, 3); // 3 tools
112
113
assert.strictEqual(userSelectedTools[toolData1.id], true);
114
assert.strictEqual(userSelectedTools[toolData2.id], false);
115
assert.strictEqual(userSelectedTools[toolData3.id], false);
116
});
117
});
118
119
test('Can still enable/disable user toolsets #251640', () => {
120
return runWithFakedTimers({}, async () => {
121
const toolData1: IToolData = {
122
id: 'testTool1',
123
modelDescription: 'Test Tool 1',
124
displayName: 'Test Tool 1',
125
canBeReferencedInPrompt: true,
126
toolReferenceName: 't1',
127
source: ToolDataSource.Internal,
128
};
129
130
const toolData2: IToolData = {
131
id: 'testTool2',
132
modelDescription: 'Test Tool 2',
133
displayName: 'Test Tool 2',
134
source: mcpSource,
135
canBeReferencedInPrompt: true,
136
toolReferenceName: 't2',
137
};
138
139
const toolData3: IToolData = {
140
id: 'testTool3',
141
modelDescription: 'Test Tool 3',
142
displayName: 'Test Tool 3',
143
source: ToolDataSource.Internal,
144
canBeReferencedInPrompt: true,
145
toolReferenceName: 't3',
146
};
147
148
const toolset = toolsService.createToolSet(
149
{ type: 'user', label: 'User Toolset', file: URI.file('/userToolset.json') },
150
'userToolset', 'userToolset'
151
);
152
153
store.add(toolsService.registerToolData(toolData1));
154
store.add(toolsService.registerToolData(toolData2));
155
store.add(toolsService.registerToolData(toolData3));
156
157
store.add(toolset);
158
store.add(toolset.addTool(toolData1));
159
store.add(toolset.addTool(toolData2));
160
store.add(toolset.addTool(toolData3));
161
162
assert.strictEqual(Iterable.length(toolsService.getTools()), 3);
163
164
const size = Iterable.length(toolset.getTools());
165
assert.strictEqual(size, 3);
166
167
await timeout(1000); // UGLY the tools service updates its state sync but emits the event async (750ms) delay. This affects the observable that depends on the event
168
169
assert.strictEqual(selectedTools.entriesMap.get().size, 4); // 1 toolset, 3 tools
170
171
// Toolset is checked, tools 2 and 3 are unchecked
172
const toSet = new Map<IToolData | ToolSet, boolean>([[toolData1, true], [toolData2, false], [toolData3, false], [toolset, true]]);
173
selectedTools.set(toSet, false);
174
175
const userSelectedTools = selectedTools.userSelectedTools.get();
176
assert.strictEqual(Object.keys(userSelectedTools).length, 3); // 3 tools
177
178
// User toolset is enabled - all tools are enabled
179
assert.strictEqual(userSelectedTools[toolData1.id], true);
180
assert.strictEqual(userSelectedTools[toolData2.id], true);
181
assert.strictEqual(userSelectedTools[toolData3.id], true);
182
});
183
});
184
});
185
186