Skip to content

Commit ffaa01f

Browse files
authored
Merge branch 'main' into team-dashboard
2 parents 2a3236c + 7bdfe49 commit ffaa01f

File tree

5 files changed

+76
-26
lines changed

5 files changed

+76
-26
lines changed

.github/skills/refresh-json-data/SKILL.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,12 @@ Before updating these files, ensure you have:
106106

107107
1. **Check official pricing pages**:
108108
- OpenAI: https://openai.com/api/pricing/
109-
- Anthropic: https://www.anthropic.com/pricing
110-
- Google Gemini: https://ai.google.dev/pricing
109+
- Anthropic: https://www.anthropic.com/pricing (also https://platform.claude.com/docs/en/about-claude/pricing)
110+
- Google Gemini: https://ai.google.dev/gemini-api/docs/pricing
111+
- xAI Grok: https://x.ai/api
111112
- GitHub Copilot Models: https://docs.github.com/en/copilot/reference/ai-models/supported-models
113+
- GitHub Copilot Premium Requests: https://docs.github.com/en/copilot/managing-copilot/monitoring-usage-and-entitlements/about-premium-requests
114+
- OpenRouter (cross-provider verification): https://openrouter.ai
112115

113116
2. **Update pricing entries** in the `pricing` object:
114117
```json

vscode-extension/src/README.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -53,21 +53,25 @@ Contains pricing information for AI models, including input and output token cos
5353
**How to update:**
5454
1. Check official pricing pages:
5555
- OpenAI: https://openai.com/api/pricing/
56-
- Anthropic: https://www.anthropic.com/pricing
57-
- Google Gemini: https://ai.google.dev/pricing
56+
- Anthropic: https://www.anthropic.com/pricing (also https://platform.claude.com/docs/en/about-claude/pricing)
57+
- Google Gemini: https://ai.google.dev/gemini-api/docs/pricing
58+
- xAI Grok: https://x.ai/api
5859
- GitHub Copilot Supported Models: https://docs.github.com/en/copilot/reference/ai-models/supported-models
60+
- GitHub Copilot Premium Requests: https://docs.github.com/en/copilot/managing-copilot/monitoring-usage-and-entitlements/about-premium-requests
61+
- OpenRouter (cross-provider verification): https://openrouter.ai
5962
2. Update the `pricing` object with new rates
6063
3. Update `metadata.lastUpdated` with current date
6164
4. Update source URLs and dates if needed
62-
5. Rebuild the extension after making changes
65+
5. Rebuild the extension after making changes (`npm run compile`)
6366

6467
### Current Gemini Model Pricing (per million tokens)
6568

66-
Based on Google AI pricing (retrieved December 27, 2025):
69+
Based on Google AI pricing (retrieved March 30, 2026):
6770

6871
- **Gemini 2.5 Pro**: $1.25 input / $10.00 output
69-
- **Gemini 3 Flash**: $0.50 input / $3.00 output
72+
- **Gemini 3 Flash**: $0.50 input / $3.00 output
7073
- **Gemini 3 Pro**: $2.00 input / $12.00 output (for prompts ≤ 200k tokens)
74+
- **Gemini 3.1 Flash Lite**: $0.25 input / $1.50 output
7175

7276
Note: These are the current GitHub Copilot supported Gemini models. Pricing from direct Google AI API usage applies.
7377

vscode-extension/src/modelPricing.json

Lines changed: 52 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,29 +2,46 @@
22
"$schema": "http://json-schema.org/draft-07/schema#",
33
"description": "Model pricing data - costs per million tokens for input and output",
44
"metadata": {
5-
"lastUpdated": "2026-03-09",
5+
"lastUpdated": "2026-03-30",
66
"sources": [
77
{
88
"name": "OpenAI API Pricing",
99
"url": "https://openai.com/api/pricing/",
10-
"retrievedDate": "2026-01-16"
10+
"retrievedDate": "2026-03-30"
1111
},
1212
{
1313
"name": "Anthropic Claude Pricing",
1414
"url": "https://www.anthropic.com/pricing",
15-
"note": "Standard rates",
16-
"retrievedDate": "2026-01-16"
15+
"note": "Standard rates; also see https://platform.claude.com/docs/en/about-claude/pricing",
16+
"retrievedDate": "2026-03-30"
1717
},
1818
{
1919
"name": "Google AI Gemini API Pricing",
20-
"url": "https://ai.google.dev/pricing",
21-
"retrievedDate": "2026-01-16"
20+
"url": "https://ai.google.dev/gemini-api/docs/pricing",
21+
"retrievedDate": "2026-03-30"
22+
},
23+
{
24+
"name": "xAI Grok API Pricing",
25+
"url": "https://x.ai/api",
26+
"retrievedDate": "2026-03-30"
2227
},
2328
{
2429
"name": "GitHub Copilot Supported Models",
2530
"url": "https://docs.github.com/en/copilot/reference/ai-models/supported-models",
26-
"retrievedDate": "2026-01-30",
31+
"retrievedDate": "2026-03-30",
2732
"note": "Source for tier/multiplier data"
33+
},
34+
{
35+
"name": "GitHub Copilot Premium Requests",
36+
"url": "https://docs.github.com/en/copilot/managing-copilot/monitoring-usage-and-entitlements/about-premium-requests",
37+
"retrievedDate": "2026-03-30",
38+
"note": "Source for premium request multiplier values"
39+
},
40+
{
41+
"name": "OpenRouter Model Pricing",
42+
"url": "https://openrouter.ai",
43+
"retrievedDate": "2026-03-30",
44+
"note": "Cross-provider pricing aggregator used for verification"
2845
}
2946
],
3047
"disclaimer": "GitHub Copilot uses these models but pricing may differ from direct API usage. These are reference prices for cost estimation purposes only."
@@ -109,11 +126,20 @@
109126
"multiplier": 1
110127
},
111128
"gpt-5.4": {
112-
"inputCostPerMillion": 0.00,
113-
"outputCostPerMillion": 0.00,
129+
"inputCostPerMillion": 2.50,
130+
"outputCostPerMillion": 15.0,
114131
"category": "GPT-5 models",
115132
"tier": "premium",
116-
"multiplier": 1
133+
"multiplier": 1,
134+
"displayNames": ["GPT-5.4"]
135+
},
136+
"gpt-5.4-mini": {
137+
"inputCostPerMillion": 0.25,
138+
"outputCostPerMillion": 2.0,
139+
"category": "GPT-5 models",
140+
"tier": "standard",
141+
"multiplier": 0,
142+
"displayNames": ["GPT-5.4 mini"]
117143
},
118144
"gpt-4": {
119145
"inputCostPerMillion": 3.0,
@@ -201,11 +227,12 @@
201227
"multiplier": 1
202228
},
203229
"claude-sonnet-4.6": {
204-
"inputCostPerMillion": 0.00,
205-
"outputCostPerMillion": 0.00,
230+
"inputCostPerMillion": 3.0,
231+
"outputCostPerMillion": 15.0,
206232
"category": "Claude models (Anthropic)",
207233
"tier": "premium",
208-
"multiplier": 3
234+
"multiplier": 3,
235+
"displayNames": ["Claude Sonnet 4.6"]
209236
},
210237
"claude-haiku": {
211238
"inputCostPerMillion": 0.25,
@@ -257,16 +284,16 @@
257284
"multiplier": 30
258285
},
259286
"o3-mini": {
260-
"inputCostPerMillion": 4.0,
261-
"outputCostPerMillion": 16.0,
287+
"inputCostPerMillion": 1.10,
288+
"outputCostPerMillion": 4.40,
262289
"category": "OpenAI reasoning models",
263290
"tier": "premium",
264291
"multiplier": 1,
265292
"displayNames": ["o3-mini"]
266293
},
267294
"o4-mini": {
268-
"inputCostPerMillion": 4.0,
269-
"outputCostPerMillion": 16.0,
295+
"inputCostPerMillion": 1.10,
296+
"outputCostPerMillion": 4.40,
270297
"category": "OpenAI reasoning models",
271298
"tier": "premium",
272299
"multiplier": 1,
@@ -363,6 +390,14 @@
363390
"multiplier": 1,
364391
"displayNames": ["Gemini 3.1 Pro", "Gemini 3.1 Pro (Preview)"]
365392
},
393+
"gemini-3.1-flash-lite": {
394+
"inputCostPerMillion": 0.25,
395+
"outputCostPerMillion": 1.50,
396+
"category": "Google Gemini models",
397+
"tier": "unknown",
398+
"multiplier": 0.33,
399+
"displayNames": ["Gemini 3.1 Flash Lite"]
400+
},
366401
"grok-code-fast-1": {
367402
"inputCostPerMillion": 0.20,
368403
"outputCostPerMillion": 1.50,

vscode-extension/src/tokenEstimators.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"gpt-5.2-pro": 0.25,
2222
"gpt-5.3-codex": 0.25,
2323
"gpt-5.4": 0.25,
24+
"gpt-5.4-mini": 0.25,
2425
"gpt-4.1-nano": 0.25,
2526
"gemini-2.0-flash": 0.25,
2627
"gemini-2.0-flash-lite": 0.25,
@@ -43,6 +44,7 @@
4344
"gemini-3-pro": 0.25,
4445
"gemini-3-pro-preview": 0.25,
4546
"gemini-3.1-pro": 0.25,
47+
"gemini-3.1-flash-lite": 0.25,
4648
"grok-code-fast-1": 0.25,
4749
"raptor-mini": 0.25,
4850
"goldeneye": 0.25,

vscode-extension/src/usageAnalysis.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,7 @@ export async function trackEnhancedMetrics(deps: Pick<UsageAnalysisDeps, 'warn'>
811811
// Track edit scope and apply usage
812812
if (request.response && Array.isArray(request.response)) {
813813
for (const resp of request.response) {
814+
if (!resp) { continue; }
814815
if (resp.kind === 'textEditGroup' && resp.uri) {
815816
const filePath = resp.uri.path || JSON.stringify(resp.uri);
816817
editedFiles.add(filePath);
@@ -866,6 +867,7 @@ export async function trackEnhancedMetrics(deps: Pick<UsageAnalysisDeps, 'warn'>
866867
// Track edit scope and apply usage
867868
if (request.response && Array.isArray(request.response)) {
868869
for (const resp of request.response) {
870+
if (!resp) { continue; }
869871
if (resp.kind === 'textEditGroup' && resp.uri) {
870872
const filePath = resp.uri.path || JSON.stringify(resp.uri);
871873
editedFiles.add(filePath);
@@ -1258,6 +1260,7 @@ export async function analyzeSessionUsage(deps: UsageAnalysisDeps, sessionFile:
12581260
// Extract tool calls and MCP tools from request.response array
12591261
if (request.response && Array.isArray(request.response)) {
12601262
for (const responseItem of request.response) {
1263+
if (!responseItem) { continue; }
12611264
if (responseItem.kind === 'toolInvocationSerialized' || responseItem.kind === 'prepareToolInvocation') {
12621265
const toolName = responseItem.toolId || responseItem.toolName || responseItem.invocationMessage?.toolName || responseItem.toolSpecificData?.kind || 'unknown';
12631266

@@ -1365,6 +1368,7 @@ export async function analyzeSessionUsage(deps: UsageAnalysisDeps, sessionFile:
13651368
// Extract tool calls from request.response array (when full request is added)
13661369
if (request.response && Array.isArray(request.response)) {
13671370
for (const responseItem of request.response) {
1371+
if (!responseItem) { continue; }
13681372
if (responseItem.kind === 'toolInvocationSerialized' || responseItem.kind === 'prepareToolInvocation') {
13691373
analysis.toolCalls.total++;
13701374
const toolName = responseItem.toolId || responseItem.toolName || responseItem.invocationMessage?.toolName || responseItem.toolSpecificData?.kind || 'unknown';
@@ -1378,6 +1382,7 @@ export async function analyzeSessionUsage(deps: UsageAnalysisDeps, sessionFile:
13781382
// Handle VS Code incremental format - tool invocations in responses
13791383
if (event.kind === 2 && event.k?.includes('response') && Array.isArray(event.v)) {
13801384
for (const responseItem of event.v) {
1385+
if (!responseItem) { continue; }
13811386
if (responseItem.kind === 'toolInvocationSerialized') {
13821387
analysis.toolCalls.total++;
13831388
const toolName = responseItem.toolId || responseItem.toolName || responseItem.invocationMessage?.toolName || responseItem.toolSpecificData?.kind || 'unknown';
@@ -1485,6 +1490,7 @@ export async function analyzeSessionUsage(deps: UsageAnalysisDeps, sessionFile:
14851490
// Analyze response for tool calls and MCP tools
14861491
if (request.response && Array.isArray(request.response)) {
14871492
for (const responseItem of request.response) {
1493+
if (!responseItem) { continue; }
14881494
// Detect tool invocations
14891495
if (responseItem.kind === 'toolInvocationSerialized' ||
14901496
responseItem.kind === 'prepareToolInvocation') {
@@ -1706,7 +1712,7 @@ export async function getModelUsageFromSession(deps: Pick<UsageAnalysisDeps, 'wa
17061712
}
17071713
if (request.response && Array.isArray(request.response)) {
17081714
for (const responseItem of request.response) {
1709-
if (responseItem.value) {
1715+
if (responseItem?.value) {
17101716
modelUsage[requestModel].outputTokens += estimateTokensFromText(responseItem.value, requestModel, deps.tokenEstimators);
17111717
}
17121718
}
@@ -1774,7 +1780,7 @@ export async function getModelUsageFromSession(deps: Pick<UsageAnalysisDeps, 'wa
17741780
// Estimate tokens from assistant response (output)
17751781
if (request.response && Array.isArray(request.response)) {
17761782
for (const responseItem of request.response) {
1777-
if (responseItem.value) {
1783+
if (responseItem?.value) {
17781784
const tokens = estimateTokensFromText(responseItem.value, model, deps.tokenEstimators);
17791785
modelUsage[model].outputTokens += tokens;
17801786
}

0 commit comments

Comments
 (0)