vibezcheck 0.4.2 → 0.5.3

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 (80) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +249 -273
  3. package/bin/vibezcheck.js +4 -4
  4. package/dist/ai-sdk/index.d.mts +28 -59
  5. package/dist/ai-sdk/index.d.ts +28 -59
  6. package/dist/ai-sdk/index.js +307 -69
  7. package/dist/ai-sdk/index.js.map +1 -1
  8. package/dist/ai-sdk/index.mjs +307 -69
  9. package/dist/ai-sdk/index.mjs.map +1 -1
  10. package/dist/ai-sdk/middleware.d.mts +15 -0
  11. package/dist/ai-sdk/middleware.d.ts +15 -0
  12. package/dist/ai-sdk/middleware.js +1433 -0
  13. package/dist/ai-sdk/middleware.js.map +1 -0
  14. package/dist/ai-sdk/middleware.mjs +1396 -0
  15. package/dist/ai-sdk/middleware.mjs.map +1 -0
  16. package/dist/auth/index.js.map +1 -1
  17. package/dist/auth/index.mjs.map +1 -1
  18. package/dist/billing/index.d.mts +90 -1
  19. package/dist/billing/index.d.ts +90 -1
  20. package/dist/billing/index.js +1542 -2
  21. package/dist/billing/index.js.map +1 -1
  22. package/dist/billing/index.mjs +1537 -1
  23. package/dist/billing/index.mjs.map +1 -1
  24. package/dist/cli/index.js +59 -7
  25. package/dist/cli/index.js.map +1 -1
  26. package/dist/cli/index.mjs +59 -7
  27. package/dist/cli/index.mjs.map +1 -1
  28. package/dist/{client-ynfWcHX6.d.ts → client-BORmJy8s.d.ts} +1 -1
  29. package/dist/{client-w_hWhQ6g.d.mts → client-CbuXmoXz.d.mts} +1 -1
  30. package/dist/compat/stripe-meter.d.mts +21 -0
  31. package/dist/compat/stripe-meter.d.ts +21 -0
  32. package/dist/compat/stripe-meter.js +1451 -0
  33. package/dist/compat/stripe-meter.js.map +1 -0
  34. package/dist/compat/stripe-meter.mjs +1414 -0
  35. package/dist/compat/stripe-meter.mjs.map +1 -0
  36. package/dist/compat/stripe-provider.d.mts +34 -0
  37. package/dist/compat/stripe-provider.d.ts +34 -0
  38. package/dist/compat/stripe-provider.js +1620 -0
  39. package/dist/compat/stripe-provider.js.map +1 -0
  40. package/dist/compat/stripe-provider.mjs +1587 -0
  41. package/dist/compat/stripe-provider.mjs.map +1 -0
  42. package/dist/compat/token-meter.d.mts +28 -0
  43. package/dist/compat/token-meter.d.ts +28 -0
  44. package/dist/compat/token-meter.js +1320 -0
  45. package/dist/compat/token-meter.js.map +1 -0
  46. package/dist/compat/token-meter.mjs +1283 -0
  47. package/dist/compat/token-meter.mjs.map +1 -0
  48. package/dist/customers/index.d.mts +1 -1
  49. package/dist/customers/index.d.ts +1 -1
  50. package/dist/customers/index.js.map +1 -1
  51. package/dist/customers/index.mjs.map +1 -1
  52. package/dist/index.d.mts +25 -11
  53. package/dist/index.d.ts +25 -11
  54. package/dist/index.js +810 -72
  55. package/dist/index.js.map +1 -1
  56. package/dist/index.mjs +798 -72
  57. package/dist/index.mjs.map +1 -1
  58. package/dist/meter/index.d.mts +2 -2
  59. package/dist/meter/index.d.ts +2 -2
  60. package/dist/meter/index.js +176 -42
  61. package/dist/meter/index.js.map +1 -1
  62. package/dist/meter/index.mjs +176 -42
  63. package/dist/meter/index.mjs.map +1 -1
  64. package/dist/pricing/index.d.mts +15 -4
  65. package/dist/pricing/index.d.ts +15 -4
  66. package/dist/pricing/index.js +165 -29
  67. package/dist/pricing/index.js.map +1 -1
  68. package/dist/pricing/index.mjs +164 -29
  69. package/dist/pricing/index.mjs.map +1 -1
  70. package/dist/react/index.d.mts +46 -2
  71. package/dist/react/index.d.ts +46 -2
  72. package/dist/react/index.js +471 -29
  73. package/dist/react/index.js.map +1 -1
  74. package/dist/react/index.mjs +469 -29
  75. package/dist/react/index.mjs.map +1 -1
  76. package/dist/{types-Mozk4-Mr.d.mts → types-6P71CXAD.d.mts} +16 -6
  77. package/dist/{types-Mozk4-Mr.d.ts → types-6P71CXAD.d.ts} +16 -6
  78. package/dist/with-billing-Bj6Ya_Iy.d.ts +49 -0
  79. package/dist/with-billing-DndWGxL8.d.mts +49 -0
  80. package/package.json +35 -10
