@openagentsy/ledger 0.0.0-stage → 0.2.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.
Files changed (86) hide show
  1. package/LICENSE +674 -0
  2. package/README.md +20 -2
  3. package/data/pricing/litellm.json +8882 -0
  4. package/data/pricing/models.dev.json +12151 -0
  5. package/data/pricing/openrouter.json +1017 -0
  6. package/dist/db.d.ts +44 -0
  7. package/dist/db.d.ts.map +1 -0
  8. package/dist/db.js +97 -0
  9. package/dist/db.js.map +1 -0
  10. package/dist/export.d.ts +71 -0
  11. package/dist/export.d.ts.map +1 -0
  12. package/dist/export.js +78 -0
  13. package/dist/export.js.map +1 -0
  14. package/dist/footprint.d.ts +24 -0
  15. package/dist/footprint.d.ts.map +1 -0
  16. package/dist/footprint.js +33 -0
  17. package/dist/footprint.js.map +1 -0
  18. package/dist/git.d.ts +44 -0
  19. package/dist/git.d.ts.map +1 -0
  20. package/dist/git.js +126 -0
  21. package/dist/git.js.map +1 -0
  22. package/dist/git.test.d.ts +2 -0
  23. package/dist/git.test.d.ts.map +1 -0
  24. package/dist/git.test.js +101 -0
  25. package/dist/git.test.js.map +1 -0
  26. package/dist/index.d.ts +19 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +19 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/ingest.d.ts +133 -0
  31. package/dist/ingest.d.ts.map +1 -0
  32. package/dist/ingest.js +324 -0
  33. package/dist/ingest.js.map +1 -0
  34. package/dist/ingest.test.d.ts +2 -0
  35. package/dist/ingest.test.d.ts.map +1 -0
  36. package/dist/ingest.test.js +245 -0
  37. package/dist/ingest.test.js.map +1 -0
  38. package/dist/outcomes.d.ts +81 -0
  39. package/dist/outcomes.d.ts.map +1 -0
  40. package/dist/outcomes.js +85 -0
  41. package/dist/outcomes.js.map +1 -0
  42. package/dist/outcomes.test.d.ts +2 -0
  43. package/dist/outcomes.test.d.ts.map +1 -0
  44. package/dist/outcomes.test.js +160 -0
  45. package/dist/outcomes.test.js.map +1 -0
  46. package/dist/policy.d.ts +48 -0
  47. package/dist/policy.d.ts.map +1 -0
  48. package/dist/policy.js +99 -0
  49. package/dist/policy.js.map +1 -0
  50. package/dist/policy.test.d.ts +2 -0
  51. package/dist/policy.test.d.ts.map +1 -0
  52. package/dist/policy.test.js +126 -0
  53. package/dist/policy.test.js.map +1 -0
  54. package/dist/pricing.d.ts +92 -0
  55. package/dist/pricing.d.ts.map +1 -0
  56. package/dist/pricing.js +341 -0
  57. package/dist/pricing.js.map +1 -0
  58. package/dist/pricing.test.d.ts +2 -0
  59. package/dist/pricing.test.d.ts.map +1 -0
  60. package/dist/pricing.test.js +167 -0
  61. package/dist/pricing.test.js.map +1 -0
  62. package/dist/query.d.ts +134 -0
  63. package/dist/query.d.ts.map +1 -0
  64. package/dist/query.js +247 -0
  65. package/dist/query.js.map +1 -0
  66. package/dist/query.test.d.ts +2 -0
  67. package/dist/query.test.d.ts.map +1 -0
  68. package/dist/query.test.js +242 -0
  69. package/dist/query.test.js.map +1 -0
  70. package/dist/schema.d.ts +33 -0
  71. package/dist/schema.d.ts.map +1 -0
  72. package/dist/schema.js +139 -0
  73. package/dist/schema.js.map +1 -0
  74. package/dist/schema.test.d.ts +2 -0
  75. package/dist/schema.test.d.ts.map +1 -0
  76. package/dist/schema.test.js +129 -0
  77. package/dist/schema.test.js.map +1 -0
  78. package/dist/waste.d.ts +116 -0
  79. package/dist/waste.d.ts.map +1 -0
  80. package/dist/waste.js +553 -0
  81. package/dist/waste.js.map +1 -0
  82. package/dist/waste.test.d.ts +2 -0
  83. package/dist/waste.test.d.ts.map +1 -0
  84. package/dist/waste.test.js +278 -0
  85. package/dist/waste.test.js.map +1 -0
  86. package/package.json +52 -4
