Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 22 additions & 14 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ interface ModelPricing {
inputCostPerMillion: number;
outputCostPerMillion: number;
category?: string;
displayNames?: string[];
}

interface EditorUsage {
Expand Down Expand Up @@ -2294,22 +2295,29 @@ class CopilotTokenTracker implements vscode.Disposable {
if (request.result && request.result.metadata && request.result.metadata.modelId) {
return request.result.metadata.modelId;
}

// Build a lookup map from display names to model IDs from modelPricing.json
if (request.result && request.result.details) {
if (request.result.details.includes('Claude Sonnet 3.5')) { return 'claude-sonnet-3.5'; }
if (request.result.details.includes('Claude Sonnet 3.7')) { return 'claude-sonnet-3.7'; }
if (request.result.details.includes('Claude Sonnet 4')) { return 'claude-sonnet-4'; }
if (request.result.details.includes('Gemini 2.5 Pro')) { return 'gemini-2.5-pro'; }
if (request.result.details.includes('Gemini 3 Pro (Preview)')) { return 'gemini-3-pro-preview'; }
if (request.result.details.includes('Gemini 3 Pro')) { return 'gemini-3-pro'; }
if (request.result.details.includes('GPT-4.1')) { return 'gpt-4.1'; }
if (request.result.details.includes('GPT-4o-mini')) { return 'gpt-4o-mini'; }
if (request.result.details.includes('GPT-4o')) { return 'gpt-4o'; }
if (request.result.details.includes('GPT-4')) { return 'gpt-4'; }
if (request.result.details.includes('GPT-5')) { return 'gpt-5'; }
if (request.result.details.includes('GPT-3.5-Turbo')) { return 'gpt-3.5-turbo'; }
if (request.result.details.includes('o3-mini')) { return 'o3-mini'; }
if (request.result.details.includes('o4-mini')) { return 'o4-mini'; }
// Create reverse lookup: displayName -> modelId
const displayNameToModelId: { [displayName: string]: string } = {};
for (const [modelId, pricing] of Object.entries(this.modelPricing)) {
if (pricing.displayNames) {
for (const displayName of pricing.displayNames) {
displayNameToModelId[displayName] = modelId;
}
}
}

// Check which display name appears in the details
// Sort by length descending to match longer names first (e.g., "Gemini 3 Pro (Preview)" before "Gemini 3 Pro")
const sortedDisplayNames = Object.keys(displayNameToModelId).sort((a, b) => b.length - a.length);
for (const displayName of sortedDisplayNames) {
if (request.result.details.includes(displayName)) {
return displayNameToModelId[displayName];
}
}
}

return 'gpt-4'; // default
}

Expand Down
42 changes: 28 additions & 14 deletions src/modelPricing.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@
"gpt-5": {
"inputCostPerMillion": 1.25,
"outputCostPerMillion": 10.0,
"category": "GPT-5 models"
"category": "GPT-5 models",
"displayNames": ["GPT-5"]
},
"gpt-5-codex": {
"inputCostPerMillion": 1.25,
Expand Down Expand Up @@ -82,12 +83,14 @@
"gpt-4": {
"inputCostPerMillion": 3.0,
"outputCostPerMillion": 12.0,
"category": "GPT-4 models"
"category": "GPT-4 models",
"displayNames": ["GPT-4"]
},
"gpt-4.1": {
"inputCostPerMillion": 3.0,
"outputCostPerMillion": 12.0,
"category": "GPT-4 models"
"category": "GPT-4 models",
"displayNames": ["GPT-4.1"]
},
"gpt-4.1-mini": {
"inputCostPerMillion": 0.8,
Expand All @@ -102,27 +105,32 @@
"gpt-4o": {
"inputCostPerMillion": 2.5,
"outputCostPerMillion": 10.0,
"category": "GPT-4 models"
"category": "GPT-4 models",
"displayNames": ["GPT-4o"]
},
"gpt-4o-mini": {
"inputCostPerMillion": 0.15,
"outputCostPerMillion": 0.6,
"category": "GPT-4 models"
"category": "GPT-4 models",
"displayNames": ["GPT-4o-mini", "GPT-4o Mini"]
},
"claude-sonnet-3.5": {
"inputCostPerMillion": 3.0,
"outputCostPerMillion": 15.0,
"category": "Claude models (Anthropic)"
"category": "Claude models (Anthropic)",
"displayNames": ["Claude Sonnet 3.5"]
},
"claude-sonnet-3.7": {
"inputCostPerMillion": 3.0,
"outputCostPerMillion": 15.0,
"category": "Claude models (Anthropic)"
"category": "Claude models (Anthropic)",
"displayNames": ["Claude Sonnet 3.7"]
},
"claude-sonnet-4": {
"inputCostPerMillion": 3.0,
"outputCostPerMillion": 15.0,
"category": "Claude models (Anthropic)"
"category": "Claude models (Anthropic)",
"displayNames": ["Claude Sonnet 4"]
},
"claude-sonnet-4.5": {
"inputCostPerMillion": 3.0,
Expand Down Expand Up @@ -152,22 +160,26 @@
"o3-mini": {
"inputCostPerMillion": 4.0,
"outputCostPerMillion": 16.0,
"category": "OpenAI reasoning models"
"category": "OpenAI reasoning models",
"displayNames": ["o3-mini"]
},
"o4-mini": {
"inputCostPerMillion": 4.0,
"outputCostPerMillion": 16.0,
"category": "OpenAI reasoning models"
"category": "OpenAI reasoning models",
"displayNames": ["o4-mini"]
},
"gpt-3.5-turbo": {
"inputCostPerMillion": 0.5,
"outputCostPerMillion": 1.5,
"category": "Legacy models"
"category": "Legacy models",
"displayNames": ["GPT-3.5-Turbo", "GPT-3.5 Turbo"]
},
"gemini-2.5-pro": {
"inputCostPerMillion": 1.25,
"outputCostPerMillion": 10.0,
"category": "Google Gemini models"
"category": "Google Gemini models",
"displayNames": ["Gemini 2.5 Pro"]
},
"gemini-2.5-flash": {
"inputCostPerMillion": 0.30,
Expand Down Expand Up @@ -197,12 +209,14 @@
"gemini-3-pro": {
"inputCostPerMillion": 2.0,
"outputCostPerMillion": 12.0,
"category": "Google Gemini models"
"category": "Google Gemini models",
"displayNames": ["Gemini 3 Pro"]
},
"gemini-3-pro-preview": {
"inputCostPerMillion": 2.0,
"outputCostPerMillion": 12.0,
"category": "Google Gemini models"
"category": "Google Gemini models",
"displayNames": ["Gemini 3 Pro (Preview)"]
},
"grok-code-fast-1": {
"inputCostPerMillion": 0.20,
Expand Down