@ipv9/tokentracker-cli 0.39.55 → 0.39.57
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/dashboard/dist/assets/{Card-CtvDXZw0.js → Card-ci17PdAV.js} +1 -1
- package/dashboard/dist/assets/DashboardPage-7tVtPxb8.js +60 -0
- package/dashboard/dist/assets/{IpCheckPage-Bor8g4lv.js → IpCheckPage-DNA0Q23M.js} +1 -1
- package/dashboard/dist/assets/{LimitsPage-CIac9eQA.js → LimitsPage-BpjYvnWF.js} +1 -1
- package/dashboard/dist/assets/{LocalOnlyNotice-Dr74ws0K.js → LocalOnlyNotice-B5JmBabh.js} +1 -1
- package/dashboard/dist/assets/{PopoverPopup-DhIs9KLg.js → PopoverPopup-CcfJZMpE.js} +1 -1
- package/dashboard/dist/assets/{Select-B-hdqLRw.js → Select-DkdVUiwm.js} +1 -1
- package/dashboard/dist/assets/{SettingsPage-CGmMNqQ4.js → SettingsPage-B3KR1Y9S.js} +1 -1
- package/dashboard/dist/assets/{SkillsPage-D70T7nYt.js → SkillsPage-ced5E6ai.js} +1 -1
- package/dashboard/dist/assets/{WrappedPage-CXCjVq_h.js → WrappedPage-BMgJxQLZ.js} +1 -1
- package/dashboard/dist/assets/{format-Cf1THGDG.js → format-wJPB8ygX.js} +1 -1
- package/dashboard/dist/assets/{limitDisplay-HHiXLwsI.js → limitDisplay-BWIfB8aR.js} +1 -1
- package/dashboard/dist/assets/{main-B5OWbrt1.js → main-DFczPkAN.js} +3 -3
- package/dashboard/dist/assets/{mock-data-D7OvhHFq.js → mock-data-BQNbqt40.js} +1 -1
- package/dashboard/dist/assets/{use-limits-display-prefs-CJObZZbn.js → use-limits-display-prefs-fr-_DlHb.js} +1 -1
- package/dashboard/dist/assets/{useCurrency-AuPUpwUc.js → useCurrency-CRUry3mm.js} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/package.json +1 -1
- package/src/cli.js +1 -1
- package/src/commands/sync.js +5 -0
- package/src/lib/pricing/index.js +88 -2
- package/src/lib/pricing/seed-snapshot.json +1 -1
- package/dashboard/dist/assets/DashboardPage-Y1wWgbMc.js +0 -60
package/src/lib/pricing/index.js
CHANGED
|
@@ -49,13 +49,85 @@ const RELOAD_COOLDOWN_MS = 5 * 60 * 1000;
|
|
|
49
49
|
// curated fuzzy rule rather than an exact id, so the price is plausible but may
|
|
50
50
|
// belong to a different model. Worth surfacing — a wrong price never looks
|
|
51
51
|
// wrong, unlike a $0 one.
|
|
52
|
-
const FUZZY_SOURCES = new Set([
|
|
52
|
+
const FUZZY_SOURCES = new Set([
|
|
53
|
+
"curated:fuzzy",
|
|
54
|
+
"litellm:fuzzy",
|
|
55
|
+
"litellm:prefix-strip",
|
|
56
|
+
"routed-estimated",
|
|
57
|
+
]);
|
|
53
58
|
|
|
54
59
|
// Placeholder ids that stand in for "this row has no model", so they resolve to
|
|
55
60
|
// the "unattributed" tier instead of being looked up and recorded as a miss.
|
|
56
61
|
// Closed set on purpose: a real model id must never be silently un-priced here.
|
|
57
62
|
const UNATTRIBUTED_MODEL_IDS = new Set(["unknown"]);
|
|
58
63
|
|
|
64
|
+
// These ids are logical routes that deliberately select a physical child model
|
|
65
|
+
// per request. Their names can include one tier name, but that is not evidence
|
|
66
|
+
// that every request used that tier. Do not run them through fuzzy pricing: a
|
|
67
|
+
// route-level estimate would silently charge every request at the wrong child
|
|
68
|
+
// rate. They stay visible as unpriced until per-request resolved-model or
|
|
69
|
+
// authoritative cost data is persisted.
|
|
70
|
+
const UNRESOLVED_LOGICAL_ROUTE_IDS = new Set([
|
|
71
|
+
"claude-auto-pilot-fable-v1-canary",
|
|
72
|
+
]);
|
|
73
|
+
const UNPRICED_TIERS = new Set(["miss", "routed-unresolved"]);
|
|
74
|
+
|
|
75
|
+
// Owner-approved route mix estimates. The child-model ratios are a pricing
|
|
76
|
+
// policy, not routing telemetry: callers must surface `routed-estimated` as a
|
|
77
|
+
// non-exact price. Any configured child whose current price cannot be resolved
|
|
78
|
+
// makes the route unpriced rather than silently using a partial blend.
|
|
79
|
+
const ROUTED_ESTIMATE_POLICIES = new Map([
|
|
80
|
+
["claude-auto-pilot-fable-v1-canary", [
|
|
81
|
+
{ model: "anthropic/claude-sonnet-5", pricingModel: "claude-sonnet-5", weight: 0.6 },
|
|
82
|
+
{ model: "anthropic/claude-opus-5", pricingModel: "claude-opus-5", weight: 0.4 },
|
|
83
|
+
]],
|
|
84
|
+
["gpt-5.6-auto-pilot", [
|
|
85
|
+
{ model: "gpt-5.6-terra", weight: 0.6 },
|
|
86
|
+
{ model: "gpt-5.6-sol", weight: 0.4 },
|
|
87
|
+
]],
|
|
88
|
+
["gpt-5.6-auto-pilot-045-canary", [
|
|
89
|
+
{ model: "gpt-5.6-terra", weight: 0.6 },
|
|
90
|
+
{ model: "gpt-5.6-sol", weight: 0.4 },
|
|
91
|
+
]],
|
|
92
|
+
["gpt-5.6-auto-pilot-055-v2", [
|
|
93
|
+
{ model: "gpt-5.6-terra", weight: 0.6 },
|
|
94
|
+
{ model: "gpt-5.6-sol", weight: 0.4 },
|
|
95
|
+
]],
|
|
96
|
+
["gpt-5.6-auto-pilot-056-claude-reasoning-canary", [
|
|
97
|
+
{ model: "gpt-5.6-terra", weight: 0.6 },
|
|
98
|
+
{ model: "gpt-5.6-sol", weight: 0.4 },
|
|
99
|
+
]],
|
|
100
|
+
]);
|
|
101
|
+
const ROUTE_CHILD_EXACT_TIERS = new Set([
|
|
102
|
+
"curated:exact",
|
|
103
|
+
"curated:exact-dot",
|
|
104
|
+
"litellm:exact",
|
|
105
|
+
"litellm:exact-dot",
|
|
106
|
+
]);
|
|
107
|
+
|
|
108
|
+
function isPotentialGptAutoRoute(model) {
|
|
109
|
+
return /^gpt-5\.6-auto/i.test(String(model || "").trim());
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function resolveRoutedEstimate(model, lookupSource) {
|
|
113
|
+
const policy = ROUTED_ESTIMATE_POLICIES.get(String(model || "").trim());
|
|
114
|
+
if (!policy) return null;
|
|
115
|
+
const mixed = { input: 0, output: 0, cache_read: 0, cache_write: 0 };
|
|
116
|
+
for (const child of policy) {
|
|
117
|
+
const result = lookupPricing(child.pricingModel || child.model, {
|
|
118
|
+
curated: curatedOverrides,
|
|
119
|
+
litellm: state.litellmPerMillionMap,
|
|
120
|
+
source: lookupSource,
|
|
121
|
+
});
|
|
122
|
+
if (!result.hit || !result.value || !ROUTE_CHILD_EXACT_TIERS.has(result.source)) return null;
|
|
123
|
+
for (const field of Object.keys(mixed)) {
|
|
124
|
+
if (!Number.isFinite(result.value[field])) return null;
|
|
125
|
+
mixed[field] += child.weight * result.value[field];
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return mixed;
|
|
129
|
+
}
|
|
130
|
+
|
|
59
131
|
// `last_refresh_error` is served over HTTP to the dashboard, so it is built
|
|
60
132
|
// from CLOSED sets, never from an arbitrary value. A previous version accepted
|
|
61
133
|
// anything symbol-shaped, which a QA pass broke immediately: a 32-character
|
|
@@ -235,6 +307,20 @@ function getModelPricingMeta(model, opts = {}) {
|
|
|
235
307
|
const lookupSource = resolveLookupSource(opts);
|
|
236
308
|
const cacheKey = lookupSource ? `${lookupSource}\0${model}` : model;
|
|
237
309
|
|
|
310
|
+
const routedEstimate = resolveRoutedEstimate(model, lookupSource);
|
|
311
|
+
if (routedEstimate) {
|
|
312
|
+
state.tiers.set(cacheKey, { model, source: lookupSource, tier: "routed-estimated" });
|
|
313
|
+
return { pricing: routedEstimate, tier: "routed-estimated" };
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (
|
|
317
|
+
UNRESOLVED_LOGICAL_ROUTE_IDS.has(String(model || "").trim())
|
|
318
|
+
|| isPotentialGptAutoRoute(model)
|
|
319
|
+
) {
|
|
320
|
+
state.tiers.set(cacheKey, { model, source: lookupSource, tier: "routed-unresolved" });
|
|
321
|
+
return { pricing: ZERO_PRICING, tier: "routed-unresolved" };
|
|
322
|
+
}
|
|
323
|
+
|
|
238
324
|
if (state.negativeCache.has(cacheKey)) {
|
|
239
325
|
// Still unknown as of the current snapshot. If that snapshot has aged out,
|
|
240
326
|
// a new model may have appeared upstream — refresh for the next caller.
|
|
@@ -287,7 +373,7 @@ function getPricingDiagnostics() {
|
|
|
287
373
|
const unpriced = new Set();
|
|
288
374
|
const fuzzy = [];
|
|
289
375
|
for (const entry of state.tiers.values()) {
|
|
290
|
-
if (entry.tier
|
|
376
|
+
if (UNPRICED_TIERS.has(entry.tier)) unpriced.add(entry.model);
|
|
291
377
|
else if (FUZZY_SOURCES.has(entry.tier)) fuzzy.push({ model: entry.model, tier: entry.tier });
|
|
292
378
|
}
|
|
293
379
|
return {
|