@drawbridge/drawbridge-utils 0.0.64 → 0.0.66

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/ai.cjs CHANGED
@@ -20,13 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  var ai_exports = {};
21
21
  __export(ai_exports, {
22
22
  MARKUP: () => MARKUP,
23
- cost: () => cost,
24
- costForRequest: () => costForRequest,
25
- google: () => google,
26
- priceForRequest: () => priceForRequest,
27
- pricing: () => pricing,
28
- toolCost: () => toolCost,
29
- toolPricing: () => toolPricing
23
+ google: () => google
30
24
  });
31
25
  module.exports = __toCommonJS(ai_exports);
32
26
  var import_genai = require("@google/genai");
@@ -155,19 +149,10 @@ var debit = async ({
155
149
  });
156
150
  };
157
151
 
158
- // lib/ai.js
159
- var google_client = new import_genai.GoogleGenAI({
160
- apiKey: process.env.GOOGLE_GEMINI_API_KEY
161
- });
162
- var googleCircuit = circuit({
163
- name: "google-ai",
164
- threshold: 3,
165
- timeout: 6e4
166
- });
167
- var models = {
168
- image: "gemini-2.5-flash-image",
169
- text: "gemini-2.5-flash"
170
- };
152
+ // lib/billing.js
153
+ var import_drawbridge_telemetry2 = require("@drawbridge/drawbridge-telemetry");
154
+ var logger = (0, import_drawbridge_telemetry2.createLogger)();
155
+ var MARKUP = 1.3;
171
156
  var cost = {
172
157
  "gemini-2.5-flash": {
173
158
  cached: 3,
@@ -180,7 +165,6 @@ var cost = {
180
165
  output: 3e3
181
166
  }
182
167
  };
183
- var MARKUP = 1.3;
184
168
  var toolCost = {
185
169
  search: 3.5
186
170
  };
@@ -221,25 +205,7 @@ var costForRequest = (model, usage, tools) => {
221
205
  }
222
206
  return sumCents(rates, usage) + Math.ceil(sumToolCents(toolCost, tools));
223
207
  };
