Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/platform/chat/test/common/streamingMockChatMLFetcher.ts
13405 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 { CancellationToken } from '../../../../util/vs/base/common/cancellation';
7
import { Event } from '../../../../util/vs/base/common/event';
8
import { IChatMLFetcher, IFetchMLOptions } from '../../common/chatMLFetcher';
9
import { ChatFetchResponseType, ChatResponse, ChatResponses } from '../../common/commonTypes';
10
11
/**
12
* A mock IChatMLFetcher that simulates streaming LLM responses by calling `finishedCb`
13
* incrementally for each configured line. This enables testing the full streaming pipeline
14
* in XtabProvider's `streamEdits` without hitting a real endpoint.
15
*/
16
export class StreamingMockChatMLFetcher implements IChatMLFetcher {
17
declare readonly _serviceBrand: undefined;
18
readonly onDidMakeChatMLRequest = Event.None;
19
20
private _responseLines: string[] | undefined;
21
private _errorResponse: ChatResponse | undefined;
22
private _responseQueue: ChatResponse[] = [];
23
private _callCount = 0;
24
private _capturedOptions: IFetchMLOptions[] = [];
25
26
/**
27
* Configure the next fetchOne call to stream lines of text via `finishedCb`.
28
*/
29
setStreamingLines(lines: string[]): void {
30
this._responseLines = lines;
31
this._errorResponse = undefined;
32
}
33
34
/**
35
* Configure the next fetchOne call to return an error response.
36
*/
37
setErrorResponse(response: ChatResponse): void {
38
this._errorResponse = response;
39
this._responseLines = undefined;
40
}
41
42
/**
43
* Enqueue ordered responses. Each `fetchOne` call dequeues from the front.
44
* If the queue is empty, falls back to `_responseLines` / `_errorResponse`.
45
*/
46
enqueueResponse(response: ChatResponse): void {
47
this._responseQueue.push(response);
48
}
49
50
/**
51
* Get all captured request options from previous calls.
52
*/
53
get capturedOptions(): readonly IFetchMLOptions[] {
54
return this._capturedOptions;
55
}
56
57
/**
58
* Get how many times fetchOne was called.
59
*/
60
get callCount(): number {
61
return this._callCount;
62
}
63
64
/**
65
* Reset call tracking state.
66
*/
67
resetTracking(): void {
68
this._callCount = 0;
69
this._capturedOptions = [];
70
}
71
72
async fetchOne(options: IFetchMLOptions, _token: CancellationToken): Promise<ChatResponse> {
73
this._callCount++;
74
this._capturedOptions.push(options);
75
76
// Check queued responses first
77
if (this._responseQueue.length > 0) {
78
const queuedResponse = this._responseQueue.shift()!;
79
// For success responses, still call finishedCb if available
80
if (queuedResponse.type === ChatFetchResponseType.Success && options.finishedCb) {
81
await options.finishedCb(queuedResponse.value, 0, { text: queuedResponse.value });
82
}
83
return queuedResponse;
84
}
85
86
if (this._errorResponse) {
87
return this._errorResponse;
88
}
89
90
const lines = this._responseLines ?? [];
91
const fullText = lines.join('\n');
92
93
// call finishedCb for each line incrementally to simulate streaming
94
if (options.finishedCb) {
95
let soFar = '';
96
for (let i = 0; i < lines.length; i++) {
97
if (i > 0) {
98
soFar += '\n';
99
}
100
soFar += lines[i];
101
await options.finishedCb(soFar, 0, { text: (i > 0 ? '\n' : '') + lines[i] });
102
}
103
}
104
105
return {
106
type: ChatFetchResponseType.Success,
107
requestId: 'test-request-id',
108
serverRequestId: 'test-server-request-id',
109
usage: { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0, prompt_tokens_details: { cached_tokens: 0 } },
110
value: fullText,
111
resolvedModel: 'test-model',
112
};
113
}
114
115
async fetchMany(options: IFetchMLOptions, token: CancellationToken): Promise<ChatResponses> {
116
const response = await this.fetchOne(options, token);
117
if (response.type === ChatFetchResponseType.Success) {
118
return { ...response, value: [response.value] };
119
}
120
return response;
121
}
122
}
123
124