@@ -0,0 +1,1283 @@
1
+ // src/meter/client.ts
2
+ import Stripe from "stripe";
3
+
4
+ // src/meter/batcher.ts
5
+ var MeterBatcher = class {
6
+ queue = [];
7
+ timer = null;
8
+ isFlushing = false;
9
+ maxBatchSize;
10
+ flushIntervalMs;
11
+ stripeClient;
12
+ eventName;
13
+ onUsageCallback;
14
+ onErrorCallback;
15
+ debug;
16
+ // In-memory ledger for local stats
17
+ totalRequests = 0;
18
+ totalTokens = 0;
19
+ totalInputTokens = 0;
20
+ totalOutputTokens = 0;
21
+ totalReasoningTokens = 0;
22
+ totalCostUSD = 0;
23
+ byModel = {};
24
+ constructor(options = {}) {
25
+ this.stripeClient = options.stripe;
26
+ this.eventName = options.eventName || "token-billing-tokens";
27
+ this.maxBatchSize = options.batching?.maxBatchSize ?? 50;
28
+ this.flushIntervalMs = options.batching?.flushIntervalMs ?? 50;
29
+ this.onUsageCallback = options.onUsage;
30
+ this.onErrorCallback = options.onError;
31
+ this.debug = options.debug ?? false;
32
+ }
33
+ /**
34
+ * Enqueue a usage event for batch dispatching
35
+ */
36
+ enqueue(event) {
37
+ this.recordInLedger(event);
38
+ if (this.onUsageCallback) {
39
+ try {
40
+ const res = this.onUsageCallback(event);
41
+ if (res instanceof Promise) {
42
+ res.catch((err) => {
43
+ if (this.debug) console.error("[vibezcheck] Error in onUsage callback:", err);
44
+ });
45
+ }
46
+ } catch (err) {
47
+ if (this.debug) console.error("[vibezcheck] Error in onUsage callback:", err);
48
+ }
49
+ }
50
+ if (!this.stripeClient) {
51
+ if (this.debug) {
52
+ console.log(
53
+ `[vibezcheck:local] \u{1F4CA} ${event.model} | Tokens: ${event.usage.totalTokens} | Cost: $${event.cost.totalUSD.toFixed(6)}`
54
+ );
55
+ }
56
+ return;
57
+ }
58
+ this.queue.push(event);
59
+ if (this.queue.length >= this.maxBatchSize) {
60
+ this.flush().catch((err) => {
61
+ if (this.debug) console.error("[vibezcheck] Batch flush error:", err);
62
+ });
63
+ } else if (!this.timer) {
64
+ this.timer = setTimeout(() => {
65
+ this.timer = null;
66
+ this.flush().catch((err) => {
67
+ if (this.debug) console.error("[vibezcheck] Debounce flush error:", err);
68
+ });
69
+ }, this.flushIntervalMs);
70
+ }
71
+ }
72
+ /**
73
+ * Immediately flush all queued events to Stripe
74
+ */
75
+ async flush() {
76
+ if (this.timer) {
77
+ clearTimeout(this.timer);
78
+ this.timer = null;
79
+ }
80
+ if (this.queue.length === 0 || !this.stripeClient || this.isFlushing) {
81
+ return;
82
+ }
83
+ this.isFlushing = true;
84
+ const eventsToSend = [...this.queue];
85
+ this.queue = [];
86
+ try {
87
+ await this.sendEventsToStripe(eventsToSend);
88
+ } catch (error) {
89
+ const err = error instanceof Error ? error : new Error(String(error));
90
+ if (this.debug) {
91
+ console.error("[vibezcheck] Failed to send meter events to Stripe:", err);
92
+ }
93
+ if (this.onErrorCallback) {
94
+ this.onErrorCallback(err, eventsToSend);
95
+ }
96
+ } finally {
97
+ this.isFlushing = false;
98
+ if (this.queue.length > 0) {
99
+ this.flush().catch(() => {
100
+ });
101
+ }
102
+ }
103
+ }
104
+ /**
105
+ * Sends events to Stripe Billing Meter Events API
106
+ */
107
+ async sendEventsToStripe(events) {
108
+ if (!this.stripeClient) return;
109
+ for (const event of events) {
110
+ const customerId = event.customerId;
111
+ if (!customerId) {
112
+ continue;
113
+ }
114
+ const timestamp = event.timestamp || (/* @__PURE__ */ new Date()).toISOString();
115
+ const model = `${event.provider}/${event.model}`;
116
+ if (event.usage.inputTokens > 0) {
117
+ try {
118
+ await this.stripeClient.v2.billing.meterEvents.create({
119
+ event_name: this.eventName,
120
+ timestamp,
121
+ payload: {
122
+ stripe_customer_id: customerId,
123
+ value: event.usage.inputTokens.toString(),
124
+ model,
125
+ token_type: "input",
126
+ cached_tokens: (event.usage.cachedTokens ?? 0).toString(),
127
+ ...event.metadata ? event.metadata : {}
128
+ }
129
+ });
130
+ } catch (e) {
131
+ if (this.debug) console.warn("[vibezcheck] Input meter event error:", e);
132
+ }
133
+ }
134
+ if (event.usage.outputTokens > 0) {
135
+ try {
136
+ await this.stripeClient.v2.billing.meterEvents.create({
137
+ event_name: this.eventName,
138
+ timestamp,
139
+ payload: {
140
+ stripe_customer_id: customerId,
141
+ value: event.usage.outputTokens.toString(),
142
+ model,
143
+ token_type: "output",
144
+ reasoning_tokens: (event.usage.reasoningTokens ?? 0).toString(),
145
+ visible_tokens: (event.usage.visibleOutputTokens ?? event.usage.outputTokens).toString(),
146
+ ...event.metadata ? event.metadata : {}
147
+ }
148
+ });
149
+ } catch (e) {
150
+ if (this.debug) console.warn("[vibezcheck] Output meter event error:", e);
151
+ }
152
+ }
153
+ }
154
+ }
155
+ /**
156
+ * Updates internal in-memory ledger
157
+ */
158
+ recordInLedger(event) {
159
+ this.totalRequests += 1;
160
+ this.totalTokens += event.usage.totalTokens;
161
+ this.totalInputTokens += event.usage.inputTokens;
162
+ this.totalOutputTokens += event.usage.outputTokens;
163
+ this.totalReasoningTokens += event.usage.reasoningTokens ?? 0;
164
+ this.totalCostUSD += event.cost.totalUSD;
165
+ const modelKey = event.model;
166
+ if (!this.byModel[modelKey]) {
167
+ this.byModel[modelKey] = { requests: 0, tokens: 0, costUSD: 0 };
168
+ }
169
+ this.byModel[modelKey].requests += 1;
170
+ this.byModel[modelKey].tokens += event.usage.totalTokens;
171
+ this.byModel[modelKey].costUSD += event.cost.totalUSD;
172
+ }
173
+ /**
174
+ * Get in-memory usage summary
175
+ */
176
+ getSummary() {
177
+ return {
178
+ totalRequests: this.totalRequests,
179
+ totalTokens: this.totalTokens,
180
+ totalInputTokens: this.totalInputTokens,
181
+ totalOutputTokens: this.totalOutputTokens,
182
+ totalReasoningTokens: this.totalReasoningTokens,
183
+ totalCostUSD: Number(this.totalCostUSD.toFixed(6)),
184
+ byModel: { ...this.byModel }
185
+ };
186
+ }
187
+ /**
188
+ * Reset in-memory ledger
189
+ */
190
+ resetLedger() {
191
+ this.totalRequests = 0;
192
+ this.totalTokens = 0;
193
+ this.totalInputTokens = 0;
194
+ this.totalOutputTokens = 0;
195
+ this.totalReasoningTokens = 0;
196
+ this.totalCostUSD = 0;
197
+ this.byModel = {};
198
+ }
199
+ };
200
+
201
+ // src/meter/extractors/openai.ts
202
+ function extractOpenAIResponseUsage(response) {
203
+ if (!response || typeof response !== "object") return null;
204
+ if ("choices" in response && "usage" in response && response.usage) {
205
+ const rawUsage = response.usage;
206
+ const model = response.model || "gpt-4o";
207
+ const inputTokens = rawUsage.prompt_tokens ?? 0;
208
+ const outputTokens = rawUsage.completion_tokens ?? 0;
209
+ const reasoningTokens = rawUsage.completion_tokens_details?.reasoning_tokens ?? 0;
210
+ const cachedTokens = rawUsage.prompt_tokens_details?.cached_tokens ?? 0;
211
+ return {
212
+ model,
213
+ provider: "openai",
214
+ usage: {
215
+ inputTokens,
216
+ outputTokens,
217
+ totalTokens: inputTokens + outputTokens,
218
+ reasoningTokens: reasoningTokens > 0 ? reasoningTokens : void 0,
219
+ visibleOutputTokens: Math.max(0, outputTokens - reasoningTokens),
220
+ cachedTokens: cachedTokens > 0 ? cachedTokens : void 0
221
+ }
222
+ };
223
+ }
224
+ if ("data" in response && "usage" in response && response.usage && "model" in response) {
225
+ const rawUsage = response.usage;
226
+ const inputTokens = rawUsage.prompt_tokens ?? 0;
227
+ return {
228
+ model: response.model || "text-embedding-3-small",
229
+ provider: "openai",
230
+ usage: {
231
+ inputTokens,
232
+ outputTokens: 0,
233
+ totalTokens: inputTokens
234
+ }
235
+ };
236
+ }
237
+ if ("status" in response && "usage" in response && response.usage) {
238
+ const rawUsage = response.usage;
239
+ const model = response.model || "gpt-5.6-sol";
240
+ const inputTokens = rawUsage.input_tokens ?? rawUsage.prompt_tokens ?? 0;
241
+ const outputTokens = rawUsage.output_tokens ?? rawUsage.completion_tokens ?? 0;
242
+ const reasoningTokens = rawUsage.output_token_details?.reasoning_tokens ?? rawUsage.completion_tokens_details?.reasoning_tokens ?? 0;
243
+ const cachedTokens = rawUsage.input_token_details?.cached_tokens ?? rawUsage.prompt_tokens_details?.cached_tokens ?? 0;
244
+ return {
245
+ model,
246
+ provider: "openai",
247
+ usage: {
248
+ inputTokens,
249
+ outputTokens,
250
+ totalTokens: inputTokens + outputTokens,
251
+ reasoningTokens: reasoningTokens > 0 ? reasoningTokens : void 0,
252
+ visibleOutputTokens: Math.max(0, outputTokens - reasoningTokens),
253
+ cachedTokens: cachedTokens > 0 ? cachedTokens : void 0
254
+ }
255
+ };
256
+ }
257
+ return null;
258
+ }
259
+ function inspectOpenAIStreamChunk(chunk) {
260
+ if (!chunk || typeof chunk !== "object") return {};
261
+ const model = chunk.model;
262
+ const rawUsage = chunk.usage || chunk.token_usage || chunk.x_groq?.usage || chunk.usageMetadata;
263
+ if (rawUsage) {
264
+ const inputTokens = rawUsage.prompt_tokens ?? rawUsage.input_tokens ?? rawUsage.promptTokens ?? rawUsage.inputTokens ?? rawUsage.promptTokenCount ?? 0;
265
+ const outputTokens = rawUsage.completion_tokens ?? rawUsage.output_tokens ?? rawUsage.completionTokens ?? rawUsage.outputTokens ?? rawUsage.candidatesTokenCount ?? 0;
266
+ const reasoningTokens = rawUsage.completion_tokens_details?.reasoning_tokens ?? rawUsage.output_token_details?.reasoning_tokens ?? rawUsage.reasoning_tokens ?? rawUsage.reasoningTokens ?? rawUsage.thoughtsTokenCount ?? 0;
267
+ const cachedTokens = rawUsage.prompt_tokens_details?.cached_tokens ?? rawUsage.input_token_details?.cached_tokens ?? rawUsage.prompt_cache_hit_tokens ?? rawUsage.cached_tokens ?? rawUsage.cachedTokens ?? rawUsage.cachedContentTokenCount ?? 0;
268
+ return {
269
+ model,
270
+ usage: {
271
+ inputTokens,
272
+ outputTokens,
273
+ totalTokens: inputTokens + outputTokens,
274
+ reasoningTokens: reasoningTokens > 0 ? reasoningTokens : void 0,
275
+ visibleOutputTokens: Math.max(0, outputTokens - reasoningTokens),
276
+ cachedTokens: cachedTokens > 0 ? cachedTokens : void 0
277
+ }
278
+ };
279
+ }
280
+ if (chunk.type === "response.completed" || chunk.type === "response.done") {
281
+ if (chunk.response?.usage) {
282
+ const rawUsage2 = chunk.response.usage;
283
+ const inputTokens = rawUsage2.input_tokens ?? 0;
284
+ const outputTokens = rawUsage2.output_tokens ?? 0;
285
+ const reasoningTokens = rawUsage2.output_token_details?.reasoning_tokens ?? 0;
286
+ const cachedTokens = rawUsage2.input_token_details?.cached_tokens ?? 0;
287
+ return {
288
+ model: chunk.response.model || model,
289
+ usage: {
290
+ inputTokens,
291
+ outputTokens,
292
+ totalTokens: inputTokens + outputTokens,
293
+ reasoningTokens: reasoningTokens > 0 ? reasoningTokens : void 0,
294
+ visibleOutputTokens: Math.max(0, outputTokens - reasoningTokens),
295
+ cachedTokens: cachedTokens > 0 ? cachedTokens : void 0
296
+ }
297
+ };
298
+ }
299
+ }
300
+ return { model };
301
+ }
302
+
303
+ // src/meter/extractors/anthropic.ts
304
+ function extractAnthropicResponseUsage(response) {
305
+ if (!response || typeof response !== "object") return null;
306
+ if (response.type === "message" || "content" in response && "usage" in response) {
307
+ const rawUsage = response.usage || {};
308
+ const model = response.model || "claude-3-7-sonnet";
309
+ const inputTokens = rawUsage.input_tokens ?? 0;
310
+ const outputTokens = rawUsage.output_tokens ?? 0;
311
+ const cachedTokens = rawUsage.cache_read_input_tokens ?? 0;
312
+ const cacheWriteTokens = rawUsage.cache_creation_input_tokens ?? 0;
313
+ let reasoningTokens = void 0;
314
+ if (Array.isArray(response.content)) {
315
+ const thinkingBlocks = response.content.filter((b) => b.type === "thinking");
316
+ if (thinkingBlocks.length > 0) {
317
+ reasoningTokens = rawUsage.thinking_tokens ?? void 0;
318
+ }
319
+ }
320
+ return {
321
+ model,
322
+ provider: "anthropic",
323
+ usage: {
324
+ inputTokens,
325
+ outputTokens,
326
+ totalTokens: inputTokens + outputTokens,
327
+ reasoningTokens,
328
+ visibleOutputTokens: reasoningTokens !== void 0 ? Math.max(0, outputTokens - reasoningTokens) : outputTokens,
329
+ cachedTokens: cachedTokens > 0 ? cachedTokens : void 0,
330
+ cacheWriteTokens: cacheWriteTokens > 0 ? cacheWriteTokens : void 0
331
+ }
332
+ };
333
+ }
334
+ return null;
335
+ }
336
+ var AnthropicStreamAccumulator = class {
337
+ model = "claude-3-7-sonnet";
338
+ inputTokens = 0;
339
+ outputTokens = 0;
340
+ cachedTokens = 0;
341
+ cacheWriteTokens = 0;
342
+ reasoningTokens = 0;
343
+ processEvent(event) {
344
+ if (!event || typeof event !== "object") return;
345
+ if (event.type === "message_start" && event.message) {
346
+ if (event.message.model) {
347
+ this.model = event.message.model;
348
+ }
349
+ if (event.message.usage) {
350
+ this.inputTokens = event.message.usage.input_tokens ?? 0;
351
+ this.cachedTokens = event.message.usage.cache_read_input_tokens ?? 0;
352
+ this.cacheWriteTokens = event.message.usage.cache_creation_input_tokens ?? 0;
353
+ }
354
+ }
355
+ if (event.type === "message_delta" && event.usage) {
356
+ this.outputTokens = event.usage.output_tokens ?? 0;
357
+ if (event.usage.thinking_tokens) {
358
+ this.reasoningTokens = event.usage.thinking_tokens;
359
+ }
360
+ }
361
+ if (event.type === "content_block_start" && event.content_block?.type === "thinking") {
362
+ }
363
+ }
364
+ getUsage() {
365
+ return {
366
+ model: this.model,
367
+ provider: "anthropic",
368
+ usage: {
369
+ inputTokens: this.inputTokens,
370
+ outputTokens: this.outputTokens,
371
+ totalTokens: this.inputTokens + this.outputTokens,
372
+ reasoningTokens: this.reasoningTokens > 0 ? this.reasoningTokens : void 0,
373
+ visibleOutputTokens: this.reasoningTokens > 0 ? Math.max(0, this.outputTokens - this.reasoningTokens) : this.outputTokens,
374
+ cachedTokens: this.cachedTokens > 0 ? this.cachedTokens : void 0,
375
+ cacheWriteTokens: this.cacheWriteTokens > 0 ? this.cacheWriteTokens : void 0
376
+ }
377
+ };
378
+ }
379
+ };
380
+
381
+ // src/meter/extractors/gemini.ts
382
+ function extractGeminiResponseUsage(response, fallbackModel = "gemini-3.7-flash") {
383
+ if (!response || typeof response !== "object") return null;
384
+ const usageMetadata = response.usageMetadata || response.response?.usageMetadata;
385
+ if (usageMetadata) {
386
+ const inputTokens = usageMetadata.promptTokenCount ?? 0;
387
+ const baseOutputTokens = usageMetadata.candidatesTokenCount ?? 0;
388
+ const thoughtsTokenCount = usageMetadata.thoughtsTokenCount ?? usageMetadata.reasoningTokenCount ?? 0;
389
+ const cachedTokens = usageMetadata.cachedContentTokenCount ?? 0;
390
+ const totalOutput = baseOutputTokens + thoughtsTokenCount;
391
+ const model = response.model || response.response?.model || fallbackModel;
392
+ return {
393
+ model,
394
+ provider: "google",
395
+ usage: {
396
+ inputTokens,
397
+ outputTokens: totalOutput,
398
+ totalTokens: inputTokens + totalOutput,
399
+ reasoningTokens: thoughtsTokenCount > 0 ? thoughtsTokenCount : void 0,
400
+ visibleOutputTokens: baseOutputTokens,
401
+ cachedTokens: cachedTokens > 0 ? cachedTokens : void 0
402
+ }
403
+ };
404
+ }
405
+ return null;
406
+ }
407
+
408
+ // src/meter/extractors/generic.ts
409
+ function extractGenericResponseUsage(response, fallbackModel = "generic-llm", fallbackProvider = "generic") {
410
+ if (!response || typeof response !== "object") return null;
411
+ const usage = response.usage || response.token_usage || response.usageMetadata;
412
+ if (usage) {
413
+ const inputTokens = usage.prompt_tokens ?? usage.input_tokens ?? usage.promptTokenCount ?? usage.prompt_eval_count ?? 0;
414
+ const outputTokens = usage.completion_tokens ?? usage.output_tokens ?? usage.candidatesTokenCount ?? usage.eval_count ?? 0;
415
+ const reasoningTokens = usage.reasoning_tokens ?? usage.thoughtsTokenCount ?? usage.completion_tokens_details?.reasoning_tokens ?? 0;
416
+ const cachedTokens = usage.prompt_tokens_details?.cached_tokens ?? usage.cached_tokens ?? usage.cachedContentTokenCount ?? 0;
417
+ const model = response.model || fallbackModel;
418
+ const provider = response.provider || fallbackProvider;
419
+ return {
420
+ model,
421
+ provider,
422
+ usage: {
423
+ inputTokens,
424
+ outputTokens,
425
+ totalTokens: inputTokens + outputTokens,
426
+ reasoningTokens: reasoningTokens > 0 ? reasoningTokens : void 0,
427
+ visibleOutputTokens: Math.max(0, outputTokens - reasoningTokens),
428
+ cachedTokens: cachedTokens > 0 ? cachedTokens : void 0
429
+ }
430
+ };
431
+ }
432
+ return null;
433
+ }
434
+
435
+ // src/meter/extractors/index.ts
436
+ function detectAndExtractUsage(response, fallbackModel, fallbackProvider) {
437
+ if (!response || typeof response !== "object") return null;
438
+ const openaiResult = extractOpenAIResponseUsage(response);
439
+ if (openaiResult) return openaiResult;
440
+ const anthropicResult = extractAnthropicResponseUsage(response);
441
+ if (anthropicResult) return anthropicResult;
442
+ const geminiResult = extractGeminiResponseUsage(response, fallbackModel);
443
+ if (geminiResult) return geminiResult;
444
+ const genericResult = extractGenericResponseUsage(response, fallbackModel, fallbackProvider);
445
+ if (genericResult) return genericResult;
446
+ return null;
447
+ }
448
+
449
+ // src/pricing/table.ts
450
+ var MODEL_PRICING_TABLE = {
451
+ // --- OpenAI (Modern & Reasoning) ---
452
+ "gpt-5.6-sol": { inputPer1M: 4, outputPer1M: 20, cachedInputPer1M: 0.4 },
453
+ "gpt-5.6-terra": { inputPer1M: 2, outputPer1M: 12, cachedInputPer1M: 0.2 },
454
+ "gpt-5.6-luna": { inputPer1M: 0.2, outputPer1M: 1.2, cachedInputPer1M: 0.02 },
455
+ "gpt-5": { inputPer1M: 4, outputPer1M: 20, cachedInputPer1M: 0.4 },
456
+ "gpt-5-mini": { inputPer1M: 0.2, outputPer1M: 1.2, cachedInputPer1M: 0.02 },
457
+ "o1": { inputPer1M: 15, outputPer1M: 60, cachedInputPer1M: 7.5 },
458
+ "o1-mini": { inputPer1M: 1.1, outputPer1M: 4.4, cachedInputPer1M: 0.55 },
459
+ "o3": { inputPer1M: 15, outputPer1M: 60, cachedInputPer1M: 7.5 },
460
+ "o3-mini": { inputPer1M: 1.1, outputPer1M: 4.4, cachedInputPer1M: 0.55 },
461
+ "gpt-4o": { inputPer1M: 2.5, outputPer1M: 10, cachedInputPer1M: 1.25 },
462
+ "gpt-4o-mini": { inputPer1M: 0.15, outputPer1M: 0.6, cachedInputPer1M: 0.075 },
463
+ "gpt-4.5": { inputPer1M: 75, outputPer1M: 150, cachedInputPer1M: 37.5 },
464
+ "gpt-4.5-preview": { inputPer1M: 75, outputPer1M: 150, cachedInputPer1M: 37.5 },
465
+ "chatgpt-4o-latest": { inputPer1M: 5, outputPer1M: 15 },
466
+ "gpt-4.1": { inputPer1M: 2, outputPer1M: 8, cachedInputPer1M: 1 },
467
+ "gpt-4.1-nano": { inputPer1M: 0.1, outputPer1M: 0.4, cachedInputPer1M: 0.05 },
468
+ // --- OpenAI (Legacy & Backward Compatibility) ---
469
+ "gpt-4-turbo": { inputPer1M: 10, outputPer1M: 30, cachedInputPer1M: 5 },
470
+ "gpt-4-turbo-preview": { inputPer1M: 10, outputPer1M: 30, cachedInputPer1M: 5 },
471
+ "gpt-4": { inputPer1M: 30, outputPer1M: 60 },
472
+ "gpt-4-32k": { inputPer1M: 60, outputPer1M: 120 },
473
+ "gpt-3.5-turbo": { inputPer1M: 0.5, outputPer1M: 1.5 },
474
+ "gpt-3.5-turbo-16k": { inputPer1M: 3, outputPer1M: 4 },
475
+ "text-embedding-3-small": { inputPer1M: 0.02, outputPer1M: 0 },
476
+ "text-embedding-3-large": { inputPer1M: 0.13, outputPer1M: 0 },
477
+ "text-embedding-ada-002": { inputPer1M: 0.1, outputPer1M: 0 },
478
+ // --- Anthropic (Modern & Extended Thinking) ---
479
+ "claude-3-7-sonnet": { inputPer1M: 0.59, outputPer1M: 2.93, cachedInputPer1M: 0.3 },
480
+ "claude-sonnet-5": { inputPer1M: 2, outputPer1M: 10, cachedInputPer1M: 0.3 },
481
+ "claude-3-5-sonnet": { inputPer1M: 3, outputPer1M: 15, cachedInputPer1M: 0.3 },
482
+ "claude-3-5-haiku": { inputPer1M: 0.8, outputPer1M: 4, cachedInputPer1M: 0.08 },
483
+ "haiku-4.5": { inputPer1M: 1, outputPer1M: 5, cachedInputPer1M: 0.1 },
484
+ "claude-opus-5": { inputPer1M: 5, outputPer1M: 25, cachedInputPer1M: 1.5 },
485
+ "claude-3-opus": { inputPer1M: 15, outputPer1M: 75, cachedInputPer1M: 1.5 },
486
+ // --- Anthropic (Legacy & Backward Compatibility) ---
487
+ "claude-3-sonnet": { inputPer1M: 3, outputPer1M: 15, cachedInputPer1M: 0.3 },
488
+ "claude-3-haiku": { inputPer1M: 0.25, outputPer1M: 1.25, cachedInputPer1M: 0.025 },
489
+ "claude-2.1": { inputPer1M: 8, outputPer1M: 24 },
490
+ "claude-2.0": { inputPer1M: 8, outputPer1M: 24 },
491
+ "claude-instant-1.2": { inputPer1M: 1.63, outputPer1M: 5.51 },
492
+ // --- Google Gemini (Modern & Thoughts) ---
493
+ "gemini-3.7-flash": { inputPer1M: 0.75, outputPer1M: 3.75, cachedInputPer1M: 0.18 },
494
+ "gemini-3.1-pro": { inputPer1M: 2, outputPer1M: 12, cachedInputPer1M: 0.5 },
495
+ "gemini-3.5-flash": { inputPer1M: 1.5, outputPer1M: 9, cachedInputPer1M: 0.38 },
496
+ "gemini-3.1-flash-lite": { inputPer1M: 0.25, outputPer1M: 1.5, cachedInputPer1M: 0.06 },
497
+ "gemini-2.5-pro": { inputPer1M: 1.25, outputPer1M: 5, cachedInputPer1M: 0.3125 },
498
+ "gemini-2.5-flash": { inputPer1M: 0.15, outputPer1M: 0.6, cachedInputPer1M: 0.0375 },
499
+ "gemini-2.0-flash": { inputPer1M: 0.1, outputPer1M: 0.4, cachedInputPer1M: 0.025 },
500
+ "gemini-2.0-flash-lite": { inputPer1M: 0.075, outputPer1M: 0.3, cachedInputPer1M: 0.01875 },
501
+ "gemini-2.0-pro": { inputPer1M: 1.25, outputPer1M: 5, cachedInputPer1M: 0.3125 },
502
+ // --- Google Gemini (Legacy & Backward Compatibility) ---
503
+ "gemini-1.5-pro": { inputPer1M: 1.25, outputPer1M: 5, cachedInputPer1M: 0.3125 },
504
+ "gemini-1.5-flash": { inputPer1M: 0.075, outputPer1M: 0.3, cachedInputPer1M: 0.01875 },
505
+ "gemini-1.5-flash-8b": { inputPer1M: 0.0375, outputPer1M: 0.15, cachedInputPer1M: 9375e-6 },
506
+ "gemini-1.0-pro": { inputPer1M: 0.5, outputPer1M: 1.5 },
507
+ // --- xAI Grok ---
508
+ "grok-4.6": { inputPer1M: 3, outputPer1M: 15 },
509
+ "grok-3": { inputPer1M: 3, outputPer1M: 15 },
510
+ "grok-3-mini": { inputPer1M: 0.3, outputPer1M: 1.5 },
511
+ "grok-2": { inputPer1M: 2, outputPer1M: 10 },
512
+ "grok-2-vision": { inputPer1M: 2, outputPer1M: 10 },
513
+ "grok-beta": { inputPer1M: 5, outputPer1M: 15 },
514
+ // --- Mistral ---
515
+ "mistral-large-3": { inputPer1M: 2, outputPer1M: 6 },
516
+ "mistral-large-latest": { inputPer1M: 2, outputPer1M: 6 },
517
+ "mistral-large-2411": { inputPer1M: 2, outputPer1M: 6 },
518
+ "codestral-latest": { inputPer1M: 0.3, outputPer1M: 0.9 },
519
+ "mistral-medium-latest": { inputPer1M: 2.7, outputPer1M: 8.1 },
520
+ "mistral-small-latest": { inputPer1M: 0.2, outputPer1M: 0.6 },
521
+ "ministral-8b-latest": { inputPer1M: 0.1, outputPer1M: 0.1 },
522
+ "ministral-3b-latest": { inputPer1M: 0.04, outputPer1M: 0.04 },
523
+ "open-mistral-7b": { inputPer1M: 0.2, outputPer1M: 0.2 },
524
+ "open-mixtral-8x7b": { inputPer1M: 0.7, outputPer1M: 0.7 },
525
+ "open-mixtral-8x22b": { inputPer1M: 2, outputPer1M: 6 },
526
+ // --- Groq & Meta Llama LPUs ---
527
+ "llama-3.3-70b-versatile": { inputPer1M: 0.59, outputPer1M: 0.79 },
528
+ "llama-3.1-405b": { inputPer1M: 3, outputPer1M: 3 },
529
+ "llama-3.1-70b-versatile": { inputPer1M: 0.59, outputPer1M: 0.79 },
530
+ "llama-3.1-8b-instant": { inputPer1M: 0.05, outputPer1M: 0.08 },
531
+ "llama-3.2-1b-preview": { inputPer1M: 0.04, outputPer1M: 0.04 },
532
+ "llama-3.2-3b-preview": { inputPer1M: 0.06, outputPer1M: 0.06 },
533
+ "llama-3.2-11b-vision": { inputPer1M: 0.18, outputPer1M: 0.18 },
534
+ "llama-3.2-90b-vision": { inputPer1M: 0.9, outputPer1M: 0.9 },
535
+ "deepseek-r1-distill-llama-70b": { inputPer1M: 0.75, outputPer1M: 0.99 },
536
+ "deepseek-r1-distill-qwen-32b": { inputPer1M: 0.49, outputPer1M: 0.49 },
537
+ "qwen-2.5-32b": { inputPer1M: 0.29, outputPer1M: 0.39 },
538
+ "qwen-2.5-72b": { inputPer1M: 0.35, outputPer1M: 0.4 },
539
+ "mixtral-8x7b-32768": { inputPer1M: 0.24, outputPer1M: 0.24 },
540
+ "gemma2-9b-it": { inputPer1M: 0.2, outputPer1M: 0.2 },
541
+ // --- DeepSeek ---
542
+ "deepseek-v4-pro": { inputPer1M: 0.66, outputPer1M: 1.98, cachedInputPer1M: 0.15 },
543
+ "deepseek-v4-flash": { inputPer1M: 0.22, outputPer1M: 0.66, cachedInputPer1M: 0.05 },
544
+ "deepseek-v3": { inputPer1M: 0.14, outputPer1M: 0.28, cachedInputPer1M: 0.014 },
545
+ "deepseek-chat": { inputPer1M: 0.14, outputPer1M: 0.28, cachedInputPer1M: 0.014 },
546
+ "deepseek-r1": { inputPer1M: 0.55, outputPer1M: 2.19, cachedInputPer1M: 0.14 },
547
+ "deepseek-reasoner": { inputPer1M: 0.55, outputPer1M: 2.19, cachedInputPer1M: 0.14 },
548
+ // --- Cohere ---
549
+ "command-r-plus": { inputPer1M: 2.5, outputPer1M: 10 },
550
+ "command-r": { inputPer1M: 0.15, outputPer1M: 0.6 },
551
+ "command": { inputPer1M: 1, outputPer1M: 2 },
552
+ "command-light": { inputPer1M: 0.3, outputPer1M: 0.6 },
553
+ "embed-english-v3.0": { inputPer1M: 0.1, outputPer1M: 0 },
554
+ // --- Perplexity ---
555
+ "sonar": { inputPer1M: 1, outputPer1M: 1 },
556
+ "sonar-pro": { inputPer1M: 3, outputPer1M: 15 },
557
+ "sonar-reasoning": { inputPer1M: 1, outputPer1M: 5 },
558
+ "sonar-reasoning-pro": { inputPer1M: 2, outputPer1M: 8 }
559
+ };
560
+ var MODEL_ALIASES = {
561
+ // OpenAI Shorthands & Aliases
562
+ "gpt4": "gpt-4",
563
+ "gpt4o": "gpt-4o",
564
+ "gpt-4-preview": "gpt-4-turbo",
565
+ "gpt-4-0125-preview": "gpt-4-turbo",
566
+ "gpt-4-1106-preview": "gpt-4-turbo",
567
+ "gpt-3.5": "gpt-3.5-turbo",
568
+ "gpt-3.5-turbo-0125": "gpt-3.5-turbo",
569
+ "gpt-3.5-turbo-1106": "gpt-3.5-turbo",
570
+ "gpt-3.5-turbo-16k-0613": "gpt-3.5-turbo-16k",
571
+ // Anthropic Shorthands
572
+ "sonnet": "claude-3-5-sonnet",
573
+ "sonnet-3.7": "claude-3-7-sonnet",
574
+ "sonnet-3.5": "claude-3-5-sonnet",
575
+ "haiku": "claude-3-5-haiku",
576
+ "haiku-3.5": "claude-3-5-haiku",
577
+ "opus": "claude-3-opus",
578
+ "claude-2": "claude-2.0",
579
+ "claude-instant": "claude-instant-1.2",
580
+ // DeepSeek Shorthands
581
+ "r1": "deepseek-r1",
582
+ "v3": "deepseek-v3",
583
+ // Gemini Shorthands
584
+ "flash": "gemini-2.0-flash",
585
+ "pro": "gemini-1.5-pro",
586
+ // Meta / Groq Shorthands
587
+ "llama-3.3-70b": "llama-3.3-70b-versatile",
588
+ "llama-3.1-70b": "llama-3.1-70b-versatile",
589
+ "llama-3.1-8b": "llama-3.1-8b-instant",
590
+ "llama-3-70b": "llama-3.1-70b-versatile",
591
+ "llama-3-8b": "llama-3.1-8b-instant",
592
+ // Mistral Shorthands
593
+ "codestral": "codestral-latest",
594
+ "mistral-large": "mistral-large-latest",
595
+ "mistral-small": "mistral-small-latest"
596
+ };
597
+ var customPricingRegistry = {};
598
+ function normalizeModelKey(rawModel) {
599
+ if (!rawModel) return "unknown";
600
+ let model = rawModel.toLowerCase().trim();
601
+ model = model.replace(/^[a-z0-9_-]+\.(anthropic|meta|amazon|cohere|mistral|ai21)\./i, "");
602
+ model = model.replace(/^(anthropic|meta|amazon|cohere|mistral|ai21)\./i, "");
603
+ model = model.replace(/-v\d+(:\d+)?$/, "");
604
+ model = model.replace(/:\d+$/, "");
605
+ if (model.includes("/")) {
606
+ model = model.split("/").slice(1).join("/");
607
+ }
608
+ model = model.replace(/:(latest|free|beta)$/, "");
609
+ model = model.replace(/-\d{8}$/, "");
610
+ model = model.replace(/-\d{4}-\d{2}-\d{2}$/, "");
611
+ model = model.replace(/llama(\d+)-(\d+)-/g, "llama-$1.$2-");
612
+ model = model.replace(/llama(\d+)\.(\d+)-/g, "llama-$1.$2-");
613
+ if (MODEL_ALIASES[model]) {
614
+ return MODEL_ALIASES[model];
615
+ }
616
+ if (!MODEL_PRICING_TABLE[model]) {
617
+ const withoutInstruct = model.replace(/-(instruct|chat|preview)$/, "");
618
+ if (MODEL_PRICING_TABLE[withoutInstruct]) {
619
+ return withoutInstruct;
620
+ }
621
+ if (MODEL_ALIASES[withoutInstruct]) {
622
+ return MODEL_ALIASES[withoutInstruct];
623
+ }
624
+ }
625
+ return model;
626
+ }
627
+ function getModelPricing(modelName) {
628
+ const normalized = normalizeModelKey(modelName);
629
+ if (customPricingRegistry[normalized]) {
630
+ return customPricingRegistry[normalized];
631
+ }
632
+ if (customPricingRegistry[modelName]) {
633
+ return customPricingRegistry[modelName];
634
+ }
635
+ if (MODEL_PRICING_TABLE[normalized]) {
636
+ return MODEL_PRICING_TABLE[normalized];
637
+ }
638
+ if (MODEL_PRICING_TABLE[modelName]) {
639
+ return MODEL_PRICING_TABLE[modelName];
640
+ }
641
+ const alias = MODEL_ALIASES[modelName.toLowerCase().trim()];
642
+ if (alias && MODEL_PRICING_TABLE[alias]) {
643
+ return MODEL_PRICING_TABLE[alias];
644
+ }
645
+ return {
646
+ inputPer1M: 1,
647
+ outputPer1M: 3,
648
+ cachedInputPer1M: 0.5
649
+ };
650
+ }
651
+
652
+ // src/pricing/calculator.ts
653
+ function calculateCost(params) {
654
+ let rates;
655
+ if (params.customRate) {
656
+ const outRate = params.customRate.out ?? params.customRate.output ?? 0;
657
+ rates = {
658
+ inputPer1M: params.customRate.in,
659
+ outputPer1M: outRate,
660
+ reasoningPer1M: params.customRate.reasoning ?? outRate,
661
+ cachedInputPer1M: params.customRate.cached ?? params.customRate.in * 0.15,
662
+ currency: "USD"
663
+ };
664
+ } else {
665
+ rates = getModelPricing(params.model);
666
+ }
667
+ const inputTokens = BigInt(Math.max(0, params.inputTokens ?? 0));
668
+ const outputTokens = BigInt(Math.max(0, params.outputTokens ?? 0));
669
+ const reasoningTokens = BigInt(Math.max(0, params.reasoningTokens ?? 0));
670
+ const cachedTokens = BigInt(Math.max(0, params.cachedTokens ?? 0));
671
+ const regularInputTokens = inputTokens > cachedTokens ? inputTokens - cachedTokens : 0n;
672
+ const rateInputNano = BigInt(Math.round(rates.inputPer1M * 1e3));
673
+ const cachedPer1M = rates.cachedInputPer1M ?? rates.inputPer1M * 0.15;
674
+ const rateCachedNano = BigInt(Math.round(cachedPer1M * 1e3));
675
+ const rateOutputNano = BigInt(Math.round(rates.outputPer1M * 1e3));
676
+ const reasoningPer1M = rates.reasoningPer1M ?? rates.outputPer1M;
677
+ const rateReasoningNano = BigInt(Math.round(reasoningPer1M * 1e3));
678
+ const regularInputCostNano = regularInputTokens * rateInputNano;
679
+ const cachedInputCostNano = cachedTokens * rateCachedNano;
680
+ const inputCostNano = regularInputCostNano + cachedInputCostNano;
681
+ const outputCostNano = outputTokens * rateOutputNano;
682
+ const reasoningCostNano = reasoningTokens * rateReasoningNano;
683
+ const standardCacheCostNano = cachedTokens * rateInputNano;
684
+ const cachedDiscountNano = standardCacheCostNano > cachedInputCostNano ? standardCacheCostNano - cachedInputCostNano : 0n;
685
+ const wholesaleNano = inputCostNano + outputCostNano;
686
+ const markup = params.markupMultiplier ?? 1;
687
+ let billedNano = wholesaleNano;
688
+ let hasRetail = false;
689
+ if (markup !== 1 || params.minimumChargeUSD !== void 0) {
690
+ hasRetail = true;
691
+ const markupMultiplierNano = BigInt(Math.round(markup * 1e3));
692
+ billedNano = wholesaleNano * markupMultiplierNano / 1000n;
693
+ if (params.minimumChargeUSD !== void 0 && params.minimumChargeUSD > 0) {
694
+ const minChargeNano = BigInt(Math.round(params.minimumChargeUSD * 1e9));
695
+ if (billedNano < minChargeNano) {
696
+ billedNano = minChargeNano;
697
+ }
698
+ }
699
+ }
700
+ const inputCostUSD = Number(inputCostNano) / 1e9;
701
+ const outputCostUSD = Number(outputCostNano) / 1e9;
702
+ const reasoningCostUSD = Number(reasoningCostNano) / 1e9;
703
+ const cachedDiscountUSD = Number(cachedDiscountNano) / 1e9;
704
+ const totalUSD = Number(wholesaleNano) / 1e9;
705
+ const wholesaleTotalUSD = totalUSD;
706
+ const billedUSD = Number(billedNano) / 1e9;
707
+ const profitUSD = Math.max(0, billedUSD - wholesaleTotalUSD);
708
+ const retailUSD = hasRetail ? billedUSD : void 0;
709
+ return {
710
+ inputCostUSD: Number(inputCostUSD.toFixed(8)),
711
+ outputCostUSD: Number(outputCostUSD.toFixed(8)),
712
+ reasoningCostUSD: reasoningTokens > 0n ? Number(reasoningCostUSD.toFixed(8)) : void 0,
713
+ cachedDiscountUSD: cachedTokens > 0n ? Number(cachedDiscountUSD.toFixed(8)) : void 0,
714
+ totalUSD: Number(totalUSD.toFixed(8)),
715
+ wholesaleTotalUSD: Number(wholesaleTotalUSD.toFixed(8)),
716
+ billedUSD: Number(billedUSD.toFixed(8)),
717
+ profitUSD: Number(profitUSD.toFixed(8)),
718
+ retailUSD: retailUSD !== void 0 ? Number(retailUSD.toFixed(8)) : void 0,
719
+ currency: rates.currency || "USD"
720
+ };
721
+ }
722
+ function calculateUsageCost(model, usage, optionsOrMarkup) {
723
+ const options = typeof optionsOrMarkup === "number" ? { markupMultiplier: optionsOrMarkup } : optionsOrMarkup || {};
724
+ return calculateCost({
725
+ model,
726
+ inputTokens: usage.inputTokens,
727
+ outputTokens: usage.outputTokens,
728
+ reasoningTokens: usage.reasoningTokens,
729
+ cachedTokens: usage.cachedTokens,
730
+ cacheWriteTokens: usage.cacheWriteTokens,
731
+ markupMultiplier: options.markupMultiplier,
732
+ minimumChargeUSD: options.minimumChargeUSD,
733
+ customRate: options.customRate
734
+ });
735
+ }
736
+
737
+ // src/customers/helpers.ts
738
+ function normalizeCustomer(customer, extraCustomerId) {
739
+ if (!customer && !extraCustomerId) {
740
+ return {
741
+ customerId: void 0,
742
+ customerEmail: void 0,
743
+ customerObj: void 0,
744
+ customerMetadata: {}
745
+ };
746
+ }
747
+ if (typeof customer === "string") {
748
+ const isEmail = customer.includes("@");
749
+ const customerObj2 = {
750
+ id: customer,
751
+ email: isEmail ? customer : void 0
752
+ };
753
+ return {
754
+ customerId: customer,
755
+ customerEmail: isEmail ? customer : void 0,
756
+ customerObj: customerObj2,
757
+ customerMetadata: {}
758
+ };
759
+ }
760
+ const customerObj = customer || (extraCustomerId ? { id: extraCustomerId } : void 0);
761
+ const customerId = customerObj?.id || customerObj?.userId || extraCustomerId;
762
+ const customerEmail = customerObj?.email;
763
+ const customerMetadata = {};
764
+ if (customerObj) {
765
+ const orgId = customerObj.orgId || customerObj.organizationId;
766
+ if (orgId) customerMetadata.org_id = String(orgId);
767
+ const teamId = customerObj.teamId || customerObj.workspaceId;
768
+ if (teamId) customerMetadata.team_id = String(teamId);
769
+ if (customerObj.plan) customerMetadata.plan = String(customerObj.plan);
770
+ if (customerObj.tier) customerMetadata.tier = String(customerObj.tier);
771
+ if (customerObj.role) customerMetadata.role = String(customerObj.role);
772
+ if (customerObj.orgName) customerMetadata.org_name = String(customerObj.orgName);
773
+ if (customerObj.metadata) {
774
+ for (const [key, val] of Object.entries(customerObj.metadata)) {
775
+ if (val !== null && val !== void 0) {
776
+ customerMetadata[key] = val;
777
+ }
778
+ }
779
+ }
780
+ }
781
+ return {
782
+ customerId,
783
+ customerEmail,
784
+ customerObj,
785
+ customerMetadata
786
+ };
787
+ }
788
+
789
+ // src/meter/stream.ts
790
+ function wrapOpenAIStream(stream, options, onComplete) {
791
+ let detectedModel = options.model || "gpt-4o";
792
+ let finalUsage = null;
793
+ const wrappedAsyncIterable = {
794
+ async *[Symbol.asyncIterator]() {
795
+ try {
796
+ for await (const chunk of stream) {
797
+ const inspected = inspectOpenAIStreamChunk(chunk);
798
+ if (inspected.model) {
799
+ detectedModel = inspected.model;
800
+ }
801
+ if (inspected.usage) {
802
+ finalUsage = inspected.usage;
803
+ }
804
+ yield chunk;
805
+ }
806
+ } finally {
807
+ if (finalUsage) {
808
+ const cost = calculateUsageCost(detectedModel, finalUsage);
809
+ const normalized = normalizeCustomer(options.customer, options.customerId);
810
+ const event = {
811
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
812
+ model: detectedModel,
813
+ provider: "openai",
814
+ usage: finalUsage,
815
+ cost,
816
+ customerId: normalized.customerId,
817
+ customerEmail: normalized.customerEmail,
818
+ customer: normalized.customerObj,
819
+ metadata: {
820
+ ...normalized.customerMetadata,
821
+ ...options.metadata
822
+ }
823
+ };
824
+ onComplete(event);
825
+ if (options.onUsage) {
826
+ options.onUsage(event);
827
+ }
828
+ }
829
+ }
830
+ }
831
+ };
832
+ return wrappedAsyncIterable;
833
+ }
834
+ function wrapAnthropicStream(stream, options, onComplete) {
835
+ const accumulator = new AnthropicStreamAccumulator();
836
+ const wrappedAsyncIterable = {
837
+ async *[Symbol.asyncIterator]() {
838
+ try {
839
+ for await (const event of stream) {
840
+ accumulator.processEvent(event);
841
+ yield event;
842
+ }
843
+ } finally {
844
+ const extracted = accumulator.getUsage();
845
+ const model = options.model || extracted.model;
846
+ const cost = calculateUsageCost(model, extracted.usage);
847
+ const normalized = normalizeCustomer(options.customer, options.customerId);
848
+ const usageEvent = {
849
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
850
+ model,
851
+ provider: "anthropic",
852
+ usage: extracted.usage,
853
+ cost,
854
+ customerId: normalized.customerId,
855
+ customerEmail: normalized.customerEmail,
856
+ customer: normalized.customerObj,
857
+ metadata: {
858
+ ...normalized.customerMetadata,
859
+ ...options.metadata
860
+ }
861
+ };
862
+ onComplete(usageEvent);
863
+ if (options.onUsage) {
864
+ options.onUsage(usageEvent);
865
+ }
866
+ }
867
+ }
868
+ };
869
+ return wrappedAsyncIterable;
870
+ }
871
+ function wrapGeminiStream(result, options, onComplete) {
872
+ if (!result || !result.stream) return result;
873
+ const originalStream = result.stream;
874
+ const model = options.model || "gemini-3.7-flash";
875
+ let lastChunkWithUsage = null;
876
+ const wrappedStream = (async function* () {
877
+ try {
878
+ for await (const chunk of originalStream) {
879
+ if (chunk.usageMetadata) {
880
+ lastChunkWithUsage = chunk;
881
+ }
882
+ yield chunk;
883
+ }
884
+ } finally {
885
+ if (lastChunkWithUsage) {
886
+ const extracted = extractGeminiResponseUsage(lastChunkWithUsage, model);
887
+ if (extracted) {
888
+ const cost = calculateUsageCost(extracted.model, extracted.usage);
889
+ const normalized = normalizeCustomer(options.customer, options.customerId);
890
+ const event = {
891
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
892
+ model: extracted.model,
893
+ provider: "google",
894
+ usage: extracted.usage,
895
+ cost,
896
+ customerId: normalized.customerId,
897
+ customerEmail: normalized.customerEmail,
898
+ customer: normalized.customerObj,
899
+ metadata: {
900
+ ...normalized.customerMetadata,
901
+ ...options.metadata
902
+ }
903
+ };
904
+ onComplete(event);
905
+ if (options.onUsage) {
906
+ options.onUsage(event);
907
+ }
908
+ }
909
+ }
910
+ }
911
+ })();
912
+ return {
913
+ ...result,
914
+ stream: wrappedStream
915
+ };
916
+ }
917
+ function wrapUniversalStream(stream, options = {}, onComplete) {
918
+ if (!stream || typeof stream !== "object") return stream;
919
+ if ("stream" in stream && "response" in stream) {
920
+ return wrapGeminiStream(stream, options, onComplete);
921
+ }
922
+ if (Symbol.asyncIterator in stream) {
923
+ if (options.provider === "anthropic" || options.model?.toLowerCase().includes("claude")) {
924
+ return wrapAnthropicStream(stream, options, onComplete);
925
+ }
926
+ return wrapOpenAIStream(stream, options, onComplete);
927
+ }
928
+ return stream;
929
+ }
930
+
931
+ // src/meter/client.ts
932
+ var VibezMeter = class {
933
+ batcher;
934
+ stripeClient;
935
+ markupMultiplier;
936
+ constructor(options = {}) {
937
+ this.markupMultiplier = options.markupMultiplier;
938
+ if (options.stripe) {
939
+ this.stripeClient = options.stripe;
940
+ } else if (options.apiKey || process.env.STRIPE_SECRET_KEY) {
941
+ const key = options.apiKey || process.env.STRIPE_SECRET_KEY;
942
+ this.stripeClient = new Stripe(key, {
943
+ appInfo: {
944
+ name: "vibezcheck",
945
+ version: "0.5.3",
946
+ url: "https://vibezcheck.xyz"
947
+ }
948
+ });
949
+ }
950
+ this.batcher = new MeterBatcher({
951
+ ...options,
952
+ stripe: this.stripeClient
953
+ });
954
+ }
955
+ /**
956
+ * Track token usage from a non-streaming response object (OpenAI, Anthropic, Gemini, etc.)
957
+ */
958
+ trackUsage(response, options = {}) {
959
+ const extracted = detectAndExtractUsage(response, options.model, options.provider);
960
+ if (!extracted) {
961
+ return null;
962
+ }
963
+ const model = options.model || extracted.model;
964
+ const cost = calculateUsageCost(model, extracted.usage, this.markupMultiplier);
965
+ const normalized = normalizeCustomer(options.customer, options.customerId);
966
+ const mergedMeta = {
967
+ ...normalized.customerMetadata,
968
+ ...options.metadata
969
+ };
970
+ const event = {
971
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
972
+ model,
973
+ provider: extracted.provider,
974
+ usage: extracted.usage,
975
+ cost,
976
+ customerId: normalized.customerId,
977
+ customerEmail: normalized.customerEmail,
978
+ customer: normalized.customerObj,
979
+ metadata: mergedMeta
980
+ };
981
+ this.batcher.enqueue(event);
982
+ return event;
983
+ }
984
+ /**
985
+ * Wrap any LLM stream (OpenAI, Anthropic, Gemini) with zero added latency
986
+ */
987
+ wrapStream(stream, options = {}) {
988
+ return wrapUniversalStream(stream, options, (event) => {
989
+ this.batcher.enqueue(event);
990
+ });
991
+ }
992
+ /**
993
+ * Directly record token usage manually
994
+ */
995
+ recordUsage(options) {
996
+ const inputTokens = options.inputTokens ?? 0;
997
+ const outputTokens = options.outputTokens ?? 0;
998
+ const reasoningTokens = options.reasoningTokens;
999
+ const cachedTokens = options.cachedTokens;
1000
+ const usage = {
1001
+ inputTokens,
1002
+ outputTokens,
1003
+ totalTokens: inputTokens + outputTokens,
1004
+ reasoningTokens,
1005
+ visibleOutputTokens: reasoningTokens !== void 0 ? Math.max(0, outputTokens - reasoningTokens) : outputTokens,
1006
+ cachedTokens
1007
+ };
1008
+ const cost = calculateUsageCost(options.model, usage, this.markupMultiplier);
1009
+ const normalized = normalizeCustomer(options.customer, options.customerId);
1010
+ const mergedMeta = {
1011
+ ...normalized.customerMetadata,
1012
+ ...options.metadata
1013
+ };
1014
+ const event = {
1015
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1016
+ model: options.model,
1017
+ provider: options.provider || "custom",
1018
+ usage,
1019
+ cost,
1020
+ customerId: normalized.customerId,
1021
+ customerEmail: normalized.customerEmail,
1022
+ customer: normalized.customerObj,
1023
+ metadata: mergedMeta
1024
+ };
1025
+ this.batcher.enqueue(event);
1026
+ return event;
1027
+ }
1028
+ /**
1029
+ * Flush pending events to Stripe (vital for Serverless & Edge environments)
1030
+ */
1031
+ async flush() {
1032
+ await this.batcher.flush();
1033
+ }
1034
+ /**
1035
+ * Get in-memory aggregated usage statistics
1036
+ */
1037
+ getUsageSummary() {
1038
+ return this.batcher.getSummary();
1039
+ }
1040
+ /**
1041
+ * Reset in-memory ledger
1042
+ */
1043
+ resetSummary() {
1044
+ this.batcher.resetLedger();
1045
+ }
1046
+ };
1047
+
1048
+ // src/compat/token-meter.ts
1049
+ function createTokenMeter(stripeApiKey, config = {}) {
1050
+ const apiKey = stripeApiKey || (typeof process !== "undefined" ? process.env?.STRIPE_SECRET_KEY || process.env?.STRIPE_API_KEY : void 0);
1051
+ const meter = new VibezMeter({
1052
+ apiKey,
1053
+ eventName: config.eventName
1054
+ });
1055
+ const resolveCustomer = (cust) => {
1056
+ if (typeof cust === "string") return cust;
1057
+ return cust.customerId || cust.customer || "unknown-customer";
1058
+ };
1059
+ return {
1060
+ trackUsage(response, customer) {
1061
+ if (!response) return;
1062
+ const customerId = resolveCustomer(customer);
1063
+ meter.trackUsage(response, { customer: customerId });
1064
+ },
1065
+ track(response, customer) {
1066
+ if (!response) return;
1067
+ const customerId = resolveCustomer(customer);
1068
+ meter.trackUsage(response, { customer: customerId });
1069
+ },
1070
+ trackUsageStreamOpenAI(stream, customer) {
1071
+ if (!stream || typeof stream[Symbol.asyncIterator] !== "function") {
1072
+ return stream;
1073
+ }
1074
+ const stripeCustomerId = resolveCustomer(customer);
1075
+ const originalIterator = stream[Symbol.asyncIterator].bind(stream);
1076
+ let capturedUsage = null;
1077
+ let capturedModel = "gpt-4o";
1078
+ const wrappedAsyncGenerator = async function* () {
1079
+ try {
1080
+ for await (const chunk of originalIterator()) {
1081
+ if (chunk.model) {
1082
+ capturedModel = chunk.model;
1083
+ }
1084
+ if (chunk.usage || chunk.token_usage) {
1085
+ capturedUsage = chunk.usage || chunk.token_usage;
1086
+ }
1087
+ yield chunk;
1088
+ }
1089
+ } finally {
1090
+ if (capturedUsage) {
1091
+ meter.recordUsage({
1092
+ model: capturedModel,
1093
+ provider: "openai",
1094
+ inputTokens: capturedUsage.prompt_tokens ?? capturedUsage.inputTokens ?? 0,
1095
+ outputTokens: capturedUsage.completion_tokens ?? capturedUsage.outputTokens ?? 0,
1096
+ reasoningTokens: capturedUsage.completion_tokens_details?.reasoning_tokens ?? capturedUsage.output_token_details?.reasoning_tokens ?? 0,
1097
+ cachedTokens: capturedUsage.prompt_tokens_details?.cached_tokens ?? capturedUsage.input_token_details?.cached_tokens ?? 0,
1098
+ customer: stripeCustomerId
1099
+ });
1100
+ }
1101
+ }
1102
+ };
1103
+ return wrappedAsyncGenerator();
1104
+ },
1105
+ trackUsageStreamAnthropic(stream, customer) {
1106
+ if (!stream || typeof stream[Symbol.asyncIterator] !== "function") {
1107
+ return stream;
1108
+ }
1109
+ const stripeCustomerId = resolveCustomer(customer);
1110
+ const originalIterator = stream[Symbol.asyncIterator].bind(stream);
1111
+ let capturedUsage = null;
1112
+ let capturedModel = "claude-3-5-sonnet";
1113
+ const wrappedAsyncGenerator = async function* () {
1114
+ try {
1115
+ for await (const chunk of originalIterator()) {
1116
+ if (chunk.type === "message_start" && chunk.message?.model) {
1117
+ capturedModel = chunk.message.model;
1118
+ if (chunk.message.usage) {
1119
+ capturedUsage = { ...capturedUsage, ...chunk.message.usage };
1120
+ }
1121
+ } else if (chunk.type === "message_delta" && chunk.usage) {
1122
+ capturedUsage = { ...capturedUsage, ...chunk.usage };
1123
+ }
1124
+ yield chunk;
1125
+ }
1126
+ } finally {
1127
+ if (capturedUsage) {
1128
+ meter.recordUsage({
1129
+ model: capturedModel,
1130
+ provider: "anthropic",
1131
+ inputTokens: capturedUsage.input_tokens ?? 0,
1132
+ outputTokens: capturedUsage.output_tokens ?? 0,
1133
+ cachedTokens: capturedUsage.cache_read_input_tokens ?? 0,
1134
+ customer: stripeCustomerId
1135
+ });
1136
+ }
1137
+ }
1138
+ };
1139
+ return wrappedAsyncGenerator();
1140
+ },
1141
+ trackUsageStreamGemini(streamResult, customer, modelName = "gemini-2.0-flash") {
1142
+ const stripeCustomerId = resolveCustomer(customer);
1143
+ const originalStream = streamResult?.stream || streamResult;
1144
+ if (!originalStream || typeof originalStream[Symbol.asyncIterator] !== "function") {
1145
+ return streamResult;
1146
+ }
1147
+ const wrappedAsyncGenerator = async function* () {
1148
+ let lastUsageMetadata = null;
1149
+ try {
1150
+ for await (const chunk of originalStream) {
1151
+ if (chunk.usageMetadata) {
1152
+ lastUsageMetadata = chunk.usageMetadata;
1153
+ }
1154
+ yield chunk;
1155
+ }
1156
+ } finally {
1157
+ if (lastUsageMetadata) {
1158
+ meter.recordUsage({
1159
+ model: modelName,
1160
+ provider: "google",
1161
+ inputTokens: lastUsageMetadata.promptTokenCount ?? 0,
1162
+ outputTokens: (lastUsageMetadata.candidatesTokenCount ?? 0) + (lastUsageMetadata.thoughtsTokenCount ?? 0),
1163
+ reasoningTokens: lastUsageMetadata.thoughtsTokenCount ?? 0,
1164
+ customer: stripeCustomerId
1165
+ });
1166
+ }
1167
+ }
1168
+ };
1169
+ if (streamResult && streamResult.stream) {
1170
+ return {
1171
+ ...streamResult,
1172
+ stream: wrappedAsyncGenerator()
1173
+ };
1174
+ }
1175
+ return wrappedAsyncGenerator();
1176
+ },
1177
+ trackUsageStreamDeepSeek(stream, customer, modelName = "deepseek-chat") {
1178
+ const stripeCustomerId = resolveCustomer(customer);
1179
+ if (!stream || typeof stream[Symbol.asyncIterator] !== "function") {
1180
+ return stream;
1181
+ }
1182
+ const originalIterator = stream[Symbol.asyncIterator].bind(stream);
1183
+ let capturedUsage = null;
1184
+ let capturedModel = modelName;
1185
+ const wrappedAsyncGenerator = async function* () {
1186
+ try {
1187
+ for await (const chunk of originalIterator()) {
1188
+ if (chunk.model) capturedModel = chunk.model;
1189
+ if (chunk.usage) capturedUsage = chunk.usage;
1190
+ yield chunk;
1191
+ }
1192
+ } finally {
1193
+ if (capturedUsage) {
1194
+ meter.recordUsage({
1195
+ model: capturedModel,
1196
+ provider: "deepseek",
1197
+ inputTokens: capturedUsage.prompt_tokens ?? capturedUsage.inputTokens ?? 0,
1198
+ outputTokens: capturedUsage.completion_tokens ?? capturedUsage.outputTokens ?? 0,
1199
+ reasoningTokens: capturedUsage.completion_tokens_details?.reasoning_tokens ?? 0,
1200
+ cachedTokens: capturedUsage.prompt_cache_hit_tokens ?? capturedUsage.prompt_tokens_details?.cached_tokens ?? 0,
1201
+ customer: stripeCustomerId
1202
+ });
1203
+ }
1204
+ }
1205
+ };
1206
+ return wrappedAsyncGenerator();
1207
+ },
1208
+ trackUsageStreamGroq(stream, customer, modelName = "llama-3.3-70b-versatile") {
1209
+ const stripeCustomerId = resolveCustomer(customer);
1210
+ if (!stream || typeof stream[Symbol.asyncIterator] !== "function") {
1211
+ return stream;
1212
+ }
1213
+ const originalIterator = stream[Symbol.asyncIterator].bind(stream);
1214
+ let capturedUsage = null;
1215
+ let capturedModel = modelName;
1216
+ const wrappedAsyncGenerator = async function* () {
1217
+ try {
1218
+ for await (const chunk of originalIterator()) {
1219
+ if (chunk.model) capturedModel = chunk.model;
1220
+ if (chunk.usage || chunk.x_groq?.usage) {
1221
+ capturedUsage = chunk.usage || chunk.x_groq?.usage;
1222
+ }
1223
+ yield chunk;
1224
+ }
1225
+ } finally {
1226
+ if (capturedUsage) {
1227
+ meter.recordUsage({
1228
+ model: capturedModel,
1229
+ provider: "groq",
1230
+ inputTokens: capturedUsage.prompt_tokens ?? 0,
1231
+ outputTokens: capturedUsage.completion_tokens ?? 0,
1232
+ customer: stripeCustomerId
1233
+ });
1234
+ }
1235
+ }
1236
+ };
1237
+ return wrappedAsyncGenerator();
1238
+ },
1239
+ trackUsageStreamMistral(stream, customer, modelName = "mistral-large-latest") {
1240
+ const stripeCustomerId = resolveCustomer(customer);
1241
+ if (!stream || typeof stream[Symbol.asyncIterator] !== "function") {
1242
+ return stream;
1243
+ }
1244
+ const originalIterator = stream[Symbol.asyncIterator].bind(stream);
1245
+ let capturedUsage = null;
1246
+ let capturedModel = modelName;
1247
+ const wrappedAsyncGenerator = async function* () {
1248
+ try {
1249
+ for await (const chunk of originalIterator()) {
1250
+ if (chunk.model) capturedModel = chunk.model;
1251
+ if (chunk.usage) capturedUsage = chunk.usage;
1252
+ yield chunk;
1253
+ }
1254
+ } finally {
1255
+ if (capturedUsage) {
1256
+ meter.recordUsage({
1257
+ model: capturedModel,
1258
+ provider: "mistral",
1259
+ inputTokens: capturedUsage.prompt_tokens ?? 0,
1260
+ outputTokens: capturedUsage.completion_tokens ?? 0,
1261
+ customer: stripeCustomerId
1262
+ });
1263
+ }
1264
+ }
1265
+ };
1266
+ return wrappedAsyncGenerator();
1267
+ },
1268
+ trackUsageStream(stream, customer, options = {}) {
1269
+ const stripeCustomerId = resolveCustomer(customer);
1270
+ return meter.wrapStream(stream, {
1271
+ customer: stripeCustomerId,
1272
+ model: options.model,
1273
+ provider: options.provider
1274
+ });
1275
+ }
1276
+ };
1277
+ }
1278
+ var token_meter_default = createTokenMeter;
1279
+ export {
1280
+ createTokenMeter,
1281
+ token_meter_default as default
1282
+ };
1283
+ //# sourceMappingURL=token-meter.mjs.map