Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/test/base/simulationContext.ts
13388 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 * as fs from 'fs';
7
import path from 'path';
8
import { ApiEmbeddingsIndex, IApiEmbeddingsIndex } from '../../src/extension/context/node/resolvers/extensionApi';
9
import { ConversationStore, IConversationStore } from '../../src/extension/conversationStore/node/conversationStore';
10
import { IIntentService, IntentService } from '../../src/extension/intents/node/intentService';
11
import { ITestGenInfoStorage, TestGenInfoStorage } from '../../src/extension/intents/node/testIntent/testInfoStorage';
12
import { ILinkifyService, LinkifyService } from '../../src/extension/linkify/common/linkifyService';
13
import { ChatMLFetcherImpl } from '../../src/extension/prompt/node/chatMLFetcher';
14
import { createExtensionUnitTestingServices, ISimulationModelConfig } from '../../src/extension/test/node/services';
15
import { AIEvaluationService, IAIEvaluationService } from '../../src/extension/testing/node/aiEvaluationService';
16
import { IChatMLFetcher } from '../../src/platform/chat/common/chatMLFetcher';
17
import { ChatFetchResponseType, ChatResponses } from '../../src/platform/chat/common/commonTypes';
18
import { IChunkingEndpointClient } from '../../src/platform/chunking/common/chunkingEndpointClient';
19
import { ChunkingEndpointClientImpl } from '../../src/platform/chunking/common/chunkingEndpointClientImpl';
20
import { INaiveChunkingService, NaiveChunkingService } from '../../src/platform/chunking/node/naiveChunkerService';
21
import { CHAT_MODEL, Config, ConfigKey, ExperimentBasedConfig, ExperimentBasedConfigType, globalConfigRegistry, IConfigurationService } from '../../src/platform/configuration/common/configurationService';
22
import { DefaultsOnlyConfigurationService } from '../../src/platform/configuration/common/defaultsOnlyConfigurationService';
23
import { InMemoryConfigurationService } from '../../src/platform/configuration/test/common/inMemoryConfigurationService';
24
import { IEmbeddingsComputer } from '../../src/platform/embeddings/common/embeddingsComputer';
25
import { RemoteEmbeddingsComputer } from '../../src/platform/embeddings/common/remoteEmbeddingsComputer';
26
import { ICombinedEmbeddingIndex, VSCodeCombinedIndexImpl } from '../../src/platform/embeddings/common/vscodeIndex';
27
import { IVSCodeExtensionContext } from '../../src/platform/extContext/common/extensionContext';
28
import { IGitExtensionService } from '../../src/platform/git/common/gitExtensionService';
29
import { NullGitExtensionService } from '../../src/platform/git/common/nullGitExtensionService';
30
import { ICompletionsFetchService } from '../../src/platform/nesFetch/common/completionsFetchService';
31
import { CompletionsFetchService } from '../../src/platform/nesFetch/node/completionsFetchServiceImpl';
32
import { IProjectTemplatesIndex, ProjectTemplatesIndex } from '../../src/platform/projectTemplatesIndex/common/projectTemplatesIndex';
33
import { IReleaseNotesService } from '../../src/platform/releaseNotes/common/releaseNotesService';
34
import { ReleaseNotesService } from '../../src/platform/releaseNotes/vscode/releaseNotesServiceImpl';
35
import { IDocsSearchClient } from '../../src/platform/remoteSearch/common/codeOrDocsSearchClient';
36
import { DocsSearchClient } from '../../src/platform/remoteSearch/node/codeOrDocsSearchClientImpl';
37
import { IReviewService } from '../../src/platform/review/common/reviewService';
38
import { constructGlobalStateMemento, MockExtensionContext } from '../../src/platform/test/node/extensionContext';
39
import { TestingServiceCollection } from '../../src/platform/test/node/services';
40
import { SimulationReviewService } from '../../src/platform/test/node/simulationWorkspaceServices';
41
import { NullTestProvider } from '../../src/platform/testing/common/nullTestProvider';
42
import { ITestProvider } from '../../src/platform/testing/common/testProvider';
43
import { ITokenizerProvider, TokenizerProvider } from '../../src/platform/tokenizer/node/tokenizer';
44
import { IGithubAvailableEmbeddingTypesService, MockGithubAvailableEmbeddingTypesService } from '../../src/platform/workspaceChunkSearch/common/githubAvailableEmbeddingTypes';
45
import { IWorkspaceChunkSearchService, WorkspaceChunkSearchService } from '../../src/platform/workspaceChunkSearch/node/workspaceChunkSearchService';
46
import { IWorkspaceFileIndex, WorkspaceFileIndex } from '../../src/platform/workspaceChunkSearch/node/workspaceFileIndex';
47
import { createServiceIdentifier } from '../../src/util/common/services';
48
import { SyncDescriptor } from '../../src/util/vs/platform/instantiation/common/descriptors';
49
import { IJSONOutputPrinter, NoopJSONOutputPrinter } from '../jsonOutputPrinter';
50
import { SIMULATION_FOLDER_NAME } from '../simulation/shared/sharedTypes';
51
import { ITestInformation, TestInformation } from '../simulation/testInformation';
52
import { CachedTestInfo, CachingChatMLFetcher, IChatMLCache } from './cachingChatMLFetcher';
53
import { CachingChunkingEndpointClient, ChunkingEndpointClientSQLiteCache } from './cachingChunksEndpointClient';
54
import { CachingCodeOrDocSearchClient, CodeOrDocSearchSQLiteCache } from './cachingCodeSearchClient';
55
import { CachingCompletionsFetchService } from './cachingCompletionsFetchService';
56
import { CachingEmbeddingsComputer } from './cachingEmbeddingsFetcher';
57
import { CachingResourceFetcher } from './cachingResourceFetcher';
58
import { ICompletionsCache } from './completionsCache';
59
import { EmbeddingsSQLiteCache } from './embeddingsCache';
60
import { TestingCacheSalts } from './salts';
61
import { ISimulationEndpointHealth, SimulationEndpointHealthImpl } from './simulationEndpointHealth';
62
import { SimulationCodeSearchChunkSearchService } from './simuliationWorkspaceChunkSearch';
63
import { FetchRequestCollector, SpyingChatMLFetcher } from './spyingChatMLFetcher';
64
import { SimulationTest } from './stest';
65
import { ChatModelThrottlingTaskLaunchers, ThrottlingLimits as SimulationThrottlingLimits, ThrottlingChatMLFetcher } from './throttlingChatMLFetcher';
66
import { ThrottlingCodeOrDocsSearchClient } from './throttlingCodeOrDocsSearchClient';
67
68
const dotSimulationPath = path.join(__dirname, `../${SIMULATION_FOLDER_NAME}`);
69
70
export enum CacheScope {
71
Embeddings = 'embeddings',
72
TSC = 'tsc',
73
Roslyn = 'roslyn',
74
ESLint = 'eslint',
75
Pylint = 'pylint',
76
Ruff = 'ruff',
77
Pyright = 'pyright',
78
Python = 'python',
79
Notebook = 'notebook',
80
DocSearch = 'docs-search',
81
CodeSearch = 'code-search',
82
CPP = 'cpp',
83
Chunks = 'chunks-endpoint',
84
}
85
86
export const ICachingResourceFetcher = createServiceIdentifier<ICachingResourceFetcher>('ICachingResourceFetcher');
87
88
export interface ICachingResourceFetcher {
89
invokeWithCache<I, R>(cacheScope: CacheScope, input: I, cacheSalt: string, inputCacheKey: string, fn: (input: I) => Promise<R>): Promise<R>;
90
}
91
92
export enum CacheMode {
93
Disable = 'disable', // never use the cache, don't update the cache
94
Require = 'require', // always use the cache, and fail if it's not available
95
Default = 'default', // use cache of available, but don't require it
96
}
97
98
export class NoFetchChatMLFetcher extends ChatMLFetcherImpl {
99
public override fetchMany(...args: any[]): Promise<ChatResponses> {
100
return Promise.resolve({
101
type: ChatFetchResponseType.Success,
102
usage: { completion_tokens: 0, prompt_tokens: 0, total_tokens: 0, prompt_tokens_details: { cached_tokens: 0 } },
103
value: ['--no-fetch option is provided to simulations -- using a fixed ChatML response'],
104
requestId: 'no-fetch-request-id',
105
serverRequestId: undefined,
106
resolvedModel: ''
107
});
108
}
109
}
110
111
export function createSimulationChatModelThrottlingTaskLaunchers(boost: boolean): ChatModelThrottlingTaskLaunchers {
112
113
const throttlingLimits: SimulationThrottlingLimits = {
114
[CHAT_MODEL.GPT41]: { limit: 3, type: 'RPS' },
115
[CHAT_MODEL.GPT4OPROXY]: { limit: 1, type: 'RPS' },
116
[CHAT_MODEL.EXPERIMENTAL]: { limit: 3, type: 'RPS' },
117
[CHAT_MODEL.GPT4OMINI]: { limit: 18, type: 'RPS' },
118
[CHAT_MODEL.CUSTOM_NES]: { limit: 5, type: 'RPS' },
119
[CHAT_MODEL.O3MINI]: { limit: 1, type: 'RPS' },
120
[CHAT_MODEL.CLAUDE_SONNET]: { limit: 3, type: 'RPS' },
121
[CHAT_MODEL.CLAUDE_37_SONNET]: { limit: 4, type: 'RPS' },
122
[CHAT_MODEL.O1]: { limit: 4, type: 'RPS' },
123
[CHAT_MODEL.O1MINI]: { limit: 5, type: 'RPM' },
124
[CHAT_MODEL.GEMINI_FLASH]: { limit: 20, type: 'RPM' },
125
[CHAT_MODEL.DEEPSEEK_CHAT]: { limit: 1, type: 'RPS' },
126
[CHAT_MODEL.XTAB_4O_MINI_FINETUNED]: { limit: 5, type: 'RPS' }
127
};
128
129
if (boost) {
130
throttlingLimits[CHAT_MODEL.CLAUDE_SONNET] = { limit: 20, type: 'RPS' };
131
throttlingLimits[CHAT_MODEL.CLAUDE_37_SONNET] = { limit: 20, type: 'RPS' };
132
}
133
134
return new ChatModelThrottlingTaskLaunchers(throttlingLimits);
135
}
136
137
export interface SimulationServicesOptions {
138
chatModelThrottlingTaskLaunchers: ChatModelThrottlingTaskLaunchers;
139
isNoFetchModeEnabled: boolean;
140
languageModelCacheMode: CacheMode;
141
createChatMLCache?: (info: CurrentTestRunInfo) => IChatMLCache;
142
createNesFetchCache?: (info: CurrentTestRunInfo) => ICompletionsCache;
143
resourcesCacheMode: CacheMode;
144
disabledTools: Set<string>;
145
swebenchPrompt: boolean;
146
summarizeHistory: boolean;
147
useExperimentalCodeSearchService: boolean;
148
configs: Record<string, unknown> | undefined;
149
}
150
151
export interface CurrentTestRunInfo {
152
/**
153
* Current test being run.
154
*/
155
test: SimulationTest;
156
157
/**
158
* Each test is run `n` times. This specifies which run this is [0..n-1]
159
*/
160
testRunNumber: number;
161
162
/**
163
* For each test run, we capture fetch requests made.
164
*/
165
fetchRequestCollector: FetchRequestCollector;
166
167
/**
168
* Whether we're working in a real workspace and extension host.
169
*/
170
isInRealExtensionHost: boolean;
171
}
172
173
/**
174
* Creates an accessor suitable for running tests.
175
* The `IChatMLFetcher` will use caching and the chat endpoint is configurable via the `chatModel` parameter.
176
* The `IEmbeddingsComputer` will use caching and the embeddings endpoint is configurable via the `embeddingsModel` parameter.
177
*/
178
export async function createSimulationAccessor(
179
modelConfig: ISimulationModelConfig,
180
opts: SimulationServicesOptions,
181
currentTestRunInfo: CurrentTestRunInfo
182
): Promise<TestingServiceCollection> {
183
const testingServiceCollection = createExtensionUnitTestingServices(undefined, currentTestRunInfo, modelConfig);
184
if (currentTestRunInfo.isInRealExtensionHost) {
185
const { addExtensionHostSimulationServices } = await import('./extHostContext/simulationExtHostContext');
186
await addExtensionHostSimulationServices(testingServiceCollection);
187
}
188
189
testingServiceCollection.define(ITestInformation, new SyncDescriptor(TestInformation, [currentTestRunInfo.test]));
190
try {
191
const newLocal = new Map<string, any>(currentTestRunInfo.test.nonExtensionConfigurations);
192
193
const configs = Object.entries(opts.configs ?? {}).map(([key, value]) => [lookupConfigKey(key), value] as const);
194
195
testingServiceCollection.define(IConfigurationService, new SyncDescriptor(
196
InMemoryConfigurationService,
197
[
198
new DefaultsOnlyConfigurationService(),
199
new Map<ExperimentBasedConfig<ExperimentBasedConfigType> | Config<any>, unknown>([
200
[ConfigKey.UseProjectTemplates, false],
201
[ConfigKey.SummarizeAgentConversationHistory, opts.summarizeHistory],
202
...currentTestRunInfo.test.configurations?.map<[ExperimentBasedConfig<ExperimentBasedConfigType> | Config<any>, unknown]>(c => [c.key, c.value]) ?? [],
203
...configs,
204
]),
205
newLocal,
206
])
207
);
208
} catch (err) {
209
console.log(currentTestRunInfo.test.nonExtensionConfigurations);
210
console.error('Error in createSimulationAccessor', err);
211
console.error(currentTestRunInfo.test.fullName);
212
throw err;
213
}
214
215
const globalStoragePath = path.join(dotSimulationPath, 'cache', 'global-storage');
216
const globalStatePath = path.join(dotSimulationPath, 'cache', 'global-state');
217
218
testingServiceCollection.define(ISimulationEndpointHealth, new SyncDescriptor(SimulationEndpointHealthImpl));
219
testingServiceCollection.define(IJSONOutputPrinter, new SyncDescriptor(NoopJSONOutputPrinter));
220
testingServiceCollection.define(ICachingResourceFetcher, new SyncDescriptor(CachingResourceFetcher, [currentTestRunInfo, opts.resourcesCacheMode]));
221
testingServiceCollection.define(IVSCodeExtensionContext, new SyncDescriptor(MockExtensionContext, [globalStoragePath, constructGlobalStateMemento(globalStatePath)]));
222
testingServiceCollection.define(IIntentService, new SyncDescriptor(IntentService));
223
224
testingServiceCollection.define(IAIEvaluationService, new SyncDescriptor(AIEvaluationService));
225
226
const docsSearchClient = new SyncDescriptor(ThrottlingCodeOrDocsSearchClient, [new SyncDescriptor(DocsSearchClient)]);
227
testingServiceCollection.define(ITokenizerProvider, new SyncDescriptor(TokenizerProvider, [false]));
228
229
const cacheTestInfo = new CachedTestInfo(currentTestRunInfo.test, currentTestRunInfo.testRunNumber);
230
231
let chatMLFetcher: SyncDescriptor<IChatMLFetcher> =
232
opts.isNoFetchModeEnabled
233
? new SyncDescriptor(NoFetchChatMLFetcher)
234
: new SyncDescriptor(ThrottlingChatMLFetcher, [
235
new SyncDescriptor(ChatMLFetcherImpl),
236
opts.chatModelThrottlingTaskLaunchers
237
]);
238
if (opts.createChatMLCache) {
239
chatMLFetcher = new SyncDescriptor(CachingChatMLFetcher, [
240
chatMLFetcher,
241
opts.createChatMLCache(currentTestRunInfo),
242
cacheTestInfo,
243
{ endpointVersion: 'CAPI' },
244
opts.languageModelCacheMode ?? CacheMode.Default
245
]);
246
}
247
if (currentTestRunInfo.fetchRequestCollector) {
248
chatMLFetcher = new SyncDescriptor(SpyingChatMLFetcher, [currentTestRunInfo.fetchRequestCollector, chatMLFetcher]);
249
}
250
251
testingServiceCollection.define(IChatMLFetcher, chatMLFetcher);
252
253
if (opts.createNesFetchCache === undefined || cacheTestInfo === undefined) {
254
testingServiceCollection.define(ICompletionsFetchService, new SyncDescriptor(CompletionsFetchService));
255
} else {
256
testingServiceCollection.define(ICompletionsFetchService, new SyncDescriptor(
257
CachingCompletionsFetchService,
258
[
259
opts.createNesFetchCache(currentTestRunInfo),
260
cacheTestInfo,
261
opts.languageModelCacheMode ?? CacheMode.Default,
262
currentTestRunInfo.fetchRequestCollector,
263
opts.isNoFetchModeEnabled,
264
])
265
);
266
}
267
268
if (opts.languageModelCacheMode === CacheMode.Disable) {
269
testingServiceCollection.define(IEmbeddingsComputer, new SyncDescriptor(RemoteEmbeddingsComputer));
270
testingServiceCollection.define(IDocsSearchClient, docsSearchClient);
271
testingServiceCollection.define(IChunkingEndpointClient, new SyncDescriptor(ChunkingEndpointClientImpl));
272
testingServiceCollection.define(ICombinedEmbeddingIndex, new SyncDescriptor(VSCodeCombinedIndexImpl, [/*useRemoteCache*/ true]));
273
testingServiceCollection.define(IApiEmbeddingsIndex, new SyncDescriptor(ApiEmbeddingsIndex, [/*useRemoteCache*/ true]));
274
testingServiceCollection.define(IProjectTemplatesIndex, new SyncDescriptor(ProjectTemplatesIndex, [/*useRemoteCache*/ true]));
275
} else {
276
const embeddingCache = new EmbeddingsSQLiteCache(TestingCacheSalts.embeddingsCacheSalt, currentTestRunInfo);
277
testingServiceCollection.define(IEmbeddingsComputer, new SyncDescriptor(CachingEmbeddingsComputer, [embeddingCache]));
278
279
const codeOrDocSearchCache = new CodeOrDocSearchSQLiteCache(TestingCacheSalts.codeSearchCacheSalt, currentTestRunInfo);
280
const chunksEndpointCache = new ChunkingEndpointClientSQLiteCache(TestingCacheSalts.chunksEndpointCacheSalt, currentTestRunInfo);
281
testingServiceCollection.define(IDocsSearchClient, new SyncDescriptor(CachingCodeOrDocSearchClient, [docsSearchClient, codeOrDocSearchCache]));
282
testingServiceCollection.define(ICombinedEmbeddingIndex, new SyncDescriptor(VSCodeCombinedIndexImpl, [/*useRemoteCache*/ false]));
283
testingServiceCollection.define(IApiEmbeddingsIndex, new SyncDescriptor(ApiEmbeddingsIndex, [/*useRemoteCache*/ false]));
284
testingServiceCollection.define(IProjectTemplatesIndex, new SyncDescriptor(ProjectTemplatesIndex, [/*useRemoteCache*/ false]));
285
testingServiceCollection.define(IChunkingEndpointClient, new SyncDescriptor(CachingChunkingEndpointClient, [chunksEndpointCache]));
286
}
287
288
testingServiceCollection.define(INaiveChunkingService, new SyncDescriptor(NaiveChunkingService));
289
testingServiceCollection.define(ILinkifyService, new SyncDescriptor(LinkifyService));
290
testingServiceCollection.define(ITestProvider, new SyncDescriptor(NullTestProvider));
291
testingServiceCollection.define(ITestGenInfoStorage, new SyncDescriptor(TestGenInfoStorage));
292
testingServiceCollection.define(IConversationStore, new SyncDescriptor(ConversationStore));
293
testingServiceCollection.define(IReviewService, new SyncDescriptor(SimulationReviewService));
294
testingServiceCollection.define(IGitExtensionService, new SyncDescriptor(NullGitExtensionService));
295
testingServiceCollection.define(IReleaseNotesService, new SyncDescriptor(ReleaseNotesService));
296
testingServiceCollection.define(IWorkspaceFileIndex, new SyncDescriptor(WorkspaceFileIndex));
297
testingServiceCollection.define(IGithubAvailableEmbeddingTypesService, new SyncDescriptor(MockGithubAvailableEmbeddingTypesService));
298
299
if (opts.useExperimentalCodeSearchService) {
300
testingServiceCollection.define(IWorkspaceChunkSearchService, new SyncDescriptor(SimulationCodeSearchChunkSearchService, []));
301
} else {
302
testingServiceCollection.define(IWorkspaceChunkSearchService, new SyncDescriptor(WorkspaceChunkSearchService));
303
}
304
305
return testingServiceCollection;
306
}
307
308
function lookupConfigKey(key: string): ExperimentBasedConfig<ExperimentBasedConfigType> | Config<any> {
309
const config = globalConfigRegistry.configs.get(key);
310
if (!config) {
311
throw new Error(`Configuration '${key}' provided does not exist in product. Double check if the configuration key exists by using it in vscode settings.json.`);
312
}
313
return config;
314
}
315
316
export function loadConfigFile(configFilePath: string): Record<string, unknown> {
317
const resolvedPath = path.isAbsolute(configFilePath) ? configFilePath : path.join(process.cwd(), configFilePath);
318
const contents = fs.readFileSync(resolvedPath, 'utf-8');
319
const configs = JSON.parse(contents) as Record<string, unknown>;
320
if (!configs || typeof configs !== 'object') {
321
throw new Error('Invalid configuration file: ' + configFilePath);
322
}
323
return configs;
324
}
325
326
export async function applyConfigFile(configService: IConfigurationService, configs: Record<string, unknown>): Promise<void> {
327
for (const [key, value] of Object.entries(configs)) {
328
const configKey = lookupConfigKey(key);
329
await configService.setConfig(configKey, value);
330
}
331
}
332
333