agent-nuvira 3.5.2 → 3.5.6
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 +66 -0
- package/dist/cli/chat.d.ts.map +1 -1
- package/dist/cli/chat.js +72 -19
- package/dist/cli/chat.js.map +1 -1
- package/dist/cli/config.d.ts.map +1 -1
- package/dist/cli/config.js +17 -3
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/execute.d.ts.map +1 -1
- package/dist/cli/execute.js +63 -22
- package/dist/cli/execute.js.map +1 -1
- package/dist/cli/failover-runner.js +4 -4
- package/dist/cli/failover-runner.js.map +1 -1
- package/dist/cli/gateway.js +13 -13
- package/dist/cli/gateway.js.map +1 -1
- package/dist/cli/loop-executor.d.ts +22 -0
- package/dist/cli/loop-executor.d.ts.map +1 -1
- package/dist/cli/loop-executor.js +56 -12
- package/dist/cli/loop-executor.js.map +1 -1
- package/dist/cli/stats.d.ts +62 -4
- package/dist/cli/stats.d.ts.map +1 -1
- package/dist/cli/stats.js +282 -5
- package/dist/cli/stats.js.map +1 -1
- package/dist/cli/ui/index.d.ts +2 -2
- package/dist/cli/ui/index.d.ts.map +1 -1
- package/dist/cli/ui/index.js +1 -1
- package/dist/cli/ui/index.js.map +1 -1
- package/dist/cli/ui/presenter.d.ts +43 -0
- package/dist/cli/ui/presenter.d.ts.map +1 -1
- package/dist/cli/ui/presenter.js +88 -1
- package/dist/cli/ui/presenter.js.map +1 -1
- package/dist/config/manager.d.ts +29 -1
- package/dist/config/manager.d.ts.map +1 -1
- package/dist/config/manager.js +178 -143
- package/dist/config/manager.js.map +1 -1
- package/dist/config/paths.d.ts +48 -1
- package/dist/config/paths.d.ts.map +1 -1
- package/dist/config/paths.js +59 -9
- package/dist/config/paths.js.map +1 -1
- package/dist/config/service-catalog.js +1 -1
- package/dist/config/service-catalog.js.map +1 -1
- package/dist/config/service-probe.js +1 -1
- package/dist/config/service-probe.js.map +1 -1
- package/dist/config/types.d.ts +22 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/gateway/adapters.js +8 -8
- package/dist/gateway/adapters.js.map +1 -1
- package/dist/gateway/registry.js +1 -1
- package/dist/gateway/registry.js.map +1 -1
- package/dist/learning/auto-router.d.ts +30 -4
- package/dist/learning/auto-router.d.ts.map +1 -1
- package/dist/learning/auto-router.js +54 -38
- package/dist/learning/auto-router.js.map +1 -1
- package/dist/learning/cost-classification.d.ts +188 -0
- package/dist/learning/cost-classification.d.ts.map +1 -0
- package/dist/learning/cost-classification.js +294 -0
- package/dist/learning/cost-classification.js.map +1 -0
- package/dist/learning/cost-ledger-maintenance.d.ts +122 -0
- package/dist/learning/cost-ledger-maintenance.d.ts.map +1 -0
- package/dist/learning/cost-ledger-maintenance.js +289 -0
- package/dist/learning/cost-ledger-maintenance.js.map +1 -0
- package/dist/learning/cost-reconcile.d.ts +160 -0
- package/dist/learning/cost-reconcile.d.ts.map +1 -0
- package/dist/learning/cost-reconcile.js +238 -0
- package/dist/learning/cost-reconcile.js.map +1 -0
- package/dist/learning/cost-tracker.d.ts +134 -13
- package/dist/learning/cost-tracker.d.ts.map +1 -1
- package/dist/learning/cost-tracker.js +190 -68
- package/dist/learning/cost-tracker.js.map +1 -1
- package/dist/learning/model-pricing.d.ts +240 -0
- package/dist/learning/model-pricing.d.ts.map +1 -0
- package/dist/learning/model-pricing.js +309 -0
- package/dist/learning/model-pricing.js.map +1 -0
- package/dist/learning/model-selection.d.ts +15 -0
- package/dist/learning/model-selection.d.ts.map +1 -1
- package/dist/learning/model-selection.js +22 -2
- package/dist/learning/model-selection.js.map +1 -1
- package/dist/learning/model-warmup.d.ts.map +1 -1
- package/dist/learning/model-warmup.js +4 -1
- package/dist/learning/model-warmup.js.map +1 -1
- package/dist/learning/session-store.d.ts +1 -5
- package/dist/learning/session-store.d.ts.map +1 -1
- package/dist/learning/session-store.js +10 -1
- package/dist/learning/session-store.js.map +1 -1
- package/dist/learning/step-checkpoint.d.ts +32 -3
- package/dist/learning/step-checkpoint.d.ts.map +1 -1
- package/dist/learning/step-checkpoint.js +33 -4
- package/dist/learning/step-checkpoint.js.map +1 -1
- package/dist/mcp/settings.d.ts +15 -3
- package/dist/mcp/settings.d.ts.map +1 -1
- package/dist/mcp/settings.js +30 -15
- package/dist/mcp/settings.js.map +1 -1
- package/dist/parity/drivers.d.ts.map +1 -1
- package/dist/parity/drivers.js +31 -5
- package/dist/parity/drivers.js.map +1 -1
- package/dist/tools/child-agent-runtime.d.ts.map +1 -1
- package/dist/tools/child-agent-runtime.js +13 -7
- package/dist/tools/child-agent-runtime.js.map +1 -1
- package/dist/tools/followup-utils.d.ts +36 -0
- package/dist/tools/followup-utils.d.ts.map +1 -1
- package/dist/tools/followup-utils.js +66 -0
- package/dist/tools/followup-utils.js.map +1 -1
- package/dist/tools/modality/browser.js +2 -2
- package/dist/tools/modality/browser.js.map +1 -1
- package/dist/tools/modality/image-gen.d.ts +1 -1
- package/dist/tools/modality/image-gen.js +1 -1
- package/dist/tools/modality/image-providers.js +1 -1
- package/dist/tools/modality/image-providers.js.map +1 -1
- package/dist/tools/modality/vision.d.ts +1 -1
- package/dist/tools/modality/vision.js +1 -1
- package/dist/tools/modality/voice.js +2 -2
- package/dist/tools/modality/voice.js.map +1 -1
- package/dist/tools/neutts-synth.d.ts +3 -3
- package/dist/tools/neutts-synth.js +3 -3
- package/dist/tools/neutts-synth.js.map +1 -1
- package/dist/tools/process-liveness.d.ts +50 -0
- package/dist/tools/process-liveness.d.ts.map +1 -1
- package/dist/tools/process-liveness.js +82 -9
- package/dist/tools/process-liveness.js.map +1 -1
- package/dist/tools/registry.d.ts +1 -1
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +27 -4
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/run-registry.d.ts.map +1 -1
- package/dist/tools/run-registry.js +14 -1
- package/dist/tools/run-registry.js.map +1 -1
- package/dist/tools/run-terminal.d.ts.map +1 -1
- package/dist/tools/run-terminal.js +18 -1
- package/dist/tools/run-terminal.js.map +1 -1
- package/dist/tools/tool-loop.d.ts.map +1 -1
- package/dist/tools/tool-loop.js +5 -3
- package/dist/tools/tool-loop.js.map +1 -1
- package/dist/tools/web-research.d.ts +1 -1
- package/dist/tools/web-research.js +3 -3
- package/dist/tools/web-research.js.map +1 -1
- package/dist/web-dashboard/chat-console.d.ts +56 -0
- package/dist/web-dashboard/chat-console.d.ts.map +1 -1
- package/dist/web-dashboard/chat-console.js +57 -1
- package/dist/web-dashboard/chat-console.js.map +1 -1
- package/dist/web-dashboard/server.d.ts.map +1 -1
- package/dist/web-dashboard/server.js +122 -2
- package/dist/web-dashboard/server.js.map +1 -1
- package/dist/web-dashboard/src/types.d.ts +63 -0
- package/dist/web-dashboard/src/types.d.ts.map +1 -1
- package/dist/web-dashboard/task-runner.d.ts +17 -1
- package/dist/web-dashboard/task-runner.d.ts.map +1 -1
- package/dist/web-dashboard/task-runner.js +84 -9
- package/dist/web-dashboard/task-runner.js.map +1 -1
- package/package.json +1 -1
- package/src/web-dashboard/public/assets/index-DeSsNU94.css +1 -0
- package/src/web-dashboard/public/assets/index-Do_O67Kv.js +236 -0
- package/src/web-dashboard/public/assets/index-Do_O67Kv.js.map +1 -0
- package/src/web-dashboard/public/index.html +2 -2
- package/src/web-dashboard/public/assets/index-DycGBWwy.js +0 -236
- package/src/web-dashboard/public/assets/index-DycGBWwy.js.map +0 -1
- package/src/web-dashboard/public/assets/index-aO3aG7ex.css +0 -1
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model pricing — the ONE price source of truth for what a call cost.
|
|
3
|
+
*
|
|
4
|
+
* THE DEFECT THIS REPLACES (measured on a real profile, 2026-10-15: a ledger of
|
|
5
|
+
* 9,405 calls reporting `Total Spent $7.21`). There were TWO price tables and
|
|
6
|
+
* neither knew what the account was actually billed:
|
|
7
|
+
*
|
|
8
|
+
* 1. `cost-tracker.ts` held a hardcoded five-entry `DEFAULT_PRICING` (groq,
|
|
9
|
+
* nim, gemini, openrouter, local) with a `$0.00010/1K` fallback for every
|
|
10
|
+
* OTHER provider, plus a three-entry `expensiveModels` hack. The ledger
|
|
11
|
+
* priced from it and the dashboard printed the sum as **"Total Spent"**.
|
|
12
|
+
* 2. `provider-catalog.ts` held the real per-provider list prices (with 21
|
|
13
|
+
* providers, not 5) and the ROUTER scored cost from THAT.
|
|
14
|
+
*
|
|
15
|
+
* The two disagreed in both directions, and every disagreement was invisible:
|
|
16
|
+
*
|
|
17
|
+
* - `deepseek` was absent from the ledger table → priced at the generic
|
|
18
|
+
* `$0.00010/1K` while the catalog declared `$0.00027/$0.00110`. The one
|
|
19
|
+
* provider the user actually pays for was the one most mis-priced (≈$5.94
|
|
20
|
+
* recorded where the catalog's own numbers give ≈$17.36).
|
|
21
|
+
* - `omniroute` was absent too → invented $0.3995 of spend on a localhost
|
|
22
|
+
* gateway the catalog itself declares `{0, 0}`.
|
|
23
|
+
* - `openrouter` was 25×/100× lower in the ledger than in the catalog.
|
|
24
|
+
* - a 7B toy (`allam-2-7b`) and a prompt-guard classifier were billed at the
|
|
25
|
+
* provider's Llama-3.3-70B-class rate, because price was per PROVIDER.
|
|
26
|
+
* - and "my Groq is a free tier" had nowhere to live, so free usage was
|
|
27
|
+
* charged a stranger's list rate (the billing fact belongs to the KEY —
|
|
28
|
+
* this is the same doctrine `inference/model-entitlement.ts` states: the
|
|
29
|
+
* module may LABEL, never bill).
|
|
30
|
+
*
|
|
31
|
+
* ─── DOCTRINE (read before changing the order below) ────────────────────────
|
|
32
|
+
*
|
|
33
|
+
* 1. **Two numbers, never conflated.** `billedUsd` is money we have EVIDENCE
|
|
34
|
+
* was charged. `listUsd` is what the same tokens would cost at the resolved
|
|
35
|
+
* LIST price — the VALUE of a free call, never a charge. A single number
|
|
36
|
+
* cannot express both, and calling the second "spend" is the whole bug.
|
|
37
|
+
* 2. **Evidence outranks inference, in this order.** Provider-reported cost for
|
|
38
|
+
* THIS call > the account's own declared billing tier (a key fact) > a price
|
|
39
|
+
* we can name a source for (config override, declared model price, catalog).
|
|
40
|
+
* Nothing here guesses.
|
|
41
|
+
* 3. **No generic fallback. Ever.** An unknown provider has NO price: it is
|
|
42
|
+
* `unpriced`, which is a first-class answer. The old `$0.00010/1K` default is
|
|
43
|
+
* exactly how $0.40 of spend appeared on a free local gateway. (Unknown is
|
|
44
|
+
* not zero either — that is the mirror-image lie, and `gemini`'s declared
|
|
45
|
+
* `0/0` is why a zero price is deliberately NOT read as "free".)
|
|
46
|
+
* 4. **A zero catalog price is not free, and a keyless gateway is not free.**
|
|
47
|
+
* A keyed provider with no listed price is `unpriced` (Gemini's paid models
|
|
48
|
+
* 403 without billing). A keyless pass-through gateway (`omniroute`,
|
|
49
|
+
* `nuvira` sidecar) bills whatever its UPSTREAM charged, which this machine
|
|
50
|
+
* cannot see — so it is `passthrough`: zero we can attribute, and explicitly
|
|
51
|
+
* NOT claimed as either spend or saving.
|
|
52
|
+
* 5. **Per-model before per-provider, and say which one you used.**
|
|
53
|
+
* `pricingBasis` names the evidence ("config override", "declared model
|
|
54
|
+
* price", "catalog deepseek", "groq provider-level default (Llama-3.3-70B
|
|
55
|
+
* class) — no per-model price for this id"). "Provider-level default" is
|
|
56
|
+
* marked as such because that is precisely the number that used to bill a
|
|
57
|
+
* 7B toy at 70B rates.
|
|
58
|
+
* 6. **The router still gets a number.** `estimatedListCostUsd()` is the
|
|
59
|
+
* scoring primitive (`calculateCost` keeps its signature for that reason); it
|
|
60
|
+
* answers "what should this cost on the open market", which is the right
|
|
61
|
+
* question for ranking and the WRONG question for accounting. Accounting uses
|
|
62
|
+
* `resolveCallCost()`.
|
|
63
|
+
*/
|
|
64
|
+
/**
|
|
65
|
+
* What the user's ACCOUNT (key) is billed by this provider — the fact the
|
|
66
|
+
* ledger had no way to express. Declared per provider in config
|
|
67
|
+
* (`providers.<id>.billingTier`), because it is a property of the key, not of
|
|
68
|
+
* the model.
|
|
69
|
+
*
|
|
70
|
+
* paid — calls bill at list price. List-priced calls count as billed
|
|
71
|
+
* (still an estimate; only a provider-reported figure is exact).
|
|
72
|
+
* free — a free tier / free credits are absorbing the cost. Calls are
|
|
73
|
+
* $0 billed BY EVIDENCE, and their list price is reported as
|
|
74
|
+
* value (what the free tier saved).
|
|
75
|
+
* subscription — a flat-rate plan covers the calls. Same accounting as `free`
|
|
76
|
+
* (nothing is charged per call), different reason in the basis.
|
|
77
|
+
* unknown — nothing declared. NOT an assumption of paid: list-priced
|
|
78
|
+
* calls land in a separate `unverified` bucket that is never
|
|
79
|
+
* presented as spend, and the surfaces say how to declare it.
|
|
80
|
+
*/
|
|
81
|
+
export type BillingTier = 'paid' | 'free' | 'subscription' | 'unknown';
|
|
82
|
+
export declare const BILLING_TIERS: readonly BillingTier[];
|
|
83
|
+
/** True when the tier means "nothing is charged per call". */
|
|
84
|
+
export declare function tierIsUnbilled(tier: BillingTier): boolean;
|
|
85
|
+
/** Narrow an arbitrary config value to a tier. Anything unrecognised is `unknown`. */
|
|
86
|
+
export declare function asBillingTier(value: unknown): BillingTier;
|
|
87
|
+
/**
|
|
88
|
+
* The provenance of a call's cost. Six values, because they need different
|
|
89
|
+
* handling and a boolean would flatten them:
|
|
90
|
+
*
|
|
91
|
+
* reported the provider/gateway stated this call's exact charge.
|
|
92
|
+
* catalog list-priced from a named source (config override, declared
|
|
93
|
+
* model price, or the provider catalog).
|
|
94
|
+
* free provably $0 per call: a local runtime, a provider-declared
|
|
95
|
+
* `:free` id, or an account declared free/subscription.
|
|
96
|
+
* passthrough a keyless gateway to somewhere else — its upstream bill is not
|
|
97
|
+
* visible here (see doctrine 4).
|
|
98
|
+
* unknown no price evidence at all — unpriced, never zero, never guessed.
|
|
99
|
+
*/
|
|
100
|
+
export type CostSource = 'reported' | 'catalog' | 'free' | 'passthrough' | 'unknown';
|
|
101
|
+
/**
|
|
102
|
+
* Which accounting bucket an entry belongs to. Derived, so every surface
|
|
103
|
+
* (CLI, dashboard, reconciliation) buckets a call the same way.
|
|
104
|
+
*
|
|
105
|
+
* billed-reported exact, provider-stated money.
|
|
106
|
+
* billed-listed list-priced and the account is declared `paid` — a
|
|
107
|
+
* best estimate of money owed, labelled as an estimate.
|
|
108
|
+
* unverified-listed list-priced, account tier undeclared: shown, never as spend.
|
|
109
|
+
* not-billed-value free/subscription: $0 billed, list price is the value saved.
|
|
110
|
+
* unpriced unknown price, or a pass-through gateway: no dollars at all.
|
|
111
|
+
*/
|
|
112
|
+
export type CostBucket = 'billed-reported' | 'billed-listed' | 'unverified-listed' | 'not-billed-value' | 'unpriced';
|
|
113
|
+
/** The bucket for one classified call. Pure. */
|
|
114
|
+
export declare function bucketFor(source: CostSource, tier: BillingTier): CostBucket;
|
|
115
|
+
/** A price we can name a source for. USD per 1K tokens. */
|
|
116
|
+
export interface PriceQuote {
|
|
117
|
+
inputPer1K: number;
|
|
118
|
+
outputPer1K: number;
|
|
119
|
+
/** The evidence, one line — printed beside the number, never omitted. */
|
|
120
|
+
basis: string;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* DECLARED per-model list prices, keyed `provider/model` OR a bare model id.
|
|
124
|
+
*
|
|
125
|
+
* These are the models whose real price differs materially from their
|
|
126
|
+
* provider's declared default — the entries that used to live as an ad-hoc
|
|
127
|
+
* `expensiveModels` map inside `cost-tracker.ts` (kept, so no accounting
|
|
128
|
+
* behaviour regresses silently, but now first-class and self-describing: each
|
|
129
|
+
* carries the id it applies to AND a basis string the surfaces print).
|
|
130
|
+
*
|
|
131
|
+
* A key with a `/` is matched against `${provider}/${model}` first, so the same
|
|
132
|
+
* model id can price differently on two providers. Nothing is inferred from a
|
|
133
|
+
* model's NAME here — a price is a fact about an offering, not a word.
|
|
134
|
+
*/
|
|
135
|
+
export declare const DECLARED_MODEL_PRICES: Record<string, PriceQuote>;
|
|
136
|
+
/** True when a provider×model is provably $0 per call at the MODEL level. */
|
|
137
|
+
export declare function isModelLevelFree(provider: string, model: string): boolean;
|
|
138
|
+
/**
|
|
139
|
+
* A keyless provider that is NOT a local runtime: a gateway whose cost depends
|
|
140
|
+
* on whatever it forwards to. Its own price table says 0, and that zero means
|
|
141
|
+
* "not priced here", not "free here" (doctrine 4).
|
|
142
|
+
*/
|
|
143
|
+
export declare function isPassthroughGateway(provider: string): boolean;
|
|
144
|
+
/**
|
|
145
|
+
* Resolve the LIST price for a call, or `undefined` when this machine has no
|
|
146
|
+
* price for the pair. Precedence, highest first:
|
|
147
|
+
*
|
|
148
|
+
* 1. the caller's config override (`pricing.<provider>` — the user's own number),
|
|
149
|
+
* 2. a declared per-model price (`DECLARED_MODEL_PRICES`),
|
|
150
|
+
* 3. the caller's per-model catalogue row (the live feed, when a host wires it),
|
|
151
|
+
* 4. the provider catalog's default, MARKED as provider-level.
|
|
152
|
+
*
|
|
153
|
+
* Step 4 is deliberately last and deliberately labelled: applying a provider
|
|
154
|
+
* average to every model is exactly how a 7B toy was billed at 70B rates.
|
|
155
|
+
*/
|
|
156
|
+
export declare function resolveListPrice(provider: string, model: string, opts?: {
|
|
157
|
+
configPricing?: {
|
|
158
|
+
inputPer1K?: number;
|
|
159
|
+
outputPer1K?: number;
|
|
160
|
+
};
|
|
161
|
+
/** A per-model price from a live catalogue (e.g. OpenRouter's own rows). */
|
|
162
|
+
catalogRow?: {
|
|
163
|
+
inputPer1K?: number;
|
|
164
|
+
outputPer1K?: number;
|
|
165
|
+
basis?: string;
|
|
166
|
+
};
|
|
167
|
+
}): PriceQuote | undefined;
|
|
168
|
+
/** The classified cost of ONE call. */
|
|
169
|
+
export interface ResolvedCallCost {
|
|
170
|
+
/** Money we have evidence was charged, USD. 0 for free/unpriced/passthrough. */
|
|
171
|
+
billedUsd: number;
|
|
172
|
+
/** List-price value of the same tokens, when a price exists. Never "spend". */
|
|
173
|
+
listUsd?: number;
|
|
174
|
+
costSource: CostSource;
|
|
175
|
+
bucket: CostBucket;
|
|
176
|
+
billingTier: BillingTier;
|
|
177
|
+
/** One line naming the evidence behind both numbers. */
|
|
178
|
+
basis: string;
|
|
179
|
+
/** The resolved prices, when there is a quote (for display/aggregation). */
|
|
180
|
+
quote?: PriceQuote;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Classify one call's cost from the best available evidence.
|
|
184
|
+
*
|
|
185
|
+
* Total, never throws. `billedUsd` is 0 whenever we do not have evidence of a
|
|
186
|
+
* charge — which is the honest default, and the reason the headline can no
|
|
187
|
+
* longer be inflated by a provider average (doctrine 1, 3).
|
|
188
|
+
*/
|
|
189
|
+
export declare function resolveCallCost(input: {
|
|
190
|
+
provider: string;
|
|
191
|
+
model: string;
|
|
192
|
+
inputTokens: number;
|
|
193
|
+
outputTokens: number;
|
|
194
|
+
/** The provider's exact figure for this call, when it gave one. */
|
|
195
|
+
reportedCostUsd?: number;
|
|
196
|
+
/** The account's declared billing tier for this provider. */
|
|
197
|
+
billingTier?: BillingTier;
|
|
198
|
+
configPricing?: {
|
|
199
|
+
inputPer1K?: number;
|
|
200
|
+
outputPer1K?: number;
|
|
201
|
+
};
|
|
202
|
+
catalogRow?: {
|
|
203
|
+
inputPer1K?: number;
|
|
204
|
+
outputPer1K?: number;
|
|
205
|
+
basis?: string;
|
|
206
|
+
};
|
|
207
|
+
}): ResolvedCallCost;
|
|
208
|
+
/**
|
|
209
|
+
* The LIST-priced estimate, for callers that need a number to RANK with
|
|
210
|
+
* (`auto-router`'s cost score, the fallback ladder, hybrid cost estimates).
|
|
211
|
+
*
|
|
212
|
+
* Deliberately returns the market price rather than the billed figure: ranking
|
|
213
|
+
* wants "what would this cost on the open market", while accounting wants "what
|
|
214
|
+
* did it cost ME". Conflating them is how the dashboard came to print an
|
|
215
|
+
* estimate under the word "spent".
|
|
216
|
+
*/
|
|
217
|
+
export declare function estimatedListCostUsd(provider: string, model: string, inputTokens: number, outputTokens: number, opts?: {
|
|
218
|
+
configPricing?: {
|
|
219
|
+
inputPer1K?: number;
|
|
220
|
+
outputPer1K?: number;
|
|
221
|
+
};
|
|
222
|
+
catalogRow?: {
|
|
223
|
+
inputPer1K?: number;
|
|
224
|
+
outputPer1K?: number;
|
|
225
|
+
basis?: string;
|
|
226
|
+
};
|
|
227
|
+
}): number;
|
|
228
|
+
/** Convenience for display surfaces: the price quote alone, or undefined. */
|
|
229
|
+
export declare function priceQuoteFor(provider: string, model: string, opts?: {
|
|
230
|
+
configPricing?: {
|
|
231
|
+
inputPer1K?: number;
|
|
232
|
+
outputPer1K?: number;
|
|
233
|
+
};
|
|
234
|
+
catalogRow?: {
|
|
235
|
+
inputPer1K?: number;
|
|
236
|
+
outputPer1K?: number;
|
|
237
|
+
basis?: string;
|
|
238
|
+
};
|
|
239
|
+
}): PriceQuote | undefined;
|
|
240
|
+
//# sourceMappingURL=model-pricing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-pricing.d.ts","sourceRoot":"","sources":["../../src/learning/model-pricing.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Model pricing — the ONE price source of truth for what a call cost.
|
|
3
|
+
*
|
|
4
|
+
* THE DEFECT THIS REPLACES (measured on a real profile, 2026-10-15: a ledger of
|
|
5
|
+
* 9,405 calls reporting `Total Spent $7.21`). There were TWO price tables and
|
|
6
|
+
* neither knew what the account was actually billed:
|
|
7
|
+
*
|
|
8
|
+
* 1. `cost-tracker.ts` held a hardcoded five-entry `DEFAULT_PRICING` (groq,
|
|
9
|
+
* nim, gemini, openrouter, local) with a `$0.00010/1K` fallback for every
|
|
10
|
+
* OTHER provider, plus a three-entry `expensiveModels` hack. The ledger
|
|
11
|
+
* priced from it and the dashboard printed the sum as **"Total Spent"**.
|
|
12
|
+
* 2. `provider-catalog.ts` held the real per-provider list prices (with 21
|
|
13
|
+
* providers, not 5) and the ROUTER scored cost from THAT.
|
|
14
|
+
*
|
|
15
|
+
* The two disagreed in both directions, and every disagreement was invisible:
|
|
16
|
+
*
|
|
17
|
+
* - `deepseek` was absent from the ledger table → priced at the generic
|
|
18
|
+
* `$0.00010/1K` while the catalog declared `$0.00027/$0.00110`. The one
|
|
19
|
+
* provider the user actually pays for was the one most mis-priced (≈$5.94
|
|
20
|
+
* recorded where the catalog's own numbers give ≈$17.36).
|
|
21
|
+
* - `omniroute` was absent too → invented $0.3995 of spend on a localhost
|
|
22
|
+
* gateway the catalog itself declares `{0, 0}`.
|
|
23
|
+
* - `openrouter` was 25×/100× lower in the ledger than in the catalog.
|
|
24
|
+
* - a 7B toy (`allam-2-7b`) and a prompt-guard classifier were billed at the
|
|
25
|
+
* provider's Llama-3.3-70B-class rate, because price was per PROVIDER.
|
|
26
|
+
* - and "my Groq is a free tier" had nowhere to live, so free usage was
|
|
27
|
+
* charged a stranger's list rate (the billing fact belongs to the KEY —
|
|
28
|
+
* this is the same doctrine `inference/model-entitlement.ts` states: the
|
|
29
|
+
* module may LABEL, never bill).
|
|
30
|
+
*
|
|
31
|
+
* ─── DOCTRINE (read before changing the order below) ────────────────────────
|
|
32
|
+
*
|
|
33
|
+
* 1. **Two numbers, never conflated.** `billedUsd` is money we have EVIDENCE
|
|
34
|
+
* was charged. `listUsd` is what the same tokens would cost at the resolved
|
|
35
|
+
* LIST price — the VALUE of a free call, never a charge. A single number
|
|
36
|
+
* cannot express both, and calling the second "spend" is the whole bug.
|
|
37
|
+
* 2. **Evidence outranks inference, in this order.** Provider-reported cost for
|
|
38
|
+
* THIS call > the account's own declared billing tier (a key fact) > a price
|
|
39
|
+
* we can name a source for (config override, declared model price, catalog).
|
|
40
|
+
* Nothing here guesses.
|
|
41
|
+
* 3. **No generic fallback. Ever.** An unknown provider has NO price: it is
|
|
42
|
+
* `unpriced`, which is a first-class answer. The old `$0.00010/1K` default is
|
|
43
|
+
* exactly how $0.40 of spend appeared on a free local gateway. (Unknown is
|
|
44
|
+
* not zero either — that is the mirror-image lie, and `gemini`'s declared
|
|
45
|
+
* `0/0` is why a zero price is deliberately NOT read as "free".)
|
|
46
|
+
* 4. **A zero catalog price is not free, and a keyless gateway is not free.**
|
|
47
|
+
* A keyed provider with no listed price is `unpriced` (Gemini's paid models
|
|
48
|
+
* 403 without billing). A keyless pass-through gateway (`omniroute`,
|
|
49
|
+
* `nuvira` sidecar) bills whatever its UPSTREAM charged, which this machine
|
|
50
|
+
* cannot see — so it is `passthrough`: zero we can attribute, and explicitly
|
|
51
|
+
* NOT claimed as either spend or saving.
|
|
52
|
+
* 5. **Per-model before per-provider, and say which one you used.**
|
|
53
|
+
* `pricingBasis` names the evidence ("config override", "declared model
|
|
54
|
+
* price", "catalog deepseek", "groq provider-level default (Llama-3.3-70B
|
|
55
|
+
* class) — no per-model price for this id"). "Provider-level default" is
|
|
56
|
+
* marked as such because that is precisely the number that used to bill a
|
|
57
|
+
* 7B toy at 70B rates.
|
|
58
|
+
* 6. **The router still gets a number.** `estimatedListCostUsd()` is the
|
|
59
|
+
* scoring primitive (`calculateCost` keeps its signature for that reason); it
|
|
60
|
+
* answers "what should this cost on the open market", which is the right
|
|
61
|
+
* question for ranking and the WRONG question for accounting. Accounting uses
|
|
62
|
+
* `resolveCallCost()`.
|
|
63
|
+
*/
|
|
64
|
+
import { catalogPricing, isCatalogKeyless, getCatalogProvider } from '../inference/provider-catalog.js';
|
|
65
|
+
import { LOCAL_RUNTIME_IDS } from '../inference/model-entitlement.js';
|
|
66
|
+
export const BILLING_TIERS = ['paid', 'free', 'subscription', 'unknown'];
|
|
67
|
+
/** True when the tier means "nothing is charged per call". */
|
|
68
|
+
export function tierIsUnbilled(tier) {
|
|
69
|
+
return tier === 'free' || tier === 'subscription';
|
|
70
|
+
}
|
|
71
|
+
/** Narrow an arbitrary config value to a tier. Anything unrecognised is `unknown`. */
|
|
72
|
+
export function asBillingTier(value) {
|
|
73
|
+
return typeof value === 'string' && BILLING_TIERS.includes(value)
|
|
74
|
+
? value
|
|
75
|
+
: 'unknown';
|
|
76
|
+
}
|
|
77
|
+
/** The bucket for one classified call. Pure. */
|
|
78
|
+
export function bucketFor(source, tier) {
|
|
79
|
+
switch (source) {
|
|
80
|
+
case 'reported':
|
|
81
|
+
return 'billed-reported';
|
|
82
|
+
case 'free':
|
|
83
|
+
return 'not-billed-value';
|
|
84
|
+
case 'passthrough':
|
|
85
|
+
case 'unknown':
|
|
86
|
+
return 'unpriced';
|
|
87
|
+
case 'catalog':
|
|
88
|
+
return tier === 'paid' ? 'billed-listed' : 'unverified-listed';
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
// ─── Model-level facts ──────────────────────────────────────────────────────
|
|
92
|
+
const PROVIDER_FREE_SUFFIX = ':free';
|
|
93
|
+
/**
|
|
94
|
+
* DECLARED per-model list prices, keyed `provider/model` OR a bare model id.
|
|
95
|
+
*
|
|
96
|
+
* These are the models whose real price differs materially from their
|
|
97
|
+
* provider's declared default — the entries that used to live as an ad-hoc
|
|
98
|
+
* `expensiveModels` map inside `cost-tracker.ts` (kept, so no accounting
|
|
99
|
+
* behaviour regresses silently, but now first-class and self-describing: each
|
|
100
|
+
* carries the id it applies to AND a basis string the surfaces print).
|
|
101
|
+
*
|
|
102
|
+
* A key with a `/` is matched against `${provider}/${model}` first, so the same
|
|
103
|
+
* model id can price differently on two providers. Nothing is inferred from a
|
|
104
|
+
* model's NAME here — a price is a fact about an offering, not a word.
|
|
105
|
+
*/
|
|
106
|
+
export const DECLARED_MODEL_PRICES = {
|
|
107
|
+
'openai/gpt-oss-120b': {
|
|
108
|
+
inputPer1K: 0.00015,
|
|
109
|
+
outputPer1K: 0.0006,
|
|
110
|
+
basis: 'declared model price (gpt-oss-120b)',
|
|
111
|
+
},
|
|
112
|
+
'openai/gpt-oss-20b': {
|
|
113
|
+
inputPer1K: 0.000075,
|
|
114
|
+
outputPer1K: 0.0003,
|
|
115
|
+
basis: 'declared model price (gpt-oss-20b)',
|
|
116
|
+
},
|
|
117
|
+
'qwen/qwen3.6-27b': {
|
|
118
|
+
inputPer1K: 0.0006,
|
|
119
|
+
outputPer1K: 0.003,
|
|
120
|
+
basis: 'declared model price (qwen3.6-27b)',
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
/** True when a provider×model is provably $0 per call at the MODEL level. */
|
|
124
|
+
export function isModelLevelFree(provider, model) {
|
|
125
|
+
const id = (model ?? '').trim();
|
|
126
|
+
if (id.endsWith(PROVIDER_FREE_SUFFIX))
|
|
127
|
+
return true;
|
|
128
|
+
return LOCAL_RUNTIME_IDS.includes(provider);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* A keyless provider that is NOT a local runtime: a gateway whose cost depends
|
|
132
|
+
* on whatever it forwards to. Its own price table says 0, and that zero means
|
|
133
|
+
* "not priced here", not "free here" (doctrine 4).
|
|
134
|
+
*/
|
|
135
|
+
export function isPassthroughGateway(provider) {
|
|
136
|
+
if (LOCAL_RUNTIME_IDS.includes(provider))
|
|
137
|
+
return false;
|
|
138
|
+
return isCatalogKeyless(provider);
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Resolve the LIST price for a call, or `undefined` when this machine has no
|
|
142
|
+
* price for the pair. Precedence, highest first:
|
|
143
|
+
*
|
|
144
|
+
* 1. the caller's config override (`pricing.<provider>` — the user's own number),
|
|
145
|
+
* 2. a declared per-model price (`DECLARED_MODEL_PRICES`),
|
|
146
|
+
* 3. the caller's per-model catalogue row (the live feed, when a host wires it),
|
|
147
|
+
* 4. the provider catalog's default, MARKED as provider-level.
|
|
148
|
+
*
|
|
149
|
+
* Step 4 is deliberately last and deliberately labelled: applying a provider
|
|
150
|
+
* average to every model is exactly how a 7B toy was billed at 70B rates.
|
|
151
|
+
*/
|
|
152
|
+
export function resolveListPrice(provider, model, opts = {}) {
|
|
153
|
+
const usable = (q) => !!q &&
|
|
154
|
+
typeof q.inputPer1K === 'number' &&
|
|
155
|
+
typeof q.outputPer1K === 'number' &&
|
|
156
|
+
Number.isFinite(q.inputPer1K) &&
|
|
157
|
+
Number.isFinite(q.outputPer1K) &&
|
|
158
|
+
q.inputPer1K >= 0 &&
|
|
159
|
+
q.outputPer1K >= 0;
|
|
160
|
+
// 1. The user's own declared price for this provider wins over every builtin.
|
|
161
|
+
if (usable(opts.configPricing)) {
|
|
162
|
+
return {
|
|
163
|
+
inputPer1K: opts.configPricing.inputPer1K,
|
|
164
|
+
outputPer1K: opts.configPricing.outputPer1K,
|
|
165
|
+
basis: `your config override (pricing.${provider})`,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
// 2. A declared per-model price, matched `provider/model` then bare id.
|
|
169
|
+
const declared = DECLARED_MODEL_PRICES[`${provider}/${model}`] ?? DECLARED_MODEL_PRICES[model];
|
|
170
|
+
if (declared)
|
|
171
|
+
return { ...declared };
|
|
172
|
+
// 3. A live catalogue row for this exact model, when the host supplied one.
|
|
173
|
+
if (usable(opts.catalogRow)) {
|
|
174
|
+
return {
|
|
175
|
+
inputPer1K: opts.catalogRow.inputPer1K,
|
|
176
|
+
outputPer1K: opts.catalogRow.outputPer1K,
|
|
177
|
+
basis: opts.catalogRow.basis ?? `catalogue price for ${model}`,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
// 4. The provider catalog's default — only when it is a real (>0) price.
|
|
181
|
+
const entry = getCatalogProvider(provider);
|
|
182
|
+
const catalog = catalogPricing(provider);
|
|
183
|
+
if (catalog && (catalog.inputPer1K > 0 || catalog.outputPer1K > 0)) {
|
|
184
|
+
return {
|
|
185
|
+
inputPer1K: catalog.inputPer1K,
|
|
186
|
+
outputPer1K: catalog.outputPer1K,
|
|
187
|
+
basis: `${provider} provider-level default (${entry?.defaultModel ?? 'unknown default'} class) — no per-model price for this id`,
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
// A keyed provider with no positive price has NO price we can name (doctrine 4).
|
|
191
|
+
return undefined;
|
|
192
|
+
}
|
|
193
|
+
function round6(usd) {
|
|
194
|
+
return Math.round(usd * 100000) / 100000;
|
|
195
|
+
}
|
|
196
|
+
function costOf(quote, inputTokens, outputTokens) {
|
|
197
|
+
return (inputTokens / 1000) * quote.inputPer1K + (outputTokens / 1000) * quote.outputPer1K;
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* Classify one call's cost from the best available evidence.
|
|
201
|
+
*
|
|
202
|
+
* Total, never throws. `billedUsd` is 0 whenever we do not have evidence of a
|
|
203
|
+
* charge — which is the honest default, and the reason the headline can no
|
|
204
|
+
* longer be inflated by a provider average (doctrine 1, 3).
|
|
205
|
+
*/
|
|
206
|
+
export function resolveCallCost(input) {
|
|
207
|
+
const tier = input.billingTier ?? 'unknown';
|
|
208
|
+
const quote = resolveListPrice(input.provider, input.model, {
|
|
209
|
+
configPricing: input.configPricing,
|
|
210
|
+
catalogRow: input.catalogRow,
|
|
211
|
+
});
|
|
212
|
+
const listUsd = quote ? round6(costOf(quote, input.inputTokens, input.outputTokens)) : undefined;
|
|
213
|
+
// 1. The provider's own accounting is exact, and it is exact about ZERO too.
|
|
214
|
+
if (typeof input.reportedCostUsd === 'number' && Number.isFinite(input.reportedCostUsd)) {
|
|
215
|
+
return {
|
|
216
|
+
billedUsd: round6(Math.max(0, input.reportedCostUsd)),
|
|
217
|
+
...(listUsd !== undefined ? { listUsd } : {}),
|
|
218
|
+
costSource: 'reported',
|
|
219
|
+
bucket: 'billed-reported',
|
|
220
|
+
billingTier: tier,
|
|
221
|
+
basis: 'provider-reported cost for this call (exact)',
|
|
222
|
+
...(quote ? { quote } : {}),
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
// 2. Provably nothing per call — a local runtime, a provider-declared free id,
|
|
226
|
+
// or an account the user has declared free/subscription. The list price
|
|
227
|
+
// becomes VALUE (what the tier saved), never spend.
|
|
228
|
+
if (isModelLevelFree(input.provider, input.model)) {
|
|
229
|
+
return {
|
|
230
|
+
billedUsd: 0,
|
|
231
|
+
...(listUsd !== undefined ? { listUsd } : {}),
|
|
232
|
+
costSource: 'free',
|
|
233
|
+
bucket: 'not-billed-value',
|
|
234
|
+
billingTier: tier,
|
|
235
|
+
basis: LOCAL_RUNTIME_IDS.includes(input.provider)
|
|
236
|
+
? 'runs on this machine — no per-call charge'
|
|
237
|
+
: `provider declares this id free ("${PROVIDER_FREE_SUFFIX}")`,
|
|
238
|
+
...(quote ? { quote } : {}),
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
if (tierIsUnbilled(tier)) {
|
|
242
|
+
return {
|
|
243
|
+
billedUsd: 0,
|
|
244
|
+
...(listUsd !== undefined ? { listUsd } : {}),
|
|
245
|
+
costSource: 'free',
|
|
246
|
+
bucket: 'not-billed-value',
|
|
247
|
+
billingTier: tier,
|
|
248
|
+
basis: tier === 'subscription'
|
|
249
|
+
? 'your account is on a flat-rate plan — nothing is charged per call'
|
|
250
|
+
: 'your account is declared a free tier — nothing is charged per call',
|
|
251
|
+
...(quote ? { quote } : {}),
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
// 3. A pass-through gateway: the upstream bill is not visible from here.
|
|
255
|
+
if (isPassthroughGateway(input.provider)) {
|
|
256
|
+
return {
|
|
257
|
+
billedUsd: 0,
|
|
258
|
+
costSource: 'passthrough',
|
|
259
|
+
bucket: 'unpriced',
|
|
260
|
+
billingTier: tier,
|
|
261
|
+
basis: `${input.provider} is a keyless pass-through gateway — its upstream charge is not visible here`,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
// 4. No price we can name: UNPRICED. Not zero, not an invented rate.
|
|
265
|
+
if (!quote || listUsd === undefined) {
|
|
266
|
+
return {
|
|
267
|
+
billedUsd: 0,
|
|
268
|
+
costSource: 'unknown',
|
|
269
|
+
bucket: 'unpriced',
|
|
270
|
+
billingTier: tier,
|
|
271
|
+
basis: `no price evidence for ${input.provider}/${input.model} — set pricing.${input.provider}.inputPer1K/outputPer1K or declare providers.${input.provider}.billingTier`,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
// 5. A real list price. Whether it counts as BILLED depends on the one fact
|
|
275
|
+
// only the user has: what their key is billed.
|
|
276
|
+
return {
|
|
277
|
+
billedUsd: tier === 'paid' ? listUsd : 0,
|
|
278
|
+
listUsd,
|
|
279
|
+
costSource: 'catalog',
|
|
280
|
+
bucket: tier === 'paid' ? 'billed-listed' : 'unverified-listed',
|
|
281
|
+
billingTier: tier,
|
|
282
|
+
basis: tier === 'paid'
|
|
283
|
+
? `${quote.basis} — account declared paid, so this is an estimated bill`
|
|
284
|
+
: `${quote.basis} — account tier undeclared, so this is NOT counted as spend (set providers.${input.provider}.billingTier)`,
|
|
285
|
+
quote,
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
/**
|
|
289
|
+
* The LIST-priced estimate, for callers that need a number to RANK with
|
|
290
|
+
* (`auto-router`'s cost score, the fallback ladder, hybrid cost estimates).
|
|
291
|
+
*
|
|
292
|
+
* Deliberately returns the market price rather than the billed figure: ranking
|
|
293
|
+
* wants "what would this cost on the open market", while accounting wants "what
|
|
294
|
+
* did it cost ME". Conflating them is how the dashboard came to print an
|
|
295
|
+
* estimate under the word "spent".
|
|
296
|
+
*/
|
|
297
|
+
export function estimatedListCostUsd(provider, model, inputTokens, outputTokens, opts = {}) {
|
|
298
|
+
if (isModelLevelFree(provider, model))
|
|
299
|
+
return 0;
|
|
300
|
+
const quote = resolveListPrice(provider, model, opts);
|
|
301
|
+
if (!quote)
|
|
302
|
+
return 0;
|
|
303
|
+
return round6(costOf(quote, inputTokens, outputTokens));
|
|
304
|
+
}
|
|
305
|
+
/** Convenience for display surfaces: the price quote alone, or undefined. */
|
|
306
|
+
export function priceQuoteFor(provider, model, opts = {}) {
|
|
307
|
+
return resolveListPrice(provider, model, opts);
|
|
308
|
+
}
|
|
309
|
+
//# sourceMappingURL=model-pricing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"model-pricing.js","sourceRoot":"","sources":["../../src/learning/model-pricing.ts"],"names":[],"mappings":"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"}
|
|
@@ -52,6 +52,21 @@ export declare function hasCredentials(configManager: ConfigManager, provider: s
|
|
|
52
52
|
* failed outright.
|
|
53
53
|
*/
|
|
54
54
|
export declare const MODEL_HEALTH_CEILING = 0.6;
|
|
55
|
+
/**
|
|
56
|
+
* Could this model id ONLY have come from a test harness?
|
|
57
|
+
*
|
|
58
|
+
* The one definition of that question, because two callers need it and they
|
|
59
|
+
* must not drift: the router uses it to keep stub ids out of the routable pool
|
|
60
|
+
* (`isPairPlausible`, below), and `learning/cost-ledger-maintenance.ts` uses it
|
|
61
|
+
* to find the ledger rows a test run wrote into a real profile — MEASURED: 747
|
|
62
|
+
* rows of `local/wire-stub-model` sit in `~/.nuvira/memory/cost-tracker.json`
|
|
63
|
+
* from the provider-WIRE harness, and they were counted as real usage.
|
|
64
|
+
*
|
|
65
|
+
* Deliberately narrow, and a NAME SHAPE rather than a substring: `…-latest`
|
|
66
|
+
* aliases (`gemini-flash-latest`, `deepseek-coder:latest`) are real provider
|
|
67
|
+
* models, and a looser rule once flagged 25 real OpenRouter ids.
|
|
68
|
+
*/
|
|
69
|
+
export declare function isFixtureModelId(model: string): boolean;
|
|
55
70
|
/**
|
|
56
71
|
* POOL HYGIENE: could this provider really serve this model id?
|
|
57
72
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model-selection.d.ts","sourceRoot":"","sources":["../../src/learning/model-selection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAU1D,qFAAqF;AACrF,eAAO,MAAM,iBAAiB,qEAAsE,CAAC;AAErG;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,CAAC;AAMnE;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,EAA6B,CAAC;AAEpE;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,aAAa,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAOtF;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAyDxC;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"model-selection.d.ts","sourceRoot":"","sources":["../../src/learning/model-selection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAU1D,qFAAqF;AACrF,eAAO,MAAM,iBAAiB,qEAAsE,CAAC;AAErG;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,CAAC;AAMnE;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,EAA6B,CAAC;AAEpE;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,aAAa,EAAE,aAAa,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAOtF;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAyDxC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAGvD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAaxE;AAED;;;;;;;;;GASG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,EAAE,CAwD7D;AAcD,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,8EAA8E;IAC9E,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,aAAa,GAAG,cAAc,EAAE,CAuErF;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,aAAa,GAAG,MAAM,CAE3E;AAED,gFAAgF;AAChF,MAAM,WAAW,iBAAiB;IAChC,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,OAAO,CAAC;IACvC,SAAS,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;CACnC;AAED;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,iBAAiB,EAC1B,aAAa,CAAC,EAAE,aAAa,GAC5B;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,CA0BjD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,YAAY,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGxG;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,YAAY,EAAE,MAAM,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,MAAM,CAS1F;AAED,qFAAqF;AACrF,wBAAgB,uBAAuB,CAAC,aAAa,EAAE,aAAa,GAAG,MAAM,CAa5E"}
|
|
@@ -121,6 +121,24 @@ function definitivelyMissing(entry) {
|
|
|
121
121
|
text.includes('not found') ||
|
|
122
122
|
text.includes('no such model'));
|
|
123
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Could this model id ONLY have come from a test harness?
|
|
126
|
+
*
|
|
127
|
+
* The one definition of that question, because two callers need it and they
|
|
128
|
+
* must not drift: the router uses it to keep stub ids out of the routable pool
|
|
129
|
+
* (`isPairPlausible`, below), and `learning/cost-ledger-maintenance.ts` uses it
|
|
130
|
+
* to find the ledger rows a test run wrote into a real profile — MEASURED: 747
|
|
131
|
+
* rows of `local/wire-stub-model` sit in `~/.nuvira/memory/cost-tracker.json`
|
|
132
|
+
* from the provider-WIRE harness, and they were counted as real usage.
|
|
133
|
+
*
|
|
134
|
+
* Deliberately narrow, and a NAME SHAPE rather than a substring: `…-latest`
|
|
135
|
+
* aliases (`gemini-flash-latest`, `deepseek-coder:latest`) are real provider
|
|
136
|
+
* models, and a looser rule once flagged 25 real OpenRouter ids.
|
|
137
|
+
*/
|
|
138
|
+
export function isFixtureModelId(model) {
|
|
139
|
+
const id = (model ?? '').trim().toLowerCase();
|
|
140
|
+
return /^(wire-|test-|fixture-|stub-)/.test(id) || /-stub(-|$)|\.stub\./.test(id);
|
|
141
|
+
}
|
|
124
142
|
/**
|
|
125
143
|
* POOL HYGIENE: could this provider really serve this model id?
|
|
126
144
|
*
|
|
@@ -151,8 +169,10 @@ export function isPairPlausible(provider, model) {
|
|
|
151
169
|
const id = (model ?? '').trim().toLowerCase();
|
|
152
170
|
if (!id)
|
|
153
171
|
return false;
|
|
154
|
-
// 1. Fixture/stub ids
|
|
155
|
-
|
|
172
|
+
// 1. Fixture/stub ids — the SAME predicate the cost-ledger pruner uses, so
|
|
173
|
+
// "this id came from a test harness" has one definition (see
|
|
174
|
+
// `isFixtureModelId`).
|
|
175
|
+
if (isFixtureModelId(id))
|
|
156
176
|
return false;
|
|
157
177
|
// 2. Ollama-style tags only on a local runner.
|
|
158
178
|
if (id.includes(':')) {
|