224
- var tokensFromMetadata = (metadata) => ({
225
- cached: Number(metadata == null ? void 0 : metadata.cachedContentTokenCount) || 0,
226
- input: Number(metadata == null ? void 0 : metadata.promptTokenCount) || 0,
227
- output: Number(metadata == null ? void 0 : metadata.candidatesTokenCount) || 0,
228
- thinking: Number(metadata == null ? void 0 : metadata.thoughtsTokenCount) || 0
229
- });
230
- var toolsFromResponse = (response) => {
231
- var _a, _b;
232
- return {
233
- search: Boolean((_b = (_a = response == null ? void 0 : response.candidates) == null ? void 0 : _a[0]) == null ? void 0 : _b.groundingMetadata)
234
- };
235
- };
236
- var billRequest = async ({
237
- db,
238
- user,
239
- model,
240
- usage,
241
- tools
242
- }) => {
208
+ var billRequest = async ({ db, user, model, usage, tools }) => {
243
209
  const gross = priceForRequest(model, usage, tools);
244
210
  const cogs = costForRequest(model, usage, tools);
245
211
  await debit({
@@ -266,6 +232,42 @@ var billRequest = async ({
266
232
  }
267
233
  });
268
234
  };
235
+ var ai = {
236
+ // Retail cents for a request (what the user pays).
237
+ price: priceForRequest,
238
+ // Wholesale cents for a request (what we pay the provider).
239
+ cost: costForRequest,
240
+ // Deferred bill() the caller fires on success, so the debit lands only when
241
+ // the request succeeds and bundles into that request's Sentry trace. No user
242
+ // → a no-op.
243
+ bill: ({ db, user, model, usage, tools }) => (user == null ? void 0 : user.id) ? () => billRequest({ db, user, model, usage, tools }) : () => Promise.resolve()
244
+ };
245
+
246
+ // lib/ai.js
247
+ var google_client = new import_genai.GoogleGenAI({
248
+ apiKey: process.env.GOOGLE_GEMINI_API_KEY
249
+ });
250
+ var googleCircuit = circuit({
251
+ name: "google-ai",
252
+ threshold: 3,
253
+ timeout: 6e4
254
+ });
255
+ var models = {
256
+ image: "gemini-2.5-flash-image",
257
+ text: "gemini-2.5-flash"
258
+ };
259
+ var tokensFromMetadata = (metadata) => ({
260
+ cached: Number(metadata == null ? void 0 : metadata.cachedContentTokenCount) || 0,
261
+ input: Number(metadata == null ? void 0 : metadata.promptTokenCount) || 0,
262
+ output: Number(metadata == null ? void 0 : metadata.candidatesTokenCount) || 0,
263
+ thinking: Number(metadata == null ? void 0 : metadata.thoughtsTokenCount) || 0
264
+ });
265
+ var toolsFromResponse = (response) => {
266
+ var _a, _b;
267
+ return {
268
+ search: Boolean((_b = (_a = response == null ? void 0 : response.candidates) == null ? void 0 : _a[0]) == null ? void 0 : _b.groundingMetadata)
269
+ };
270
+ };
269
271
  var google = {
270
272
  image: async ({
271
273
  config = {},
@@ -297,7 +299,7 @@ var google = {
297
299
  }
298
300
  const usage = tokensFromMetadata(response == null ? void 0 : response.usageMetadata);
299
301
  const tools = toolsFromResponse(response);
300
- const bill = (user == null ? void 0 : user.id) ? () => billRequest({ db, user, model, usage, tools }) : () => Promise.resolve();
302
+ const bill = ai.bill({ db, user, model, usage, tools });
301
303
  return { content, bill };
302
304
  },
303
305
  text: async ({
@@ -325,18 +327,12 @@ var google = {
325
327
  }
326
328
  const usage = tokensFromMetadata(response == null ? void 0 : response.usageMetadata);
327
329
  const tools = toolsFromResponse(response);
328
- const bill = (user == null ? void 0 : user.id) ? () => billRequest({ db, user, model, usage, tools }) : () => Promise.resolve();
330
+ const bill = ai.bill({ db, user, model, usage, tools });
329
331
  return { content, bill };
330
332
  }
331
333
  };
332
334
  // Annotate the CommonJS export names for ESM import in node:
333
335
  0 && (module.exports = {
334
336
  MARKUP,
335
- cost,
336
- costForRequest,
337
- google,
338
- priceForRequest,
339
- pricing,
340
- toolCost,
341
- toolPricing
337
+ google
342
338
  });
package/dist/ai.d.cts CHANGED
@@ -1,7 +1,10 @@
1
1
  import { GoogleGenAI } from '@google/genai';
2
2
  import { circuit } from './circuit.cjs';
3
- import { debit } from './transactions.cjs';
3
+ import { ai } from './billing.cjs';
4
+ export { MARKUP } from './billing.cjs';
5
+ import './transactions.cjs';
4
6
  import '@drawbridge/drawbridge-telemetry';
7
+ import './usage.cjs';
5
8
 
6
9
  // AI request entry point. Wraps the Google GenAI client with a per-process
7
10
  // circuit breaker, owns the per-model pricing table, and atomically debits
@@ -37,117 +40,6 @@ const models = {
37
40
  text : 'gemini-2.5-flash'
38
41
  };
39
42
 
40
- // Raw per-model provider rates in cents per 1,000,000 tokens. This is what
41
- // Drawbridge pays the provider — the wholesale cost. Sync from LiteLLM:
42
- // https://github.com/BerriAI/litellm/blob/main/litellm/model_prices_and_context_window_backup.json
43
- // Never apply markup here; the MARKUP constant below derives the retail
44
- // `pricing` table.
45
- const cost = {
46
- 'gemini-2.5-flash' : {
47
- cached : 3,
48
- input : 30,
49
- output : 250
50
- },
51
- 'gemini-2.5-flash-image' : {
52
- cached : 3,
53
- input : 30,
54
- output : 3000
55
- }
56
- };
57
-
58
- // Markup multiplier applied to every model's cost to produce the retail
59
- // price. 1.3 = 30% margin. Change one number, all pricing updates. Mirrors
60
- // the per-customer markup Stripe rate cards used to provide; keeping it
61
- // global (rather than per-model or per-plan) for simplicity — easy to
62
- // refactor later if tier-based pricing becomes a real need.
63
- const MARKUP = 1.3;
64
-
65
- // Flat per-request fees Google charges on top of tokens when specific tools
66
- // fire. Search grounding is $35/1k requests → 3.5 cents/request. Billed only
67
- // when the response actually used the tool (response.candidates[0].
68
- // groundingMetadata is populated) — enabling the tool in config without it
69
- // being invoked is free. Add more tools here as Google introduces them.
70
- const toolCost = {
71
- search : 3.5
72
- };
73
-
74
- const toolPricing = Object.fromEntries(
75
- Object.entries( toolCost ).map( ( [ tool, value ] ) => [
76
- tool,
77
- Math.ceil( value * MARKUP )
78
- ] )
79
- );
80
-
81
- // Derived retail pricing in cents per 1,000,000 tokens — what users pay.
82
- // Rounded so the per-1M rate is a whole-cent value; per-request rounding
83
- // happens in priceForRequest via Math.ceil so we never under-bill.
84
- const pricing = Object.fromEntries(
85
- Object.entries( cost ).map( ( [ model, rates ] ) => [
86
- model,
87
- {
88
- cached : Math.round( rates.cached * MARKUP ),
89
- input : Math.round( rates.input * MARKUP ),
90
- output : Math.round( rates.output * MARKUP )
91
- }
92
- ] )
93
- );
94
-
95
- // Gemini 2.5 emits internal reasoning ("thinking") tokens alongside the
96
- // visible response. Google bills them at the standard output rate, so they
97
- // roll into the same multiplier here even though they live as a distinct
98
- // field on the tokens object for visibility / margin analysis.
99
- const sumCents = ( rates, tokens ) => Math.ceil(
100
- (
101
- Number( tokens?.cached || 0 ) * rates.cached +
102
- Number( tokens?.input || 0 ) * rates.input +
103
- (
104
- Number( tokens?.output || 0 ) +
105
- Number( tokens?.thinking || 0 )
106
- ) * rates.output
107
- ) / 1_000_000
108
- );
109
-
110
- // Sum the per-request flat fees for the tools that actually fired. `tools`
111
- // is the same { search: true } shape stamped on the transaction row, so the
112
- // row's `tools` object and the amount it contributes are recomputable from
113
- // the same source of truth.
114
- const sumToolCents = ( table, tools ) => Object.entries( tools || {} ).reduce(
115
- ( total, [ name, used ] ) => total + ( used && table[ name ] ? table[ name ] : 0 ),
116
- 0
117
- );
118
-
119
- // What we charge the user, rounded up so we never under-bill. Throws on an
120
- // unknown model so a typo doesn't silently fall back to a wrong price.
121
- const priceForRequest = ( model, usage, tools ) => {
122
-
123
- const rates = pricing[ model ];
124
-
125
- if( !rates ){
126
-
127
- throw new Error( `Unknown model for pricing: ${ model }` );
128
-
129
- }
130
-
131
- return sumCents( rates, usage ) + Math.ceil( sumToolCents( toolPricing, tools ) );
132
-
133
- };
134
-
135
- // What we pay the provider. Recorded on each transaction row so margin is
136
- // computable per request without backfilling from git history.
137
- const costForRequest = ( model, usage, tools ) => {
138
-
139
- const rates = cost[ model ];
140
-
141
- if( !rates ){
142
-
143
- throw new Error( `Unknown model for cost: ${ model }` );
144
-
145
- }
146
-
147
- return sumCents( rates, usage ) + Math.ceil( sumToolCents( toolCost, tools ) );
148
-
149
- };
150
-
151
43
  // Map Google's usageMetadata shape into the pricing.tokens shape.
152
44
  // `thinking` is Gemini 2.5's internal reasoning token count — billed at the
153
45
  // same rate as `output` but tracked separately so the transaction row shows
@@ -167,48 +59,6 @@ const toolsFromResponse = ( response ) => ({
167
59
  search : Boolean( response?.candidates?.[ 0 ]?.groundingMetadata )
168
60
  });
169
61
 
170
- // Compute gross + cost from response usage and atomically debit the user.
171
- // Records the full income-statement line on the transaction row's `ai`
172
- // sub-doc: wholesale + retail per-1M rate snapshots, plus rolled-up cost,
173
- // net, and gross totals. Margin per request is `ai.net`; margin per query
174
- // is a single $sum: '$ai.net'.
175
- const billRequest = async ({
176
- db,
177
- user,
178
- model,
179
- usage,
180
- tools
181
- }) => {
182
-
183
- const gross = priceForRequest( model, usage, tools );
184
- const cogs = costForRequest( model, usage, tools );
185
-
186
- await debit({
187
- db,
188
- user,
189
- amount : gross,
190
- type : 'ai',
191
- category : 'usage',
192
- source : 'user',
193
- ai : {
194
- name : 'google',
195
- model,
196
- tokens : usage,
197
- tools,
198
- rates : {
199
- wholesale : cost[ model ],
200
- retail : pricing[ model ]
201
- },
202
- totals : {
203
- cost : cogs,
204
- net : gross - cogs,
205
- gross
206
- }
207
- }
208
- });
209
-
210
- };
211
-
212
62
  const google = {
213
63
 
214
64
  image : async (
@@ -254,9 +104,7 @@ const google = {
254
104
  const usage = tokensFromMetadata( response?.usageMetadata );
255
105
  const tools = toolsFromResponse( response );
256
106
 
257
- const bill = user?.id
258
- ? () => billRequest({ db, user, model, usage, tools })
259
- : () => Promise.resolve();
107
+ const bill = ai.bill({ db, user, model, usage, tools });
260
108
 
261
109
  return { content, bill };
262
110
 
@@ -310,9 +158,7 @@ const google = {
310
158
  const usage = tokensFromMetadata( response?.usageMetadata );
311
159
  const tools = toolsFromResponse( response );
312
160
 
313
- const bill = user?.id
314
- ? () => billRequest({ db, user, model, usage, tools })
315
- : () => Promise.resolve();
161
+ const bill = ai.bill({ db, user, model, usage, tools });
316
162
 
317
163
  return { content, bill };
318
164
 
@@ -320,4 +166,4 @@ const google = {
320
166
 
321
167
  };
322
168
 
323
- export { MARKUP, cost, costForRequest, google, priceForRequest, pricing, toolCost, toolPricing };
169
+ export { google };
package/dist/ai.d.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  import { GoogleGenAI } from '@google/genai';
2
2
  import { circuit } from './circuit.js';
3
- import { debit } from './transactions.js';
3
+ import { ai } from './billing.js';
4
+ export { MARKUP } from './billing.js';
5
+ import './transactions.js';
4
6
  import '@drawbridge/drawbridge-telemetry';
7
+ import './usage.js';
5
8
 
6
9
  // AI request entry point. Wraps the Google GenAI client with a per-process
7
10
  // circuit breaker, owns the per-model pricing table, and atomically debits
@@ -37,117 +40,6 @@ const models = {
37
40
  text : 'gemini-2.5-flash'
38
41
  };
39
42
 
40
- // Raw per-model provider rates in cents per 1,000,000 tokens. This is what
41
- // Drawbridge pays the provider — the wholesale cost. Sync from LiteLLM:
42
- // https://github.com/BerriAI/litellm/blob/main/litellm/model_prices_and_context_window_backup.json
43
- // Never apply markup here; the MARKUP constant below derives the retail
44
- // `pricing` table.
45
- const cost = {
46
- 'gemini-2.5-flash' : {
47
- cached : 3,
48
- input : 30,
49
- output : 250
50
- },
51
- 'gemini-2.5-flash-image' : {
52
- cached : 3,
53
- input : 30,
54
- output : 3000
55
- }
56
- };
57
-
58
- // Markup multiplier applied to every model's cost to produce the retail
59
- // price. 1.3 = 30% margin. Change one number, all pricing updates. Mirrors
60
- // the per-customer markup Stripe rate cards used to provide; keeping it
61
- // global (rather than per-model or per-plan) for simplicity — easy to
62
- // refactor later if tier-based pricing becomes a real need.
63
- const MARKUP = 1.3;
64
-
65
- // Flat per-request fees Google charges on top of tokens when specific tools
66
- // fire. Search grounding is $35/1k requests → 3.5 cents/request. Billed only
67
- // when the response actually used the tool (response.candidates[0].
68
- // groundingMetadata is populated) — enabling the tool in config without it
69
- // being invoked is free. Add more tools here as Google introduces them.
70
- const toolCost = {
71
- search : 3.5
72
- };
73
-
74
- const toolPricing = Object.fromEntries(
75
- Object.entries( toolCost ).map( ( [ tool, value ] ) => [
76
- tool,
77
- Math.ceil( value * MARKUP )
78
- ] )
79
- );
80
-
81
- // Derived retail pricing in cents per 1,000,000 tokens — what users pay.
82
- // Rounded so the per-1M rate is a whole-cent value; per-request rounding
83
- // happens in priceForRequest via Math.ceil so we never under-bill.
84
- const pricing = Object.fromEntries(
85
- Object.entries( cost ).map( ( [ model, rates ] ) => [
86
- model,
87
- {
88
- cached : Math.round( rates.cached * MARKUP ),
89
- input : Math.round( rates.input * MARKUP ),
90
- output : Math.round( rates.output * MARKUP )
91
- }
92
- ] )
93
- );
94
-
95
- // Gemini 2.5 emits internal reasoning ("thinking") tokens alongside the
96
- // visible response. Google bills them at the standard output rate, so they
97
- // roll into the same multiplier here even though they live as a distinct
98
- // field on the tokens object for visibility / margin analysis.
99
- const sumCents = ( rates, tokens ) => Math.ceil(
100
- (
101
- Number( tokens?.cached || 0 ) * rates.cached +
102
- Number( tokens?.input || 0 ) * rates.input +
103
- (
104
- Number( tokens?.output || 0 ) +
105
- Number( tokens?.thinking || 0 )
106
- ) * rates.output
107
- ) / 1_000_000
108
- );
109
-
110
- // Sum the per-request flat fees for the tools that actually fired. `tools`
111
- // is the same { search: true } shape stamped on the transaction row, so the
112
- // row's `tools` object and the amount it contributes are recomputable from
113
- // the same source of truth.
114
- const sumToolCents = ( table, tools ) => Object.entries( tools || {} ).reduce(
115
- ( total, [ name, used ] ) => total + ( used && table[ name ] ? table[ name ] : 0 ),
116
- 0
117
- );
118
-
119
- // What we charge the user, rounded up so we never under-bill. Throws on an
120
- // unknown model so a typo doesn't silently fall back to a wrong price.
121
- const priceForRequest = ( model, usage, tools ) => {
122
-
123
- const rates = pricing[ model ];
124
-
125
- if( !rates ){
126
-
127
- throw new Error( `Unknown model for pricing: ${ model }` );
128
-
129
- }
130
-
131
- return sumCents( rates, usage ) + Math.ceil( sumToolCents( toolPricing, tools ) );
132
-
133
- };
134
-
135
- // What we pay the provider. Recorded on each transaction row so margin is
136
- // computable per request without backfilling from git history.
137
- const costForRequest = ( model, usage, tools ) => {
138
-
139
- const rates = cost[ model ];
140
-
141
- if( !rates ){
142
-
143
- throw new Error( `Unknown model for cost: ${ model }` );
144
-
145
- }
146
-
147
- return sumCents( rates, usage ) + Math.ceil( sumToolCents( toolCost, tools ) );
148
-
149
- };
150
-
151
43
  // Map Google's usageMetadata shape into the pricing.tokens shape.
152
44
  // `thinking` is Gemini 2.5's internal reasoning token count — billed at the
153
45
  // same rate as `output` but tracked separately so the transaction row shows
@@ -167,48 +59,6 @@ const toolsFromResponse = ( response ) => ({
167
59
  search : Boolean( response?.candidates?.[ 0 ]?.groundingMetadata )
168
60
  });
169
61
 
170
- // Compute gross + cost from response usage and atomically debit the user.
171
- // Records the full income-statement line on the transaction row's `ai`
172
- // sub-doc: wholesale + retail per-1M rate snapshots, plus rolled-up cost,
173
- // net, and gross totals. Margin per request is `ai.net`; margin per query
174
- // is a single $sum: '$ai.net'.
175
- const billRequest = async ({
176
- db,
177
- user,
178
- model,
179
- usage,
180
- tools
181
- }) => {
182
-
183
- const gross = priceForRequest( model, usage, tools );
184
- const cogs = costForRequest( model, usage, tools );
185
-
186
- await debit({
187
- db,
188
- user,
189
- amount : gross,
190
- type : 'ai',
191
- category : 'usage',
192
- source : 'user',
193
- ai : {
194
- name : 'google',
195
- model,
196
- tokens : usage,
197
- tools,
198
- rates : {
199
- wholesale : cost[ model ],
200
- retail : pricing[ model ]
201
- },
202
- totals : {
203
- cost : cogs,
204
- net : gross - cogs,
205
- gross
206
- }
207
- }
208
- });
209
-
210
- };
211
-
212
62
  const google = {
213
63
 
214
64
  image : async (
@@ -254,9 +104,7 @@ const google = {
254
104
  const usage = tokensFromMetadata( response?.usageMetadata );
255
105
  const tools = toolsFromResponse( response );
256
106
 
257
- const bill = user?.id
258
- ? () => billRequest({ db, user, model, usage, tools })
259
- : () => Promise.resolve();
107
+ const bill = ai.bill({ db, user, model, usage, tools });
260
108
 
261
109
  return { content, bill };
262
110
 
@@ -310,9 +158,7 @@ const google = {
310
158
  const usage = tokensFromMetadata( response?.usageMetadata );
311
159
  const tools = toolsFromResponse( response );
312
160
 
313
- const bill = user?.id
314
- ? () => billRequest({ db, user, model, usage, tools })
315
- : () => Promise.resolve();
161
+ const bill = ai.bill({ db, user, model, usage, tools });
316
162
 
317
163
  return { content, bill };
318
164
 
@@ -320,4 +166,4 @@ const google = {
320
166
 
321
167
  };
322
168
 
323
- export { MARKUP, cost, costForRequest, google, priceForRequest, pricing, toolCost, toolPricing };
169
+ export { google };
package/dist/ai.js CHANGED
@@ -125,19 +125,10 @@ var debit = async ({
125
125
  });
126
126
  };
127
127
 
128
- // lib/ai.js
129
- var google_client = new GoogleGenAI({
130
- apiKey: process.env.GOOGLE_GEMINI_API_KEY
131
- });
132
- var googleCircuit = circuit({
133
- name: "google-ai",
134
- threshold: 3,
135
- timeout: 6e4
136
- });
137
- var models = {
138
- image: "gemini-2.5-flash-image",
139
- text: "gemini-2.5-flash"
140
- };
128
+ // lib/billing.js
129
+ import { createLogger } from "@drawbridge/drawbridge-telemetry";
130
+ var logger = createLogger();
131
+ var MARKUP = 1.3;
141
132
  var cost = {
142
133
  "gemini-2.5-flash": {
143
134
  cached: 3,
@@ -150,7 +141,6 @@ var cost = {
150
141
  output: 3e3
151
142
  }
152
143
  };
153
- var MARKUP = 1.3;
154
144
  var toolCost = {
155
145
  search: 3.5
156
146
  };
@@ -191,25 +181,7 @@ var costForRequest = (model, usage, tools) => {
191
181
  }
192
182
  return sumCents(rates, usage) + Math.ceil(sumToolCents(toolCost, tools));
193
183
  };
194
- var tokensFromMetadata = (metadata) => ({
195
- cached: Number(metadata == null ? void 0 : metadata.cachedContentTokenCount) || 0,
196
- input: Number(metadata == null ? void 0 : metadata.promptTokenCount) || 0,
197
- output: Number(metadata == null ? void 0 : metadata.candidatesTokenCount) || 0,
198
- thinking: Number(metadata == null ? void 0 : metadata.thoughtsTokenCount) || 0
199
- });
200
- var toolsFromResponse = (response) => {
201
- var _a, _b;
202
- return {
203
- search: Boolean((_b = (_a = response == null ? void 0 : response.candidates) == null ? void 0 : _a[0]) == null ? void 0 : _b.groundingMetadata)
204
- };
205
- };
206
- var billRequest = async ({
207
- db,
208
- user,
209
- model,
210
- usage,
211
- tools
212
- }) => {
184
+ var billRequest = async ({ db, user, model, usage, tools }) => {
213
185
  const gross = priceForRequest(model, usage, tools);
214
186
  const cogs = costForRequest(model, usage, tools);
215
187
  await debit({
@@ -236,6 +208,42 @@ var billRequest = async ({
236
208
  }
237
209
  });
238
210
  };
211
+ var ai = {
212
+ // Retail cents for a request (what the user pays).
213
+ price: priceForRequest,
214
+ // Wholesale cents for a request (what we pay the provider).
215
+ cost: costForRequest,
216
+ // Deferred bill() the caller fires on success, so the debit lands only when
217
+ // the request succeeds and bundles into that request's Sentry trace. No user
218
+ // → a no-op.
219
+ bill: ({ db, user, model, usage, tools }) => (user == null ? void 0 : user.id) ? () => billRequest({ db, user, model, usage, tools }) : () => Promise.resolve()
220
+ };
221
+
222
+ // lib/ai.js
223
+ var google_client = new GoogleGenAI({
224
+ apiKey: process.env.GOOGLE_GEMINI_API_KEY
225
+ });
226
+ var googleCircuit = circuit({
227
+ name: "google-ai",
228
+ threshold: 3,
229
+ timeout: 6e4
230
+ });
231
+ var models = {
232
+ image: "gemini-2.5-flash-image",
233
+ text: "gemini-2.5-flash"
234
+ };
235
+ var tokensFromMetadata = (metadata) => ({
236
+ cached: Number(metadata == null ? void 0 : metadata.cachedContentTokenCount) || 0,
237
+ input: Number(metadata == null ? void 0 : metadata.promptTokenCount) || 0,
238
+ output: Number(metadata == null ? void 0 : metadata.candidatesTokenCount) || 0,
239
+ thinking: Number(metadata == null ? void 0 : metadata.thoughtsTokenCount) || 0
240
+ });
241
+ var toolsFromResponse = (response) => {
242
+ var _a, _b;
243
+ return {
244
+ search: Boolean((_b = (_a = response == null ? void 0 : response.candidates) == null ? void 0 : _a[0]) == null ? void 0 : _b.groundingMetadata)
245
+ };
246
+ };
239
247
  var google = {
240
248
  image: async ({
241
249
  config = {},
@@ -267,7 +275,7 @@ var google = {
267
275
  }
268
276
  const usage = tokensFromMetadata(response == null ? void 0 : response.usageMetadata);
269
277
  const tools = toolsFromResponse(response);
270
- const bill = (user == null ? void 0 : user.id) ? () => billRequest({ db, user, model, usage, tools }) : () => Promise.resolve();
278
+ const bill = ai.bill({ db, user, model, usage, tools });
271
279
  return { content, bill };
272
280
  },
273
281
  text: async ({
@@ -295,17 +303,11 @@ var google = {
295
303
  }
296
304
  const usage = tokensFromMetadata(response == null ? void 0 : response.usageMetadata);
297
305
  const tools = toolsFromResponse(response);
298
- const bill = (user == null ? void 0 : user.id) ? () => billRequest({ db, user, model, usage, tools }) : () => Promise.resolve();
306
+ const bill = ai.bill({ db, user, model, usage, tools });
299
307
  return { content, bill };
300
308
  }
301
309
  };
302
310
  export {
303
311
  MARKUP,
304
- cost,
305
- costForRequest,
306
- google,
307
- priceForRequest,
308
- pricing,
309
- toolCost,
310
- toolPricing
312
+ google
311
313
  };