Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/src/vs/workbench/contrib/chat/test/common/languageModels.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 { AsyncIterableSource, DeferredPromise, timeout } from '../../../../../base/common/async.js';
8
import { CancellationToken, CancellationTokenSource } from '../../../../../base/common/cancellation.js';
9
import { DisposableStore } from '../../../../../base/common/lifecycle.js';
10
import { mock } from '../../../../../base/test/common/mock.js';
11
import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';
12
import { NullLogService } from '../../../../../platform/log/common/log.js';
13
import { ChatMessageRole, languageModelChatProviderExtensionPoint, LanguageModelsService, IChatMessage, IChatResponsePart } from '../../common/languageModels.js';
14
import { IExtensionService, nullExtensionDescription } from '../../../../services/extensions/common/extensions.js';
15
import { ExtensionsRegistry } from '../../../../services/extensions/common/extensionsRegistry.js';
16
import { DEFAULT_MODEL_PICKER_CATEGORY } from '../../common/modelPicker/modelPickerWidget.js';
17
import { ExtensionIdentifier } from '../../../../../platform/extensions/common/extensions.js';
18
import { TestStorageService } from '../../../../test/common/workbenchTestServices.js';
19
import { Event } from '../../../../../base/common/event.js';
20
import { MockContextKeyService } from '../../../../../platform/keybinding/test/common/mockKeybindingService.js';
21
22
suite('LanguageModels', function () {
23
24
let languageModels: LanguageModelsService;
25
26
const store = new DisposableStore();
27
const activationEvents = new Set<string>();
28
29
setup(function () {
30
31
languageModels = new LanguageModelsService(
32
new class extends mock<IExtensionService>() {
33
override activateByEvent(name: string) {
34
activationEvents.add(name);
35
return Promise.resolve();
36
}
37
},
38
new NullLogService(),
39
new TestStorageService(),
40
new MockContextKeyService()
41
);
42
43
const ext = ExtensionsRegistry.getExtensionPoints().find(e => e.name === languageModelChatProviderExtensionPoint.name)!;
44
45
ext.acceptUsers([{
46
description: { ...nullExtensionDescription },
47
value: { vendor: 'test-vendor' },
48
collector: null!
49
}, {
50
description: { ...nullExtensionDescription },
51
value: { vendor: 'actual-vendor' },
52
collector: null!
53
}]);
54
55
store.add(languageModels.registerLanguageModelProvider('test-vendor', {
56
onDidChange: Event.None,
57
provideLanguageModelChatInfo: async () => {
58
const modelMetadata = [
59
{
60
extension: nullExtensionDescription.identifier,
61
name: 'Pretty Name',
62
vendor: 'test-vendor',
63
family: 'test-family',
64
version: 'test-version',
65
modelPickerCategory: undefined,
66
id: 'test-id-1',
67
maxInputTokens: 100,
68
maxOutputTokens: 100,
69
},
70
{
71
extension: nullExtensionDescription.identifier,
72
name: 'Pretty Name',
73
vendor: 'test-vendor',
74
family: 'test2-family',
75
version: 'test2-version',
76
modelPickerCategory: undefined,
77
id: 'test-id-12',
78
maxInputTokens: 100,
79
maxOutputTokens: 100,
80
}
81
];
82
const modelMetadataAndIdentifier = modelMetadata.map(m => ({
83
metadata: m,
84
identifier: m.id,
85
}));
86
return modelMetadataAndIdentifier;
87
},
88
sendChatRequest: async () => {
89
throw new Error();
90
},
91
provideTokenCount: async () => {
92
throw new Error();
93
}
94
}));
95
});
96
97
teardown(function () {
98
languageModels.dispose();
99
activationEvents.clear();
100
store.clear();
101
});
102
103
ensureNoDisposablesAreLeakedInTestSuite();
104
105
test('empty selector returns all', async function () {
106
107
const result1 = await languageModels.selectLanguageModels({});
108
assert.deepStrictEqual(result1.length, 2);
109
assert.deepStrictEqual(result1[0], 'test-id-1');
110
assert.deepStrictEqual(result1[1], 'test-id-12');
111
});
112
113
test('selector with id works properly', async function () {
114
const result1 = await languageModels.selectLanguageModels({ id: 'test-id-1' });
115
assert.deepStrictEqual(result1.length, 1);
116
assert.deepStrictEqual(result1[0], 'test-id-1');
117
});
118
119
test('no warning that a matching model was not found #213716', async function () {
120
const result1 = await languageModels.selectLanguageModels({ vendor: 'test-vendor' });
121
assert.deepStrictEqual(result1.length, 2);
122
123
const result2 = await languageModels.selectLanguageModels({ vendor: 'test-vendor', family: 'FAKE' });
124
assert.deepStrictEqual(result2.length, 0);
125
});
126
127
test('sendChatRequest returns a response-stream', async function () {
128
129
store.add(languageModels.registerLanguageModelProvider('actual-vendor', {
130
onDidChange: Event.None,
131
provideLanguageModelChatInfo: async () => {
132
const modelMetadata = [
133
{
134
extension: nullExtensionDescription.identifier,
135
name: 'Pretty Name',
136
vendor: 'actual-vendor',
137
family: 'actual-family',
138
version: 'actual-version',
139
id: 'actual-lm',
140
maxInputTokens: 100,
141
maxOutputTokens: 100,
142
modelPickerCategory: DEFAULT_MODEL_PICKER_CATEGORY,
143
}
144
];
145
const modelMetadataAndIdentifier = modelMetadata.map(m => ({
146
metadata: m,
147
identifier: m.id,
148
}));
149
return modelMetadataAndIdentifier;
150
},
151
sendChatRequest: async (modelId: string, messages: IChatMessage[], _from: ExtensionIdentifier, _options: { [name: string]: any }, token: CancellationToken) => {
152
// const message = messages.at(-1);
153
154
const defer = new DeferredPromise();
155
const stream = new AsyncIterableSource<IChatResponsePart>();
156
157
(async () => {
158
while (!token.isCancellationRequested) {
159
stream.emitOne({ type: 'text', value: Date.now().toString() });
160
await timeout(10);
161
}
162
defer.complete(undefined);
163
})();
164
165
return {
166
stream: stream.asyncIterable,
167
result: defer.p
168
};
169
},
170
provideTokenCount: async () => {
171
throw new Error();
172
}
173
}));
174
175
// Register the extension point for the actual vendor
176
const ext = ExtensionsRegistry.getExtensionPoints().find(e => e.name === languageModelChatProviderExtensionPoint.name)!;
177
ext.acceptUsers([{
178
description: { ...nullExtensionDescription },
179
value: { vendor: 'actual-vendor' },
180
collector: null!
181
}]);
182
183
const models = await languageModels.selectLanguageModels({ id: 'actual-lm' });
184
assert.ok(models.length === 1);
185
186
const first = models[0];
187
188
const cts = new CancellationTokenSource();
189
190
const request = await languageModels.sendChatRequest(first, nullExtensionDescription.identifier, [{ role: ChatMessageRole.User, content: [{ type: 'text', value: 'hello' }] }], {}, cts.token);
191
192
assert.ok(request);
193
194
cts.dispose(true);
195
196
await request.result;
197
});
198
});
199
200