@@ -0,0 +1,341 @@
1
+ import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ /** How long a cached source stays fresh. */
6
+ export const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
7
+ /** How long a fetch may take before the chain moves on. */
8
+ export const FETCH_TIMEOUT_MS = 10_000;
9
+ /** A number, from a number or a numeric string. */
10
+ function num(value) {
11
+ if (typeof value === 'number')
12
+ return Number.isFinite(value) ? value : 0;
13
+ if (typeof value === 'string' && value.trim() !== '') {
14
+ const parsed = Number(value);
15
+ return Number.isFinite(parsed) ? parsed : 0;
16
+ }
17
+ return 0;
18
+ }
19
+ function record(value) {
20
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
21
+ ? value
22
+ : {};
23
+ }
24
+ function readString(value) {
25
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
26
+ }
27
+ /** The rates of one entry, with its bands. */
28
+ function toTiers(value) {
29
+ const entry = record(value);
30
+ const input = num(entry.input);
31
+ const output = num(entry.output);
32
+ if (input === 0 && output === 0)
33
+ return null;
34
+ const cacheRead = num(entry.cacheRead);
35
+ const cacheWrite = num(entry.cacheWrite);
36
+ const tiers = [
37
+ {
38
+ above: 0,
39
+ input,
40
+ output,
41
+ ...(cacheRead > 0 ? { cacheRead } : {}),
42
+ ...(cacheWrite > 0 ? { cacheWrite } : {}),
43
+ },
44
+ ];
45
+ const extra = Array.isArray(entry.tiers) ? entry.tiers : [];
46
+ for (const band of extra) {
47
+ const b = record(band);
48
+ const bandInput = num(b.input);
49
+ const bandOutput = num(b.output);
50
+ if (bandInput === 0 && bandOutput === 0)
51
+ continue;
52
+ const bandCacheRead = num(b.cacheRead);
53
+ const bandCacheWrite = num(b.cacheWrite);
54
+ tiers.push({
55
+ above: num(b.above),
56
+ input: bandInput,
57
+ output: bandOutput,
58
+ ...(bandCacheRead > 0 ? { cacheRead: bandCacheRead } : {}),
59
+ ...(bandCacheWrite > 0 ? { cacheWrite: bandCacheWrite } : {}),
60
+ });
61
+ }
62
+ return tiers.sort((a, b) => a.above - b.above);
63
+ }
64
+ /** The band that applies to a request of this many input tokens. */
65
+ function applicableTier(tiers, inputTokens) {
66
+ let chosen;
67
+ for (const tier of tiers) {
68
+ if (tier.above > inputTokens)
69
+ continue;
70
+ if (chosen === undefined || tier.above > chosen.above)
71
+ chosen = tier;
72
+ }
73
+ return chosen;
74
+ }
75
+ /**
76
+ * The price of the tokens.
77
+ *
78
+ * A band prices the whole request: a source that raises its rate above a
79
+ * threshold charges the higher rate for everything once the threshold is
80
+ * passed, which is what the providers do.
81
+ */
82
+ function costOf(tiers, tokens) {
83
+ const tier = applicableTier(tiers, tokens.input);
84
+ if (tier === undefined)
85
+ return null;
86
+ const output = tokens.output + (tokens.reasoning ?? 0);
87
+ const cost = (tokens.input * tier.input +
88
+ output * tier.output +
89
+ (tokens.cacheRead ?? 0) * (tier.cacheRead ?? 0) +
90
+ (tokens.cacheWrite ?? 0) * (tier.cacheWrite ?? 0)) /
91
+ 1_000_000;
92
+ return Number.isFinite(cost) && cost >= 0 ? cost : null;
93
+ }
94
+ /** The models.dev shape: a provider, then its models, then their rates. */
95
+ function reduceModelsDev(raw) {
96
+ const out = {};
97
+ for (const [providerId, provider] of Object.entries(record(raw))) {
98
+ const models = record(record(provider).models);
99
+ const rates = {};
100
+ for (const [modelId, model] of Object.entries(models)) {
101
+ const cost = record(record(model).cost);
102
+ const input = num(cost.input);
103
+ const output = num(cost.output);
104
+ if (input === 0 && output === 0)
105
+ continue;
106
+ const cacheRead = num(cost.cache_read);
107
+ const cacheWrite = num(cost.cache_write);
108
+ rates[modelId] = {
109
+ input,
110
+ output,
111
+ ...(cacheRead > 0 ? { cacheRead } : {}),
112
+ ...(cacheWrite > 0 ? { cacheWrite } : {}),
113
+ };
114
+ }
115
+ if (Object.keys(rates).length > 0)
116
+ out[providerId] = rates;
117
+ }
118
+ return out;
119
+ }
120
+ /** The LiteLLM shape: a model key, per-token rates, and its bands. */
121
+ function reduceLiteLLM(raw) {
122
+ const out = {};
123
+ for (const [key, entry] of Object.entries(record(raw))) {
124
+ const model = record(entry);
125
+ const input = num(model.input_cost_per_token) * 1_000_000;
126
+ const output = num(model.output_cost_per_token) * 1_000_000;
127
+ if (input === 0 && output === 0)
128
+ continue;
129
+ const cacheRead = num(model.cache_read_input_token_cost) * 1_000_000;
130
+ const cacheWrite = num(model.cache_creation_input_token_cost) * 1_000_000;
131
+ const tiers = liteLlmBands(model);
132
+ out[key] = {
133
+ input,
134
+ output,
135
+ ...(cacheRead > 0 ? { cacheRead } : {}),
136
+ ...(cacheWrite > 0 ? { cacheWrite } : {}),
137
+ ...(tiers.length > 0 ? { tiers } : {}),
138
+ };
139
+ }
140
+ return out;
141
+ }
142
+ /**
143
+ * The bands of a LiteLLM entry.
144
+ *
145
+ * The bands live in the key names: a rate above a threshold is spelled
146
+ * `input_cost_per_token_above_128k_tokens`, and the rate for the output
147
+ * sits under the same suffix.
148
+ */
149
+ function liteLlmBands(entry) {
150
+ const bands = [];
151
+ for (const [key, value] of Object.entries(entry)) {
152
+ const match = /^input_cost_per_token_above_(\d+)(k?)_tokens$/.exec(key);
153
+ if (match === null)
154
+ continue;
155
+ const rate = num(value);
156
+ if (rate === 0)
157
+ continue;
158
+ const count = Number(match[1]);
159
+ const above = match[2] === 'k' ? count * 1000 : count;
160
+ const output = num(entry[key.replace('input_cost_per_token', 'output_cost_per_token')]) * 1_000_000;
161
+ const cacheRead = num(entry[key.replace('input_cost_per_token', 'cache_read_input_token_cost')]) * 1_000_000;
162
+ const cacheWrite = num(entry[key.replace('input_cost_per_token', 'cache_creation_input_token_cost')]) *
163
+ 1_000_000;
164
+ bands.push({
165
+ above,
166
+ input: rate * 1_000_000,
167
+ output,
168
+ ...(cacheRead > 0 ? { cacheRead } : {}),
169
+ ...(cacheWrite > 0 ? { cacheWrite } : {}),
170
+ });
171
+ }
172
+ return bands.sort((a, b) => a.above - b.above);
173
+ }
174
+ /** The OpenRouter shape: a model id, per-token rates as strings. */
175
+ function reduceOpenRouter(raw) {
176
+ const out = {};
177
+ const data = record(raw).data;
178
+ if (!Array.isArray(data))
179
+ return out;
180
+ for (const model of data) {
181
+ const entry = record(model);
182
+ const id = readString(entry.id);
183
+ if (id === undefined)
184
+ continue;
185
+ const pricing = record(entry.pricing);
186
+ const input = num(pricing.prompt) * 1_000_000;
187
+ const output = num(pricing.completion) * 1_000_000;
188
+ if (input === 0 && output === 0)
189
+ continue;
190
+ const cacheRead = num(pricing.input_cache_read) * 1_000_000;
191
+ const cacheWrite = num(pricing.input_cache_write) * 1_000_000;
192
+ out[id] = {
193
+ input,
194
+ output,
195
+ ...(cacheRead > 0 ? { cacheRead } : {}),
196
+ ...(cacheWrite > 0 ? { cacheWrite } : {}),
197
+ };
198
+ }
199
+ return out;
200
+ }
201
+ /** The chain, in order. The first hit wins. */
202
+ export const SOURCES = [
203
+ {
204
+ name: 'models.dev',
205
+ file: 'models.dev',
206
+ url: 'https://models.dev/api.json',
207
+ reduce: reduceModelsDev,
208
+ lookup: (payload, provider, model) => {
209
+ const providers = record(payload);
210
+ const own = record(providers[provider]);
211
+ const hit = toTiers(own[model]);
212
+ if (hit !== null)
213
+ return hit;
214
+ // The provider id of the host and the one of the catalogue do not
215
+ // always match, so a model is looked up again under every provider.
216
+ for (const providerId of Object.keys(providers).sort()) {
217
+ const found = toTiers(record(providers[providerId])[model]);
218
+ if (found !== null)
219
+ return found;
220
+ }
221
+ return null;
222
+ },
223
+ },
224
+ {
225
+ name: 'litellm',
226
+ file: 'litellm',
227
+ url: 'https://raw.githubusercontent.com/BerriAI/litellm/main/model_prices_and_context_window.json',
228
+ reduce: reduceLiteLLM,
229
+ lookup: (payload, provider, model) => {
230
+ const entries = record(payload);
231
+ return toTiers(entries[model] ?? entries[`${provider}/${model}`]);
232
+ },
233
+ },
234
+ {
235
+ name: 'openrouter',
236
+ file: 'openrouter',
237
+ url: 'https://openrouter.ai/api/v1/models',
238
+ reduce: reduceOpenRouter,
239
+ lookup: (payload, provider, model) => {
240
+ const entries = record(payload);
241
+ return toTiers(entries[`${provider}/${model}`] ?? entries[model]);
242
+ },
243
+ },
244
+ ];
245
+ /** Where the cached sources live. */
246
+ export function defaultCacheDir() {
247
+ return join(homedir(), '.cache', 'openagentsy', 'pricing');
248
+ }
249
+ /** Where the bundled snapshot lives. */
250
+ export function defaultSnapshotDir() {
251
+ return fileURLToPath(new URL('../data/pricing/', import.meta.url));
252
+ }
253
+ function readJsonFile(path) {
254
+ try {
255
+ return JSON.parse(readFileSync(path, 'utf8'));
256
+ }
257
+ catch {
258
+ return null;
259
+ }
260
+ }
261
+ /** The cached payload, when the file is there and fresh. */
262
+ function readFresh(path, now) {
263
+ try {
264
+ const stats = statSync(path);
265
+ if (now - stats.mtimeMs >= CACHE_TTL_MS)
266
+ return null;
267
+ }
268
+ catch {
269
+ return null;
270
+ }
271
+ return readJsonFile(path);
272
+ }
273
+ /**
274
+ * The payload of one source.
275
+ *
276
+ * The disk cache comes first and lasts a day, then the network, and then the
277
+ * bundled snapshot, so an offline machine still prices what it can.
278
+ */
279
+ async function loadSource(source, options) {
280
+ const cacheDir = options.cacheDir ?? defaultCacheDir();
281
+ const cached = readFresh(join(cacheDir, `${source.file}.json`), options.now?.() ?? Date.now());
282
+ if (cached !== null)
283
+ return cached;
284
+ const fetched = await fetchSource(source, options);
285
+ if (fetched !== null) {
286
+ try {
287
+ mkdirSync(cacheDir, { recursive: true });
288
+ writeFileSync(join(cacheDir, `${source.file}.json`), JSON.stringify(fetched), 'utf8');
289
+ }
290
+ catch {
291
+ // The cache is a convenience. A cache that cannot be written is not a
292
+ // reason to lose the price.
293
+ }
294
+ return fetched;
295
+ }
296
+ const snapshotDir = options.snapshotDir ?? defaultSnapshotDir();
297
+ const snapshot = join(snapshotDir, `${source.file}.json`);
298
+ return existsSync(snapshot) ? readJsonFile(snapshot) : null;
299
+ }
300
+ /** The source, fetched and reduced. Null when the fetch fails. */
301
+ async function fetchSource(source, options) {
302
+ const doFetch = options.fetchImpl ?? ((url, init) => fetch(url, init));
303
+ const timeout = options.timeoutMs ?? FETCH_TIMEOUT_MS;
304
+ try {
305
+ const response = await doFetch(source.url, { signal: AbortSignal.timeout(timeout) });
306
+ if (!response.ok)
307
+ return null;
308
+ const payload = await response.json();
309
+ return source.reduce(payload);
310
+ }
311
+ catch {
312
+ return null;
313
+ }
314
+ }
315
+ /**
316
+ * The price of a message.
317
+ *
318
+ * The reported cost of the message wins, because the host knows what it
319
+ * charged. After that the chain runs, and when every source fails the
320
+ * result is a null cost and the source `none`: unpriced, not free.
321
+ */
322
+ export async function priceMessage(provider, model, tokens, options = {}) {
323
+ const reported = options.reportedCost;
324
+ if (typeof reported === 'number' && Number.isFinite(reported) && reported > 0) {
325
+ return { cost: reported, source: 'reported' };
326
+ }
327
+ for (const source of SOURCES) {
328
+ const payload = await loadSource(source, options);
329
+ if (payload === null)
330
+ continue;
331
+ const tiers = source.lookup(payload, provider, model);
332
+ if (tiers === null)
333
+ continue;
334
+ const cost = costOf(tiers, tokens);
335
+ if (cost === null)
336
+ continue;
337
+ return { cost, source: source.name, tiers };
338
+ }
339
+ return { cost: null, source: 'none' };
340
+ }
341
+ //# sourceMappingURL=pricing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.js","sourceRoot":"","sources":["../src/pricing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pricing.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.test.d.ts","sourceRoot":"","sources":["../src/pricing.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,167 @@
1
+ import { readFileSync, utimesSync } from 'node:fs';
2
+ import { mkdtemp, rm } from 'node:fs/promises';
3
+ import { tmpdir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest';
6
+ import { defaultSnapshotDir, priceMessage } from './pricing.js';
7
+ let root;
8
+ let cacheDir;
9
+ let snapshotDir;
10
+ beforeEach(async () => {
11
+ root = await mkdtemp(join(tmpdir(), 'openagentsy-pricing-'));
12
+ cacheDir = join(root, 'cache');
13
+ snapshotDir = join(root, 'snapshot');
14
+ });
15
+ afterEach(async () => {
16
+ await rm(root, { recursive: true, force: true });
17
+ });
18
+ const MODELS_DEV = {
19
+ anthropic: {
20
+ models: {
21
+ 'shared-x': { cost: { input: 3, output: 15, cache_read: 0.3, cache_write: 3.75 } },
22
+ },
23
+ },
24
+ };
25
+ const LITELLM = {
26
+ 'shared-x': {
27
+ input_cost_per_token: 0.000001,
28
+ output_cost_per_token: 0.000002,
29
+ },
30
+ };
31
+ const OPENROUTER = {
32
+ data: [
33
+ {
34
+ id: 'anthropic/shared-x',
35
+ pricing: { prompt: '0.0000005', completion: '0.000001' },
36
+ },
37
+ ],
38
+ };
39
+ function jsonResponse(body) {
40
+ return new Response(JSON.stringify(body), {
41
+ status: 200,
42
+ headers: { 'content-type': 'application/json' },
43
+ });
44
+ }
45
+ /** A fetch that answers per source and 404s the rest. */
46
+ function routing(sources) {
47
+ return async (input) => {
48
+ const url = String(input);
49
+ if (url.includes('models.dev')) {
50
+ return sources.modelsDev === undefined
51
+ ? new Response('not found', { status: 404 })
52
+ : jsonResponse(sources.modelsDev);
53
+ }
54
+ if (url.includes('litellm')) {
55
+ return sources.litellm === undefined
56
+ ? new Response('not found', { status: 404 })
57
+ : jsonResponse(sources.litellm);
58
+ }
59
+ if (url.includes('openrouter')) {
60
+ return sources.openrouter === undefined
61
+ ? new Response('not found', { status: 404 })
62
+ : jsonResponse(sources.openrouter);
63
+ }
64
+ return new Response('not found', { status: 404 });
65
+ };
66
+ }
67
+ const OFFLINE = async () => {
68
+ throw new Error('offline');
69
+ };
70
+ const TOKENS = { input: 1000, output: 100 };
71
+ describe('the pricing chain', () => {
72
+ it('takes the cost the host reported first', async () => {
73
+ const result = await priceMessage('anthropic', 'shared-x', TOKENS, {
74
+ reportedCost: 0.5,
75
+ cacheDir,
76
+ snapshotDir,
77
+ fetchImpl: OFFLINE,
78
+ });
79
+ expect(result.cost).toBe(0.5);
80
+ expect(result.source).toBe('reported');
81
+ });
82
+ it('prefers models.dev over the other sources', async () => {
83
+ const result = await priceMessage('anthropic', 'shared-x', TOKENS, {
84
+ cacheDir,
85
+ snapshotDir,
86
+ fetchImpl: routing({ modelsDev: MODELS_DEV, litellm: LITELLM, openrouter: OPENROUTER }),
87
+ });
88
+ expect(result.source).toBe('models.dev');
89
+ expect(result.cost).toBeCloseTo((1000 * 3) / 1_000_000 + (100 * 15) / 1_000_000, 10);
90
+ });
91
+ it('falls through to LiteLLM', async () => {
92
+ const result = await priceMessage('anthropic', 'shared-x', TOKENS, {
93
+ cacheDir,
94
+ snapshotDir,
95
+ fetchImpl: routing({ litellm: LITELLM, openrouter: OPENROUTER }),
96
+ });
97
+ expect(result.source).toBe('litellm');
98
+ expect(result.cost).toBeCloseTo(1000 / 1_000_000 + 200 / 1_000_000, 10);
99
+ });
100
+ it('falls through to OpenRouter', async () => {
101
+ const result = await priceMessage('anthropic', 'shared-x', TOKENS, {
102
+ cacheDir,
103
+ snapshotDir,
104
+ fetchImpl: routing({ openrouter: OPENROUTER }),
105
+ });
106
+ expect(result.source).toBe('openrouter');
107
+ expect(result.cost).toBeCloseTo((1000 * 0.5) / 1_000_000 + 100 / 1_000_000, 10);
108
+ });
109
+ it('leaves an unpriced model empty, never at zero', async () => {
110
+ const result = await priceMessage('anthropic', 'shared-x', TOKENS, {
111
+ cacheDir,
112
+ snapshotDir,
113
+ fetchImpl: routing({}),
114
+ });
115
+ expect(result.cost).toBe(null);
116
+ expect(result.cost).not.toBe(0);
117
+ expect(result.source).toBe('none');
118
+ });
119
+ it('respects the bands of a source', async () => {
120
+ const banded = {
121
+ 'shared-x': {
122
+ input_cost_per_token: 0.000001,
123
+ output_cost_per_token: 0.000002,
124
+ input_cost_per_token_above_128k_tokens: 0.000003,
125
+ output_cost_per_token_above_128k_tokens: 0.000004,
126
+ },
127
+ };
128
+ const options = { cacheDir, snapshotDir, fetchImpl: routing({ litellm: banded }) };
129
+ const small = await priceMessage('anthropic', 'shared-x', { input: 1000, output: 1000 }, options);
130
+ expect(small.cost).toBeCloseTo(1000 / 1_000_000 + 2000 / 1_000_000, 10);
131
+ const large = await priceMessage('anthropic', 'shared-x', { input: 200 * 1000, output: 1000 }, options);
132
+ expect(large.cost).toBeCloseTo((200 * 1000 * 3) / 1_000_000 + (1000 * 4) / 1_000_000, 10);
133
+ });
134
+ });
135
+ describe('the pricing cache', () => {
136
+ it('keeps a source for a day and fetches it again after that', async () => {
137
+ let calls = 0;
138
+ const counting = async () => {
139
+ calls += 1;
140
+ return jsonResponse(MODELS_DEV);
141
+ };
142
+ const options = { cacheDir, snapshotDir, fetchImpl: counting };
143
+ await priceMessage('anthropic', 'shared-x', TOKENS, options);
144
+ expect(calls).toBe(1);
145
+ await priceMessage('anthropic', 'shared-x', TOKENS, options);
146
+ expect(calls).toBe(1);
147
+ const file = join(cacheDir, 'models.dev.json');
148
+ const old = new Date(Date.now() - 25 * 60 * 60 * 1000);
149
+ utimesSync(file, old, old);
150
+ await priceMessage('anthropic', 'shared-x', TOKENS, options);
151
+ expect(calls).toBe(2);
152
+ });
153
+ it('falls back to the bundled snapshot when the network is not there', async () => {
154
+ const bundled = JSON.parse(readFileSync(join(defaultSnapshotDir(), 'models.dev.json'), 'utf8'));
155
+ const provider = Object.keys(bundled).sort()[0] ?? '';
156
+ const model = Object.keys(bundled[provider] ?? {}).sort()[0] ?? '';
157
+ expect(provider).not.toBe('');
158
+ expect(model).not.toBe('');
159
+ const result = await priceMessage(provider, model, TOKENS, {
160
+ cacheDir,
161
+ fetchImpl: OFFLINE,
162
+ });
163
+ expect(result.source).toBe('models.dev');
164
+ expect(result.cost).toBeGreaterThan(0);
165
+ });
166
+ });
167
+ //# sourceMappingURL=pricing.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.test.js","sourceRoot":"","sources":["../src/pricing.test.ts"],"names":[],"mappings":"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"}