Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
microsoft
GitHub Repository: microsoft/vscode
Path: blob/main/extensions/copilot/src/platform/endpoint/common/chatModelCapabilities.ts
13401 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 type { LanguageModelChat } from 'vscode';
7
import { getCachedSha256Hash } from '../../../util/common/crypto';
8
import { ServicesAccessor } from '../../../util/vs/platform/instantiation/common/instantiation';
9
import { ConfigKey, IConfigurationService } from '../../configuration/common/configurationService';
10
import type { IChatEndpoint } from '../../networking/common/networking';
11
import { IExperimentationService } from '../../telemetry/common/nullExperimentationService';
12
13
const HIDDEN_MODEL_A_HASHES = [
14
'a99dd17dfee04155d863268596b7f6dd36d0a6531cd326348dbe7416142a21a3',
15
'6b0f165d0590bf8d508540a796b4fda77bf6a0a4ed4e8524d5451b1913100a95'
16
];
17
18
19
const HIDDEN_MODEL_B_HASHES = [
20
'1f48b3271e760c69ab2b17dcae5f5c661fa5b644c5976a8a99b23e05ae3cb6d6',
21
'ffc50c70661c227edf8daae6f8dbed2dd0645386c12d43bc7fc44da166e043bd',
22
'257c934076307881132be702a901618969591f0e11e1df51b22b1d4010f0a0d0',
23
];
24
25
const VSC_MODEL_HASHES_A = [
26
'6db59e9bfe6e2ce608c0ee0ade075c64e4d054f05305e3034481234703381bb5',
27
'd7b81f23b6ab47d41130359bc203a6c653bba461b3da0185406353ce2b3abfa7',
28
];
29
30
const VSC_MODEL_HASHES_B = [
31
'6b0f165d0590bf8d508540a796b4fda77bf6a0a4ed4e8524d5451b1913100a95',
32
'1cdd4febbc7ee6b1abe0fbdd42217744c5912c79366db4befd91698b46c40a3c',
33
];
34
35
const VSC_MODEL_HASHES_C = [
36
'0425aeda24d2fd93e2a879c4d813e4f3997aa444f1f4a633241236f9f773df73',
37
];
38
39
const VSC_MODEL_HASHES_D = [
40
'e82ff0e2d4e4bae1f012dc599d520f8d61becfc4762f3717577b270be199db92',
41
];
42
43
44
// subset to allow replace string instead of apply patch.
45
const VSC_MODEL_HASHES_EDIT_TOOL_SET = [
46
'6db59e9bfe6e2ce608c0ee0ade075c64e4d054f05305e3034481234703381bb5',
47
'6b0f165d0590bf8d508540a796b4fda77bf6a0a4ed4e8524d5451b1913100a95',
48
'd7b81f23b6ab47d41130359bc203a6c653bba461b3da0185406353ce2b3abfa7',
49
'1cdd4febbc7ee6b1abe0fbdd42217744c5912c79366db4befd91698b46c40a3c',
50
'0425aeda24d2fd93e2a879c4d813e4f3997aa444f1f4a633241236f9f773df73',
51
'e82ff0e2d4e4bae1f012dc599d520f8d61becfc4762f3717577b270be199db92',
52
];
53
54
const HIDDEN_MODEL_E_HASHES: string[] = [
55
'6013de0381f648b7f21518885c02b40b7583adfb33c6d9b64d3aed52c3934798'
56
];
57
58
const HIDDEN_MODEL_F_HASHES: string[] = [
59
'ab45e8474269b026f668d49860b36850122e18a50d5ea38f3fefdae08261865c',
60
'9542d5c077c2bc379f92be32272b14be8b94a8841323465db0d5b3d6f4f0dab0',
61
];
62
63
const HIDDEN_MODEL_J_HASHES: string[] = [
64
'0a4346f806b28b3ce94905c3ac56fcd5ee2337d8613161696aba52eb0c3551cc',
65
'2a7b79b0151aa44a0abee17adc0e18df1c07d8d15d7affa989c3b3afb6bee0a0',
66
'f3c2984127dd2db50a555194925ca0d55c3c7b676e889c9406b2e6875a67e29c',
67
'5a81e6aa7556585ba7c569881d1103683adc9e0124ff7952df423afba2f167b5',
68
];
69
70
const HIDDEN_MODEL_K_HASH = 'a62e299160a1075d9973c28a7aa77f446c21c09887c7aa65c11022918cf83eda';
71
72
const HIDDEN_FAMILY_H_HASHES: string[] = [
73
'70fcded3f255d368e868cc807d8838a62108bfa5c86ce7d37966f58cda229e33',
74
];
75
76
function getModelId(model: LanguageModelChat | IChatEndpoint): string {
77
return 'id' in model ? model.id : model.model;
78
}
79
80
export function isHiddenModelA(model: LanguageModelChat | IChatEndpoint) {
81
const h = getCachedSha256Hash(model.family);
82
return HIDDEN_MODEL_A_HASHES.includes(h);
83
}
84
85
export function isHiddenModelB(model: LanguageModelChat | IChatEndpoint | string) {
86
const h = getCachedSha256Hash(typeof model === 'string' ? model : model.family);
87
return HIDDEN_MODEL_B_HASHES.includes(h);
88
}
89
90
91
export function isHiddenModelE(model: LanguageModelChat | IChatEndpoint) {
92
const h = getCachedSha256Hash(model.family);
93
return HIDDEN_MODEL_E_HASHES.includes(h);
94
}
95
96
export function isHiddenModelF(model: LanguageModelChat | IChatEndpoint) {
97
const h = getCachedSha256Hash(model.family);
98
return HIDDEN_MODEL_F_HASHES.includes(h);
99
}
100
101
export function isHiddenModelG(model: LanguageModelChat | IChatEndpoint | string) {
102
const family_hash = getCachedSha256Hash(typeof model === 'string' ? model : model.family);
103
return family_hash === '3ae755cc6122a54cc873e3ba2bd8703883b4a711d1af2707ef00f2c2c963ee8d';
104
}
105
106
export function isHiddenFamilyH(model: LanguageModelChat | IChatEndpoint) {
107
const family_hash = getCachedSha256Hash(model.family);
108
return HIDDEN_FAMILY_H_HASHES.includes(family_hash);
109
}
110
111
export function isHiddenModelK(model: LanguageModelChat | IChatEndpoint) {
112
const h = getCachedSha256Hash(model.family);
113
return h === HIDDEN_MODEL_K_HASH;
114
}
115
116
117
export function isGpt54(model: LanguageModelChat | IChatEndpoint | string) {
118
const h = getCachedSha256Hash(typeof model === 'string' ? model : model.family);
119
const family = typeof model === 'string' ? model : model.family;
120
return family.startsWith('gpt-5.4') || HIDDEN_MODEL_J_HASHES.includes(h);
121
}
122
123
export function isGpt54ConcisePromptExp(
124
accessor: ServicesAccessor,
125
model: LanguageModelChat | IChatEndpoint | string,
126
) {
127
const configurationService = accessor.get(IConfigurationService);
128
const experimentationService = accessor.get(IExperimentationService);
129
return isGpt54(model) && configurationService.getExperimentBasedConfig(ConfigKey.EnableGpt54ConcisePromptExp, experimentationService);
130
}
131
132
export function isGpt54LargePromptExp(
133
accessor: ServicesAccessor,
134
model: LanguageModelChat | IChatEndpoint | string,
135
) {
136
const configurationService = accessor.get(IConfigurationService);
137
const experimentationService = accessor.get(IExperimentationService);
138
return isGpt54(model) && configurationService.getExperimentBasedConfig(ConfigKey.EnableGpt54LargePromptExp, experimentationService);
139
}
140
141
142
export function isGpt53Codex(model: LanguageModelChat | IChatEndpoint | string) {
143
const family = typeof model === 'string' ? model : model.family;
144
return family.startsWith('gpt-5.3-codex');
145
}
146
147
export function isVSCModelA(model: LanguageModelChat | IChatEndpoint) {
148
149
const ID_hash = getCachedSha256Hash(getModelId(model));
150
const family_hash = getCachedSha256Hash(model.family);
151
return VSC_MODEL_HASHES_A.includes(ID_hash) || VSC_MODEL_HASHES_A.includes(family_hash);
152
}
153
154
export function isVSCModelB(model: LanguageModelChat | IChatEndpoint) {
155
const ID_hash = getCachedSha256Hash(getModelId(model));
156
const family_hash = getCachedSha256Hash(model.family);
157
return VSC_MODEL_HASHES_B.includes(ID_hash) || VSC_MODEL_HASHES_B.includes(family_hash);
158
}
159
160
export function isVSCModelReplaceStringSet(model: LanguageModelChat | IChatEndpoint) {
161
const ID_hash = getCachedSha256Hash(getModelId(model));
162
const family_hash = getCachedSha256Hash(model.family);
163
return VSC_MODEL_HASHES_EDIT_TOOL_SET.includes(ID_hash) || VSC_MODEL_HASHES_EDIT_TOOL_SET.includes(family_hash);
164
}
165
166
export function isVSCModelC(model: LanguageModelChat | IChatEndpoint) {
167
const ID_hash = getCachedSha256Hash(getModelId(model));
168
const family_hash = getCachedSha256Hash(model.family);
169
return VSC_MODEL_HASHES_C.includes(ID_hash) || VSC_MODEL_HASHES_C.includes(family_hash);
170
}
171
172
export function isVSCModelD(model: LanguageModelChat | IChatEndpoint) {
173
const ID_hash = getCachedSha256Hash(getModelId(model));
174
const family_hash = getCachedSha256Hash(model.family);
175
return VSC_MODEL_HASHES_D.includes(ID_hash) || VSC_MODEL_HASHES_D.includes(family_hash);
176
}
177
178
export function isGpt52CodexFamily(model: LanguageModelChat | IChatEndpoint | string): boolean {
179
const family = typeof model === 'string' ? model : model.family;
180
return family === 'gpt-5.2-codex';
181
}
182
183
export function isGpt52Family(model: LanguageModelChat | IChatEndpoint | string): boolean {
184
const family = typeof model === 'string' ? model : model.family;
185
return family === 'gpt-5.2';
186
}
187
188
/**
189
* Returns whether the instructions should be given in a user message instead
190
* of a system message when talking to the model.
191
*/
192
export function modelPrefersInstructionsInUserMessage(modelFamily: string) {
193
return modelFamily.includes('claude-3.5-sonnet');
194
}
195
196
/**
197
* Returns whether the instructions should be presented after the history
198
* for the given model.
199
*/
200
export function modelPrefersInstructionsAfterHistory(modelFamily: string) {
201
return modelFamily.includes('claude-3.5-sonnet');
202
}
203
204
/**
205
* Model supports apply_patch as an edit tool.
206
*/
207
export function modelSupportsApplyPatch(model: LanguageModelChat | IChatEndpoint): boolean {
208
// only using replace string as edit tool, disable apply_patch for VSC Models
209
if (isVSCModelReplaceStringSet(model)) {
210
return false;
211
}
212
return (model.family.startsWith('gpt') && !model.family.includes('gpt-4o'))
213
|| model.family === 'o4-mini'
214
|| isGpt52CodexFamily(model.family)
215
|| isGpt53Codex(model.family)
216
|| isVSCModelA(model)
217
|| isVSCModelB(model)
218
|| isGpt52Family(model.family)
219
|| isGpt54(model)
220
|| isHiddenModelB(model);
221
}
222
223
/**
224
* Model prefers JSON notebook representation.
225
*/
226
export function modelPrefersJsonNotebookRepresentation(model: LanguageModelChat | IChatEndpoint): boolean {
227
return (model.family.startsWith('gpt') && !model.family.includes('gpt-4o'))
228
|| model.family === 'o4-mini'
229
|| isGpt52CodexFamily(model.family)
230
|| isGpt53Codex(model.family)
231
|| isGpt52Family(model.family)
232
|| isGpt54(model)
233
|| isHiddenModelB(model);
234
}
235
236
/**
237
* Model supports replace_string_in_file as an edit tool.
238
*/
239
export function modelSupportsReplaceString(model: LanguageModelChat | IChatEndpoint): boolean {
240
return isGeminiFamily(model) || model.family.includes('grok-code') || modelSupportsMultiReplaceString(model) || isHiddenModelF(model) || isMinimaxFamily(model) || isHiddenFamilyH(model);
241
}
242
243
/**
244
* Model supports multi_replace_string_in_file as an edit tool.
245
*/
246
export function modelSupportsMultiReplaceString(model: LanguageModelChat | IChatEndpoint): boolean {
247
return isAnthropicFamily(model) || isHiddenModelE(model) || isVSCModelReplaceStringSet(model) || isMinimaxFamily(model) || isHiddenFamilyH(model);
248
}
249
250
/**
251
* The model is capable of using replace_string_in_file exclusively,
252
* without needing insert_edit_into_file.
253
*/
254
export function modelCanUseReplaceStringExclusively(model: LanguageModelChat | IChatEndpoint): boolean {
255
return isAnthropicFamily(model) || model.family.includes('grok-code') || isHiddenModelE(model) || model.family.toLowerCase().includes('gemini-3') || isVSCModelReplaceStringSet(model) || isHiddenModelF(model) || isMinimaxFamily(model) || isHiddenFamilyH(model);
256
}
257
258
/**
259
* We should attempt to automatically heal incorrect edits the model may emit.
260
* @note whether this is respected is currently controlled via EXP
261
*/
262
export function modelShouldUseReplaceStringHealing(model: LanguageModelChat | IChatEndpoint) {
263
return model.family.includes('gemini-2');
264
}
265
266
/**
267
* The model can accept image urls as the `image_url` parameter in mcp tool results.
268
*/
269
export function modelCanUseMcpResultImageURL(model: LanguageModelChat | IChatEndpoint): boolean {
270
return !isAnthropicFamily(model) && !isHiddenModelE(model);
271
}
272
273
/**
274
* The model can accept image urls as the `image_url` parameter in requests.
275
*/
276
export function modelCanUseImageURL(model: LanguageModelChat | IChatEndpoint): boolean {
277
return true;
278
}
279
280
/**
281
* The model supports native PDF document processing via document content parts.
282
*/
283
export function modelSupportsPDFDocuments(model: LanguageModelChat | IChatEndpoint): boolean {
284
return isAnthropicFamily(model);
285
}
286
287
/**
288
* The model is capable of using apply_patch as an edit tool exclusively,
289
* without needing insert_edit_into_file.
290
*/
291
export function modelCanUseApplyPatchExclusively(model: LanguageModelChat | IChatEndpoint): boolean {
292
// only using replace string as edit tool, disable apply_patch for VSC Models
293
if (isVSCModelReplaceStringSet(model)) {
294
return false;
295
}
296
return isGpt5PlusFamily(model) || isVSCModelA(model) || isVSCModelB(model);
297
}
298
299
/**
300
* Whether, when replace_string and insert_edit tools are both available,
301
* verbiage should be added in the system prompt directing the model to prefer
302
* replace_string.
303
*/
304
export function modelNeedsStrongReplaceStringHint(model: LanguageModelChat | IChatEndpoint): boolean {
305
return isGeminiFamily(model) || isHiddenModelF(model);
306
}
307
308
/**
309
* Model can take the simple, modern apply_patch instructions.
310
*/
311
export function modelSupportsSimplifiedApplyPatchInstructions(model: LanguageModelChat | IChatEndpoint): boolean {
312
return isGpt5PlusFamily(model) || isVSCModelA(model) || isVSCModelB(model);
313
}
314
315
export function isAnthropicFamily(model: LanguageModelChat | IChatEndpoint): boolean {
316
return model.family.startsWith('claude') || model.family.startsWith('Anthropic') || isHiddenModelG(model);
317
}
318
319
export function isGeminiFamily(model: LanguageModelChat | IChatEndpoint | string): boolean {
320
const family = typeof model === 'string' ? model : model.family;
321
return family.toLowerCase().startsWith('gemini') || getCachedSha256Hash(family) === HIDDEN_MODEL_K_HASH;
322
}
323
324
export function isMinimaxFamily(model: LanguageModelChat | IChatEndpoint): boolean {
325
return model.family.toLowerCase().includes('minimax');
326
}
327
328
export function isGpt5PlusFamily(model: LanguageModelChat | IChatEndpoint | string | undefined): boolean {
329
if (!model) {
330
return false;
331
}
332
333
const family = typeof model === 'string' ? model : model.family;
334
return !!family.startsWith('gpt-5');
335
}
336
337
/**
338
* Matches gpt-5-codex, gpt-5.1-codex, gpt-5.1-codex-mini, and any future models in this general family
339
*/
340
export function isGptCodexFamily(model: LanguageModelChat | IChatEndpoint | string | undefined): boolean {
341
if (!model) {
342
return false;
343
}
344
345
const family = typeof model === 'string' ? model : model.family;
346
return (!!family.startsWith('gpt-') && family.includes('-codex'));
347
}
348
349
/**
350
* GPT-5, -mini, -codex, not 5.1+
351
*/
352
export function isGpt5Family(model: LanguageModelChat | IChatEndpoint | string | undefined): boolean {
353
if (!model) {
354
return false;
355
}
356
357
const family = typeof model === 'string' ? model : model.family;
358
return family === 'gpt-5' || family === 'gpt-5-mini' || family === 'gpt-5-codex';
359
}
360
361
export function isGptFamily(model: LanguageModelChat | IChatEndpoint | string | undefined): boolean {
362
if (!model) {
363
return false;
364
}
365
366
const family = typeof model === 'string' ? model : model.family;
367
return !!family.startsWith('gpt-');
368
}
369
370
/**
371
* Any GPT-5.1+ model
372
*/
373
export function isGpt51Family(model: LanguageModelChat | IChatEndpoint | string | undefined): boolean {
374
if (!model) {
375
return false;
376
}
377
378
const family = typeof model === 'string' ? model : model.family;
379
return !!family.startsWith('gpt-5.1');
380
}
381
382
/**
383
* This takes a sync shortcut and should only be called when a model hash would have already been computed while rendering the prompt.
384
*/
385
export function getVerbosityForModelSync(model: IChatEndpoint): 'low' | 'medium' | 'high' | undefined {
386
if (model.family === 'gpt-5.1' || model.family === 'gpt-5-mini') {
387
return 'low';
388
}
389
390
return undefined;
391
}
392
393
/**
394
* Tool search is supported by:
395
* - Claude Sonnet 4.5 (claude-sonnet-4-5-* or claude-sonnet-4.5-*)
396
* - Claude Sonnet 4.6 (claude-sonnet-4-6-* or claude-sonnet-4.6-*)
397
* - Claude Opus 4.5 (claude-opus-4-5-* or claude-opus-4.5-*)
398
* - Claude Opus 4.6 (claude-opus-4-6-* or claude-opus-4.6-*)
399
* - Claude Opus 4.7 (claude-opus-4-7-* or claude-opus-4.7-*)
400
* - OpenAI gpt-5.4/gpt-5.5, but only when the `ResponsesApiToolSearchEnabled` setting is enabled
401
*/
402
export function modelSupportsToolSearch(modelId: string, configurationService?: IConfigurationService, experimentationService?: IExperimentationService): boolean {
403
const normalized = modelId.toLowerCase().replace(/\./g, '-');
404
if (isResponsesApiToolSearchModelId(normalized)) {
405
return !!configurationService && !!experimentationService && isResponsesApiToolSearchEnabled(modelId, configurationService, experimentationService);
406
}
407
408
return normalized.startsWith('claude-sonnet-4-5') ||
409
normalized.startsWith('claude-sonnet-4-6') ||
410
normalized.startsWith('claude-opus-4-5') ||
411
normalized.startsWith('claude-opus-4-6') ||
412
normalized.startsWith('claude-opus-4-7') ||
413
isHiddenModelG(modelId);
414
}
415
416
function isResponsesApiToolSearchModelId(normalizedModelId: string): boolean {
417
return normalizedModelId === 'gpt-5-4' || normalizedModelId === 'gpt-5-5';
418
}
419
420
export function isResponsesApiToolSearchEnabled(
421
endpoint: IChatEndpoint | string,
422
configurationService: IConfigurationService,
423
experimentationService: IExperimentationService,
424
): boolean {
425
const modelId = typeof endpoint === 'string' ? endpoint : endpoint.model;
426
const normalized = modelId.toLowerCase().replace(/\./g, '-');
427
return isResponsesApiToolSearchModelId(normalized) && configurationService.getExperimentBasedConfig(ConfigKey.ResponsesApiToolSearchEnabled, experimentationService);
428
}
429
430
/**
431
* Context editing is supported by:
432
* - Claude Haiku 4.5 (claude-haiku-4-5-* or claude-haiku-4.5-*)
433
* - Claude Sonnet 4.6 (claude-sonnet-4-6-* or claude-sonnet-4.6-*)
434
* - Claude Sonnet 4.5 (claude-sonnet-4-5-* or claude-sonnet-4.5-*)
435
* - Claude Sonnet 4 (claude-sonnet-4-*)
436
* - Claude Opus 4.6 (claude-opus-4-6-* or claude-opus-4.6-*)
437
* - Claude Opus 4.5 (claude-opus-4-5-* or claude-opus-4.5-*)
438
* - Claude Opus 4.1 (claude-opus-4-1-* or claude-opus-4.1-*)
439
* - Claude Opus 4 (claude-opus-4-*)
440
* Provider-agnostic: add additional model prefixes here as other providers adopt context editing.
441
*/
442
export function modelSupportsContextEditing(modelId: string): boolean {
443
const normalized = modelId.toLowerCase().replace(/\./g, '-');
444
// The 1M context variant doesn't need context editing
445
if (normalized.includes('1m')) {
446
return false;
447
}
448
return normalized.startsWith('claude-haiku-4-5') ||
449
normalized.startsWith('claude-sonnet-4-6') ||
450
normalized.startsWith('claude-sonnet-4-5') ||
451
normalized.startsWith('claude-sonnet-4') ||
452
normalized.startsWith('claude-opus-4-6') ||
453
normalized.startsWith('claude-opus-4-5') ||
454
normalized.startsWith('claude-opus-4-1') ||
455
normalized.startsWith('claude-opus-4');
456
}
457
458