Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/extension/test/node/intent.spec.ts
13399 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 { beforeEach, suite, test } from 'vitest';
8
import { IResponsePart } from '../../../platform/chat/common/chatMLFetcher';
9
import { TextDocumentSnapshot } from '../../../platform/editing/common/textDocumentSnapshot';
10
import { MockEndpoint } from '../../../platform/endpoint/test/node/mockEndpoint';
11
import { ITestingServicesAccessor } from '../../../platform/test/node/services';
12
import { ChatResponseStreamImpl } from '../../../util/common/chatResponseStreamImpl';
13
import { createTextDocumentData } from '../../../util/common/test/shims/textDocument';
14
import { AsyncIterableObject } from '../../../util/vs/base/common/async';
15
import { CancellationToken } from '../../../util/vs/base/common/cancellation';
16
import { URI } from '../../../util/vs/base/common/uri';
17
import { IInstantiationService } from '../../../util/vs/platform/instantiation/common/instantiation';
18
import { ChatResponseTextEditPart, Range, Selection, TextEdit, Uri } from '../../../vscodeTypes';
19
import { ISessionTurnStorage, OutcomeAnnotation } from '../../inlineChat/node/promptCraftingTypes';
20
import { ChatVariablesCollection } from '../../prompt/common/chatVariablesCollection';
21
import { PromptReference, getUniqueReferences } from '../../prompt/common/conversation';
22
import { IDocumentContext } from '../../prompt/node/documentContext';
23
import { IResponseProcessorContext, ReplyInterpreterMetaData } from '../../prompt/node/intents';
24
import { PromptRenderer } from '../../prompts/node/base/promptRenderer';
25
import { InlineChatEditCodePrompt } from '../../prompts/node/inline/inlineChatEditCodePrompt';
26
import { createExtensionUnitTestingServices } from './services';
27
28
29
suite('Intent Streaming', function () {
30
31
let accessor: ITestingServicesAccessor;
32
33
beforeEach(function () {
34
accessor = createExtensionUnitTestingServices().createTestingAccessor();
35
});
36
37
test.skip('[Bug] Stream processing may never terminate if model responds with something other than an edit #2080', async function () {
38
39
40
const data = createTextDocumentData(URI.from({ scheme: 'test', path: '/path/file.txt' }), 'Hello', 'fooLang');
41
const doc = TextDocumentSnapshot.create(data.document);
42
43
const context: IDocumentContext = {
44
document: doc,
45
language: { languageId: doc.languageId, lineComment: { start: '//' } },
46
fileIndentInfo: undefined,
47
wholeRange: new Range(0, 0, 1, 0),
48
selection: new Selection(0, 0, 0, 0),
49
};
50
51
const endpoint = accessor.get(IInstantiationService).createInstance(MockEndpoint, undefined);
52
const progressReporter = { report() { } };
53
const renderer = PromptRenderer.create(accessor.get(IInstantiationService), endpoint, InlineChatEditCodePrompt, {
54
documentContext: context,
55
promptContext: {
56
query: 'hello',
57
chatVariables: new ChatVariablesCollection([]),
58
history: [],
59
}
60
});
61
const result = await renderer.render(progressReporter, CancellationToken.None);
62
const replyInterpreter = result.metadata.get(ReplyInterpreterMetaData)!.replyInterpreter;
63
const stream = new ChatResponseStreamImpl((value) => {
64
if (value instanceof ChatResponseTextEditPart) {
65
values.push(...value.edits);
66
}
67
}, () => { }, undefined, undefined, undefined, () => Promise.resolve(undefined));
68
const values: TextEdit[] = [];
69
70
const part: IResponsePart = {
71
delta: {
72
text: 'What can be done'
73
}
74
};
75
const context2: IResponseProcessorContext = {
76
addAnnotations: function (annotations: OutcomeAnnotation[]): void {
77
// nothing
78
},
79
storeInInlineSession: function (store: ISessionTurnStorage): void {
80
// nothing
81
},
82
chatSessionId: '',
83
turn: null!,
84
messages: []
85
};
86
await replyInterpreter.processResponse(context2, AsyncIterableObject.fromArray([part]), stream, CancellationToken.None);
87
88
assert.strictEqual(values.length, 0);
89
});
90
});
91
92
suite('Reference Processing', function () {
93
test('combines adjacent lines and full file overlap', async () => {
94
const uri1 = Uri.file('1.txt');
95
const uri2 = Uri.file('2.txt');
96
const uri3 = Uri.file('3.txt');
97
98
const references = [
99
new PromptReference({
100
uri: uri1,
101
range: new Range(0, 0, 2, 0),
102
}), new PromptReference({
103
uri: uri1,
104
range: new Range(3, 0, 4, 0),
105
}), new PromptReference({
106
uri: uri1,
107
range: new Range(5, 0, 7, 0),
108
}), new PromptReference({
109
uri: uri2,
110
range: new Range(0, 0, 4, 0),
111
}), new PromptReference(uri3),
112
new PromptReference({
113
uri: uri3,
114
range: new Range(0, 0, 4, 0),
115
})
116
];
117
118
const result = getUniqueReferences(references);
119
120
assert.deepEqual(result,
121
[
122
new PromptReference({
123
uri: uri1,
124
range: new Range(0, 0, 7, 0),
125
}), new PromptReference({
126
uri: uri2,
127
range: new Range(0, 0, 4, 0),
128
}), new PromptReference(uri3)
129
]);
130
});
131
132
test('combines overlaping ranges', async () => {
133
const uri1 = Uri.file('1.txt');
134
const uri2 = Uri.file('2.txt');
135
136
const references = [
137
new PromptReference({
138
uri: uri1,
139
range: new Range(0, 0, 2, 0),
140
}), new PromptReference({
141
uri: uri1,
142
range: new Range(5, 0, 10, 0),
143
}), new PromptReference({
144
uri: uri1,
145
range: new Range(3, 0, 6, 0),
146
}), new PromptReference({
147
uri: uri2,
148
range: new Range(1, 0, 4, 0),
149
}), new PromptReference({
150
uri: uri2,
151
range: new Range(0, 0, 5, 0),
152
})
153
];
154
155
const result = getUniqueReferences(references);
156
157
assert.deepEqual(result,
158
[
159
new PromptReference({
160
uri: uri1,
161
range: new Range(0, 0, 10, 0),
162
}), new PromptReference({
163
uri: uri2,
164
range: new Range(0, 0, 5, 0),
165
})
166
167
]);
168
});
169
170
test('removes duplicates', async () => {
171
const uri1 = Uri.file('1.txt');
172
const uri2 = Uri.file('2.txt');
173
174
const references = [
175
new PromptReference({
176
uri: uri1,
177
range: new Range(3, 0, 4, 0),
178
}), new PromptReference({
179
uri: uri1,
180
range: new Range(3, 0, 4, 0),
181
}), new PromptReference({
182
uri: uri2,
183
range: new Range(3, 0, 4, 0),
184
}), new PromptReference({
185
uri: uri2,
186
range: new Range(3, 0, 4, 0),
187
})
188
];
189
190
const result = getUniqueReferences(references);
191
192
193
assert.deepEqual(result, [
194
new PromptReference({
195
uri: uri1,
196
range: new Range(3, 0, 4, 0),
197
}), new PromptReference({
198
uri: uri2,
199
range: new Range(3, 0, 4, 0),
200
}),
201
202
]);
203
});
204
205
206
207
208
209
test('leaves distinct ranges alone, but sorts them', async () => {
210
const uri1 = Uri.file('1.txt');
211
const uri2 = Uri.file('2.txt');
212
213
const references = [
214
new PromptReference({
215
uri: uri1,
216
range: new Range(7, 0, 10, 0),
217
}),
218
new PromptReference({
219
uri: uri2,
220
range: new Range(4, 0, 5, 0),
221
}),
222
new PromptReference({
223
uri: uri1,
224
range: new Range(0, 0, 2, 0),
225
}), new PromptReference({
226
uri: uri1,
227
range: new Range(4, 0, 5, 0),
228
}),
229
];
230
231
const result = getUniqueReferences(references);
232
233
234
assert.deepEqual(result, [
235
new PromptReference({
236
uri: uri1,
237
range: new Range(0, 0, 2, 0),
238
}), new PromptReference({
239
uri: uri1,
240
range: new Range(4, 0, 5, 0),
241
}),
242
new PromptReference({
243
uri: uri1,
244
range: new Range(7, 0, 10, 0),
245
}),
246
new PromptReference({
247
uri: uri2,
248
range: new Range(4, 0, 5, 0),
249
}),
250
]);
251
});
252
});
253
254