@cat-factory/spend 0.6.0
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/LICENSE +21 -0
- package/dist/SpendService.d.ts +43 -0
- package/dist/SpendService.d.ts.map +1 -0
- package/dist/SpendService.js +66 -0
- package/dist/SpendService.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/pricing.d.ts +41 -0
- package/dist/pricing.d.ts.map +1 -0
- package/dist/pricing.js +96 -0
- package/dist/pricing.js.map +1 -0
- package/package.json +32 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Igor Savin
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { SpendStatus } from '@cat-factory/contracts';
|
|
2
|
+
import type { AgentTokenUsage } from '@cat-factory/kernel';
|
|
3
|
+
import type { Clock, IdGenerator } from '@cat-factory/kernel';
|
|
4
|
+
import type { TokenUsageRepository } from '@cat-factory/kernel';
|
|
5
|
+
import { type SpendPricing } from './pricing.js';
|
|
6
|
+
export interface SpendServiceDependencies {
|
|
7
|
+
tokenUsageRepository: TokenUsageRepository;
|
|
8
|
+
idGenerator: IdGenerator;
|
|
9
|
+
clock: Clock;
|
|
10
|
+
pricing: SpendPricing;
|
|
11
|
+
}
|
|
12
|
+
/** Details of a single metered LLM call, handed in by the execution engine. */
|
|
13
|
+
export interface RecordUsageInput {
|
|
14
|
+
workspaceId: string;
|
|
15
|
+
executionId: string | null;
|
|
16
|
+
agentKind: string;
|
|
17
|
+
/** Model identifier as `provider:model` (as produced by AgentRunResult.model). */
|
|
18
|
+
model: string;
|
|
19
|
+
usage: AgentTokenUsage;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The spend safeguard. It meters token usage into a persistent ledger, prices
|
|
23
|
+
* each call into a single currency, and reports the current billing period's
|
|
24
|
+
* spend against the configured budget. The execution engine consults
|
|
25
|
+
* {@link isOverBudget} before every agent step and pauses when the budget is
|
|
26
|
+
* exhausted; the worker surfaces {@link status} so the frontend can warn.
|
|
27
|
+
*/
|
|
28
|
+
export declare class SpendService {
|
|
29
|
+
private readonly tokenUsageRepository;
|
|
30
|
+
private readonly idGenerator;
|
|
31
|
+
private readonly clock;
|
|
32
|
+
private readonly pricing;
|
|
33
|
+
constructor({ tokenUsageRepository, idGenerator, clock, pricing }: SpendServiceDependencies);
|
|
34
|
+
/** Parse a `provider:model` identifier into a {@link ModelRef}. */
|
|
35
|
+
private parseModel;
|
|
36
|
+
/** Meter and persist one LLM call; returns its estimated cost. */
|
|
37
|
+
record(input: RecordUsageInput): Promise<number>;
|
|
38
|
+
/** The current billing period's spend against the configured budget. */
|
|
39
|
+
status(): Promise<SpendStatus>;
|
|
40
|
+
/** Whether this period's spend has reached the budget (runs should pause). */
|
|
41
|
+
isOverBudget(): Promise<boolean>;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=SpendService.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpendService.d.ts","sourceRoot":"","sources":["../src/SpendService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AACzD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAE1D,OAAO,KAAK,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAA;AAC7D,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,EAAE,KAAK,YAAY,EAAiC,MAAM,cAAc,CAAA;AAE/E,MAAM,WAAW,wBAAwB;IACvC,oBAAoB,EAAE,oBAAoB,CAAA;IAC1C,WAAW,EAAE,WAAW,CAAA;IACxB,KAAK,EAAE,KAAK,CAAA;IACZ,OAAO,EAAE,YAAY,CAAA;CACtB;AAED,+EAA+E;AAC/E,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,kFAAkF;IAClF,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,eAAe,CAAA;CACvB;AAED;;;;;;GAMG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAsB;IAC3D,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAa;IACzC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAO;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IAEtC,YAAY,EAAE,oBAAoB,EAAE,WAAW,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,wBAAwB,EAK1F;IAED,mEAAmE;IACnE,OAAO,CAAC,UAAU;IAMlB,kEAAkE;IAC5D,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBrD;IAED,wEAAwE;IAClE,MAAM,IAAI,OAAO,CAAC,WAAW,CAAC,CAYnC;IAED,8EAA8E;IACxE,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC,CAIrC;CACF"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { estimateCost, startOfMonthUtc } from './pricing.js';
|
|
2
|
+
/**
|
|
3
|
+
* The spend safeguard. It meters token usage into a persistent ledger, prices
|
|
4
|
+
* each call into a single currency, and reports the current billing period's
|
|
5
|
+
* spend against the configured budget. The execution engine consults
|
|
6
|
+
* {@link isOverBudget} before every agent step and pauses when the budget is
|
|
7
|
+
* exhausted; the worker surfaces {@link status} so the frontend can warn.
|
|
8
|
+
*/
|
|
9
|
+
export class SpendService {
|
|
10
|
+
tokenUsageRepository;
|
|
11
|
+
idGenerator;
|
|
12
|
+
clock;
|
|
13
|
+
pricing;
|
|
14
|
+
constructor({ tokenUsageRepository, idGenerator, clock, pricing }) {
|
|
15
|
+
this.tokenUsageRepository = tokenUsageRepository;
|
|
16
|
+
this.idGenerator = idGenerator;
|
|
17
|
+
this.clock = clock;
|
|
18
|
+
this.pricing = pricing;
|
|
19
|
+
}
|
|
20
|
+
/** Parse a `provider:model` identifier into a {@link ModelRef}. */
|
|
21
|
+
parseModel(model) {
|
|
22
|
+
const idx = model.indexOf(':');
|
|
23
|
+
if (idx === -1)
|
|
24
|
+
return { provider: model, model: '' };
|
|
25
|
+
return { provider: model.slice(0, idx), model: model.slice(idx + 1) };
|
|
26
|
+
}
|
|
27
|
+
/** Meter and persist one LLM call; returns its estimated cost. */
|
|
28
|
+
async record(input) {
|
|
29
|
+
const ref = this.parseModel(input.model);
|
|
30
|
+
const costEstimate = estimateCost(this.pricing, ref, input.usage);
|
|
31
|
+
await this.tokenUsageRepository.record({
|
|
32
|
+
id: this.idGenerator.next('tok'),
|
|
33
|
+
workspaceId: input.workspaceId,
|
|
34
|
+
executionId: input.executionId,
|
|
35
|
+
agentKind: input.agentKind,
|
|
36
|
+
provider: ref.provider,
|
|
37
|
+
model: ref.model,
|
|
38
|
+
inputTokens: input.usage.inputTokens,
|
|
39
|
+
outputTokens: input.usage.outputTokens,
|
|
40
|
+
costEstimate,
|
|
41
|
+
createdAt: this.clock.now(),
|
|
42
|
+
});
|
|
43
|
+
return costEstimate;
|
|
44
|
+
}
|
|
45
|
+
/** The current billing period's spend against the configured budget. */
|
|
46
|
+
async status() {
|
|
47
|
+
const periodStart = startOfMonthUtc(this.clock.now());
|
|
48
|
+
const totals = await this.tokenUsageRepository.totalsSince(periodStart);
|
|
49
|
+
return {
|
|
50
|
+
periodStart,
|
|
51
|
+
inputTokens: totals.inputTokens,
|
|
52
|
+
outputTokens: totals.outputTokens,
|
|
53
|
+
costSpent: totals.costEstimate,
|
|
54
|
+
costLimit: this.pricing.monthlyLimit,
|
|
55
|
+
currency: this.pricing.currency,
|
|
56
|
+
exceeded: totals.costEstimate >= this.pricing.monthlyLimit,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Whether this period's spend has reached the budget (runs should pause). */
|
|
60
|
+
async isOverBudget() {
|
|
61
|
+
const periodStart = startOfMonthUtc(this.clock.now());
|
|
62
|
+
const totals = await this.tokenUsageRepository.totalsSince(periodStart);
|
|
63
|
+
return totals.costEstimate >= this.pricing.monthlyLimit;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=SpendService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SpendService.js","sourceRoot":"","sources":["../src/SpendService.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { SpendService, type SpendServiceDependencies, type RecordUsageInput, } from './SpendService.js';
|
|
2
|
+
export { type ModelPrice, type SpendPricing, DEFAULT_MODEL_PRICES, DEFAULT_MONTHLY_LIMIT_EUR, DEFAULT_SPEND_PRICING, priceFor, modelCostResolver, estimateCost, startOfMonthUtc, } from './pricing.js';
|
|
3
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,YAAY,EACZ,KAAK,wBAAwB,EAC7B,KAAK,gBAAgB,GACtB,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EACL,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,oBAAoB,EACpB,yBAAyB,EACzB,qBAAqB,EACrB,QAAQ,EACR,iBAAiB,EACjB,YAAY,EACZ,eAAe,GAChB,MAAM,cAAc,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
// Pricing tables and spend metering/gating for @cat-factory.
|
|
2
|
+
export { SpendService, } from './SpendService.js';
|
|
3
|
+
export { DEFAULT_MODEL_PRICES, DEFAULT_MONTHLY_LIMIT_EUR, DEFAULT_SPEND_PRICING, priceFor, modelCostResolver, estimateCost, startOfMonthUtc, } from './pricing.js';
|
|
4
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,6DAA6D;AAE7D,OAAO,EACL,YAAY,GAGb,MAAM,mBAAmB,CAAA;AAC1B,OAAO,EAGL,oBAAoB,EACpB,yBAAyB,EACzB,qBAAqB,EACrB,QAAQ,EACR,iBAAiB,EACjB,YAAY,EACZ,eAAe,GAChB,MAAM,cAAc,CAAA"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ModelRef } from '@cat-factory/kernel';
|
|
2
|
+
import type { AgentTokenUsage } from '@cat-factory/kernel';
|
|
3
|
+
/** Price per 1M input/output tokens for one model. */
|
|
4
|
+
export interface ModelPrice {
|
|
5
|
+
inputPerMillion: number;
|
|
6
|
+
outputPerMillion: number;
|
|
7
|
+
}
|
|
8
|
+
export interface SpendPricing {
|
|
9
|
+
/** ISO 4217 currency all prices and budgets are expressed in. */
|
|
10
|
+
currency: string;
|
|
11
|
+
/** Budget for one billing period (a calendar month). */
|
|
12
|
+
monthlyLimit: number;
|
|
13
|
+
/** Per-model prices, keyed by `provider:model` then by bare `provider`. */
|
|
14
|
+
prices: Record<string, ModelPrice>;
|
|
15
|
+
/** Fallback price for any model without a specific or provider-level entry. */
|
|
16
|
+
defaultPrice: ModelPrice;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Built-in approximate EUR prices per 1M tokens. Keys are matched most-specific
|
|
20
|
+
* first: exact `provider:model`, then the bare `provider`, then `defaultPrice`.
|
|
21
|
+
*/
|
|
22
|
+
export declare const DEFAULT_MODEL_PRICES: Record<string, ModelPrice>;
|
|
23
|
+
/** Default budget: roughly 100 EUR of tokens per calendar month. */
|
|
24
|
+
export declare const DEFAULT_MONTHLY_LIMIT_EUR = 100;
|
|
25
|
+
export declare const DEFAULT_SPEND_PRICING: SpendPricing;
|
|
26
|
+
/** Resolve the price for a model, most-specific entry first. */
|
|
27
|
+
export declare function priceFor(pricing: SpendPricing, ref: ModelRef): ModelPrice;
|
|
28
|
+
/**
|
|
29
|
+
* A {@link ModelCostResolver}-shaped closure over a {@link SpendPricing}, for the
|
|
30
|
+
* model catalog to surface each model's informational list cost in the picker.
|
|
31
|
+
*/
|
|
32
|
+
export declare function modelCostResolver(pricing: SpendPricing): (ref: ModelRef) => {
|
|
33
|
+
inputPerMillion: number;
|
|
34
|
+
outputPerMillion: number;
|
|
35
|
+
currency: string;
|
|
36
|
+
};
|
|
37
|
+
/** Cost of a single call's token usage, in the pricing currency. */
|
|
38
|
+
export declare function estimateCost(pricing: SpendPricing, ref: ModelRef, usage: AgentTokenUsage): number;
|
|
39
|
+
/** Start of the calendar month containing `epochMs`, in UTC (epoch ms). */
|
|
40
|
+
export declare function startOfMonthUtc(epochMs: number): number;
|
|
41
|
+
//# sourceMappingURL=pricing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAA;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AAY1D,sDAAsD;AACtD,MAAM,WAAW,UAAU;IACzB,eAAe,EAAE,MAAM,CAAA;IACvB,gBAAgB,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,iEAAiE;IACjE,QAAQ,EAAE,MAAM,CAAA;IAChB,wDAAwD;IACxD,YAAY,EAAE,MAAM,CAAA;IACpB,2EAA2E;IAC3E,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;IAClC,+EAA+E;IAC/E,YAAY,EAAE,UAAU,CAAA;CACzB;AAED;;;GAGG;AACH,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAmD3D,CAAA;AAED,oEAAoE;AACpE,eAAO,MAAM,yBAAyB,MAAM,CAAA;AAE5C,eAAO,MAAM,qBAAqB,EAAE,YAKnC,CAAA;AAED,gEAAgE;AAChE,wBAAgB,QAAQ,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,QAAQ,GAAG,UAAU,CAMzE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,YAAY,GACpB,CAAC,GAAG,EAAE,QAAQ,KAAK;IAAE,eAAe,EAAE,MAAM,CAAC;IAAC,gBAAgB,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAS5F;AAED,oEAAoE;AACpE,wBAAgB,YAAY,CAAC,OAAO,EAAE,YAAY,EAAE,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,eAAe,GAAG,MAAM,CAMjG;AAED,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAGvD"}
|
package/dist/pricing.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Built-in approximate EUR prices per 1M tokens. Keys are matched most-specific
|
|
3
|
+
* first: exact `provider:model`, then the bare `provider`, then `defaultPrice`.
|
|
4
|
+
*/
|
|
5
|
+
export const DEFAULT_MODEL_PRICES = {
|
|
6
|
+
// Anthropic (list prices from the Claude model catalog, USD→EUR ~0.92).
|
|
7
|
+
'anthropic:claude-opus-4-8': { inputPerMillion: 4.6, outputPerMillion: 23 },
|
|
8
|
+
'anthropic:claude-sonnet-4-6': { inputPerMillion: 2.76, outputPerMillion: 13.8 },
|
|
9
|
+
'anthropic:claude-haiku-4-5': { inputPerMillion: 0.92, outputPerMillion: 4.6 },
|
|
10
|
+
anthropic: { inputPerMillion: 2.76, outputPerMillion: 13.8 },
|
|
11
|
+
// OpenAI (approximate list prices, USD→EUR ~0.92).
|
|
12
|
+
'openai:gpt-4o': { inputPerMillion: 2.3, outputPerMillion: 9.2 },
|
|
13
|
+
'openai:gpt-4o-mini': { inputPerMillion: 0.14, outputPerMillion: 0.55 },
|
|
14
|
+
// ChatGPT/Codex subscription models (informational list prices, USD→EUR ~0.92).
|
|
15
|
+
'openai:gpt-5.5-codex': { inputPerMillion: 4.6, outputPerMillion: 27.6 },
|
|
16
|
+
'openai:gpt-5.4-codex': { inputPerMillion: 2.3, outputPerMillion: 13.8 },
|
|
17
|
+
openai: { inputPerMillion: 0.14, outputPerMillion: 0.55 },
|
|
18
|
+
// Cloudflare Workers AI is billed per "neuron"; treat it as roughly free.
|
|
19
|
+
'workers-ai': { inputPerMillion: 0.1, outputPerMillion: 0.1 },
|
|
20
|
+
// DeepSeek V4 Pro runs on Workers AI but is a partner model billed at provider
|
|
21
|
+
// rates (served via Fireworks), not the near-free neuron rate above, so it needs
|
|
22
|
+
// its own entry. Approximate (USD→EUR ~0.92); tune via SPEND_MODEL_PRICES.
|
|
23
|
+
'workers-ai:deepseek/deepseek-v4-pro': { inputPerMillion: 0.5, outputPerMillion: 2 },
|
|
24
|
+
// Kimi K2.6 / K2.7 likewise run on Workers AI as partner models billed at Workers
|
|
25
|
+
// AI's published per-token rate, NOT the near-free `workers-ai` neuron rate — without
|
|
26
|
+
// these explicit entries a Cloudflare-Kimi run (the default coder) would fall back to
|
|
27
|
+
// 0.1/0.1 and meter as ~0.00. Cloudflare lists both at $0.95 in / $4.00 out per 1M
|
|
28
|
+
// (USD→EUR ~0.92); this is Cloudflare's marked-up rate, well above Moonshot's direct
|
|
29
|
+
// list (`moonshot:kimi-k2.6`). The spend table has no cached-input tier, so we use the
|
|
30
|
+
// standard cache-miss input rate. See workers-ai/platform/pricing. Tune via SPEND_MODEL_PRICES.
|
|
31
|
+
'workers-ai:@cf/moonshotai/kimi-k2.6': { inputPerMillion: 0.87, outputPerMillion: 3.68 },
|
|
32
|
+
'workers-ai:@cf/moonshotai/kimi-k2.7-code': { inputPerMillion: 0.87, outputPerMillion: 3.68 },
|
|
33
|
+
// DeepSeek API (approximate list prices for deepseek-chat, USD→EUR ~0.92).
|
|
34
|
+
'deepseek:deepseek-chat': { inputPerMillion: 0.26, outputPerMillion: 1.01 },
|
|
35
|
+
deepseek: { inputPerMillion: 0.26, outputPerMillion: 1.01 },
|
|
36
|
+
// Alibaba DashScope (approximate qwen3-max list prices, USD→EUR ~0.92).
|
|
37
|
+
'qwen:qwen3-max': { inputPerMillion: 1.1, outputPerMillion: 5.5 },
|
|
38
|
+
qwen: { inputPerMillion: 1.1, outputPerMillion: 5.5 },
|
|
39
|
+
// Moonshot AI direct (approximate kimi-k2.6 list prices, USD→EUR ~0.92).
|
|
40
|
+
'moonshot:kimi-k2.6': { inputPerMillion: 0.55, outputPerMillion: 2.3 },
|
|
41
|
+
moonshot: { inputPerMillion: 0.55, outputPerMillion: 2.3 },
|
|
42
|
+
// OpenRouter — a passthrough gateway billed at the underlying provider's rates (no
|
|
43
|
+
// per-token markup), so each curated model carries the upstream vendor's list price
|
|
44
|
+
// (USD→EUR ~0.92). Keyed by the OpenRouter `vendor/model` slug. The bare `openrouter`
|
|
45
|
+
// fallback is a mid-range guess for any uncatalogued slug — tune via SPEND_MODEL_PRICES.
|
|
46
|
+
'openrouter:anthropic/claude-opus-4.8': { inputPerMillion: 4.6, outputPerMillion: 23 },
|
|
47
|
+
'openrouter:google/gemini-3-pro': { inputPerMillion: 1.84, outputPerMillion: 11.04 },
|
|
48
|
+
'openrouter:openai/gpt-5.5': { inputPerMillion: 3.68, outputPerMillion: 22.08 },
|
|
49
|
+
'openrouter:deepseek/deepseek-chat': { inputPerMillion: 0.26, outputPerMillion: 1.01 },
|
|
50
|
+
'openrouter:meta-llama/llama-3.3-70b-instruct': { inputPerMillion: 0.12, outputPerMillion: 0.37 },
|
|
51
|
+
openrouter: { inputPerMillion: 1.84, outputPerMillion: 11.04 },
|
|
52
|
+
// LiteLLM — an operator-hosted gateway whose true cost depends entirely on the backend
|
|
53
|
+
// model it routes to, which we can't know here. Default to the generic fallback rate;
|
|
54
|
+
// operators should override per their routing via SPEND_MODEL_PRICES.
|
|
55
|
+
litellm: { inputPerMillion: 0.14, outputPerMillion: 0.55 },
|
|
56
|
+
};
|
|
57
|
+
/** Default budget: roughly 100 EUR of tokens per calendar month. */
|
|
58
|
+
export const DEFAULT_MONTHLY_LIMIT_EUR = 100;
|
|
59
|
+
export const DEFAULT_SPEND_PRICING = {
|
|
60
|
+
currency: 'EUR',
|
|
61
|
+
monthlyLimit: DEFAULT_MONTHLY_LIMIT_EUR,
|
|
62
|
+
prices: DEFAULT_MODEL_PRICES,
|
|
63
|
+
defaultPrice: { inputPerMillion: 0.14, outputPerMillion: 0.55 },
|
|
64
|
+
};
|
|
65
|
+
/** Resolve the price for a model, most-specific entry first. */
|
|
66
|
+
export function priceFor(pricing, ref) {
|
|
67
|
+
return (pricing.prices[`${ref.provider}:${ref.model}`] ??
|
|
68
|
+
pricing.prices[ref.provider] ??
|
|
69
|
+
pricing.defaultPrice);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* A {@link ModelCostResolver}-shaped closure over a {@link SpendPricing}, for the
|
|
73
|
+
* model catalog to surface each model's informational list cost in the picker.
|
|
74
|
+
*/
|
|
75
|
+
export function modelCostResolver(pricing) {
|
|
76
|
+
return (ref) => {
|
|
77
|
+
const price = priceFor(pricing, ref);
|
|
78
|
+
return {
|
|
79
|
+
inputPerMillion: price.inputPerMillion,
|
|
80
|
+
outputPerMillion: price.outputPerMillion,
|
|
81
|
+
currency: pricing.currency,
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/** Cost of a single call's token usage, in the pricing currency. */
|
|
86
|
+
export function estimateCost(pricing, ref, usage) {
|
|
87
|
+
const price = priceFor(pricing, ref);
|
|
88
|
+
return ((usage.inputTokens / 1_000_000) * price.inputPerMillion +
|
|
89
|
+
(usage.outputTokens / 1_000_000) * price.outputPerMillion);
|
|
90
|
+
}
|
|
91
|
+
/** Start of the calendar month containing `epochMs`, in UTC (epoch ms). */
|
|
92
|
+
export function startOfMonthUtc(epochMs) {
|
|
93
|
+
const d = new Date(epochMs);
|
|
94
|
+
return Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), 1);
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=pricing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pricing.js","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cat-factory/spend",
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Pricing tables and spend metering for the Agent Architecture Board.",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist"
|
|
7
|
+
],
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "./dist/index.js",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./package.json": "./package.json"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@cat-factory/contracts": "0.6.0",
|
|
23
|
+
"@cat-factory/kernel": "0.6.0"
|
|
24
|
+
},
|
|
25
|
+
"devDependencies": {
|
|
26
|
+
"typescript": "7.0.1-rc"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc -b tsconfig.build.json",
|
|
30
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
31
|
+
}
|
|
32
|
+
}
|