@bymax-one/nest-ai-tokens 1.0.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.
@@ -0,0 +1,659 @@
1
+ 'use strict';
2
+
3
+ // src/shared/constants/provider-ids.constants.ts
4
+ var PROVIDER_IDS = [
5
+ "openai",
6
+ "azure-openai",
7
+ "anthropic",
8
+ "gemini",
9
+ "vertex",
10
+ "mistral",
11
+ "bedrock",
12
+ "openrouter",
13
+ "deepseek",
14
+ "xai",
15
+ "groq"
16
+ ];
17
+
18
+ // src/shared/constants/operations.constants.ts
19
+ var AI_OPERATIONS = [
20
+ "chat",
21
+ "responses",
22
+ "embeddings",
23
+ "image",
24
+ "video",
25
+ "audio",
26
+ "rerank",
27
+ "moderation"
28
+ ];
29
+
30
+ // src/shared/constants/service-tiers.constants.ts
31
+ var SERVICE_TIERS = ["standard", "batch", "flex", "priority"];
32
+
33
+ // src/shared/constants/token-categories.constants.ts
34
+ var TOKEN_CATEGORIES = [
35
+ "input",
36
+ "output",
37
+ "cacheRead",
38
+ "cacheWrite5m",
39
+ "cacheWrite1h",
40
+ "reasoning",
41
+ "audioIn",
42
+ "audioOut",
43
+ "imageIn",
44
+ "imageOut"
45
+ ];
46
+
47
+ // src/shared/constants/wallet-entry-types.constants.ts
48
+ var WALLET_ENTRY_TYPES = ["grant", "debit", "refund", "adjustment", "expiry"];
49
+
50
+ // src/shared/constants/error-codes.constants.ts
51
+ var AI_TOKENS_ERROR_CODES = {
52
+ AI_TOKENS_NOT_CONFIGURED: "AI_TOKENS_NOT_CONFIGURED",
53
+ AI_TOKENS_INVALID_CONFIG: "AI_TOKENS_INVALID_CONFIG",
54
+ AI_TOKENS_UNKNOWN_PROVIDER: "AI_TOKENS_UNKNOWN_PROVIDER",
55
+ AI_TOKENS_USAGE_MALFORMED: "AI_TOKENS_USAGE_MALFORMED",
56
+ AI_TOKENS_PRICE_NOT_FOUND: "AI_TOKENS_PRICE_NOT_FOUND",
57
+ AI_TOKENS_FX_REQUIRED: "AI_TOKENS_FX_REQUIRED",
58
+ AI_TOKENS_BUDGET_EXCEEDED: "AI_TOKENS_BUDGET_EXCEEDED",
59
+ AI_TOKENS_QUOTA_EXCEEDED: "AI_TOKENS_QUOTA_EXCEEDED",
60
+ AI_TOKENS_INSUFFICIENT_CREDITS: "AI_TOKENS_INSUFFICIENT_CREDITS",
61
+ AI_TOKENS_HOLD_NOT_FOUND: "AI_TOKENS_HOLD_NOT_FOUND",
62
+ AI_TOKENS_HOLD_EXPIRED: "AI_TOKENS_HOLD_EXPIRED",
63
+ AI_TOKENS_HOLD_ALREADY_SETTLED: "AI_TOKENS_HOLD_ALREADY_SETTLED",
64
+ AI_TOKENS_IDEMPOTENCY_CONFLICT: "AI_TOKENS_IDEMPOTENCY_CONFLICT",
65
+ AI_TOKENS_STREAM_USAGE_MISSING: "AI_TOKENS_STREAM_USAGE_MISSING",
66
+ AI_TOKENS_STORE_ERROR: "AI_TOKENS_STORE_ERROR"
67
+ };
68
+
69
+ // src/shared/constants/ledger-fields.constants.ts
70
+ var AMOUNT_FIELDS = [
71
+ "inputTokens",
72
+ "outputTokens",
73
+ "cacheReadTokens",
74
+ "cacheWrite5mTokens",
75
+ "cacheWrite1hTokens",
76
+ "reasoningTokens",
77
+ "audioInTokens",
78
+ "audioOutTokens",
79
+ "imageInTokens",
80
+ "imageOutTokens",
81
+ "totalTokens",
82
+ "rawCostNanoUsd",
83
+ "surchargeNanoUsd",
84
+ "billedCostNanoUsd"
85
+ ];
86
+ var SETTLEMENT_FIELDS = [
87
+ ...AMOUNT_FIELDS,
88
+ "priceVersionId",
89
+ "priceMissing",
90
+ "markupMultiplier"
91
+ ];
92
+ var RELEASE_FIELDS = ["correlationId", "requestId"];
93
+ var REVERSAL_LINKAGE_FIELD = "reversedByRecordId";
94
+ var SETTLEMENT_PATCH_KEYS = /* @__PURE__ */ new Set([...SETTLEMENT_FIELDS, REVERSAL_LINKAGE_FIELD]);
95
+ var RELEASE_FIELD_SET = new Set(RELEASE_FIELDS);
96
+ function isLegalLedgerTransition(from, to) {
97
+ if (from === "pending") return to === "posted" || to === "released";
98
+ return from === "posted" && to === "reversed";
99
+ }
100
+ function isLegalTransitionPatchKey(from, to, key) {
101
+ if (from === "pending" && to === "posted") return SETTLEMENT_PATCH_KEYS.has(key);
102
+ if (from === "pending" && to === "released") return RELEASE_FIELD_SET.has(key);
103
+ if (from === "posted" && to === "reversed") return key === REVERSAL_LINKAGE_FIELD;
104
+ return false;
105
+ }
106
+
107
+ // src/shared/normalizers/usage-fields.ts
108
+ function asObject(value) {
109
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
110
+ }
111
+ function asArray(value) {
112
+ return Array.isArray(value) ? value : void 0;
113
+ }
114
+ function num(value) {
115
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
116
+ }
117
+ function requireNum(value, provider, field) {
118
+ if (typeof value === "number" && Number.isFinite(value)) return value;
119
+ throw new Error(`${provider}: missing or invalid numeric field "${field}"`);
120
+ }
121
+ function str(value) {
122
+ return typeof value === "string" ? value : void 0;
123
+ }
124
+ function knownServiceTier(value) {
125
+ const candidate = str(value);
126
+ return candidate !== void 0 && SERVICE_TIERS.includes(candidate) ? candidate : void 0;
127
+ }
128
+ function openAiServiceTier(value) {
129
+ const candidate = str(value);
130
+ return knownServiceTier(candidate === "default" ? "standard" : candidate);
131
+ }
132
+ function toolUseCounts(value) {
133
+ const source = asObject(value);
134
+ if (source === void 0) return void 0;
135
+ const counts = {};
136
+ for (const [key, raw] of Object.entries(source)) {
137
+ if (typeof raw === "number" && Number.isFinite(raw)) counts[key] = raw;
138
+ }
139
+ return Object.keys(counts).length > 0 ? counts : void 0;
140
+ }
141
+ function buildUsage(draft) {
142
+ const usage = {
143
+ provider: draft.provider,
144
+ model: draft.model,
145
+ operation: draft.operation,
146
+ inputTokens: draft.inputTokens,
147
+ outputTokens: draft.outputTokens,
148
+ cacheReadTokens: draft.cacheReadTokens ?? 0,
149
+ cacheWrite5mTokens: draft.cacheWrite5mTokens ?? 0,
150
+ cacheWrite1hTokens: draft.cacheWrite1hTokens ?? 0,
151
+ reasoningTokens: draft.reasoningTokens ?? 0,
152
+ audioInTokens: draft.audioInTokens ?? 0,
153
+ audioOutTokens: draft.audioOutTokens ?? 0,
154
+ imageInTokens: draft.imageInTokens ?? 0,
155
+ imageOutTokens: draft.imageOutTokens ?? 0
156
+ };
157
+ if (draft.serviceTier !== void 0) usage.serviceTier = draft.serviceTier;
158
+ if (draft.serverToolUse !== void 0) usage.serverToolUse = draft.serverToolUse;
159
+ if (draft.providerReportedCostNanoUsd !== void 0) {
160
+ usage.providerReportedCostNanoUsd = draft.providerReportedCostNanoUsd;
161
+ }
162
+ if (draft.raw !== void 0) usage.raw = draft.raw;
163
+ return usage;
164
+ }
165
+ function readResponse(raw, provider, usageKey = "usage") {
166
+ const response = asObject(raw) ?? {};
167
+ const usage = asObject(response[usageKey]);
168
+ if (usage === void 0) throw new Error(`${provider}: missing "${usageKey}" object`);
169
+ return { response, usage };
170
+ }
171
+
172
+ // src/shared/normalizers/openai-chat.normalizer.ts
173
+ function normalizeOpenAiChatUsage(raw) {
174
+ const { response, usage } = readResponse(raw, "openai-chat");
175
+ const promptTokens = requireNum(usage.prompt_tokens, "openai-chat", "usage.prompt_tokens");
176
+ const completionTokens = requireNum(usage.completion_tokens, "openai-chat", "usage.completion_tokens");
177
+ const promptDetails = asObject(usage.prompt_tokens_details);
178
+ const completionDetails = asObject(usage.completion_tokens_details);
179
+ const cacheReadTokens = num(promptDetails?.cached_tokens);
180
+ const audioInTokens = num(promptDetails?.audio_tokens);
181
+ const reasoningTokens = num(completionDetails?.reasoning_tokens);
182
+ const audioOutTokens = num(completionDetails?.audio_tokens);
183
+ const serviceTier = openAiServiceTier(response.service_tier);
184
+ return buildUsage({
185
+ provider: "openai",
186
+ model: str(response.model) ?? "",
187
+ operation: "chat",
188
+ inputTokens: promptTokens - cacheReadTokens - audioInTokens,
189
+ outputTokens: completionTokens - reasoningTokens,
190
+ cacheReadTokens,
191
+ reasoningTokens,
192
+ audioInTokens,
193
+ audioOutTokens,
194
+ serviceTier,
195
+ raw: usage
196
+ });
197
+ }
198
+
199
+ // src/shared/normalizers/openai-responses.normalizer.ts
200
+ function normalizeOpenAiResponsesUsage(raw) {
201
+ const { response, usage } = readResponse(raw, "openai-responses");
202
+ const inputTokensTotal = requireNum(usage.input_tokens, "openai-responses", "usage.input_tokens");
203
+ const outputTokensTotal = requireNum(usage.output_tokens, "openai-responses", "usage.output_tokens");
204
+ const inputDetails = asObject(usage.input_tokens_details);
205
+ const outputDetails = asObject(usage.output_tokens_details);
206
+ const cacheReadTokens = num(inputDetails?.cached_tokens);
207
+ const audioInTokens = num(inputDetails?.audio_tokens);
208
+ const reasoningTokens = num(outputDetails?.reasoning_tokens);
209
+ const audioOutTokens = num(outputDetails?.audio_tokens);
210
+ const serviceTier = openAiServiceTier(response.service_tier);
211
+ return buildUsage({
212
+ provider: "openai",
213
+ model: str(response.model) ?? "",
214
+ operation: "responses",
215
+ inputTokens: inputTokensTotal - cacheReadTokens - audioInTokens,
216
+ outputTokens: outputTokensTotal - reasoningTokens,
217
+ cacheReadTokens,
218
+ reasoningTokens,
219
+ audioInTokens,
220
+ audioOutTokens,
221
+ serviceTier,
222
+ raw: usage
223
+ });
224
+ }
225
+
226
+ // src/shared/normalizers/openai-compatible.normalizer.ts
227
+ function normalizeOpenAiCompatibleUsage(raw) {
228
+ const { response, usage } = readResponse(raw, "openai-compatible");
229
+ const promptTokens = requireNum(usage.prompt_tokens, "openai-compatible", "usage.prompt_tokens");
230
+ const completionTokens = requireNum(usage.completion_tokens, "openai-compatible", "usage.completion_tokens");
231
+ const promptDetails = asObject(usage.prompt_tokens_details);
232
+ const completionDetails = asObject(usage.completion_tokens_details);
233
+ const cacheReadTokens = num(usage.prompt_cache_hit_tokens) + num(promptDetails?.cached_tokens);
234
+ const reasoningTokens = num(completionDetails?.reasoning_tokens);
235
+ const serviceTier = openAiServiceTier(response.service_tier);
236
+ return buildUsage({
237
+ provider: "",
238
+ model: str(response.model) ?? "",
239
+ operation: "chat",
240
+ inputTokens: promptTokens - cacheReadTokens,
241
+ outputTokens: completionTokens - reasoningTokens,
242
+ cacheReadTokens,
243
+ reasoningTokens,
244
+ serviceTier,
245
+ raw: usage
246
+ });
247
+ }
248
+
249
+ // src/shared/normalizers/anthropic.normalizer.ts
250
+ function normalizeAnthropicUsage(raw) {
251
+ const { response, usage } = readResponse(raw, "anthropic");
252
+ const inputTokens = requireNum(usage.input_tokens, "anthropic", "usage.input_tokens");
253
+ const outputTokens = requireNum(usage.output_tokens, "anthropic", "usage.output_tokens");
254
+ const cacheReadTokens = num(usage.cache_read_input_tokens);
255
+ const cacheCreation = asObject(usage.cache_creation);
256
+ const cacheWrite5mTokens = cacheCreation ? num(cacheCreation.ephemeral_5m_input_tokens) : num(usage.cache_creation_input_tokens);
257
+ const cacheWrite1hTokens = cacheCreation ? num(cacheCreation.ephemeral_1h_input_tokens) : 0;
258
+ return buildUsage({
259
+ provider: "anthropic",
260
+ model: str(response.model) ?? "",
261
+ operation: "chat",
262
+ inputTokens,
263
+ outputTokens,
264
+ cacheReadTokens,
265
+ cacheWrite5mTokens,
266
+ cacheWrite1hTokens,
267
+ serviceTier: knownServiceTier(usage.service_tier),
268
+ serverToolUse: toolUseCounts(usage.server_tool_use),
269
+ raw: usage
270
+ });
271
+ }
272
+
273
+ // src/shared/normalizers/gemini.normalizer.ts
274
+ function normalizeGeminiUsage(raw) {
275
+ const { response, usage } = readResponse(raw, "gemini", "usageMetadata");
276
+ const promptTokenCount = requireNum(usage.promptTokenCount, "gemini", "usageMetadata.promptTokenCount");
277
+ const candidatesTokenCount = num(usage.candidatesTokenCount);
278
+ const cacheReadTokens = num(usage.cachedContentTokenCount);
279
+ const reasoningTokens = num(usage.thoughtsTokenCount);
280
+ const toolUseInputTokens = num(usage.toolUsePromptTokenCount);
281
+ return buildUsage({
282
+ provider: "gemini",
283
+ model: str(response.modelVersion) ?? str(response.model) ?? "",
284
+ operation: "chat",
285
+ inputTokens: promptTokenCount - cacheReadTokens + toolUseInputTokens,
286
+ outputTokens: candidatesTokenCount,
287
+ cacheReadTokens,
288
+ reasoningTokens,
289
+ raw: usage
290
+ });
291
+ }
292
+
293
+ // src/shared/normalizers/bedrock-converse.normalizer.ts
294
+ function splitCacheWrite(usage) {
295
+ const details = asArray(usage.cacheDetails);
296
+ if (details === void 0) {
297
+ return { cacheWrite5mTokens: num(usage.cacheWriteInputTokens), cacheWrite1hTokens: 0 };
298
+ }
299
+ let cacheWrite5mTokens = 0;
300
+ let cacheWrite1hTokens = 0;
301
+ for (const item of details) {
302
+ const entry = asObject(item);
303
+ if (entry === void 0) continue;
304
+ const count = num(entry.cacheWriteInputTokens);
305
+ if (str(entry.ttl) === "1h") cacheWrite1hTokens += count;
306
+ else cacheWrite5mTokens += count;
307
+ }
308
+ return { cacheWrite5mTokens, cacheWrite1hTokens };
309
+ }
310
+ function normalizeBedrockConverseUsage(raw) {
311
+ const { response, usage } = readResponse(raw, "bedrock-converse");
312
+ const inputTokens = requireNum(usage.inputTokens, "bedrock-converse", "usage.inputTokens");
313
+ const outputTokens = requireNum(usage.outputTokens, "bedrock-converse", "usage.outputTokens");
314
+ const { cacheWrite5mTokens, cacheWrite1hTokens } = splitCacheWrite(usage);
315
+ return buildUsage({
316
+ provider: "bedrock",
317
+ model: str(response.model) ?? "",
318
+ operation: "chat",
319
+ inputTokens,
320
+ outputTokens,
321
+ cacheReadTokens: num(usage.cacheReadInputTokens),
322
+ cacheWrite5mTokens,
323
+ cacheWrite1hTokens,
324
+ raw: usage
325
+ });
326
+ }
327
+
328
+ // src/shared/normalizers/mistral.normalizer.ts
329
+ function normalizeMistralUsage(raw) {
330
+ const { response, usage } = readResponse(raw, "mistral");
331
+ return buildUsage({
332
+ provider: "mistral",
333
+ model: str(response.model) ?? "",
334
+ operation: "chat",
335
+ // Stryker disable next-line StringLiteral -- provider and field names in error messages are internal diagnostics
336
+ inputTokens: requireNum(usage.prompt_tokens, "mistral", "usage.prompt_tokens"),
337
+ // Stryker disable next-line StringLiteral -- provider and field names in error messages are internal diagnostics
338
+ outputTokens: requireNum(usage.completion_tokens, "mistral", "usage.completion_tokens"),
339
+ raw: usage
340
+ });
341
+ }
342
+
343
+ // src/shared/pricing/money.ts
344
+ var NANO_PER_USD = 1000000000n;
345
+ var DEFAULT_DISPLAY_DECIMALS = 6;
346
+ function perMillion(tokens, ratePerMillionNano) {
347
+ return BigInt(tokens) * ratePerMillionNano / 1000000n;
348
+ }
349
+ function floatUsdToNanoUsd(usd) {
350
+ if (!Number.isFinite(usd)) {
351
+ throw new RangeError(`floatUsdToNanoUsd: expected a finite number, received ${String(usd)}`);
352
+ }
353
+ const sign = usd < 0 ? -1n : 1n;
354
+ const abs = Math.abs(usd);
355
+ const wholeDollars = Math.floor(abs);
356
+ const fractionalDollars = abs - wholeDollars;
357
+ const wholeNano = BigInt(wholeDollars) * NANO_PER_USD;
358
+ const fractionalNano = BigInt(Math.round(fractionalDollars * 1e9));
359
+ return sign * (wholeNano + fractionalNano);
360
+ }
361
+ function formatNanoUsd(nanoUsd, opts) {
362
+ const decimals = opts?.decimals ?? DEFAULT_DISPLAY_DECIMALS;
363
+ if (!Number.isInteger(decimals) || decimals < 0 || decimals > 9) {
364
+ throw new RangeError(`formatNanoUsd: decimals must be an integer in [0, 9], received ${String(decimals)}`);
365
+ }
366
+ const currency = opts?.currency ?? "USD";
367
+ const converted = opts?.fxRateNano == null ? nanoUsd : nanoUsd * opts.fxRateNano / NANO_PER_USD;
368
+ const negative = converted < 0n;
369
+ const magnitude = negative ? -converted : converted;
370
+ const nanoPerDisplayUnit = 10n ** BigInt(9 - decimals);
371
+ const displayUnits = (magnitude + nanoPerDisplayUnit / 2n) / nanoPerDisplayUnit;
372
+ const scale = 10n ** BigInt(decimals);
373
+ const integerPart = displayUnits / scale;
374
+ const fractionalPart = displayUnits % scale;
375
+ const fractionalText = decimals > 0 ? `.${fractionalPart.toString().padStart(decimals, "0")}` : "";
376
+ const body = `${integerPart.toString()}${fractionalText}`;
377
+ const signText = negative ? "-" : "";
378
+ return currency === "USD" ? `${signText}$${body}` : `${signText}${body} ${currency}`;
379
+ }
380
+
381
+ // src/shared/normalizers/openrouter.normalizer.ts
382
+ function normalizeOpenRouterUsage(raw) {
383
+ const { response, usage } = readResponse(raw, "openrouter");
384
+ const promptTokens = requireNum(usage.prompt_tokens, "openrouter", "usage.prompt_tokens");
385
+ const completionTokens = requireNum(usage.completion_tokens, "openrouter", "usage.completion_tokens");
386
+ const promptDetails = asObject(usage.prompt_tokens_details);
387
+ const completionDetails = asObject(usage.completion_tokens_details);
388
+ const cacheReadTokens = num(promptDetails?.cached_tokens);
389
+ const cacheWrite5mTokens = num(promptDetails?.cache_write_tokens);
390
+ const reasoningTokens = num(completionDetails?.reasoning_tokens);
391
+ const cost = usage.cost;
392
+ const providerReportedCostNanoUsd = typeof cost === "number" && Number.isFinite(cost) ? floatUsdToNanoUsd(cost) : void 0;
393
+ return buildUsage({
394
+ provider: "openrouter",
395
+ model: str(response.model) ?? "",
396
+ operation: "chat",
397
+ inputTokens: promptTokens - cacheReadTokens - cacheWrite5mTokens,
398
+ outputTokens: completionTokens - reasoningTokens,
399
+ cacheReadTokens,
400
+ cacheWrite5mTokens,
401
+ reasoningTokens,
402
+ serviceTier: openAiServiceTier(response.service_tier),
403
+ providerReportedCostNanoUsd,
404
+ raw: usage
405
+ });
406
+ }
407
+
408
+ // src/shared/normalizers/vercel-ai-sdk.normalizer.ts
409
+ function normalizeVercelAiSdkUsage(raw) {
410
+ const { response, usage } = readResponse(raw, "vercel-ai-sdk");
411
+ const inputTokensTotal = requireNum(usage.inputTokens, "vercel-ai-sdk", "usage.inputTokens");
412
+ const outputTokensTotal = requireNum(usage.outputTokens, "vercel-ai-sdk", "usage.outputTokens");
413
+ const inputDetails = asObject(usage.inputTokenDetails);
414
+ const outputDetails = asObject(usage.outputTokenDetails);
415
+ const cacheReadTokens = num(usage.cachedInputTokens) + num(inputDetails?.cacheReadTokens);
416
+ const cacheWrite5mTokens = num(inputDetails?.cacheWriteTokens);
417
+ const reasoningTokens = num(usage.reasoningTokens) + num(outputDetails?.reasoningTokens);
418
+ return buildUsage({
419
+ provider: "",
420
+ model: str(response.model) ?? "",
421
+ operation: "chat",
422
+ inputTokens: inputTokensTotal - cacheReadTokens - cacheWrite5mTokens,
423
+ outputTokens: outputTokensTotal - reasoningTokens,
424
+ cacheReadTokens,
425
+ cacheWrite5mTokens,
426
+ reasoningTokens,
427
+ raw: usage
428
+ });
429
+ }
430
+
431
+ // src/shared/pricing/compute-cost.ts
432
+ function computeCostNanoUsd(usage, rate) {
433
+ const totalInput = usage.inputTokens + usage.cacheReadTokens + usage.cacheWrite5mTokens + usage.cacheWrite1hTokens;
434
+ const overTier = rate.tierThresholdTokens != null && totalInput > rate.tierThresholdTokens;
435
+ const inputRate = overTier ? rate.tierInputNanoUsdPerMillion ?? rate.inputNanoUsdPerMillion : rate.inputNanoUsdPerMillion;
436
+ const outputRate = overTier ? rate.tierOutputNanoUsdPerMillion ?? rate.outputNanoUsdPerMillion : rate.outputNanoUsdPerMillion;
437
+ const tokenNanoUsd = perMillion(usage.inputTokens, inputRate) + perMillion(usage.outputTokens, outputRate) + perMillion(usage.cacheReadTokens, rate.cacheReadNanoUsdPerMillion) + perMillion(usage.cacheWrite5mTokens, rate.cacheWrite5mNanoUsdPerMillion) + perMillion(usage.cacheWrite1hTokens, rate.cacheWrite1hNanoUsdPerMillion) + perMillion(usage.reasoningTokens, rate.reasoningNanoUsdPerMillion) + perMillion(usage.audioInTokens, rate.audioInNanoUsdPerMillion) + perMillion(usage.audioOutTokens, rate.audioOutNanoUsdPerMillion) + perMillion(usage.imageInTokens, rate.imageInNanoUsdPerMillion) + perMillion(usage.imageOutTokens, rate.imageOutNanoUsdPerMillion);
438
+ let surchargeNanoUsd = 0n;
439
+ for (const [unit, count] of Object.entries({ ...usage.serverToolUse })) {
440
+ const unitRate = rate.unitRates?.[unit];
441
+ if (unitRate != null) surchargeNanoUsd += BigInt(count) * unitRate;
442
+ }
443
+ return { totalNanoUsd: tokenNanoUsd + surchargeNanoUsd, tokenNanoUsd, surchargeNanoUsd };
444
+ }
445
+
446
+ // src/shared/pricing/apply-markup.ts
447
+ var MARKUP_SCALE = 10000n;
448
+ function assertValidMultiplier(multiplier) {
449
+ if (!Number.isFinite(multiplier) || multiplier <= 0) {
450
+ throw new RangeError(`applyMarkup: multiplier must be finite and > 0, received ${String(multiplier)}`);
451
+ }
452
+ }
453
+ function resolveMultiplier4dp(multiplier) {
454
+ assertValidMultiplier(multiplier);
455
+ return Math.round(multiplier * 1e4) / 1e4;
456
+ }
457
+ function applyMarkup(rawCostNanoUsd, multiplier) {
458
+ assertValidMultiplier(multiplier);
459
+ return rawCostNanoUsd * BigInt(Math.round(multiplier * 1e4)) / MARKUP_SCALE;
460
+ }
461
+
462
+ // src/shared/utils/sha256.ts
463
+ var K_WORDS = [
464
+ 1116352408,
465
+ 1899447441,
466
+ 3049323471,
467
+ 3921009573,
468
+ 961987163,
469
+ 1508970993,
470
+ 2453635748,
471
+ 2870763221,
472
+ 3624381080,
473
+ 310598401,
474
+ 607225278,
475
+ 1426881987,
476
+ 1925078388,
477
+ 2162078206,
478
+ 2614888103,
479
+ 3248222580,
480
+ 3835390401,
481
+ 4022224774,
482
+ 264347078,
483
+ 604807628,
484
+ 770255983,
485
+ 1249150122,
486
+ 1555081692,
487
+ 1996064986,
488
+ 2554220882,
489
+ 2821834349,
490
+ 2952996808,
491
+ 3210313671,
492
+ 3336571891,
493
+ 3584528711,
494
+ 113926993,
495
+ 338241895,
496
+ 666307205,
497
+ 773529912,
498
+ 1294757372,
499
+ 1396182291,
500
+ 1695183700,
501
+ 1986661051,
502
+ 2177026350,
503
+ 2456956037,
504
+ 2730485921,
505
+ 2820302411,
506
+ 3259730800,
507
+ 3345764771,
508
+ 3516065817,
509
+ 3600352804,
510
+ 4094571909,
511
+ 275423344,
512
+ 430227734,
513
+ 506948616,
514
+ 659060556,
515
+ 883997877,
516
+ 958139571,
517
+ 1322822218,
518
+ 1537002063,
519
+ 1747873779,
520
+ 1955562222,
521
+ 2024104815,
522
+ 2227730452,
523
+ 2361852424,
524
+ 2428436474,
525
+ 2756734187,
526
+ 3204031479,
527
+ 3329325298
528
+ ];
529
+ var INITIAL_HASH = [
530
+ 1779033703,
531
+ 3144134277,
532
+ 1013904242,
533
+ 2773480762,
534
+ 1359893119,
535
+ 2600822924,
536
+ 528734635,
537
+ 1541459225
538
+ ];
539
+ var kView = new DataView(new ArrayBuffer(64 * 4));
540
+ K_WORDS.forEach((value, index) => {
541
+ kView.setUint32(index * 4, value);
542
+ });
543
+ function rotr(x, n) {
544
+ return (x >>> n | x << 32 - n) >>> 0;
545
+ }
546
+ function expandSchedule(w) {
547
+ for (let i = 16; i < 64; i++) {
548
+ const w15 = w.getUint32((i - 15) * 4);
549
+ const w2 = w.getUint32((i - 2) * 4);
550
+ const s0 = rotr(w15, 7) ^ rotr(w15, 18) ^ w15 >>> 3;
551
+ const s1 = rotr(w2, 17) ^ rotr(w2, 19) ^ w2 >>> 10;
552
+ w.setUint32(i * 4, w.getUint32((i - 16) * 4) + s0 + w.getUint32((i - 7) * 4) + s1 >>> 0);
553
+ }
554
+ }
555
+ function compressBlock(h, w) {
556
+ let a = h.getUint32(0);
557
+ let b = h.getUint32(4);
558
+ let c = h.getUint32(8);
559
+ let d = h.getUint32(12);
560
+ let e = h.getUint32(16);
561
+ let f = h.getUint32(20);
562
+ let g = h.getUint32(24);
563
+ let hh = h.getUint32(28);
564
+ for (let i = 0; i < 64; i++) {
565
+ const s1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
566
+ const ch = e & f ^ ~e & g;
567
+ const t1 = hh + s1 + ch + kView.getUint32(i * 4) + w.getUint32(i * 4) >>> 0;
568
+ const s0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);
569
+ const maj = a & b ^ a & c ^ b & c;
570
+ const t2 = s0 + maj >>> 0;
571
+ hh = g;
572
+ g = f;
573
+ f = e;
574
+ e = d + t1 >>> 0;
575
+ d = c;
576
+ c = b;
577
+ b = a;
578
+ a = t1 + t2 >>> 0;
579
+ }
580
+ const next = [a, b, c, d, e, f, g, hh];
581
+ next.forEach((value, index) => {
582
+ h.setUint32(index * 4, h.getUint32(index * 4) + value >>> 0);
583
+ });
584
+ }
585
+ function sha256Hex(input) {
586
+ const message = new TextEncoder().encode(input);
587
+ const bitLength = message.length * 8;
588
+ const paddedLength = Math.ceil((message.length + 9) / 64) * 64;
589
+ const bytes = new Uint8Array(paddedLength);
590
+ bytes.set(message);
591
+ bytes[message.length] = 128;
592
+ const view = new DataView(bytes.buffer);
593
+ view.setUint32(paddedLength - 8, Math.floor(bitLength / 4294967296));
594
+ view.setUint32(paddedLength - 4, bitLength >>> 0);
595
+ const hView = new DataView(new ArrayBuffer(32));
596
+ INITIAL_HASH.forEach((value, index) => {
597
+ hView.setUint32(index * 4, value);
598
+ });
599
+ const wView = new DataView(new ArrayBuffer(64 * 4));
600
+ for (let offset = 0; offset < paddedLength; offset += 64) {
601
+ for (let i = 0; i < 16; i++) wView.setUint32(i * 4, view.getUint32(offset + i * 4));
602
+ expandSchedule(wView);
603
+ compressBlock(hView, wView);
604
+ }
605
+ let hex = "";
606
+ for (let i = 0; i < 8; i++) hex += hView.getUint32(i * 4).toString(16).padStart(8, "0");
607
+ return hex;
608
+ }
609
+
610
+ // src/shared/utils/idempotency.ts
611
+ function canonicalize(value) {
612
+ if (value === null || value === void 0) return "null";
613
+ if (typeof value === "bigint") return JSON.stringify(`${value.toString()}n`);
614
+ if (typeof value === "string" || typeof value === "boolean") return JSON.stringify(value);
615
+ if (typeof value === "number") return Number.isFinite(value) ? String(value) : "null";
616
+ if (value instanceof Date) return JSON.stringify(value.toISOString());
617
+ if (Array.isArray(value)) {
618
+ return `[${value.map((element) => canonicalize(element)).join(",")}]`;
619
+ }
620
+ if (typeof value === "object") {
621
+ const record = value;
622
+ const members = Object.keys(record).filter((key) => record[key] !== void 0).sort().map((key) => `${JSON.stringify(key)}:${canonicalize(record[key])}`);
623
+ return `{${members.join(",")}}`;
624
+ }
625
+ return "null";
626
+ }
627
+ function deriveIdempotencyKey(payload) {
628
+ return sha256Hex(canonicalize(payload));
629
+ }
630
+
631
+ exports.AI_OPERATIONS = AI_OPERATIONS;
632
+ exports.AI_TOKENS_ERROR_CODES = AI_TOKENS_ERROR_CODES;
633
+ exports.AMOUNT_FIELDS = AMOUNT_FIELDS;
634
+ exports.PROVIDER_IDS = PROVIDER_IDS;
635
+ exports.RELEASE_FIELDS = RELEASE_FIELDS;
636
+ exports.REVERSAL_LINKAGE_FIELD = REVERSAL_LINKAGE_FIELD;
637
+ exports.SERVICE_TIERS = SERVICE_TIERS;
638
+ exports.SETTLEMENT_FIELDS = SETTLEMENT_FIELDS;
639
+ exports.SETTLEMENT_PATCH_KEYS = SETTLEMENT_PATCH_KEYS;
640
+ exports.TOKEN_CATEGORIES = TOKEN_CATEGORIES;
641
+ exports.WALLET_ENTRY_TYPES = WALLET_ENTRY_TYPES;
642
+ exports.applyMarkup = applyMarkup;
643
+ exports.computeCostNanoUsd = computeCostNanoUsd;
644
+ exports.deriveIdempotencyKey = deriveIdempotencyKey;
645
+ exports.floatUsdToNanoUsd = floatUsdToNanoUsd;
646
+ exports.formatNanoUsd = formatNanoUsd;
647
+ exports.isLegalLedgerTransition = isLegalLedgerTransition;
648
+ exports.isLegalTransitionPatchKey = isLegalTransitionPatchKey;
649
+ exports.normalizeAnthropicUsage = normalizeAnthropicUsage;
650
+ exports.normalizeBedrockConverseUsage = normalizeBedrockConverseUsage;
651
+ exports.normalizeGeminiUsage = normalizeGeminiUsage;
652
+ exports.normalizeMistralUsage = normalizeMistralUsage;
653
+ exports.normalizeOpenAiChatUsage = normalizeOpenAiChatUsage;
654
+ exports.normalizeOpenAiCompatibleUsage = normalizeOpenAiCompatibleUsage;
655
+ exports.normalizeOpenAiResponsesUsage = normalizeOpenAiResponsesUsage;
656
+ exports.normalizeOpenRouterUsage = normalizeOpenRouterUsage;
657
+ exports.normalizeVercelAiSdkUsage = normalizeVercelAiSdkUsage;
658
+ exports.perMillion = perMillion;
659
+ exports.resolveMultiplier4dp = resolveMultiplier4dp;