@leo000001/opencode-quota-sidebar 4.0.9 → 4.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +504 -446
- package/README.zh-CN.md +516 -458
- package/dist/cli.d.ts +31 -0
- package/dist/cli.js +123 -46
- package/dist/cli_render.d.ts +4 -4
- package/dist/cli_render.js +74 -74
- package/dist/cost.d.ts +21 -4
- package/dist/cost.js +493 -264
- package/dist/format.d.ts +5 -5
- package/dist/format.js +288 -287
- package/dist/history_usage.d.ts +15 -9
- package/dist/history_usage.js +28 -22
- package/dist/index.js +15 -1
- package/dist/models_dev_pricing.d.ts +6 -0
- package/dist/models_dev_pricing.js +226 -0
- package/dist/opencode_pricing.d.ts +14 -0
- package/dist/opencode_pricing.js +273 -0
- package/dist/storage.d.ts +3 -3
- package/dist/storage.js +27 -28
- package/dist/storage_parse.d.ts +1 -1
- package/dist/storage_parse.js +51 -45
- package/dist/storage_paths.d.ts +1 -0
- package/dist/storage_paths.js +26 -11
- package/dist/title_apply.d.ts +1 -17
- package/dist/title_apply.js +17 -48
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +29 -15
- package/dist/tui.d.ts +1 -1
- package/dist/tui.tsx +481 -471
- package/dist/tui_helpers.d.ts +5 -3
- package/dist/tui_helpers.js +62 -34
- package/dist/types.d.ts +8 -10
- package/dist/usage.d.ts +9 -6
- package/dist/usage.js +27 -21
- package/dist/usage_service.d.ts +8 -7
- package/dist/usage_service.js +261 -150
- package/package.json +1 -1
package/dist/cost.js
CHANGED
|
@@ -1,23 +1,28 @@
|
|
|
1
|
-
import { asNumber, isRecord } from
|
|
1
|
+
import { asNumber, isRecord } from "./helpers.js";
|
|
2
2
|
export const API_COST_ENABLED_PROVIDERS = new Set([
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
3
|
+
"openai",
|
|
4
|
+
"anthropic",
|
|
5
|
+
"kimi-for-coding",
|
|
6
|
+
"zhipu",
|
|
7
|
+
"minimax-cn-coding-plan",
|
|
8
8
|
]);
|
|
9
|
+
export const API_COST_RULES_VERSION = 2;
|
|
10
|
+
const MODEL_COST_STANDARD_UNIT = 1_000_000;
|
|
11
|
+
const MODEL_COST_RATE_UNIT_THRESHOLD = 0.001;
|
|
12
|
+
const OPENAI_FAST_COST_MULTIPLIER = 2;
|
|
13
|
+
const ANTHROPIC_FAST_COST_MULTIPLIER = 6;
|
|
9
14
|
const MODEL_COST_RATE_ALIASES = {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
15
|
+
"zhipuai-coding-plan:glm-5.1": ["zhipu:glm-5"],
|
|
16
|
+
"zhipuai-coding-plan:glm-5.1-thinking": ["zhipu:glm-5"],
|
|
17
|
+
"zhipu:glm-5.1": ["zhipu:glm-5"],
|
|
18
|
+
"zhipu:glm-5.1-thinking": ["zhipu:glm-5"],
|
|
14
19
|
};
|
|
15
20
|
function moonshotCanonicalModelID(modelID) {
|
|
16
|
-
const stripped = modelID.replace(/^moonshotai[/:]/i,
|
|
21
|
+
const stripped = modelID.replace(/^moonshotai[/:]/i, "");
|
|
17
22
|
switch (stripped) {
|
|
18
|
-
case
|
|
19
|
-
case
|
|
20
|
-
return
|
|
23
|
+
case "k2p5":
|
|
24
|
+
case "kimi-k2-5":
|
|
25
|
+
return "kimi-k2.5";
|
|
21
26
|
default:
|
|
22
27
|
return stripped;
|
|
23
28
|
}
|
|
@@ -30,7 +35,7 @@ function moonshotModelAliases(modelID, options) {
|
|
|
30
35
|
if (!aliases.includes(value))
|
|
31
36
|
aliases.push(value);
|
|
32
37
|
};
|
|
33
|
-
const stripped = modelID.replace(/^moonshotai[/:]/i,
|
|
38
|
+
const stripped = modelID.replace(/^moonshotai[/:]/i, "");
|
|
34
39
|
const canonical = moonshotCanonicalModelID(modelID);
|
|
35
40
|
if (!options?.canonicalProviderKeys)
|
|
36
41
|
push(modelID);
|
|
@@ -49,11 +54,11 @@ function minimaxModelAliases(modelID) {
|
|
|
49
54
|
};
|
|
50
55
|
push(modelID);
|
|
51
56
|
push(modelID.toLowerCase());
|
|
52
|
-
const stripped = modelID.replace(/^(?:minimax|minimax-cn-coding-plan)[/:]/i,
|
|
57
|
+
const stripped = modelID.replace(/^(?:minimax|minimax-cn-coding-plan)[/:]/i, "");
|
|
53
58
|
push(stripped);
|
|
54
59
|
push(stripped.toLowerCase());
|
|
55
60
|
if (/^minimax-/i.test(stripped)) {
|
|
56
|
-
const suffix = stripped.slice(
|
|
61
|
+
const suffix = stripped.slice("minimax-".length);
|
|
57
62
|
push(`MiniMax-${suffix}`);
|
|
58
63
|
push(`minimax-${suffix.toLowerCase()}`);
|
|
59
64
|
}
|
|
@@ -77,19 +82,19 @@ function zhipuModelAliases(modelID) {
|
|
|
77
82
|
push(modelID);
|
|
78
83
|
for (let index = 0; index < queue.length; index++) {
|
|
79
84
|
const stem = queue[index];
|
|
80
|
-
const withoutProviderPrefix = stem.replace(/^(?:zhipu|z-ai|bigmodel|zhipuai-coding-plan)[/:]/,
|
|
85
|
+
const withoutProviderPrefix = stem.replace(/^(?:zhipu|z-ai|bigmodel|zhipuai-coding-plan)[/:]/, "");
|
|
81
86
|
push(withoutProviderPrefix);
|
|
82
87
|
push(`zhipu/${withoutProviderPrefix}`);
|
|
83
|
-
const withoutBillingSuffix = withoutProviderPrefix.replace(/-billing$/,
|
|
88
|
+
const withoutBillingSuffix = withoutProviderPrefix.replace(/-billing$/, "");
|
|
84
89
|
push(withoutBillingSuffix);
|
|
85
90
|
push(`zhipu/${withoutBillingSuffix}`);
|
|
86
|
-
const withoutThinkingSuffix = withoutBillingSuffix.replace(/-thinking$/,
|
|
91
|
+
const withoutThinkingSuffix = withoutBillingSuffix.replace(/-thinking$/, "");
|
|
87
92
|
push(withoutThinkingSuffix);
|
|
88
93
|
push(`zhipu/${withoutThinkingSuffix}`);
|
|
89
|
-
const dotted = withoutThinkingSuffix.replace(/(\d)-(\d)(?=-|$)/g,
|
|
94
|
+
const dotted = withoutThinkingSuffix.replace(/(\d)-(\d)(?=-|$)/g, "$1.$2");
|
|
90
95
|
push(dotted);
|
|
91
96
|
push(`zhipu/${dotted}`);
|
|
92
|
-
const hyphenated = withoutThinkingSuffix.replace(/(\d)\.(\d)(?=-|$)/g,
|
|
97
|
+
const hyphenated = withoutThinkingSuffix.replace(/(\d)\.(\d)(?=-|$)/g, "$1-$2");
|
|
93
98
|
push(hyphenated);
|
|
94
99
|
push(`zhipu/${hyphenated}`);
|
|
95
100
|
}
|
|
@@ -110,71 +115,92 @@ function anthropicModelAliases(modelID) {
|
|
|
110
115
|
for (let index = 0; index < queue.length; index++) {
|
|
111
116
|
const stem = queue[index];
|
|
112
117
|
const withoutProviderPrefix = stem
|
|
113
|
-
.replace(/^(?:[a-z]+\.)*anthropic\./,
|
|
114
|
-
.replace(/^anthropic[/.]/,
|
|
118
|
+
.replace(/^(?:[a-z]+\.)*anthropic\./, "")
|
|
119
|
+
.replace(/^anthropic[/.]/, "");
|
|
115
120
|
push(withoutProviderPrefix);
|
|
116
121
|
push(`anthropic/${withoutProviderPrefix}`);
|
|
117
|
-
const withoutVersionSuffix = withoutProviderPrefix.replace(/-v\d+(?::\d+)?$/,
|
|
122
|
+
const withoutVersionSuffix = withoutProviderPrefix.replace(/-v\d+(?::\d+)?$/, "");
|
|
118
123
|
push(withoutVersionSuffix);
|
|
119
124
|
push(`anthropic/${withoutVersionSuffix}`);
|
|
120
|
-
const atDate = withoutVersionSuffix.replace(/@(\d{8})$/,
|
|
125
|
+
const atDate = withoutVersionSuffix.replace(/@(\d{8})$/, "-$1");
|
|
121
126
|
push(atDate);
|
|
122
127
|
push(`anthropic/${atDate}`);
|
|
123
|
-
const withAtDate = withoutVersionSuffix.replace(/-(\d{8})$/,
|
|
128
|
+
const withAtDate = withoutVersionSuffix.replace(/-(\d{8})$/, "@$1");
|
|
124
129
|
push(withAtDate);
|
|
125
130
|
push(`anthropic/${withAtDate}`);
|
|
126
|
-
const withoutThinkingSuffix = withoutVersionSuffix.replace(/-thinking$/,
|
|
131
|
+
const withoutThinkingSuffix = withoutVersionSuffix.replace(/-thinking$/, "");
|
|
127
132
|
push(withoutThinkingSuffix);
|
|
128
133
|
push(`anthropic/${withoutThinkingSuffix}`);
|
|
129
|
-
const withoutLatestSuffix = withoutThinkingSuffix.replace(/-latest$/,
|
|
134
|
+
const withoutLatestSuffix = withoutThinkingSuffix.replace(/-latest$/, "");
|
|
130
135
|
push(withoutLatestSuffix);
|
|
131
136
|
push(`anthropic/${withoutLatestSuffix}`);
|
|
132
137
|
const withoutDateSuffix = withoutLatestSuffix
|
|
133
|
-
.replace(/-\d{8}$/,
|
|
134
|
-
.replace(/@\d{8}$/,
|
|
138
|
+
.replace(/-\d{8}$/, "")
|
|
139
|
+
.replace(/@\d{8}$/, "");
|
|
135
140
|
push(withoutDateSuffix);
|
|
136
141
|
push(`anthropic/${withoutDateSuffix}`);
|
|
137
|
-
const dotted = withoutDateSuffix.replace(/(\d)-(\d)(?=-|$)/g,
|
|
142
|
+
const dotted = withoutDateSuffix.replace(/(\d)-(\d)(?=-|$)/g, "$1.$2");
|
|
138
143
|
push(dotted);
|
|
139
144
|
push(`anthropic/${dotted}`);
|
|
140
|
-
const hyphenated = withoutDateSuffix.replace(/(\d)\.(\d)(?=-|$)/g,
|
|
145
|
+
const hyphenated = withoutDateSuffix.replace(/(\d)\.(\d)(?=-|$)/g, "$1-$2");
|
|
141
146
|
push(hyphenated);
|
|
142
147
|
push(`anthropic/${hyphenated}`);
|
|
143
148
|
}
|
|
144
149
|
return aliases;
|
|
145
150
|
}
|
|
146
151
|
function normalizeKnownProviderID(providerID) {
|
|
147
|
-
if (providerID.startsWith(
|
|
148
|
-
return
|
|
152
|
+
if (providerID.toLowerCase().startsWith("github-copilot")) {
|
|
153
|
+
return "github-copilot";
|
|
154
|
+
}
|
|
149
155
|
return providerID;
|
|
150
156
|
}
|
|
157
|
+
function isOpenAICompatibleProviderID(providerID) {
|
|
158
|
+
return (providerID === "openai" ||
|
|
159
|
+
providerID.startsWith("openai-") ||
|
|
160
|
+
providerID.endsWith("-openai") ||
|
|
161
|
+
providerID.startsWith("openai/") ||
|
|
162
|
+
providerID.endsWith("/openai") ||
|
|
163
|
+
providerID.includes(".openai.") ||
|
|
164
|
+
providerID.endsWith("-oai"));
|
|
165
|
+
}
|
|
166
|
+
function isAnthropicCompatibleProviderID(providerID) {
|
|
167
|
+
return (providerID === "anthropic" ||
|
|
168
|
+
providerID === "claude" ||
|
|
169
|
+
providerID.startsWith("anthropic-") ||
|
|
170
|
+
providerID.endsWith("-anthropic") ||
|
|
171
|
+
providerID.startsWith("anthropic/") ||
|
|
172
|
+
providerID.endsWith("/anthropic") ||
|
|
173
|
+
providerID.includes(".anthropic.") ||
|
|
174
|
+
providerID.startsWith("claude-") ||
|
|
175
|
+
providerID.endsWith("-claude"));
|
|
176
|
+
}
|
|
151
177
|
function isCanonicalZhipuProviderID(providerID) {
|
|
152
|
-
return (providerID ===
|
|
153
|
-
providerID ===
|
|
154
|
-
providerID ===
|
|
155
|
-
providerID ===
|
|
178
|
+
return (providerID === "zhipu" ||
|
|
179
|
+
providerID === "bigmodel" ||
|
|
180
|
+
providerID === "z-ai" ||
|
|
181
|
+
providerID === "zhipuai-coding-plan");
|
|
156
182
|
}
|
|
157
183
|
function isCanonicalMiniMaxProviderID(providerID) {
|
|
158
|
-
return providerID ===
|
|
184
|
+
return providerID === "minimax" || providerID === "minimax-cn-coding-plan";
|
|
159
185
|
}
|
|
160
186
|
export function canonicalPricingProviderID(providerID) {
|
|
161
187
|
const normalized = normalizeKnownProviderID(providerID);
|
|
162
188
|
const lowered = normalized.toLowerCase();
|
|
163
189
|
if (isCanonicalMiniMaxProviderID(lowered)) {
|
|
164
|
-
return
|
|
190
|
+
return "minimax";
|
|
165
191
|
}
|
|
166
192
|
if (isCanonicalZhipuProviderID(lowered)) {
|
|
167
|
-
return
|
|
193
|
+
return "zhipu";
|
|
168
194
|
}
|
|
169
|
-
if (lowered ===
|
|
170
|
-
return
|
|
171
|
-
if (
|
|
172
|
-
return
|
|
195
|
+
if (lowered === "kimi-for-coding")
|
|
196
|
+
return "moonshotai";
|
|
197
|
+
if (isAnthropicCompatibleProviderID(lowered)) {
|
|
198
|
+
return "anthropic";
|
|
173
199
|
}
|
|
174
|
-
if (
|
|
175
|
-
return
|
|
176
|
-
if (lowered.includes(
|
|
177
|
-
return
|
|
200
|
+
if (isOpenAICompatibleProviderID(lowered))
|
|
201
|
+
return "openai";
|
|
202
|
+
if (lowered.includes("copilot"))
|
|
203
|
+
return "github-copilot";
|
|
178
204
|
return normalized;
|
|
179
205
|
}
|
|
180
206
|
export function canonicalApiCostProviderID(providerID) {
|
|
@@ -182,17 +208,17 @@ export function canonicalApiCostProviderID(providerID) {
|
|
|
182
208
|
const lowered = normalized.toLowerCase();
|
|
183
209
|
if (API_COST_ENABLED_PROVIDERS.has(lowered))
|
|
184
210
|
return lowered;
|
|
185
|
-
if (lowered ===
|
|
186
|
-
return
|
|
187
|
-
if (lowered.includes(
|
|
188
|
-
return
|
|
189
|
-
if (
|
|
190
|
-
return
|
|
191
|
-
if (
|
|
192
|
-
return
|
|
211
|
+
if (lowered === "minimax")
|
|
212
|
+
return "minimax-cn-coding-plan";
|
|
213
|
+
if (lowered.includes("copilot"))
|
|
214
|
+
return "github-copilot";
|
|
215
|
+
if (isOpenAICompatibleProviderID(lowered))
|
|
216
|
+
return "openai";
|
|
217
|
+
if (isAnthropicCompatibleProviderID(lowered)) {
|
|
218
|
+
return "anthropic";
|
|
193
219
|
}
|
|
194
220
|
if (isCanonicalZhipuProviderID(lowered)) {
|
|
195
|
-
return
|
|
221
|
+
return "zhipu";
|
|
196
222
|
}
|
|
197
223
|
return normalized;
|
|
198
224
|
}
|
|
@@ -251,325 +277,325 @@ function minimaxPricing(input, output, cacheRead, cacheWrite) {
|
|
|
251
277
|
}
|
|
252
278
|
const BUNDLED_CANONICAL_PRICE_ENTRIES = [
|
|
253
279
|
{
|
|
254
|
-
provider:
|
|
280
|
+
provider: "openai",
|
|
255
281
|
// OpenCode commonly reports the flagship alias as `gpt-5`; keep a bundled
|
|
256
282
|
// fallback so API-equivalent cost still renders when runtime metadata omits
|
|
257
283
|
// OpenAI pricing on a given client or subscription path.
|
|
258
|
-
model:
|
|
284
|
+
model: "gpt-5",
|
|
259
285
|
rates: openaiPricing(2.5, 15, 0.25),
|
|
260
|
-
source:
|
|
261
|
-
sourceURL:
|
|
286
|
+
source: "official-doc",
|
|
287
|
+
sourceURL: "https://openai.com/api/pricing/",
|
|
262
288
|
},
|
|
263
289
|
{
|
|
264
|
-
provider:
|
|
265
|
-
model:
|
|
290
|
+
provider: "openai",
|
|
291
|
+
model: "gpt-5.3",
|
|
266
292
|
rates: openaiPricing(1.75, 14, 0.175),
|
|
267
|
-
source:
|
|
268
|
-
sourceURL:
|
|
293
|
+
source: "official-doc",
|
|
294
|
+
sourceURL: "https://developers.openai.com/api/docs/models/gpt-5.3-chat-latest",
|
|
269
295
|
},
|
|
270
296
|
{
|
|
271
|
-
provider:
|
|
272
|
-
model:
|
|
297
|
+
provider: "openai",
|
|
298
|
+
model: "gpt-5.3-chat-latest",
|
|
273
299
|
rates: openaiPricing(1.75, 14, 0.175),
|
|
274
|
-
source:
|
|
275
|
-
sourceURL:
|
|
300
|
+
source: "official-doc",
|
|
301
|
+
sourceURL: "https://developers.openai.com/api/docs/models/gpt-5.3-chat-latest",
|
|
276
302
|
},
|
|
277
303
|
{
|
|
278
|
-
provider:
|
|
279
|
-
model:
|
|
304
|
+
provider: "openai",
|
|
305
|
+
model: "gpt-5.3-codex",
|
|
280
306
|
rates: openaiPricing(1.75, 14, 0.175),
|
|
281
|
-
source:
|
|
282
|
-
sourceURL:
|
|
307
|
+
source: "official-doc",
|
|
308
|
+
sourceURL: "https://developers.openai.com/api/docs/models/gpt-5.3-codex",
|
|
283
309
|
},
|
|
284
310
|
{
|
|
285
|
-
provider:
|
|
286
|
-
model:
|
|
311
|
+
provider: "openai",
|
|
312
|
+
model: "gpt-5.2",
|
|
287
313
|
rates: openaiPricing(1.75, 14, 0.175),
|
|
288
|
-
source:
|
|
289
|
-
sourceURL:
|
|
314
|
+
source: "official-doc",
|
|
315
|
+
sourceURL: "https://developers.openai.com/api/docs/models/gpt-5.2",
|
|
290
316
|
},
|
|
291
317
|
{
|
|
292
|
-
provider:
|
|
293
|
-
model:
|
|
318
|
+
provider: "openai",
|
|
319
|
+
model: "gpt-5.2-chat-latest",
|
|
294
320
|
rates: openaiPricing(1.75, 14, 0.175),
|
|
295
|
-
source:
|
|
296
|
-
sourceURL:
|
|
321
|
+
source: "official-doc",
|
|
322
|
+
sourceURL: "https://developers.openai.com/api/docs/models/gpt-5.2-chat-latest",
|
|
297
323
|
},
|
|
298
324
|
{
|
|
299
|
-
provider:
|
|
300
|
-
model:
|
|
325
|
+
provider: "openai",
|
|
326
|
+
model: "gpt-5.2-pro",
|
|
301
327
|
rates: openaiPricing(21, 168, 0),
|
|
302
|
-
source:
|
|
303
|
-
sourceURL:
|
|
328
|
+
source: "official-doc",
|
|
329
|
+
sourceURL: "https://openai.com/index/introducing-gpt-5-2/",
|
|
304
330
|
},
|
|
305
331
|
{
|
|
306
|
-
provider:
|
|
307
|
-
model:
|
|
332
|
+
provider: "openai",
|
|
333
|
+
model: "gpt-5.4",
|
|
308
334
|
rates: openaiPricing(2.5, 15, 0.25),
|
|
309
|
-
source:
|
|
310
|
-
sourceURL:
|
|
335
|
+
source: "official-doc",
|
|
336
|
+
sourceURL: "https://openai.com/api/pricing/",
|
|
311
337
|
},
|
|
312
338
|
{
|
|
313
|
-
provider:
|
|
314
|
-
model:
|
|
339
|
+
provider: "openai",
|
|
340
|
+
model: "gpt-5-mini",
|
|
315
341
|
rates: openaiPricing(0.75, 4.5, 0.075),
|
|
316
|
-
source:
|
|
317
|
-
sourceURL:
|
|
342
|
+
source: "official-doc",
|
|
343
|
+
sourceURL: "https://openai.com/api/pricing/",
|
|
318
344
|
},
|
|
319
345
|
{
|
|
320
|
-
provider:
|
|
321
|
-
model:
|
|
346
|
+
provider: "openai",
|
|
347
|
+
model: "gpt-5.4-mini",
|
|
322
348
|
rates: openaiPricing(0.75, 4.5, 0.075),
|
|
323
|
-
source:
|
|
324
|
-
sourceURL:
|
|
349
|
+
source: "official-doc",
|
|
350
|
+
sourceURL: "https://openai.com/api/pricing/",
|
|
325
351
|
},
|
|
326
352
|
{
|
|
327
|
-
provider:
|
|
328
|
-
model:
|
|
353
|
+
provider: "openai",
|
|
354
|
+
model: "gpt-5-nano",
|
|
329
355
|
rates: openaiPricing(0.2, 1.25, 0.02),
|
|
330
|
-
source:
|
|
331
|
-
sourceURL:
|
|
356
|
+
source: "official-doc",
|
|
357
|
+
sourceURL: "https://openai.com/api/pricing/",
|
|
332
358
|
},
|
|
333
359
|
{
|
|
334
|
-
provider:
|
|
335
|
-
model:
|
|
360
|
+
provider: "openai",
|
|
361
|
+
model: "gpt-5.4-nano",
|
|
336
362
|
rates: openaiPricing(0.2, 1.25, 0.02),
|
|
337
|
-
source:
|
|
338
|
-
sourceURL:
|
|
363
|
+
source: "official-doc",
|
|
364
|
+
sourceURL: "https://openai.com/api/pricing/",
|
|
339
365
|
},
|
|
340
366
|
{
|
|
341
|
-
provider:
|
|
342
|
-
model:
|
|
367
|
+
provider: "anthropic",
|
|
368
|
+
model: "claude-opus-4-6",
|
|
343
369
|
rates: anthropicPricing(5, 25),
|
|
344
|
-
source:
|
|
345
|
-
sourceURL:
|
|
370
|
+
source: "official-doc",
|
|
371
|
+
sourceURL: "https://docs.anthropic.com/en/docs/about-claude/pricing",
|
|
346
372
|
},
|
|
347
373
|
{
|
|
348
|
-
provider:
|
|
349
|
-
model:
|
|
374
|
+
provider: "anthropic",
|
|
375
|
+
model: "claude-opus-4-5",
|
|
350
376
|
rates: anthropicPricing(5, 25),
|
|
351
|
-
source:
|
|
352
|
-
sourceURL:
|
|
377
|
+
source: "official-doc",
|
|
378
|
+
sourceURL: "https://docs.anthropic.com/en/docs/about-claude/pricing",
|
|
353
379
|
},
|
|
354
380
|
{
|
|
355
|
-
provider:
|
|
356
|
-
model:
|
|
381
|
+
provider: "anthropic",
|
|
382
|
+
model: "claude-opus-4-1",
|
|
357
383
|
rates: anthropicPricing(15, 75),
|
|
358
|
-
source:
|
|
359
|
-
sourceURL:
|
|
384
|
+
source: "official-doc",
|
|
385
|
+
sourceURL: "https://docs.anthropic.com/en/docs/about-claude/pricing",
|
|
360
386
|
},
|
|
361
387
|
{
|
|
362
|
-
provider:
|
|
363
|
-
model:
|
|
388
|
+
provider: "anthropic",
|
|
389
|
+
model: "claude-opus-4",
|
|
364
390
|
rates: anthropicPricing(15, 75),
|
|
365
|
-
source:
|
|
366
|
-
sourceURL:
|
|
391
|
+
source: "official-doc",
|
|
392
|
+
sourceURL: "https://docs.anthropic.com/en/docs/about-claude/pricing",
|
|
367
393
|
},
|
|
368
394
|
{
|
|
369
|
-
provider:
|
|
370
|
-
model:
|
|
395
|
+
provider: "anthropic",
|
|
396
|
+
model: "claude-sonnet-4-6",
|
|
371
397
|
rates: anthropicPricing(3, 15),
|
|
372
|
-
source:
|
|
373
|
-
sourceURL:
|
|
398
|
+
source: "official-doc",
|
|
399
|
+
sourceURL: "https://docs.anthropic.com/en/docs/about-claude/pricing",
|
|
374
400
|
},
|
|
375
401
|
{
|
|
376
|
-
provider:
|
|
377
|
-
model:
|
|
402
|
+
provider: "anthropic",
|
|
403
|
+
model: "claude-sonnet-4-5",
|
|
378
404
|
rates: anthropicPricing(3, 15, {
|
|
379
405
|
longContextInput: 6,
|
|
380
406
|
longContextOutput: 22.5,
|
|
381
407
|
}),
|
|
382
|
-
source:
|
|
383
|
-
sourceURL:
|
|
408
|
+
source: "official-doc",
|
|
409
|
+
sourceURL: "https://docs.anthropic.com/en/docs/about-claude/pricing",
|
|
384
410
|
},
|
|
385
411
|
{
|
|
386
|
-
provider:
|
|
387
|
-
model:
|
|
412
|
+
provider: "anthropic",
|
|
413
|
+
model: "claude-sonnet-4",
|
|
388
414
|
rates: anthropicPricing(3, 15, {
|
|
389
415
|
longContextInput: 6,
|
|
390
416
|
longContextOutput: 22.5,
|
|
391
417
|
}),
|
|
392
|
-
source:
|
|
393
|
-
sourceURL:
|
|
418
|
+
source: "official-doc",
|
|
419
|
+
sourceURL: "https://docs.anthropic.com/en/docs/about-claude/pricing",
|
|
394
420
|
},
|
|
395
421
|
{
|
|
396
|
-
provider:
|
|
397
|
-
model:
|
|
422
|
+
provider: "anthropic",
|
|
423
|
+
model: "claude-3-7-sonnet",
|
|
398
424
|
rates: anthropicPricing(3, 15),
|
|
399
|
-
source:
|
|
400
|
-
sourceURL:
|
|
425
|
+
source: "official-doc",
|
|
426
|
+
sourceURL: "https://docs.anthropic.com/en/docs/about-claude/pricing",
|
|
401
427
|
},
|
|
402
428
|
{
|
|
403
|
-
provider:
|
|
404
|
-
model:
|
|
429
|
+
provider: "anthropic",
|
|
430
|
+
model: "claude-3-5-sonnet",
|
|
405
431
|
rates: anthropicPricing(3, 15),
|
|
406
|
-
source:
|
|
407
|
-
sourceURL:
|
|
432
|
+
source: "official-doc",
|
|
433
|
+
sourceURL: "https://docs.anthropic.com/en/docs/about-claude/pricing",
|
|
408
434
|
},
|
|
409
435
|
{
|
|
410
|
-
provider:
|
|
411
|
-
model:
|
|
436
|
+
provider: "anthropic",
|
|
437
|
+
model: "claude-haiku-4-5",
|
|
412
438
|
rates: anthropicPricing(1, 5),
|
|
413
|
-
source:
|
|
414
|
-
sourceURL:
|
|
439
|
+
source: "official-doc",
|
|
440
|
+
sourceURL: "https://docs.anthropic.com/en/docs/about-claude/pricing",
|
|
415
441
|
},
|
|
416
442
|
{
|
|
417
|
-
provider:
|
|
418
|
-
model:
|
|
443
|
+
provider: "anthropic",
|
|
444
|
+
model: "claude-3-5-haiku",
|
|
419
445
|
rates: anthropicPricing(0.8, 4),
|
|
420
|
-
source:
|
|
421
|
-
sourceURL:
|
|
446
|
+
source: "official-doc",
|
|
447
|
+
sourceURL: "https://docs.anthropic.com/en/docs/about-claude/pricing",
|
|
422
448
|
},
|
|
423
449
|
{
|
|
424
|
-
provider:
|
|
425
|
-
model:
|
|
450
|
+
provider: "anthropic",
|
|
451
|
+
model: "claude-3-opus",
|
|
426
452
|
rates: anthropicPricing(15, 75),
|
|
427
|
-
source:
|
|
428
|
-
sourceURL:
|
|
453
|
+
source: "official-doc",
|
|
454
|
+
sourceURL: "https://docs.anthropic.com/en/docs/about-claude/pricing",
|
|
429
455
|
},
|
|
430
456
|
{
|
|
431
|
-
provider:
|
|
432
|
-
model:
|
|
457
|
+
provider: "anthropic",
|
|
458
|
+
model: "claude-3-haiku",
|
|
433
459
|
rates: anthropicPricing(0.25, 1.25),
|
|
434
|
-
source:
|
|
435
|
-
sourceURL:
|
|
460
|
+
source: "official-doc",
|
|
461
|
+
sourceURL: "https://docs.anthropic.com/en/docs/about-claude/pricing",
|
|
436
462
|
},
|
|
437
463
|
{
|
|
438
|
-
provider:
|
|
439
|
-
model:
|
|
464
|
+
provider: "zhipu",
|
|
465
|
+
model: "glm-5",
|
|
440
466
|
rates: zhipuPricing(1, 3.2, 0.2),
|
|
441
|
-
source:
|
|
442
|
-
sourceURL:
|
|
467
|
+
source: "official-doc",
|
|
468
|
+
sourceURL: "https://docs.z.ai/guides/overview/pricing",
|
|
443
469
|
},
|
|
444
470
|
{
|
|
445
|
-
provider:
|
|
446
|
-
model:
|
|
471
|
+
provider: "zhipu",
|
|
472
|
+
model: "glm-4.7",
|
|
447
473
|
rates: zhipuPricing(0.6, 2.2, 0.11),
|
|
448
|
-
source:
|
|
449
|
-
sourceURL:
|
|
474
|
+
source: "official-doc",
|
|
475
|
+
sourceURL: "https://docs.z.ai/guides/overview/pricing",
|
|
450
476
|
},
|
|
451
477
|
{
|
|
452
|
-
provider:
|
|
453
|
-
model:
|
|
478
|
+
provider: "zhipu",
|
|
479
|
+
model: "glm-4.6",
|
|
454
480
|
rates: zhipuPricing(0.6, 2.2, 0.11),
|
|
455
|
-
source:
|
|
456
|
-
sourceURL:
|
|
481
|
+
source: "official-doc",
|
|
482
|
+
sourceURL: "https://docs.z.ai/guides/overview/pricing",
|
|
457
483
|
},
|
|
458
484
|
{
|
|
459
|
-
provider:
|
|
460
|
-
model:
|
|
485
|
+
provider: "zhipu",
|
|
486
|
+
model: "glm-4.6v",
|
|
461
487
|
rates: zhipuPricing(0.3, 0.9, 0.05),
|
|
462
|
-
source:
|
|
463
|
-
sourceURL:
|
|
488
|
+
source: "official-doc",
|
|
489
|
+
sourceURL: "https://docs.z.ai/guides/overview/pricing",
|
|
464
490
|
},
|
|
465
491
|
{
|
|
466
|
-
provider:
|
|
467
|
-
model:
|
|
492
|
+
provider: "zhipu",
|
|
493
|
+
model: "glm-4.5",
|
|
468
494
|
rates: zhipuPricing(0.6, 2.2, 0.11),
|
|
469
|
-
source:
|
|
470
|
-
sourceURL:
|
|
495
|
+
source: "official-doc",
|
|
496
|
+
sourceURL: "https://docs.z.ai/guides/overview/pricing",
|
|
471
497
|
},
|
|
472
498
|
{
|
|
473
|
-
provider:
|
|
474
|
-
model:
|
|
499
|
+
provider: "zhipu",
|
|
500
|
+
model: "glm-4.5-air",
|
|
475
501
|
rates: zhipuPricing(0.2, 1.1, 0.03),
|
|
476
|
-
source:
|
|
477
|
-
sourceURL:
|
|
502
|
+
source: "official-doc",
|
|
503
|
+
sourceURL: "https://docs.z.ai/guides/overview/pricing",
|
|
478
504
|
},
|
|
479
505
|
{
|
|
480
|
-
provider:
|
|
481
|
-
model:
|
|
506
|
+
provider: "zhipu",
|
|
507
|
+
model: "glm-4.5v",
|
|
482
508
|
rates: zhipuPricing(0.6, 1.8, 0.11),
|
|
483
|
-
source:
|
|
484
|
-
sourceURL:
|
|
509
|
+
source: "official-doc",
|
|
510
|
+
sourceURL: "https://docs.z.ai/guides/overview/pricing",
|
|
485
511
|
},
|
|
486
512
|
{
|
|
487
|
-
provider:
|
|
488
|
-
model:
|
|
513
|
+
provider: "moonshotai",
|
|
514
|
+
model: "kimi-k2.5",
|
|
489
515
|
rates: moonshotPricing(0.6, 3, 0.1),
|
|
490
|
-
source:
|
|
491
|
-
sourceURL:
|
|
516
|
+
source: "official-doc",
|
|
517
|
+
sourceURL: "https://platform.moonshot.ai/docs/pricing/chat",
|
|
492
518
|
},
|
|
493
519
|
{
|
|
494
|
-
provider:
|
|
495
|
-
model:
|
|
520
|
+
provider: "moonshotai",
|
|
521
|
+
model: "kimi-k2-thinking",
|
|
496
522
|
rates: moonshotPricing(0.6, 2.5, 0.15),
|
|
497
|
-
source:
|
|
498
|
-
sourceURL:
|
|
523
|
+
source: "official-doc",
|
|
524
|
+
sourceURL: "https://platform.moonshot.ai/docs/pricing/chat",
|
|
499
525
|
},
|
|
500
526
|
{
|
|
501
|
-
provider:
|
|
502
|
-
model:
|
|
527
|
+
provider: "moonshotai",
|
|
528
|
+
model: "kimi-k2-0711-preview",
|
|
503
529
|
rates: moonshotPricing(0.6, 2.5, 0.15),
|
|
504
|
-
source:
|
|
505
|
-
sourceURL:
|
|
530
|
+
source: "official-doc",
|
|
531
|
+
sourceURL: "https://platform.moonshot.ai/docs/pricing/chat",
|
|
506
532
|
},
|
|
507
533
|
{
|
|
508
|
-
provider:
|
|
509
|
-
model:
|
|
534
|
+
provider: "moonshotai",
|
|
535
|
+
model: "kimi-k2-0905-preview",
|
|
510
536
|
rates: moonshotPricing(0.6, 2.5, 0.15),
|
|
511
|
-
source:
|
|
512
|
-
sourceURL:
|
|
537
|
+
source: "official-doc",
|
|
538
|
+
sourceURL: "https://platform.moonshot.ai/docs/pricing/chat",
|
|
513
539
|
},
|
|
514
540
|
{
|
|
515
|
-
provider:
|
|
516
|
-
model:
|
|
541
|
+
provider: "moonshotai",
|
|
542
|
+
model: "kimi-k2-turbo-preview",
|
|
517
543
|
rates: moonshotPricing(2.4, 10, 0.6),
|
|
518
|
-
source:
|
|
519
|
-
sourceURL:
|
|
544
|
+
source: "official-doc",
|
|
545
|
+
sourceURL: "https://platform.moonshot.ai/docs/pricing/chat",
|
|
520
546
|
},
|
|
521
547
|
{
|
|
522
|
-
provider:
|
|
523
|
-
model:
|
|
548
|
+
provider: "moonshotai",
|
|
549
|
+
model: "kimi-k2-thinking-turbo",
|
|
524
550
|
rates: moonshotPricing(1.15, 8, 0.15),
|
|
525
|
-
source:
|
|
526
|
-
sourceURL:
|
|
551
|
+
source: "official-doc",
|
|
552
|
+
sourceURL: "https://platform.moonshot.ai/docs/pricing/chat",
|
|
527
553
|
},
|
|
528
554
|
{
|
|
529
|
-
provider:
|
|
530
|
-
model:
|
|
555
|
+
provider: "minimax",
|
|
556
|
+
model: "MiniMax-M2.7",
|
|
531
557
|
// OpenCode sources provider pricing from models.dev. The bundled MiniMax
|
|
532
558
|
// fallback mirrors those USD-denominated entries rather than the CN RMB
|
|
533
559
|
// docs so API-equivalent cost stays on the same currency basis as the rest
|
|
534
560
|
// of the sidebar/report output.
|
|
535
561
|
rates: minimaxPricing(0.3, 1.2, 0.06, 0.375),
|
|
536
|
-
source:
|
|
537
|
-
sourceURL:
|
|
562
|
+
source: "runtime",
|
|
563
|
+
sourceURL: "https://github.com/anomalyco/models.dev/blob/dev/providers/minimax/models/MiniMax-M2.7.toml",
|
|
538
564
|
},
|
|
539
565
|
{
|
|
540
|
-
provider:
|
|
541
|
-
model:
|
|
566
|
+
provider: "minimax",
|
|
567
|
+
model: "MiniMax-M2.7-highspeed",
|
|
542
568
|
rates: minimaxPricing(0.6, 2.4, 0.06, 0.375),
|
|
543
|
-
source:
|
|
544
|
-
sourceURL:
|
|
569
|
+
source: "runtime",
|
|
570
|
+
sourceURL: "https://github.com/anomalyco/models.dev/blob/dev/providers/minimax/models/MiniMax-M2.7-highspeed.toml",
|
|
545
571
|
},
|
|
546
572
|
{
|
|
547
|
-
provider:
|
|
548
|
-
model:
|
|
573
|
+
provider: "minimax",
|
|
574
|
+
model: "MiniMax-M2.5",
|
|
549
575
|
rates: minimaxPricing(0.3, 1.2, 0.03, 0.375),
|
|
550
|
-
source:
|
|
551
|
-
sourceURL:
|
|
576
|
+
source: "runtime",
|
|
577
|
+
sourceURL: "https://github.com/anomalyco/models.dev/blob/dev/providers/minimax/models/MiniMax-M2.5.toml",
|
|
552
578
|
},
|
|
553
579
|
{
|
|
554
|
-
provider:
|
|
555
|
-
model:
|
|
580
|
+
provider: "minimax",
|
|
581
|
+
model: "MiniMax-M2.5-highspeed",
|
|
556
582
|
rates: minimaxPricing(0.6, 2.4, 0.06, 0.375),
|
|
557
|
-
source:
|
|
558
|
-
sourceURL:
|
|
583
|
+
source: "runtime",
|
|
584
|
+
sourceURL: "https://github.com/anomalyco/models.dev/blob/dev/providers/minimax/models/MiniMax-M2.5-highspeed.toml",
|
|
559
585
|
},
|
|
560
586
|
{
|
|
561
|
-
provider:
|
|
562
|
-
model:
|
|
587
|
+
provider: "minimax",
|
|
588
|
+
model: "MiniMax-M2.1",
|
|
563
589
|
rates: minimaxPricing(0.3, 1.2, 0.03, 0.375),
|
|
564
|
-
source:
|
|
565
|
-
sourceURL:
|
|
590
|
+
source: "runtime",
|
|
591
|
+
sourceURL: "https://github.com/anomalyco/models.dev/blob/dev/providers/minimax/models/MiniMax-M2.1.toml",
|
|
566
592
|
},
|
|
567
593
|
{
|
|
568
|
-
provider:
|
|
569
|
-
model:
|
|
594
|
+
provider: "minimax",
|
|
595
|
+
model: "MiniMax-M2",
|
|
570
596
|
rates: minimaxPricing(0.3, 1.2, 0, 0),
|
|
571
|
-
source:
|
|
572
|
-
sourceURL:
|
|
597
|
+
source: "runtime",
|
|
598
|
+
sourceURL: "https://github.com/anomalyco/models.dev/blob/dev/providers/minimax/models/MiniMax-M2.toml",
|
|
573
599
|
},
|
|
574
600
|
];
|
|
575
601
|
export function modelCostKey(providerID, modelID) {
|
|
@@ -582,13 +608,13 @@ export function modelCostLookupKeys(providerID, modelID) {
|
|
|
582
608
|
if (!keys.includes(key))
|
|
583
609
|
keys.push(key);
|
|
584
610
|
};
|
|
585
|
-
const modelIDsFor = (options) => canonicalProviderID ===
|
|
611
|
+
const modelIDsFor = (options) => canonicalProviderID === "anthropic"
|
|
586
612
|
? anthropicModelAliases(modelID)
|
|
587
|
-
: canonicalProviderID ===
|
|
613
|
+
: canonicalProviderID === "zhipu"
|
|
588
614
|
? zhipuModelAliases(modelID)
|
|
589
|
-
: canonicalProviderID ===
|
|
615
|
+
: canonicalProviderID === "moonshotai"
|
|
590
616
|
? moonshotModelAliases(modelID, options)
|
|
591
|
-
: canonicalProviderID ===
|
|
617
|
+
: canonicalProviderID === "minimax"
|
|
592
618
|
? minimaxModelAliases(modelID)
|
|
593
619
|
: [modelID];
|
|
594
620
|
for (const candidateModelID of modelIDsFor()) {
|
|
@@ -608,6 +634,190 @@ export function modelCostLookupKeys(providerID, modelID) {
|
|
|
608
634
|
}
|
|
609
635
|
return keys;
|
|
610
636
|
}
|
|
637
|
+
function setLookupRates(map, providerID, modelID, rates, modelKey) {
|
|
638
|
+
const providerPrefix = `${providerID}:`;
|
|
639
|
+
for (const key of modelCostLookupKeys(providerID, modelID)) {
|
|
640
|
+
if (!key.startsWith(providerPrefix))
|
|
641
|
+
continue;
|
|
642
|
+
map[key] = rates;
|
|
643
|
+
}
|
|
644
|
+
if (modelKey && modelKey !== modelID) {
|
|
645
|
+
for (const key of modelCostLookupKeys(providerID, modelKey)) {
|
|
646
|
+
if (!key.startsWith(providerPrefix))
|
|
647
|
+
continue;
|
|
648
|
+
map[key] = rates;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
function multiplyRates(rates, multiplier) {
|
|
653
|
+
return {
|
|
654
|
+
input: rates.input * multiplier,
|
|
655
|
+
output: rates.output * multiplier,
|
|
656
|
+
cacheRead: rates.cacheRead * multiplier,
|
|
657
|
+
cacheWrite: rates.cacheWrite * multiplier,
|
|
658
|
+
// Fast/tier-derived pricing intentionally does not inherit the base model's
|
|
659
|
+
// long-context tier. Those variants need their own explicit long-context
|
|
660
|
+
// schedule instead of multiplying a different billing rule implicitly.
|
|
661
|
+
contextOver200k: undefined,
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
function apiBaseModelID(model) {
|
|
665
|
+
return isRecord(model.api) && typeof model.api.id === "string"
|
|
666
|
+
? model.api.id
|
|
667
|
+
: undefined;
|
|
668
|
+
}
|
|
669
|
+
function headerValue(model, key) {
|
|
670
|
+
if (!isRecord(model.headers))
|
|
671
|
+
return undefined;
|
|
672
|
+
const exact = model.headers[key];
|
|
673
|
+
if (typeof exact === "string")
|
|
674
|
+
return exact;
|
|
675
|
+
const lowerKey = key.toLowerCase();
|
|
676
|
+
for (const [headerKey, value] of Object.entries(model.headers)) {
|
|
677
|
+
if (headerKey.toLowerCase() !== lowerKey)
|
|
678
|
+
continue;
|
|
679
|
+
if (typeof value === "string")
|
|
680
|
+
return value;
|
|
681
|
+
}
|
|
682
|
+
return undefined;
|
|
683
|
+
}
|
|
684
|
+
function heuristicFastBaseModelID(model) {
|
|
685
|
+
const canonicalProviderID = canonicalPricingProviderID(model.providerID);
|
|
686
|
+
const candidates = [model.modelID, model.modelKey].filter((value) => Boolean(value));
|
|
687
|
+
for (const candidate of candidates) {
|
|
688
|
+
if (canonicalProviderID === "openai") {
|
|
689
|
+
const base = candidate.replace(/(?:[-/:](?:fast|priority))$/i, "");
|
|
690
|
+
if (base !== candidate)
|
|
691
|
+
return base;
|
|
692
|
+
}
|
|
693
|
+
if (canonicalProviderID === "anthropic") {
|
|
694
|
+
const base = candidate.replace(/-fast$/i, "");
|
|
695
|
+
if (base !== candidate)
|
|
696
|
+
return base;
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
return undefined;
|
|
700
|
+
}
|
|
701
|
+
function resolveSourceBaseRates(explicitRates, providerID, modelID) {
|
|
702
|
+
return modelCostLookupKeys(providerID, modelID)
|
|
703
|
+
.map((key) => explicitRates[key])
|
|
704
|
+
.find(Boolean);
|
|
705
|
+
}
|
|
706
|
+
function isStructuredOpenAIFastModel(model) {
|
|
707
|
+
return (canonicalPricingProviderID(model.providerID) === "openai" &&
|
|
708
|
+
isRecord(model.options) &&
|
|
709
|
+
model.options.serviceTier === "priority");
|
|
710
|
+
}
|
|
711
|
+
function isStructuredAnthropicFastModel(model) {
|
|
712
|
+
return (canonicalPricingProviderID(model.providerID) === "anthropic" &&
|
|
713
|
+
((isRecord(model.options) && model.options.speed === "fast") ||
|
|
714
|
+
/\bfast\b/i.test(headerValue(model, "anthropic-beta") || "")));
|
|
715
|
+
}
|
|
716
|
+
export function derivedTierBaseModelID(model) {
|
|
717
|
+
const canonicalProviderID = canonicalPricingProviderID(model.providerID);
|
|
718
|
+
if (canonicalProviderID === "openai") {
|
|
719
|
+
return ((isStructuredOpenAIFastModel(model)
|
|
720
|
+
? apiBaseModelID(model)
|
|
721
|
+
: undefined) || heuristicFastBaseModelID(model));
|
|
722
|
+
}
|
|
723
|
+
if (canonicalProviderID === "anthropic") {
|
|
724
|
+
return ((isStructuredAnthropicFastModel(model)
|
|
725
|
+
? apiBaseModelID(model)
|
|
726
|
+
: undefined) || heuristicFastBaseModelID(model));
|
|
727
|
+
}
|
|
728
|
+
return undefined;
|
|
729
|
+
}
|
|
730
|
+
function derivedTierRatesForModel(model, explicitRates) {
|
|
731
|
+
const canonicalProviderID = canonicalPricingProviderID(model.providerID);
|
|
732
|
+
const baseModelID = derivedTierBaseModelID(model);
|
|
733
|
+
if (!baseModelID)
|
|
734
|
+
return undefined;
|
|
735
|
+
const baseRates = resolveSourceBaseRates(explicitRates, model.providerID, baseModelID);
|
|
736
|
+
if (!baseRates)
|
|
737
|
+
return undefined;
|
|
738
|
+
if (canonicalProviderID === "openai") {
|
|
739
|
+
return multiplyRates(baseRates, OPENAI_FAST_COST_MULTIPLIER);
|
|
740
|
+
}
|
|
741
|
+
if (canonicalProviderID === "anthropic") {
|
|
742
|
+
return multiplyRates(baseRates, ANTHROPIC_FAST_COST_MULTIPLIER);
|
|
743
|
+
}
|
|
744
|
+
return undefined;
|
|
745
|
+
}
|
|
746
|
+
export function explicitModelCostMap(models) {
|
|
747
|
+
const explicitRates = {};
|
|
748
|
+
for (const model of models) {
|
|
749
|
+
const rates = parseModelCostRates(model.cost);
|
|
750
|
+
if (!rates)
|
|
751
|
+
continue;
|
|
752
|
+
setLookupRates(explicitRates, model.providerID, model.modelID, rates, model.modelKey);
|
|
753
|
+
}
|
|
754
|
+
return explicitRates;
|
|
755
|
+
}
|
|
756
|
+
function hasExplicitModelCost(model, explicitRates) {
|
|
757
|
+
return [model.modelID, model.modelKey]
|
|
758
|
+
.filter((value) => Boolean(value))
|
|
759
|
+
.some((candidate) => modelCostLookupKeys(model.providerID, candidate).some((key) => Boolean(explicitRates[key])));
|
|
760
|
+
}
|
|
761
|
+
export function applyDerivedTierRatesFromSource(baseMap, metadataModels, sourceRates, options) {
|
|
762
|
+
const nextMap = { ...baseMap };
|
|
763
|
+
for (const model of metadataModels) {
|
|
764
|
+
if (options?.skipExplicitRates &&
|
|
765
|
+
hasExplicitModelCost(model, options.skipExplicitRates)) {
|
|
766
|
+
continue;
|
|
767
|
+
}
|
|
768
|
+
const derivedRates = derivedTierRatesForModel(model, sourceRates);
|
|
769
|
+
if (!derivedRates)
|
|
770
|
+
continue;
|
|
771
|
+
setLookupRates(nextMap, model.providerID, model.modelID, derivedRates, model.modelKey);
|
|
772
|
+
}
|
|
773
|
+
return nextMap;
|
|
774
|
+
}
|
|
775
|
+
export function applyExplicitRatesFromSource(baseMap, metadataModels, sourceRates, options) {
|
|
776
|
+
const nextMap = { ...baseMap };
|
|
777
|
+
for (const model of metadataModels) {
|
|
778
|
+
if (options?.skipExplicitRates &&
|
|
779
|
+
hasExplicitModelCost(model, options.skipExplicitRates)) {
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
const explicitRates = [model.modelID, model.modelKey]
|
|
783
|
+
.filter((value) => Boolean(value))
|
|
784
|
+
.flatMap((candidate) => modelCostLookupKeys(model.providerID, candidate))
|
|
785
|
+
.map((key) => sourceRates[key])
|
|
786
|
+
.find(Boolean);
|
|
787
|
+
if (!explicitRates)
|
|
788
|
+
continue;
|
|
789
|
+
setLookupRates(nextMap, model.providerID, model.modelID, explicitRates, model.modelKey);
|
|
790
|
+
}
|
|
791
|
+
return nextMap;
|
|
792
|
+
}
|
|
793
|
+
export function mergeModelCostSource(baseMap, models) {
|
|
794
|
+
const nextMap = { ...baseMap };
|
|
795
|
+
const explicitRates = explicitModelCostMap(models);
|
|
796
|
+
const explicitEntries = [];
|
|
797
|
+
for (const model of models) {
|
|
798
|
+
const rates = modelCostLookupKeys(model.providerID, model.modelID)
|
|
799
|
+
.map((key) => explicitRates[key])
|
|
800
|
+
.find(Boolean);
|
|
801
|
+
if (!rates)
|
|
802
|
+
continue;
|
|
803
|
+
explicitEntries.push({
|
|
804
|
+
providerID: model.providerID,
|
|
805
|
+
modelID: model.modelID,
|
|
806
|
+
modelKey: model.modelKey,
|
|
807
|
+
rates,
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
for (const model of models) {
|
|
811
|
+
const derivedRates = derivedTierRatesForModel(model, explicitRates);
|
|
812
|
+
if (!derivedRates)
|
|
813
|
+
continue;
|
|
814
|
+
setLookupRates(nextMap, model.providerID, model.modelID, derivedRates, model.modelKey);
|
|
815
|
+
}
|
|
816
|
+
for (const entry of explicitEntries) {
|
|
817
|
+
setLookupRates(nextMap, entry.providerID, entry.modelID, entry.rates, entry.modelKey);
|
|
818
|
+
}
|
|
819
|
+
return nextMap;
|
|
820
|
+
}
|
|
611
821
|
function createBundledModelCostMap() {
|
|
612
822
|
const map = {};
|
|
613
823
|
for (const entry of BUNDLED_CANONICAL_PRICE_ENTRIES) {
|
|
@@ -632,18 +842,48 @@ export function getBundledCanonicalPriceEntries() {
|
|
|
632
842
|
},
|
|
633
843
|
}));
|
|
634
844
|
}
|
|
845
|
+
function normalizeRateToPerMillion(input) {
|
|
846
|
+
if (!Number.isFinite(input) || input <= 0)
|
|
847
|
+
return 0;
|
|
848
|
+
return input > MODEL_COST_RATE_UNIT_THRESHOLD
|
|
849
|
+
? input
|
|
850
|
+
: input * MODEL_COST_STANDARD_UNIT;
|
|
851
|
+
}
|
|
852
|
+
export function normalizeModelCostRates(rates) {
|
|
853
|
+
return {
|
|
854
|
+
input: normalizeRateToPerMillion(rates.input),
|
|
855
|
+
output: normalizeRateToPerMillion(rates.output),
|
|
856
|
+
cacheRead: normalizeRateToPerMillion(rates.cacheRead),
|
|
857
|
+
cacheWrite: normalizeRateToPerMillion(rates.cacheWrite),
|
|
858
|
+
contextOver200k: rates.contextOver200k
|
|
859
|
+
? {
|
|
860
|
+
input: normalizeRateToPerMillion(rates.contextOver200k.input),
|
|
861
|
+
output: normalizeRateToPerMillion(rates.contextOver200k.output),
|
|
862
|
+
cacheRead: normalizeRateToPerMillion(rates.contextOver200k.cacheRead),
|
|
863
|
+
cacheWrite: normalizeRateToPerMillion(rates.contextOver200k.cacheWrite),
|
|
864
|
+
}
|
|
865
|
+
: undefined,
|
|
866
|
+
};
|
|
867
|
+
}
|
|
635
868
|
export function parseModelCostRates(value) {
|
|
636
869
|
if (!isRecord(value))
|
|
637
870
|
return undefined;
|
|
638
871
|
const readRate = (input) => {
|
|
639
|
-
if (typeof input ===
|
|
640
|
-
return input;
|
|
641
|
-
if (typeof input ===
|
|
872
|
+
if (typeof input === "number")
|
|
873
|
+
return normalizeRateToPerMillion(input);
|
|
874
|
+
if (typeof input === "string") {
|
|
642
875
|
const parsed = Number(input);
|
|
643
|
-
return Number.isFinite(parsed) ? parsed : 0;
|
|
876
|
+
return Number.isFinite(parsed) ? normalizeRateToPerMillion(parsed) : 0;
|
|
644
877
|
}
|
|
645
878
|
if (isRecord(input)) {
|
|
646
|
-
|
|
879
|
+
const perMillion = asNumber(input.per_1m) ?? asNumber(input.per1m) ?? undefined;
|
|
880
|
+
if (perMillion !== undefined)
|
|
881
|
+
return Math.max(0, perMillion);
|
|
882
|
+
const perToken = asNumber(input.per_token) ?? asNumber(input.perToken) ?? undefined;
|
|
883
|
+
if (perToken !== undefined) {
|
|
884
|
+
return Math.max(0, perToken) * MODEL_COST_STANDARD_UNIT;
|
|
885
|
+
}
|
|
886
|
+
return normalizeRateToPerMillion(asNumber(input.usd, asNumber(input.value, 0)));
|
|
647
887
|
}
|
|
648
888
|
return 0;
|
|
649
889
|
};
|
|
@@ -676,30 +916,20 @@ export function parseModelCostRates(value) {
|
|
|
676
916
|
contextOver200k: hasContextTier ? contextOver200k : undefined,
|
|
677
917
|
};
|
|
678
918
|
}
|
|
679
|
-
const MODEL_COST_DIVISOR_PER_TOKEN = 1;
|
|
680
|
-
const MODEL_COST_DIVISOR_PER_MILLION = 1_000_000;
|
|
681
|
-
export function guessModelCostDivisor(rates) {
|
|
682
|
-
// OpenCode provider pricing units can differ:
|
|
683
|
-
// - some providers expose USD per token (e.g. 0.0000025)
|
|
684
|
-
// - others expose USD per 1M tokens (e.g. 2.5)
|
|
685
|
-
// Heuristic: treat values > 0.001 as "per 1M".
|
|
686
|
-
const maxRate = Math.max(rates.input, rates.output, rates.cacheRead, rates.cacheWrite);
|
|
687
|
-
return maxRate > 0.001
|
|
688
|
-
? MODEL_COST_DIVISOR_PER_MILLION
|
|
689
|
-
: MODEL_COST_DIVISOR_PER_TOKEN;
|
|
690
|
-
}
|
|
691
919
|
export function cacheCoverageModeFromRates(rates) {
|
|
692
920
|
if (!rates)
|
|
693
|
-
return
|
|
921
|
+
return "none";
|
|
694
922
|
if (rates.cacheWrite > 0)
|
|
695
|
-
return
|
|
923
|
+
return "read-write";
|
|
696
924
|
if (rates.cacheRead > 0)
|
|
697
|
-
return
|
|
698
|
-
return
|
|
925
|
+
return "read-only";
|
|
926
|
+
return "none";
|
|
699
927
|
}
|
|
700
928
|
export function calcEquivalentApiCostForMessage(message, rates) {
|
|
701
|
-
const effectiveRates =
|
|
702
|
-
|
|
929
|
+
const effectiveRates =
|
|
930
|
+
// Long-context tiering intentionally keys off live input tokens only.
|
|
931
|
+
// Cached read tokens do not promote the request into the >200k tier.
|
|
932
|
+
message.tokens.input > 200_000 && rates.contextOver200k
|
|
703
933
|
? rates.contextOver200k
|
|
704
934
|
: rates;
|
|
705
935
|
// For providers that expose reasoning tokens separately, they are still
|
|
@@ -710,7 +940,6 @@ export function calcEquivalentApiCostForMessage(message, rates) {
|
|
|
710
940
|
billedOutput * effectiveRates.output +
|
|
711
941
|
message.tokens.cache.read * effectiveRates.cacheRead +
|
|
712
942
|
message.tokens.cache.write * effectiveRates.cacheWrite;
|
|
713
|
-
const
|
|
714
|
-
const normalized = rawCost / divisor;
|
|
943
|
+
const normalized = rawCost / MODEL_COST_STANDARD_UNIT;
|
|
715
944
|
return Number.isFinite(normalized) && normalized > 0 ? normalized : 0;
|
|
716
945
|
}
|