src/vs/platform/agentHost/node/claude/claudeModelId.ts
203 LOC · 203 covered · 0 uncovered · 57 ranges · 495 concepts · 34 introducers · 293 tests
File neighbourhood
The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.
Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file
In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.
Graph controls are ready.
Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.
/*---------------------------------------------------------------------------------------------
claudeModelId.ts ×9
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/**
* Mirror of `extensions/copilot/src/extension/chatSessions/claude/{common,node}/claudeModelId.ts`.
*
* The Claude Agent SDK speaks Anthropic-canonical hyphenated model IDs
* (e.g. `claude-opus-4-6-20251101`). CAPI speaks dotted endpoint IDs
* (`claude-opus-4.6`). The Phase 2 proxy needs bidirectional translation
* at three points: inbound `requestBody.model` (SDK→CAPI), outbound
* `model` fields on streaming events / non-streaming responses (CAPI→SDK),
* and `GET /v1/models` response IDs (CAPI→SDK).
*
* **Keep in sync with the extension copy.** When the model-ID grammar
* changes (new family name, new modifier suffix), update both files.
*/
export interface ParsedClaudeModelId {
readonly name: string;
readonly version: string;
readonly modifiers: string;
toSdkModelId(): string;
toEndpointModelId(): string;
}
/**
* Known model suffixes that are meaningful variants and should be preserved
* in SDK/endpoint model IDs. Mapped per model family. Date-based suffixes
* (e.g. 20251101) are intentionally excluded — they are build identifiers
* that should not appear in the normalized output.
*/
const VALID_SUFFIXES: ReadonlyMap<string, ReadonlySet<string>> = new Map([
['opus', new Set(['1m'])],
]);
const cache = new Map<string, ParsedClaudeModelId | undefined>();
/**
* Parses a Claude model ID string (SDK or endpoint format) into its components.
* Throws if the model ID is unparseable or not a Claude ID.
*
* Use {@link tryParseClaudeModelId} when the input may not be a valid Claude model ID
* (e.g. model IDs from disk or external sources).
*/
export function parseClaudeModelId(modelId: string): ParsedClaudeModelId {
if (!result) {
}
}
/**
* Normalize a Claude model ID to the SDK format (dash-separated version, e.g.
* `claude-haiku-4-5`). The Claude Agent SDK / CLI only recognizes this form;
* given the endpoint format (`claude-haiku-4.5`) it treats the model as unknown
* and falls back to a generic feature set (adaptive thinking + reasoning effort)
* that the model may not support, producing a 400 from CAPI. Unparseable /
* non-Claude IDs pass through unchanged; `undefined` passes through as
* `undefined` so callers can normalize an optional model id in one step.
*/
export function toSdkModelId(modelId: string): string;
export function toSdkModelId(modelId: string | undefined): string | undefined;
export function toSdkModelId(modelId: string | undefined): string | undefined {
}
}
/**
* Attempts to parse a Claude model ID string (SDK or endpoint format) into its components.
*
* Accepts either format:
* - SDK: `claude-opus-4-5-20251101`, `claude-3-5-sonnet-20241022`, `claude-sonnet-4-20250514`
* - Endpoint: `claude-opus-4.5`, `claude-sonnet-4`, `claude-haiku-3.5`
*
* Returns `undefined` for unparseable or non-Claude IDs.
*/
export function tryParseClaudeModelId(modelId: string): ParsedClaudeModelId | undefined {
if (cache.has(cacheKey)) {
}
const result = doParse(cacheKey);
cache.set(cacheKey, result);
return result;
}
const DATE_SUFFIX_RE = /^(?<base>.*)-(?<date>\d{8})$/;
let dateSuffix = '';
let base = lower;
const dateMatch = DATE_SUFFIX_RE.exec(lower);
if (dateMatch?.groups) {
dateSuffix = dateMatch.groups.date;
}
// Pattern 1: claude-{name}-{major}-{minor}[-{mod}] (e.g. claude-opus-4-5, claude-opus-4-6-1m)
const p1 = base.match(/^claude-(?<name>\w+)-(?<major>\d+)-(?<minor>\d+)(?:-(?<mod>.+))?$/);
if (p1?.groups) {
return makeResult(p1.groups.name, p1.groups.major, p1.groups.minor, joinModifiers(p1.groups.mod, dateSuffix));
claudeModelId.ts ×1
}
// Pattern 2: claude-{major}-{minor}-{name}[-{mod}] (e.g. claude-3-5-sonnet)
const p2 = base.match(/^claude-(?<major>\d+)-(?<minor>\d+)-(?<name>\w+)(?:-(?<mod>.+))?$/);
return makeResult(p2.groups.name, p2.groups.major, p2.groups.minor, joinModifiers(p2.groups.mod, dateSuffix));
claudeModelId.ts ×1
}
// Pattern 3: claude-{name}-{major}.{minor}[-{mod}] (e.g. claude-opus-4.5, claude-opus-4.6-1m)
const p3 = base.match(/^claude-(?<name>\w+)-(?<major>\d+)\.(?<minor>\d+)(?:-(?<mod>.+))?$/);
return makeResult(p3.groups.name, p3.groups.major, p3.groups.minor, joinModifiers(p3.groups.mod, dateSuffix));
claudeModelId.ts ×1
}
// Pattern 4: claude-{name}-{major}[-{mod}] (e.g. claude-sonnet-4, claude-sonnet-4-1m)
const p4 = base.match(/^claude-(?<name>\w+)-(?<major>\d+)(?:-(?<mod>.+))?$/);
return makeResult(p4.groups.name, p4.groups.major, undefined, joinModifiers(p4.groups.mod, dateSuffix));
claudeModelId.ts ×1
}
// Pattern 5: claude-{major}-{name}[-{mod}] (e.g. claude-3-opus)
const p5 = base.match(/^claude-(?<major>\d+)-(?<name>\w+)(?:-(?<mod>.+))?$/);
return makeResult(p5.groups.name, p5.groups.major, undefined, joinModifiers(p5.groups.mod, dateSuffix));
claudeModelId.ts ×1
}
// Pattern 6: bare model name with no version (e.g. nectarine)
const p6 = base.match(/^(?<name>\w+)$/);
}
return undefined;
}
function joinModifiers(mod: string | undefined, dateSuffix: string): string {
claudeModelId.ts ×4
if (mod && dateSuffix) {
}
function formatModelId(name: string, major: string, minor: string | undefined, versionSep: string, validSuffix: string): string {
claudeModelId.ts ×2
const base = minor !== undefined
}
return {
name,
version: '',
modifiers: '',
toSdkModelId: () => name,
toEndpointModelId: () => name,
};
}
function makeResult(name: string, major: string, minor: string | undefined, modifiers: string): ParsedClaudeModelId {
claudeModelId.ts ×4
const version = minor !== undefined ? `${major}.${minor}` : major;
const validSuffix = extractValidSuffix(name, modifiers);
return {
name,
version,
modifiers,
toSdkModelId: () => formatModelId(name, major, minor, '-', validSuffix),
toEndpointModelId: () => formatModelId(name, major, minor, '.', validSuffix),
};
}
/**
* Extracts the valid suffix portion from modifiers for a given model family.
* For example, given modifiers '1m-20251101' and family 'opus', returns '1m'.
* Returns an empty string if no valid suffix is found.
*/
if (!modifiers) {
}
if (!allowedSuffixes) {
}
if (allowedSuffixes.has(modifiers)) {
}
// Check the first segment of compound modifiers (e.g. '1m' from '1m-20251101')
claudeModelId.ts ×1
const firstSegment = modifiers.split('-')[0];
if (allowedSuffixes.has(firstSegment)) {
}
}