@cat-factory/spend 0.8.26 → 0.9.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/dist/SpendService.d.ts +29 -7
- package/dist/SpendService.d.ts.map +1 -1
- package/dist/SpendService.js +49 -16
- package/dist/SpendService.js.map +1 -1
- package/dist/pricing.d.ts +10 -1
- package/dist/pricing.d.ts.map +1 -1
- package/dist/pricing.js +24 -3
- package/dist/pricing.js.map +1 -1
- package/package.json +3 -3
package/dist/SpendService.d.ts
CHANGED
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import type { OpenRouterModelMeta, SpendStatus } from '@cat-factory/contracts';
|
|
2
2
|
import type { AgentTokenUsage } from '@cat-factory/kernel';
|
|
3
3
|
import type { Clock, IdGenerator } from '@cat-factory/kernel';
|
|
4
|
-
import type { TokenUsageRepository } from '@cat-factory/kernel';
|
|
4
|
+
import type { TokenUsageRepository, WorkspaceSettingsRepository } from '@cat-factory/kernel';
|
|
5
5
|
import { type SpendPricing } from './pricing.js';
|
|
6
6
|
export interface SpendServiceDependencies {
|
|
7
7
|
tokenUsageRepository: TokenUsageRepository;
|
|
8
8
|
idGenerator: IdGenerator;
|
|
9
9
|
clock: Clock;
|
|
10
|
+
/** The base (built-in) pricing table + the deployment fallback budget/currency. */
|
|
10
11
|
pricing: SpendPricing;
|
|
12
|
+
/**
|
|
13
|
+
* Per-workspace budget overrides (currency / monthly limit / per-model prices),
|
|
14
|
+
* persisted on the workspace settings row. When wired, {@link SpendService}
|
|
15
|
+
* resolves each workspace's effective pricing by overlaying its overrides onto the
|
|
16
|
+
* base table; absent ⇒ every workspace uses the base table (tests/conformance).
|
|
17
|
+
*/
|
|
18
|
+
workspaceSettingsRepository?: WorkspaceSettingsRepository;
|
|
11
19
|
/**
|
|
12
20
|
* Optional resolver for a workspace's dynamic gateway model prices (the enabled
|
|
13
21
|
* OpenRouter catalog). When wired, a metered `openrouter:<slug>` call is priced at the
|
|
@@ -29,24 +37,38 @@ export interface RecordUsageInput {
|
|
|
29
37
|
/**
|
|
30
38
|
* The spend safeguard. It meters token usage into a persistent ledger, prices
|
|
31
39
|
* each call into a single currency, and reports the current billing period's
|
|
32
|
-
* spend against the
|
|
40
|
+
* spend against the workspace's budget. The execution engine consults
|
|
33
41
|
* {@link isOverBudget} before every agent step and pauses when the budget is
|
|
34
42
|
* exhausted; the worker surfaces {@link status} so the frontend can warn.
|
|
43
|
+
*
|
|
44
|
+
* Budgets are per-workspace: each workspace's currency/monthly-limit/price overrides
|
|
45
|
+
* (on its settings row) are overlaid onto the built-in base table. Resolution is
|
|
46
|
+
* cached briefly so the hot {@link isOverBudget} gate doesn't re-read settings per step.
|
|
35
47
|
*/
|
|
36
48
|
export declare class SpendService {
|
|
37
49
|
private readonly tokenUsageRepository;
|
|
38
50
|
private readonly idGenerator;
|
|
39
51
|
private readonly clock;
|
|
40
52
|
private readonly pricing;
|
|
53
|
+
private readonly workspaceSettingsRepository?;
|
|
41
54
|
private readonly dynamicPricesFor?;
|
|
42
|
-
|
|
55
|
+
private readonly pricingCache;
|
|
56
|
+
constructor({ tokenUsageRepository, idGenerator, clock, pricing, workspaceSettingsRepository, dynamicPricesFor, }: SpendServiceDependencies);
|
|
43
57
|
/** Parse a `provider:model` identifier into a {@link ModelRef}. */
|
|
44
58
|
private parseModel;
|
|
59
|
+
/**
|
|
60
|
+
* The workspace's effective pricing (base table overlaid with its budget overrides),
|
|
61
|
+
* cached for {@link PRICING_CACHE_TTL_MS}. Falls back to the base table when no
|
|
62
|
+
* settings repository is wired.
|
|
63
|
+
*/
|
|
64
|
+
private resolvePricing;
|
|
65
|
+
/** Invalidate a workspace's cached pricing (called after a budget edit). */
|
|
66
|
+
invalidatePricing(workspaceId: string): void;
|
|
45
67
|
/** Meter and persist one LLM call; returns its estimated cost. */
|
|
46
68
|
record(input: RecordUsageInput): Promise<number>;
|
|
47
|
-
/** The current billing period's spend against the
|
|
48
|
-
status(): Promise<SpendStatus>;
|
|
49
|
-
/** Whether this period's spend has reached the budget (runs should pause). */
|
|
50
|
-
isOverBudget(): Promise<boolean>;
|
|
69
|
+
/** The current billing period's spend against the workspace's budget. */
|
|
70
|
+
status(workspaceId: string): Promise<SpendStatus>;
|
|
71
|
+
/** Whether this period's spend has reached the workspace's budget (runs should pause). */
|
|
72
|
+
isOverBudget(workspaceId: string): Promise<boolean>;
|
|
51
73
|
}
|
|
52
74
|
//# sourceMappingURL=SpendService.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpendService.d.ts","sourceRoot":"","sources":["../src/SpendService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAC9E,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;
|
|
1
|
+
{"version":3,"file":"SpendService.d.ts","sourceRoot":"","sources":["../src/SpendService.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAC9E,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,2BAA2B,EAAE,MAAM,qBAAqB,CAAA;AAC5F,OAAO,EACL,KAAK,YAAY,EAKlB,MAAM,cAAc,CAAA;AAErB,MAAM,WAAW,wBAAwB;IACvC,oBAAoB,EAAE,oBAAoB,CAAA;IAC1C,WAAW,EAAE,WAAW,CAAA;IACxB,KAAK,EAAE,KAAK,CAAA;IACZ,mFAAmF;IACnF,OAAO,EAAE,YAAY,CAAA;IACrB;;;;;OAKG;IACH,2BAA2B,CAAC,EAAE,2BAA2B,CAAA;IACzD;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,OAAO,CAAC,mBAAmB,EAAE,CAAC,CAAA;CAC3E;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;AAKD;;;;;;;;;;GAUG;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;IACtC,OAAO,CAAC,QAAQ,CAAC,2BAA2B,CAAC,CAA6B;IAC1E,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAC,CAAyD;IAC3F,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAgE;IAE7F,YAAY,EACV,oBAAoB,EACpB,WAAW,EACX,KAAK,EACL,OAAO,EACP,2BAA2B,EAC3B,gBAAgB,GACjB,EAAE,wBAAwB,EAO1B;IAED,mEAAmE;IACnE,OAAO,CAAC,UAAU;IAMlB;;;;OAIG;YACW,cAAc;IAW5B,4EAA4E;IAC5E,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAE3C;IAED,kEAAkE;IAC5D,MAAM,CAAC,KAAK,EAAE,gBAAgB,GAAG,OAAO,CAAC,MAAM,CAAC,CAuBrD;IAED,yEAAyE;IACnE,MAAM,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAatD;IAED,0FAA0F;IACpF,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAKxD;CACF"}
|
package/dist/SpendService.js
CHANGED
|
@@ -1,22 +1,31 @@
|
|
|
1
|
-
import { estimateCost, startOfMonthUtc, withDynamicPrices } from './pricing.js';
|
|
1
|
+
import { estimateCost, mergeSpendPricing, startOfMonthUtc, withDynamicPrices, } from './pricing.js';
|
|
2
|
+
/** How long a workspace's resolved pricing is cached before reloading (ms). */
|
|
3
|
+
const PRICING_CACHE_TTL_MS = 30_000;
|
|
2
4
|
/**
|
|
3
5
|
* The spend safeguard. It meters token usage into a persistent ledger, prices
|
|
4
6
|
* each call into a single currency, and reports the current billing period's
|
|
5
|
-
* spend against the
|
|
7
|
+
* spend against the workspace's budget. The execution engine consults
|
|
6
8
|
* {@link isOverBudget} before every agent step and pauses when the budget is
|
|
7
9
|
* exhausted; the worker surfaces {@link status} so the frontend can warn.
|
|
10
|
+
*
|
|
11
|
+
* Budgets are per-workspace: each workspace's currency/monthly-limit/price overrides
|
|
12
|
+
* (on its settings row) are overlaid onto the built-in base table. Resolution is
|
|
13
|
+
* cached briefly so the hot {@link isOverBudget} gate doesn't re-read settings per step.
|
|
8
14
|
*/
|
|
9
15
|
export class SpendService {
|
|
10
16
|
tokenUsageRepository;
|
|
11
17
|
idGenerator;
|
|
12
18
|
clock;
|
|
13
19
|
pricing;
|
|
20
|
+
workspaceSettingsRepository;
|
|
14
21
|
dynamicPricesFor;
|
|
15
|
-
|
|
22
|
+
pricingCache = new Map();
|
|
23
|
+
constructor({ tokenUsageRepository, idGenerator, clock, pricing, workspaceSettingsRepository, dynamicPricesFor, }) {
|
|
16
24
|
this.tokenUsageRepository = tokenUsageRepository;
|
|
17
25
|
this.idGenerator = idGenerator;
|
|
18
26
|
this.clock = clock;
|
|
19
27
|
this.pricing = pricing;
|
|
28
|
+
this.workspaceSettingsRepository = workspaceSettingsRepository;
|
|
20
29
|
this.dynamicPricesFor = dynamicPricesFor;
|
|
21
30
|
}
|
|
22
31
|
/** Parse a `provider:model` identifier into a {@link ModelRef}. */
|
|
@@ -26,14 +35,36 @@ export class SpendService {
|
|
|
26
35
|
return { provider: model, model: '' };
|
|
27
36
|
return { provider: model.slice(0, idx), model: model.slice(idx + 1) };
|
|
28
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* The workspace's effective pricing (base table overlaid with its budget overrides),
|
|
40
|
+
* cached for {@link PRICING_CACHE_TTL_MS}. Falls back to the base table when no
|
|
41
|
+
* settings repository is wired.
|
|
42
|
+
*/
|
|
43
|
+
async resolvePricing(workspaceId) {
|
|
44
|
+
if (!this.workspaceSettingsRepository)
|
|
45
|
+
return this.pricing;
|
|
46
|
+
const cached = this.pricingCache.get(workspaceId);
|
|
47
|
+
const now = this.clock.now();
|
|
48
|
+
if (cached && cached.expiresAt > now)
|
|
49
|
+
return cached.value;
|
|
50
|
+
const settings = await this.workspaceSettingsRepository.get(workspaceId);
|
|
51
|
+
const value = mergeSpendPricing(this.pricing, settings);
|
|
52
|
+
this.pricingCache.set(workspaceId, { value, expiresAt: now + PRICING_CACHE_TTL_MS });
|
|
53
|
+
return value;
|
|
54
|
+
}
|
|
55
|
+
/** Invalidate a workspace's cached pricing (called after a budget edit). */
|
|
56
|
+
invalidatePricing(workspaceId) {
|
|
57
|
+
this.pricingCache.delete(workspaceId);
|
|
58
|
+
}
|
|
29
59
|
/** Meter and persist one LLM call; returns its estimated cost. */
|
|
30
60
|
async record(input) {
|
|
31
61
|
const ref = this.parseModel(input.model);
|
|
62
|
+
const base = await this.resolvePricing(input.workspaceId);
|
|
32
63
|
// Price a dynamic OpenRouter gateway model at its real per-model rate (overlaid onto
|
|
33
|
-
// the
|
|
64
|
+
// the resolved table) rather than the bare-`openrouter` fallback, when the resolver is wired.
|
|
34
65
|
const pricing = ref.provider === 'openrouter' && this.dynamicPricesFor
|
|
35
|
-
? withDynamicPrices(
|
|
36
|
-
:
|
|
66
|
+
? withDynamicPrices(base, await this.dynamicPricesFor(input.workspaceId))
|
|
67
|
+
: base;
|
|
37
68
|
const costEstimate = estimateCost(pricing, ref, input.usage);
|
|
38
69
|
await this.tokenUsageRepository.record({
|
|
39
70
|
id: this.idGenerator.next('tok'),
|
|
@@ -49,25 +80,27 @@ export class SpendService {
|
|
|
49
80
|
});
|
|
50
81
|
return costEstimate;
|
|
51
82
|
}
|
|
52
|
-
/** The current billing period's spend against the
|
|
53
|
-
async status() {
|
|
83
|
+
/** The current billing period's spend against the workspace's budget. */
|
|
84
|
+
async status(workspaceId) {
|
|
85
|
+
const pricing = await this.resolvePricing(workspaceId);
|
|
54
86
|
const periodStart = startOfMonthUtc(this.clock.now());
|
|
55
|
-
const totals = await this.tokenUsageRepository.
|
|
87
|
+
const totals = await this.tokenUsageRepository.totalsSinceForWorkspace(workspaceId, periodStart);
|
|
56
88
|
return {
|
|
57
89
|
periodStart,
|
|
58
90
|
inputTokens: totals.inputTokens,
|
|
59
91
|
outputTokens: totals.outputTokens,
|
|
60
92
|
costSpent: totals.costEstimate,
|
|
61
|
-
costLimit:
|
|
62
|
-
currency:
|
|
63
|
-
exceeded: totals.costEstimate >=
|
|
93
|
+
costLimit: pricing.monthlyLimit,
|
|
94
|
+
currency: pricing.currency,
|
|
95
|
+
exceeded: totals.costEstimate >= pricing.monthlyLimit,
|
|
64
96
|
};
|
|
65
97
|
}
|
|
66
|
-
/** Whether this period's spend has reached the budget (runs should pause). */
|
|
67
|
-
async isOverBudget() {
|
|
98
|
+
/** Whether this period's spend has reached the workspace's budget (runs should pause). */
|
|
99
|
+
async isOverBudget(workspaceId) {
|
|
100
|
+
const pricing = await this.resolvePricing(workspaceId);
|
|
68
101
|
const periodStart = startOfMonthUtc(this.clock.now());
|
|
69
|
-
const totals = await this.tokenUsageRepository.
|
|
70
|
-
return totals.costEstimate >=
|
|
102
|
+
const totals = await this.tokenUsageRepository.totalsSinceForWorkspace(workspaceId, periodStart);
|
|
103
|
+
return totals.costEstimate >= pricing.monthlyLimit;
|
|
71
104
|
}
|
|
72
105
|
}
|
|
73
106
|
//# sourceMappingURL=SpendService.js.map
|
package/dist/SpendService.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpendService.js","sourceRoot":"","sources":["../src/SpendService.ts"],"names":[],"mappings":"AAKA,OAAO,
|
|
1
|
+
{"version":3,"file":"SpendService.js","sourceRoot":"","sources":["../src/SpendService.ts"],"names":[],"mappings":"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"}
|
package/dist/pricing.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ModelRef } from '@cat-factory/kernel';
|
|
2
2
|
import type { AgentTokenUsage } from '@cat-factory/kernel';
|
|
3
|
-
import type { OpenRouterModelMeta } from '@cat-factory/contracts';
|
|
3
|
+
import type { OpenRouterModelMeta, WorkspaceSettings } from '@cat-factory/contracts';
|
|
4
4
|
/** Price per 1M input/output tokens for one model. */
|
|
5
5
|
export interface ModelPrice {
|
|
6
6
|
inputPerMillion: number;
|
|
@@ -38,6 +38,15 @@ export declare const DEFAULT_SPEND_PRICING: SpendPricing;
|
|
|
38
38
|
* fallback instead of being metered at zero.
|
|
39
39
|
*/
|
|
40
40
|
export declare function withDynamicPrices(pricing: SpendPricing, models: OpenRouterModelMeta[]): SpendPricing;
|
|
41
|
+
/**
|
|
42
|
+
* Resolve a workspace's effective pricing from the base table + its per-workspace
|
|
43
|
+
* budget overrides (currency / monthly limit / per-model price overrides). A null
|
|
44
|
+
* override falls back to the base value, so an unconfigured workspace gets the
|
|
45
|
+
* built-in defaults unchanged. Per-model overrides are overlaid onto the base table
|
|
46
|
+
* (most-specific-first resolution in {@link priceFor} is preserved). Returns a new
|
|
47
|
+
* {@link SpendPricing}; the input is not mutated.
|
|
48
|
+
*/
|
|
49
|
+
export declare function mergeSpendPricing(base: SpendPricing, overrides: Pick<WorkspaceSettings, 'spendCurrency' | 'spendMonthlyLimit' | 'spendModelPrices'> | null): SpendPricing;
|
|
41
50
|
/** Resolve the price for a model, most-specific entry first. */
|
|
42
51
|
export declare function priceFor(pricing: SpendPricing, ref: ModelRef): ModelPrice;
|
|
43
52
|
/**
|
package/dist/pricing.d.ts.map
CHANGED
|
@@ -1 +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;AAC1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAA;
|
|
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;AAC1D,OAAO,KAAK,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAYpF,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,CAqD3D,CAAA;AAED,oEAAoE;AACpE,eAAO,MAAM,yBAAyB,MAAM,CAAA;AAE5C,eAAO,MAAM,qBAAqB,EAAE,YAKnC,CAAA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,YAAY,EACrB,MAAM,EAAE,mBAAmB,EAAE,GAC5B,YAAY,CAWd;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,YAAY,EAClB,SAAS,EAAE,IAAI,CACb,iBAAiB,EACjB,eAAe,GAAG,mBAAmB,GAAG,kBAAkB,CAC3D,GAAG,IAAI,GACP,YAAY,CAWd;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
CHANGED
|
@@ -19,7 +19,7 @@ export const DEFAULT_MODEL_PRICES = {
|
|
|
19
19
|
'workers-ai': { inputPerMillion: 0.1, outputPerMillion: 0.1 },
|
|
20
20
|
// DeepSeek V4 Pro runs on Workers AI but is a partner model billed at provider
|
|
21
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
|
|
22
|
+
// its own entry. Approximate (USD→EUR ~0.92); tune via the per-workspace price overrides.
|
|
23
23
|
'workers-ai:deepseek/deepseek-v4-pro': { inputPerMillion: 0.5, outputPerMillion: 2 },
|
|
24
24
|
// Kimi K2.5 / K2.6 / K2.7 likewise run on Workers AI as partner models billed at Workers
|
|
25
25
|
// AI's published per-token rate, NOT the near-free `workers-ai` neuron rate — without
|
|
@@ -44,7 +44,7 @@ export const DEFAULT_MODEL_PRICES = {
|
|
|
44
44
|
// OpenRouter — a passthrough gateway billed at the underlying provider's rates (no
|
|
45
45
|
// per-token markup), so each curated model carries the upstream vendor's list price
|
|
46
46
|
// (USD→EUR ~0.92). Keyed by the OpenRouter `vendor/model` slug. The bare `openrouter`
|
|
47
|
-
// fallback is a mid-range guess for any uncatalogued slug — tune via
|
|
47
|
+
// fallback is a mid-range guess for any uncatalogued slug — tune via the per-workspace price overrides.
|
|
48
48
|
'openrouter:anthropic/claude-opus-4.8': { inputPerMillion: 4.6, outputPerMillion: 23 },
|
|
49
49
|
'openrouter:google/gemini-3-pro': { inputPerMillion: 1.84, outputPerMillion: 11.04 },
|
|
50
50
|
'openrouter:openai/gpt-5.5': { inputPerMillion: 3.68, outputPerMillion: 22.08 },
|
|
@@ -53,7 +53,7 @@ export const DEFAULT_MODEL_PRICES = {
|
|
|
53
53
|
openrouter: { inputPerMillion: 1.84, outputPerMillion: 11.04 },
|
|
54
54
|
// LiteLLM — an operator-hosted gateway whose true cost depends entirely on the backend
|
|
55
55
|
// model it routes to, which we can't know here. Default to the generic fallback rate;
|
|
56
|
-
// operators should override per their routing via
|
|
56
|
+
// operators should override per their routing via the per-workspace price overrides.
|
|
57
57
|
litellm: { inputPerMillion: 0.14, outputPerMillion: 0.55 },
|
|
58
58
|
};
|
|
59
59
|
/** Default budget: roughly 100 EUR of tokens per calendar month. */
|
|
@@ -91,6 +91,27 @@ export function withDynamicPrices(pricing, models) {
|
|
|
91
91
|
}
|
|
92
92
|
return { ...pricing, prices };
|
|
93
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Resolve a workspace's effective pricing from the base table + its per-workspace
|
|
96
|
+
* budget overrides (currency / monthly limit / per-model price overrides). A null
|
|
97
|
+
* override falls back to the base value, so an unconfigured workspace gets the
|
|
98
|
+
* built-in defaults unchanged. Per-model overrides are overlaid onto the base table
|
|
99
|
+
* (most-specific-first resolution in {@link priceFor} is preserved). Returns a new
|
|
100
|
+
* {@link SpendPricing}; the input is not mutated.
|
|
101
|
+
*/
|
|
102
|
+
export function mergeSpendPricing(base, overrides) {
|
|
103
|
+
if (!overrides)
|
|
104
|
+
return base;
|
|
105
|
+
const prices = overrides.spendModelPrices
|
|
106
|
+
? { ...base.prices, ...overrides.spendModelPrices }
|
|
107
|
+
: base.prices;
|
|
108
|
+
return {
|
|
109
|
+
currency: overrides.spendCurrency ?? base.currency,
|
|
110
|
+
monthlyLimit: overrides.spendMonthlyLimit ?? base.monthlyLimit,
|
|
111
|
+
prices,
|
|
112
|
+
defaultPrice: base.defaultPrice,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
94
115
|
/** Resolve the price for a model, most-specific entry first. */
|
|
95
116
|
export function priceFor(pricing, ref) {
|
|
96
117
|
return (pricing.prices[`${ref.provider}:${ref.model}`] ??
|
package/dist/pricing.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pricing.js","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"AA+BA;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAA+B;IAC9D,wEAAwE;IACxE,2BAA2B,EAAE,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,EAAE,EAAE,EAAE;IAC3E,6BAA6B,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE;IAChF,4BAA4B,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,gBAAgB,EAAE,GAAG,EAAE;IAC9E,SAAS,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE;IAC5D,mDAAmD;IACnD,eAAe,EAAE,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE;IAChE,oBAAoB,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE;IACvE,gFAAgF;IAChF,sBAAsB,EAAE,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE;IACxE,sBAAsB,EAAE,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,EAAE,IAAI,EAAE;IACxE,MAAM,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,gBAAgB,EAAE,IAAI,EAAE;IACzD,0EAA0E;IAC1E,YAAY,EAAE,EAAE,eAAe,EAAE,GAAG,EAAE,gBAAgB,EAAE,GAAG,EAAE;IAC7D,+EAA+E;IAC/E,iFAAiF;IACjF,
|
|
1
|
+
{"version":3,"file":"pricing.js","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cat-factory/spend",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "Pricing tables and spend metering for the Agent Architecture Board.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"access": "public"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
|
-
"@cat-factory/contracts": "0.
|
|
28
|
-
"@cat-factory/kernel": "0.
|
|
27
|
+
"@cat-factory/contracts": "0.24.0",
|
|
28
|
+
"@cat-factory/kernel": "0.27.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"typescript": "7.0.1-rc"
|