@intelligo-dev/executions 1.0.0-beta.1 → 1.0.0-beta.13
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/NOTICE +6 -0
- package/README.md +121 -0
- package/dist/db/schema.d.ts +32 -21
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +23 -23
- package/dist/db/schema.js.map +1 -1
- package/dist/index.d.ts +10 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/lifecycle.d.ts +31 -5
- package/dist/lifecycle.d.ts.map +1 -1
- package/dist/lifecycle.js +179 -18
- package/dist/lifecycle.js.map +1 -1
- package/dist/ports.d.ts +33 -14
- package/dist/ports.d.ts.map +1 -1
- package/dist/ports.js +3 -8
- package/dist/ports.js.map +1 -1
- package/dist/pricing.d.ts +107 -137
- package/dist/pricing.d.ts.map +1 -1
- package/dist/pricing.js +129 -85
- package/dist/pricing.js.map +1 -1
- package/dist/queries.d.ts +39 -26
- package/dist/queries.d.ts.map +1 -1
- package/dist/queries.js +96 -39
- package/dist/queries.js.map +1 -1
- package/package.json +41 -13
- package/src/db/schema.ts +87 -0
- package/src/index.ts +73 -0
- package/src/lifecycle.ts +525 -0
- package/src/ports.ts +100 -0
- package/src/pricing.ts +322 -0
- package/src/queries.ts +235 -0
package/dist/pricing.d.ts
CHANGED
|
@@ -1,159 +1,129 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Model registry and execution cost accounting.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* A leaf: no database, no provider SDK. Its one import is
|
|
5
|
+
* `@intelligo-dev/core/registry`, which is itself dependency-free, so
|
|
6
|
+
* `@intelligo-dev/executions/pricing` is something a client bundle can
|
|
7
|
+
* read a display name or a price from without pulling in Drizzle.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* lists is how a model runs on Gemini and bills at Claude rates.
|
|
9
|
+
* The registry is open: a product registers any model it runs, and
|
|
10
|
+
* `registerModels(DEFAULT_MODELS)` from the composition root registers
|
|
11
|
+
* the shipped catalogue. Nothing self-registers. An id with no price
|
|
12
|
+
* throws at the point where the price is needed, naming the id, rather
|
|
13
|
+
* than billing at a guessed rate.
|
|
15
14
|
*/
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
readonly costPerMOutputTokens: 10;
|
|
42
|
-
readonly capabilities: {
|
|
43
|
-
readonly thinking: true;
|
|
44
|
-
readonly toolCall: true;
|
|
45
|
-
readonly vision: true;
|
|
46
|
-
readonly webSearch: true;
|
|
47
|
-
readonly codeExec: true;
|
|
48
|
-
};
|
|
49
|
-
};
|
|
50
|
-
readonly "openai/gpt-5-mini": {
|
|
51
|
-
readonly provider: "openai";
|
|
52
|
-
readonly model: "gpt-5-mini";
|
|
53
|
-
readonly displayName: "GPT-5 Mini";
|
|
54
|
-
readonly costPerMInputTokens: 0.25;
|
|
55
|
-
readonly costPerMOutputTokens: 2;
|
|
56
|
-
readonly capabilities: {
|
|
57
|
-
readonly thinking: true;
|
|
58
|
-
readonly toolCall: true;
|
|
59
|
-
readonly vision: true;
|
|
60
|
-
readonly webSearch: false;
|
|
61
|
-
readonly codeExec: false;
|
|
62
|
-
};
|
|
63
|
-
};
|
|
64
|
-
readonly "openai/gpt-5.4-mini": {
|
|
65
|
-
readonly provider: "openai";
|
|
66
|
-
readonly model: "gpt-5.4-mini";
|
|
67
|
-
readonly displayName: "GPT-5.4 Mini";
|
|
68
|
-
readonly costPerMInputTokens: 0.75;
|
|
69
|
-
readonly costPerMOutputTokens: 4.5;
|
|
70
|
-
readonly capabilities: {
|
|
71
|
-
readonly thinking: true;
|
|
72
|
-
readonly toolCall: true;
|
|
73
|
-
readonly vision: true;
|
|
74
|
-
readonly webSearch: true;
|
|
75
|
-
readonly codeExec: true;
|
|
76
|
-
};
|
|
77
|
-
};
|
|
78
|
-
readonly "openai/o4-mini": {
|
|
79
|
-
readonly provider: "openai";
|
|
80
|
-
readonly model: "o4-mini";
|
|
81
|
-
readonly displayName: "o4-mini";
|
|
82
|
-
readonly costPerMInputTokens: 1.1;
|
|
83
|
-
readonly costPerMOutputTokens: 4.4;
|
|
84
|
-
readonly capabilities: {
|
|
85
|
-
readonly thinking: true;
|
|
86
|
-
readonly toolCall: true;
|
|
87
|
-
readonly vision: true;
|
|
88
|
-
readonly webSearch: true;
|
|
89
|
-
readonly codeExec: true;
|
|
90
|
-
};
|
|
91
|
-
};
|
|
92
|
-
readonly "anthropic/claude-sonnet-4-6": {
|
|
93
|
-
readonly provider: "anthropic";
|
|
94
|
-
readonly model: "claude-sonnet-4-6-20260214";
|
|
95
|
-
readonly displayName: "Claude Sonnet 4.6";
|
|
96
|
-
readonly costPerMInputTokens: 3;
|
|
97
|
-
readonly costPerMOutputTokens: 15;
|
|
98
|
-
readonly capabilities: {
|
|
99
|
-
readonly thinking: true;
|
|
100
|
-
readonly toolCall: true;
|
|
101
|
-
readonly vision: true;
|
|
102
|
-
readonly webSearch: true;
|
|
103
|
-
readonly codeExec: true;
|
|
104
|
-
};
|
|
105
|
-
};
|
|
15
|
+
import { type CurrencyCode, type Money } from "@intelligo-dev/core/money";
|
|
16
|
+
export type ModelCapabilities = {
|
|
17
|
+
thinking: boolean;
|
|
18
|
+
toolCall: boolean;
|
|
19
|
+
vision: boolean;
|
|
20
|
+
webSearch: boolean;
|
|
21
|
+
codeExec: boolean;
|
|
22
|
+
};
|
|
23
|
+
export type ModelPricing = {
|
|
24
|
+
/** Provider-prefixed id, e.g. `google/gemini-2.5-flash`. */
|
|
25
|
+
id: string;
|
|
26
|
+
provider: string;
|
|
27
|
+
/** The id the provider's own SDK expects, which is often dated. */
|
|
28
|
+
model: string;
|
|
29
|
+
displayName: string;
|
|
30
|
+
/** USD per million input tokens, as the provider quotes it. */
|
|
31
|
+
costPerMInputTokens: number;
|
|
32
|
+
/** USD per million output tokens. */
|
|
33
|
+
costPerMOutputTokens: number;
|
|
34
|
+
/**
|
|
35
|
+
* The ceiling on streamed output used for worst-case pre-request
|
|
36
|
+
* estimation.
|
|
37
|
+
*/
|
|
38
|
+
maxOutputTokens: number;
|
|
39
|
+
capabilities: ModelCapabilities;
|
|
106
40
|
};
|
|
107
|
-
|
|
41
|
+
/** A provider-prefixed model id; any string, because the registry is open. */
|
|
42
|
+
export type ModelId = string;
|
|
43
|
+
export declare class UnknownModelError extends Error {
|
|
44
|
+
readonly code = "unknown_model";
|
|
45
|
+
readonly modelId: string;
|
|
46
|
+
constructor(modelId: string, registered: readonly string[]);
|
|
47
|
+
}
|
|
48
|
+
/** Register (or replace) one model's price and capabilities. */
|
|
49
|
+
export declare function registerModel(pricing: ModelPricing): void;
|
|
50
|
+
export declare function registerModels(pricings: readonly ModelPricing[]): void;
|
|
51
|
+
export declare function getModelPricing(modelId: string): ModelPricing | undefined;
|
|
52
|
+
export declare function isModelRegistered(modelId: string): boolean;
|
|
53
|
+
export declare function listModels(): readonly ModelPricing[];
|
|
54
|
+
export declare function registeredModelIds(): readonly string[];
|
|
55
|
+
/** For tests composing a fresh root. */
|
|
56
|
+
export declare function clearModels(): void;
|
|
108
57
|
/**
|
|
109
|
-
*
|
|
58
|
+
* The catalogue the framework ships, as data.
|
|
59
|
+
*
|
|
60
|
+
* Prices are the providers' published USD rates and go stale; a
|
|
61
|
+
* deployment that cares about the third decimal should register its own
|
|
62
|
+
* contracted rates over these.
|
|
110
63
|
*/
|
|
111
|
-
export declare
|
|
64
|
+
export declare const DEFAULT_MODELS: readonly ModelPricing[];
|
|
65
|
+
/** Providers quote in USD; every cost in this module starts there. */
|
|
66
|
+
export declare const PROVIDER_CURRENCY: CurrencyCode;
|
|
112
67
|
/**
|
|
113
|
-
*
|
|
114
|
-
* Covers infra, tool overhead, FX volatility, and profit. Tunable per
|
|
115
|
-
* deploy via billing_settings.margin_multiplier; this constant is the
|
|
116
|
-
* fallback when the DB value is absent.
|
|
68
|
+
* What the provider charges for this call, in USD.
|
|
117
69
|
*
|
|
118
|
-
*
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
*
|
|
70
|
+
* A price quoted per million tokens is, per token, that many millionths
|
|
71
|
+
* of a dollar — so the micros are `tokens × price`. The price is scaled
|
|
72
|
+
* to an integer first, because `100 × 1.1` in floating point is
|
|
73
|
+
* `110.00000000000001` and would round up to a micro nobody used.
|
|
74
|
+
* Fractions of a micro round up, which keeps the cheapest models from
|
|
75
|
+
* pricing a real call at nothing.
|
|
76
|
+
*
|
|
77
|
+
* @throws {UnknownModelError} when the id has no registered price.
|
|
125
78
|
*/
|
|
126
|
-
export declare
|
|
79
|
+
export declare function providerCost(modelId: string, inputTokens: number, outputTokens: number): Money;
|
|
127
80
|
/**
|
|
128
|
-
*
|
|
129
|
-
*
|
|
81
|
+
* What a deployment bills in: its currency, what one USD costs in it
|
|
82
|
+
* (micros, so `1_000_000` is a USD deployment and `920_000` a euro one
|
|
83
|
+
* at 0.92), and the margin over provider cost in basis points of a
|
|
84
|
+
* multiplier — `40_000` is 4×.
|
|
85
|
+
*
|
|
86
|
+
* Read per request from `billing_settings`; there is no ambient rate,
|
|
87
|
+
* because a framework that guesses an exchange rate is inventing money.
|
|
130
88
|
*/
|
|
131
|
-
export
|
|
89
|
+
export type BillingRate = {
|
|
90
|
+
currency: CurrencyCode;
|
|
91
|
+
usdRateMicros: number;
|
|
92
|
+
marginBp: number;
|
|
93
|
+
};
|
|
132
94
|
/**
|
|
133
|
-
*
|
|
134
|
-
*
|
|
135
|
-
*
|
|
95
|
+
* `40_000` bp = 4× provider cost, the fallback margin when a
|
|
96
|
+
* deployment's `billing_settings` row names none.
|
|
97
|
+
*
|
|
98
|
+
* INVARIANT — a 65% gross margin floor:
|
|
99
|
+
* gross_margin = 1 − 1/multiplier
|
|
100
|
+
* Keeping gross_margin ≥ 0.65 means a multiplier ≥ 1/0.35 ≈ 2.857. At
|
|
101
|
+
* 4× this yields 75%, leaving buffer above the floor for FX moves and
|
|
102
|
+
* provider price rises. `pricing.test.ts` pins it.
|
|
136
103
|
*/
|
|
137
|
-
export declare const
|
|
138
|
-
export type ChargedAmount = {
|
|
139
|
-
rawCostUsd: number;
|
|
140
|
-
chargedMnt: number;
|
|
141
|
-
};
|
|
104
|
+
export declare const DEFAULT_MARGIN_BP = 40000;
|
|
142
105
|
/**
|
|
143
|
-
*
|
|
106
|
+
* Provider cost and what the reader is charged for it: cost × margin,
|
|
107
|
+
* converted into the deployment's currency at its own rate.
|
|
144
108
|
*
|
|
145
|
-
*
|
|
109
|
+
* Both steps round up, so a charge is at most two micros over — a
|
|
110
|
+
* millionth of a cent, against a fraction that would otherwise be the
|
|
111
|
+
* deployment's to eat on every request.
|
|
146
112
|
*
|
|
147
|
-
*
|
|
148
|
-
*
|
|
113
|
+
* @throws {UnknownModelError} when the id has no registered price.
|
|
114
|
+
* @throws {MoneyError} when a USD deployment passes a rate that is not 1.
|
|
149
115
|
*/
|
|
150
|
-
export declare function
|
|
116
|
+
export declare function chargeFor(modelId: string, inputTokens: number, outputTokens: number, rate: BillingRate): {
|
|
117
|
+
providerCost: Money;
|
|
118
|
+
charged: Money;
|
|
119
|
+
};
|
|
151
120
|
/**
|
|
152
|
-
*
|
|
153
|
-
* model
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
121
|
+
* The ceiling one turn on this model could charge, for admission:
|
|
122
|
+
* the model's own output limit against a 16K input budget (system
|
|
123
|
+
* prompt plus history). Refusing on the ceiling is what stops a turn
|
|
124
|
+
* that cannot be paid for from burning provider tokens first.
|
|
125
|
+
*
|
|
126
|
+
* @throws {UnknownModelError} when the id has no registered price.
|
|
157
127
|
*/
|
|
158
|
-
export declare function
|
|
128
|
+
export declare function estimateWorstCaseCharge(modelId: string, rate: BillingRate): Money;
|
|
159
129
|
//# sourceMappingURL=pricing.d.ts.map
|
package/dist/pricing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,KAAK,EACX,MAAM,2BAA2B,CAAC;AAGnC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACzB,4DAA4D;IAC5D,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,+DAA+D;IAC/D,mBAAmB,EAAE,MAAM,CAAC;IAC5B,qCAAqC;IACrC,oBAAoB,EAAE,MAAM,CAAC;IAC7B;;;OAGG;IACH,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,iBAAiB,CAAC;CACjC,CAAC;AAEF,8EAA8E;AAC9E,MAAM,MAAM,OAAO,GAAG,MAAM,CAAC;AAE7B,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,QAAQ,CAAC,IAAI,mBAAmB;IAChC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;gBACb,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,MAAM,EAAE;CAS3D;AAID,gEAAgE;AAChE,wBAAgB,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,IAAI,CAEzD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,YAAY,EAAE,GAAG,IAAI,CAEtE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAEzE;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAE1D;AAED,wBAAgB,UAAU,IAAI,SAAS,YAAY,EAAE,CAEpD;AAED,wBAAgB,kBAAkB,IAAI,SAAS,MAAM,EAAE,CAEtD;AAED,wCAAwC;AACxC,wBAAgB,WAAW,IAAI,IAAI,CAElC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,cAAc,EAAE,SAAS,YAAY,EAiGjD,CAAC;AASF,sEAAsE;AACtE,eAAO,MAAM,iBAAiB,EAAE,YAA8B,CAAC;AAE/D;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GACnB,KAAK,CAWP;AAKD;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,QAAQ,EAAE,YAAY,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,QAAS,CAAC;AAIxC;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,WAAW,GAChB;IAAE,YAAY,EAAE,KAAK,CAAC;IAAC,OAAO,EAAE,KAAK,CAAA;CAAE,CAUzC;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,WAAW,GAChB,KAAK,CAGP"}
|
package/dist/pricing.js
CHANGED
|
@@ -1,31 +1,70 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Model registry and execution cost accounting.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* A leaf: no database, no provider SDK. Its one import is
|
|
5
|
+
* `@intelligo-dev/core/registry`, which is itself dependency-free, so
|
|
6
|
+
* `@intelligo-dev/executions/pricing` is something a client bundle can
|
|
7
|
+
* read a display name or a price from without pulling in Drizzle.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
* lists is how a model runs on Gemini and bills at Claude rates.
|
|
9
|
+
* The registry is open: a product registers any model it runs, and
|
|
10
|
+
* `registerModels(DEFAULT_MODELS)` from the composition root registers
|
|
11
|
+
* the shipped catalogue. Nothing self-registers. An id with no price
|
|
12
|
+
* throws at the point where the price is needed, naming the id, rather
|
|
13
|
+
* than billing at a guessed rate.
|
|
15
14
|
*/
|
|
15
|
+
import { convert, currency, money, } from "@intelligo-dev/core/money";
|
|
16
|
+
import { createRegistry } from "@intelligo-dev/core/registry";
|
|
17
|
+
export class UnknownModelError extends Error {
|
|
18
|
+
constructor(modelId, registered) {
|
|
19
|
+
super(`No price registered for model "${modelId}". ` +
|
|
20
|
+
`Registered: ${registered.length > 0 ? registered.join(", ") : "none"}. ` +
|
|
21
|
+
`Call registerModels(DEFAULT_MODELS) — or your own catalogue — from the composition root.`);
|
|
22
|
+
this.code = "unknown_model";
|
|
23
|
+
this.name = "UnknownModelError";
|
|
24
|
+
this.modelId = modelId;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
const models = createRegistry("executions/model-pricing");
|
|
28
|
+
/** Register (or replace) one model's price and capabilities. */
|
|
29
|
+
export function registerModel(pricing) {
|
|
30
|
+
models.set(pricing.id, pricing);
|
|
31
|
+
}
|
|
32
|
+
export function registerModels(pricings) {
|
|
33
|
+
for (const pricing of pricings)
|
|
34
|
+
registerModel(pricing);
|
|
35
|
+
}
|
|
36
|
+
export function getModelPricing(modelId) {
|
|
37
|
+
return models.get(modelId);
|
|
38
|
+
}
|
|
39
|
+
export function isModelRegistered(modelId) {
|
|
40
|
+
return models.has(modelId);
|
|
41
|
+
}
|
|
42
|
+
export function listModels() {
|
|
43
|
+
return [...models.values()];
|
|
44
|
+
}
|
|
45
|
+
export function registeredModelIds() {
|
|
46
|
+
return [...models.keys()];
|
|
47
|
+
}
|
|
48
|
+
/** For tests composing a fresh root. */
|
|
49
|
+
export function clearModels() {
|
|
50
|
+
models.clear();
|
|
51
|
+
}
|
|
16
52
|
/**
|
|
17
|
-
*
|
|
53
|
+
* The catalogue the framework ships, as data.
|
|
18
54
|
*
|
|
19
|
-
*
|
|
55
|
+
* Prices are the providers' published USD rates and go stale; a
|
|
56
|
+
* deployment that cares about the third decimal should register its own
|
|
57
|
+
* contracted rates over these.
|
|
20
58
|
*/
|
|
21
|
-
export const
|
|
22
|
-
|
|
23
|
-
|
|
59
|
+
export const DEFAULT_MODELS = [
|
|
60
|
+
{
|
|
61
|
+
id: "google/gemini-2.5-flash",
|
|
24
62
|
provider: "google",
|
|
25
63
|
model: "gemini-2.5-flash",
|
|
26
64
|
displayName: "Gemini 2.5 Flash",
|
|
27
65
|
costPerMInputTokens: 0.3,
|
|
28
66
|
costPerMOutputTokens: 2.5,
|
|
67
|
+
maxOutputTokens: 8000,
|
|
29
68
|
capabilities: {
|
|
30
69
|
thinking: true,
|
|
31
70
|
toolCall: true,
|
|
@@ -34,12 +73,14 @@ export const MODEL_CONFIGS = {
|
|
|
34
73
|
codeExec: true,
|
|
35
74
|
},
|
|
36
75
|
},
|
|
37
|
-
|
|
76
|
+
{
|
|
77
|
+
id: "google/gemini-2.5-pro",
|
|
38
78
|
provider: "google",
|
|
39
79
|
model: "gemini-2.5-pro",
|
|
40
80
|
displayName: "Gemini 2.5 Pro",
|
|
41
81
|
costPerMInputTokens: 1.25,
|
|
42
82
|
costPerMOutputTokens: 10,
|
|
83
|
+
maxOutputTokens: 8000,
|
|
43
84
|
capabilities: {
|
|
44
85
|
thinking: true,
|
|
45
86
|
toolCall: true,
|
|
@@ -48,13 +89,14 @@ export const MODEL_CONFIGS = {
|
|
|
48
89
|
codeExec: true,
|
|
49
90
|
},
|
|
50
91
|
},
|
|
51
|
-
|
|
52
|
-
|
|
92
|
+
{
|
|
93
|
+
id: "openai/gpt-5-mini",
|
|
53
94
|
provider: "openai",
|
|
54
95
|
model: "gpt-5-mini",
|
|
55
96
|
displayName: "GPT-5 Mini",
|
|
56
97
|
costPerMInputTokens: 0.25,
|
|
57
98
|
costPerMOutputTokens: 2.0,
|
|
99
|
+
maxOutputTokens: 8000,
|
|
58
100
|
capabilities: {
|
|
59
101
|
thinking: true,
|
|
60
102
|
toolCall: true,
|
|
@@ -63,12 +105,14 @@ export const MODEL_CONFIGS = {
|
|
|
63
105
|
codeExec: false,
|
|
64
106
|
},
|
|
65
107
|
},
|
|
66
|
-
|
|
108
|
+
{
|
|
109
|
+
id: "openai/gpt-5.4-mini",
|
|
67
110
|
provider: "openai",
|
|
68
111
|
model: "gpt-5.4-mini",
|
|
69
112
|
displayName: "GPT-5.4 Mini",
|
|
70
113
|
costPerMInputTokens: 0.75,
|
|
71
114
|
costPerMOutputTokens: 4.5,
|
|
115
|
+
maxOutputTokens: 8000,
|
|
72
116
|
capabilities: {
|
|
73
117
|
thinking: true,
|
|
74
118
|
toolCall: true,
|
|
@@ -77,12 +121,14 @@ export const MODEL_CONFIGS = {
|
|
|
77
121
|
codeExec: true,
|
|
78
122
|
},
|
|
79
123
|
},
|
|
80
|
-
|
|
124
|
+
{
|
|
125
|
+
id: "openai/o4-mini",
|
|
81
126
|
provider: "openai",
|
|
82
127
|
model: "o4-mini",
|
|
83
128
|
displayName: "o4-mini",
|
|
84
129
|
costPerMInputTokens: 1.1,
|
|
85
130
|
costPerMOutputTokens: 4.4,
|
|
131
|
+
maxOutputTokens: 8000,
|
|
86
132
|
capabilities: {
|
|
87
133
|
thinking: true,
|
|
88
134
|
toolCall: true,
|
|
@@ -91,13 +137,14 @@ export const MODEL_CONFIGS = {
|
|
|
91
137
|
codeExec: true,
|
|
92
138
|
},
|
|
93
139
|
},
|
|
94
|
-
|
|
95
|
-
|
|
140
|
+
{
|
|
141
|
+
id: "anthropic/claude-sonnet-4-6",
|
|
96
142
|
provider: "anthropic",
|
|
97
143
|
model: "claude-sonnet-4-6-20260214",
|
|
98
144
|
displayName: "Claude Sonnet 4.6",
|
|
99
145
|
costPerMInputTokens: 3.0,
|
|
100
146
|
costPerMOutputTokens: 15.0,
|
|
147
|
+
maxOutputTokens: 8000,
|
|
101
148
|
capabilities: {
|
|
102
149
|
thinking: true,
|
|
103
150
|
toolCall: true,
|
|
@@ -106,82 +153,79 @@ export const MODEL_CONFIGS = {
|
|
|
106
153
|
codeExec: true,
|
|
107
154
|
},
|
|
108
155
|
},
|
|
109
|
-
|
|
156
|
+
];
|
|
157
|
+
/** The price of a model, or a loud failure naming the id. */
|
|
158
|
+
function requireModel(modelId) {
|
|
159
|
+
const pricing = models.get(modelId);
|
|
160
|
+
if (!pricing)
|
|
161
|
+
throw new UnknownModelError(modelId, registeredModelIds());
|
|
162
|
+
return pricing;
|
|
163
|
+
}
|
|
164
|
+
/** Providers quote in USD; every cost in this module starts there. */
|
|
165
|
+
export const PROVIDER_CURRENCY = currency("USD");
|
|
110
166
|
/**
|
|
111
|
-
*
|
|
167
|
+
* What the provider charges for this call, in USD.
|
|
168
|
+
*
|
|
169
|
+
* A price quoted per million tokens is, per token, that many millionths
|
|
170
|
+
* of a dollar — so the micros are `tokens × price`. The price is scaled
|
|
171
|
+
* to an integer first, because `100 × 1.1` in floating point is
|
|
172
|
+
* `110.00000000000001` and would round up to a micro nobody used.
|
|
173
|
+
* Fractions of a micro round up, which keeps the cheapest models from
|
|
174
|
+
* pricing a real call at nothing.
|
|
175
|
+
*
|
|
176
|
+
* @throws {UnknownModelError} when the id has no registered price.
|
|
112
177
|
*/
|
|
113
|
-
export function
|
|
114
|
-
const config =
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
// away expensive completions for free, but the warning here is the
|
|
120
|
-
// contract — silent fallback is how we ended up over-billing every
|
|
121
|
-
// free Gemini turn at Claude prices.
|
|
122
|
-
console.warn(`[calculateCost] Unknown model "${modelId}" — pricing at worst-case Claude rate ($3/$15 per M). Add it to MODEL_CONFIGS.`);
|
|
123
|
-
return (inputTokens / 1000000) * 3.0 + (outputTokens / 1000000) * 15.0;
|
|
124
|
-
}
|
|
125
|
-
return ((inputTokens / 1000000) * config.costPerMInputTokens +
|
|
126
|
-
(outputTokens / 1000000) * config.costPerMOutputTokens);
|
|
178
|
+
export function providerCost(modelId, inputTokens, outputTokens) {
|
|
179
|
+
const config = requireModel(modelId);
|
|
180
|
+
const scaled = (price) => Math.round(price * PRICE_SCALE);
|
|
181
|
+
return money(Math.ceil((inputTokens * scaled(config.costPerMInputTokens) +
|
|
182
|
+
outputTokens * scaled(config.costPerMOutputTokens)) /
|
|
183
|
+
PRICE_SCALE), PROVIDER_CURRENCY);
|
|
127
184
|
}
|
|
185
|
+
/** Prices carry at most six decimals of a dollar per million tokens. */
|
|
186
|
+
const PRICE_SCALE = 1000000;
|
|
128
187
|
/**
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* deploy via billing_settings.margin_multiplier; this constant is the
|
|
132
|
-
* fallback when the DB value is absent.
|
|
188
|
+
* `40_000` bp = 4× provider cost, the fallback margin when a
|
|
189
|
+
* deployment's `billing_settings` row names none.
|
|
133
190
|
*
|
|
134
|
-
* INVARIANT — 65% gross margin floor
|
|
191
|
+
* INVARIANT — a 65% gross margin floor:
|
|
135
192
|
* gross_margin = 1 − 1/multiplier
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
* and unexpected provider price hikes. The Vitest assertion in
|
|
140
|
-
* models.test.ts pins this — drop below 2.857 at your own risk.
|
|
193
|
+
* Keeping gross_margin ≥ 0.65 means a multiplier ≥ 1/0.35 ≈ 2.857. At
|
|
194
|
+
* 4× this yields 75%, leaving buffer above the floor for FX moves and
|
|
195
|
+
* provider price rises. `pricing.test.ts` pins it.
|
|
141
196
|
*/
|
|
142
|
-
export const
|
|
197
|
+
export const DEFAULT_MARGIN_BP = 40000;
|
|
198
|
+
const BP_PER_MULTIPLE = 10000;
|
|
143
199
|
/**
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*/
|
|
147
|
-
export const DEFAULT_USD_TO_MNT_RATE = 3450;
|
|
148
|
-
/**
|
|
149
|
-
* Per-model maximum output budget used for worst-case pre-request cost
|
|
150
|
-
* estimation in checkQuota. Numbers are conservative — cap on streamed
|
|
151
|
-
* output tokens we'd actually let a single chat turn produce.
|
|
152
|
-
*/
|
|
153
|
-
export const MODEL_OUTPUT_BUDGET = {
|
|
154
|
-
"google/gemini-2.5-flash": 8000,
|
|
155
|
-
"google/gemini-2.5-pro": 8000,
|
|
156
|
-
"openai/gpt-5-mini": 8000,
|
|
157
|
-
"openai/gpt-5.4-mini": 8000,
|
|
158
|
-
"openai/o4-mini": 8000,
|
|
159
|
-
"anthropic/claude-sonnet-4-6": 8000,
|
|
160
|
-
};
|
|
161
|
-
/**
|
|
162
|
-
* Convert raw model cost to MNT user-facing charged amount.
|
|
200
|
+
* Provider cost and what the reader is charged for it: cost × margin,
|
|
201
|
+
* converted into the deployment's currency at its own rate.
|
|
163
202
|
*
|
|
164
|
-
*
|
|
203
|
+
* Both steps round up, so a charge is at most two micros over — a
|
|
204
|
+
* millionth of a cent, against a fraction that would otherwise be the
|
|
205
|
+
* deployment's to eat on every request.
|
|
165
206
|
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
207
|
+
* @throws {UnknownModelError} when the id has no registered price.
|
|
208
|
+
* @throws {MoneyError} when a USD deployment passes a rate that is not 1.
|
|
168
209
|
*/
|
|
169
|
-
export function
|
|
170
|
-
const
|
|
171
|
-
const
|
|
172
|
-
return {
|
|
210
|
+
export function chargeFor(modelId, inputTokens, outputTokens, rate) {
|
|
211
|
+
const cost = providerCost(modelId, inputTokens, outputTokens);
|
|
212
|
+
const withMargin = money(Math.ceil((cost.amount * rate.marginBp) / BP_PER_MULTIPLE), PROVIDER_CURRENCY);
|
|
213
|
+
return {
|
|
214
|
+
providerCost: cost,
|
|
215
|
+
charged: convert(withMargin, rate.currency, rate.usdRateMicros),
|
|
216
|
+
};
|
|
173
217
|
}
|
|
174
218
|
/**
|
|
175
|
-
*
|
|
176
|
-
* model
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
219
|
+
* The ceiling one turn on this model could charge, for admission:
|
|
220
|
+
* the model's own output limit against a 16K input budget (system
|
|
221
|
+
* prompt plus history). Refusing on the ceiling is what stops a turn
|
|
222
|
+
* that cannot be paid for from burning provider tokens first.
|
|
223
|
+
*
|
|
224
|
+
* @throws {UnknownModelError} when the id has no registered price.
|
|
180
225
|
*/
|
|
181
|
-
export function
|
|
182
|
-
const outputBudget =
|
|
183
|
-
|
|
184
|
-
return calculateChargedMnt(modelId, inputBudget, outputBudget, fxRate, margin)
|
|
185
|
-
.chargedMnt;
|
|
226
|
+
export function estimateWorstCaseCharge(modelId, rate) {
|
|
227
|
+
const outputBudget = requireModel(modelId).maxOutputTokens;
|
|
228
|
+
return chargeFor(modelId, ESTIMATE_INPUT_BUDGET, outputBudget, rate).charged;
|
|
186
229
|
}
|
|
230
|
+
const ESTIMATE_INPUT_BUDGET = 16000;
|
|
187
231
|
//# sourceMappingURL=pricing.js.map
|
package/dist/pricing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pricing.js","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"pricing.js","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"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"}
|