@drawbridge/drawbridge-utils 0.0.33 → 0.0.36

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
@@ -24,10 +24,13 @@ __export(ai_exports, {
24
24
  costForRequest: () => costForRequest,
25
25
  google: () => google,
26
26
  priceForRequest: () => priceForRequest,
27
- pricing: () => pricing
27
+ pricing: () => pricing,
28
+ toolCost: () => toolCost,
29
+ toolPricing: () => toolPricing
28
30
  });
29
31
  module.exports = __toCommonJS(ai_exports);
30
32
  var import_genai = require("@google/genai");
33
+ var import_drawbridge_telemetry = require("@drawbridge/drawbridge-telemetry");
31
34
 
32
35
  // circuit.js
33
36
  var CLOSED = "CLOSED";
@@ -176,6 +179,15 @@ var cost = {
176
179
  }
177
180
  };
178
181
  var MARKUP = 1.3;
182
+ var toolCost = {
183
+ search: 3.5
184
+ };
185
+ var toolPricing = Object.fromEntries(
186
+ Object.entries(toolCost).map(([tool, value]) => [
187
+ tool,
188
+ Math.ceil(value * MARKUP)
189
+ ])
190
+ );
179
191
  var pricing = Object.fromEntries(
180
192
  Object.entries(cost).map(([model, rates]) => [
181
193
  model,
@@ -187,35 +199,48 @@ var pricing = Object.fromEntries(
187
199
  ])
188
200
  );
189
201
  var sumCents = (rates, tokens) => Math.ceil(
190
- (Number((tokens == null ? void 0 : tokens.cached) || 0) * rates.cached + Number((tokens == null ? void 0 : tokens.input) || 0) * rates.input + Number((tokens == null ? void 0 : tokens.output) || 0) * rates.output) / 1e6
202
+ (Number((tokens == null ? void 0 : tokens.cached) || 0) * rates.cached + Number((tokens == null ? void 0 : tokens.input) || 0) * rates.input + (Number((tokens == null ? void 0 : tokens.output) || 0) + Number((tokens == null ? void 0 : tokens.thinking) || 0)) * rates.output) / 1e6
191
203
  );
192
- var priceForRequest = (model, usage) => {
204
+ var sumToolCents = (table, tools) => Object.entries(tools || {}).reduce(
205
+ (total, [name, used]) => total + (used && table[name] ? table[name] : 0),
206
+ 0
207
+ );
208
+ var priceForRequest = (model, usage, tools) => {
193
209
  const rates = pricing[model];
194
210
  if (!rates) {
195
211
  throw new Error(`Unknown model for pricing: ${model}`);
196
212
  }
197
- return sumCents(rates, usage);
213
+ return sumCents(rates, usage) + Math.ceil(sumToolCents(toolPricing, tools));
198
214
  };
199
- var costForRequest = (model, usage) => {
215
+ var costForRequest = (model, usage, tools) => {
200
216
  const rates = cost[model];
201
217
  if (!rates) {
202
218
  throw new Error(`Unknown model for cost: ${model}`);
203
219
  }
204
- return sumCents(rates, usage);
220
+ return sumCents(rates, usage) + Math.ceil(sumToolCents(toolCost, tools));
205
221
  };
206
222
  var tokensFromMetadata = (metadata) => ({
207
223
  cached: Number(metadata == null ? void 0 : metadata.cachedContentTokenCount) || 0,
208
224
  input: Number(metadata == null ? void 0 : metadata.promptTokenCount) || 0,
209
- output: Number(metadata == null ? void 0 : metadata.candidatesTokenCount) || 0
225
+ output: Number(metadata == null ? void 0 : metadata.candidatesTokenCount) || 0,
226
+ thinking: Number(metadata == null ? void 0 : metadata.thoughtsTokenCount) || 0
210
227
  });
228
+ var toolsFromResponse = (response) => {
229
+ var _a, _b;
230
+ return {
231
+ search: Boolean((_b = (_a = response == null ? void 0 : response.candidates) == null ? void 0 : _a[0]) == null ? void 0 : _b.groundingMetadata)
232
+ };
233
+ };
211
234
  var billRequest = async ({
212
235
  db,
213
236
  user,
214
237
  model,
215
- usage
238
+ usage,
239
+ tools
216
240
  }) => {
217
- const gross = priceForRequest(model, usage);
218
- const cogs = costForRequest(model, usage);
241
+ const gross = priceForRequest(model, usage, tools);
242
+ const cogs = costForRequest(model, usage, tools);
243
+ const id = (0, import_drawbridge_telemetry.currentTraceId)();
219
244
  await debit({
220
245
  db,
221
246
  user,
@@ -223,10 +248,12 @@ var billRequest = async ({
223
248
  type: "ai",
224
249
  category: "usage",
225
250
  source: "user",
251
+ ...id && { trace: { id } },
226
252
  ai: {
227
253
  name: "google",
228
254
  model,
229
255
  tokens: usage,
256
+ tools,
230
257
  rates: {
231
258
  wholesale: cost[model],
232
259
  retail: pricing[model]
@@ -271,7 +298,8 @@ var google = {
271
298
  db,
272
299
  user,
273
300
  model,
274
- usage: tokensFromMetadata(response == null ? void 0 : response.usageMetadata)
301
+ usage: tokensFromMetadata(response == null ? void 0 : response.usageMetadata),
302
+ tools: toolsFromResponse(response)
275
303
  });
276
304
  }
277
305
  return { content };
@@ -301,7 +329,8 @@ var google = {
301
329
  db,
302
330
  user,
303
331
  model,
304
- usage: tokensFromMetadata(response == null ? void 0 : response.usageMetadata)
332
+ usage: tokensFromMetadata(response == null ? void 0 : response.usageMetadata),
333
+ tools: toolsFromResponse(response)
305
334
  });
306
335
  }
307
336
  return { content };
@@ -314,5 +343,7 @@ var google = {
314
343
  costForRequest,
315
344
  google,
316
345
  priceForRequest,
317
- pricing
346
+ pricing,
347
+ toolCost,
348
+ toolPricing
318
349
  });
package/dist/ai.d.cts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { GoogleGenAI } from '@google/genai';
2
+ import { currentTraceId } from '@drawbridge/drawbridge-telemetry';
2
3
  import { circuit } from './circuit.cjs';
3
4
  import { debit } from './transactions.cjs';
4
5
 
@@ -61,6 +62,22 @@ const cost = {
61
62
  // refactor later if tier-based pricing becomes a real need.
62
63
  const MARKUP = 1.3;
63
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
+
64
81
  // Derived retail pricing in cents per 1,000,000 tokens — what users pay.
65
82
  // Rounded so the per-1M rate is a whole-cent value; per-request rounding
66
83
  // happens in priceForRequest via Math.ceil so we never under-bill.
@@ -75,17 +92,33 @@ const pricing = Object.fromEntries(
75
92
  ] )
76
93
  );
77
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.
78
99
  const sumCents = ( rates, tokens ) => Math.ceil(
79
100
  (
80
101
  Number( tokens?.cached || 0 ) * rates.cached +
81
102
  Number( tokens?.input || 0 ) * rates.input +
82
- Number( tokens?.output || 0 ) * rates.output
103
+ (
104
+ Number( tokens?.output || 0 ) +
105
+ Number( tokens?.thinking || 0 )
106
+ ) * rates.output
83
107
  ) / 1_000_000
84
108
  );
85
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
+
86
119
  // What we charge the user, rounded up so we never under-bill. Throws on an
87
120
  // unknown model so a typo doesn't silently fall back to a wrong price.
88
- const priceForRequest = ( model, usage ) => {
121
+ const priceForRequest = ( model, usage, tools ) => {
89
122
 
90
123
  const rates = pricing[ model ];
91
124
 
@@ -95,13 +128,13 @@ const priceForRequest = ( model, usage ) => {
95
128
 
96
129
  }
97
130
 
98
- return sumCents( rates, usage );
131
+ return sumCents( rates, usage ) + Math.ceil( sumToolCents( toolPricing, tools ) );
99
132
 
100
133
  };
101
134
 
102
135
  // What we pay the provider. Recorded on each transaction row so margin is
103
136
  // computable per request without backfilling from git history.
104
- const costForRequest = ( model, usage ) => {
137
+ const costForRequest = ( model, usage, tools ) => {
105
138
 
106
139
  const rates = cost[ model ];
107
140
 
@@ -111,15 +144,27 @@ const costForRequest = ( model, usage ) => {
111
144
 
112
145
  }
113
146
 
114
- return sumCents( rates, usage );
147
+ return sumCents( rates, usage ) + Math.ceil( sumToolCents( toolCost, tools ) );
115
148
 
116
149
  };
117
150
 
118
151
  // Map Google's usageMetadata shape into the pricing.tokens shape.
152
+ // `thinking` is Gemini 2.5's internal reasoning token count — billed at the
153
+ // same rate as `output` but tracked separately so the transaction row shows
154
+ // the visible-vs-reasoning split.
119
155
  const tokensFromMetadata = ( metadata ) => ({
120
156
  cached : Number( metadata?.cachedContentTokenCount ) || 0,
121
157
  input : Number( metadata?.promptTokenCount ) || 0,
122
- output : Number( metadata?.candidatesTokenCount ) || 0
158
+ output : Number( metadata?.candidatesTokenCount ) || 0,
159
+ thinking : Number( metadata?.thoughtsTokenCount ) || 0
160
+ });
161
+
162
+ // Derive which billable tools actually fired from the response. Google bills
163
+ // per-request when the tool was invoked, not when it was merely enabled in
164
+ // config — `groundingMetadata` is only present on candidates when search was
165
+ // used. Add more tools here as Google adds them.
166
+ const toolsFromResponse = ( response ) => ({
167
+ search : Boolean( response?.candidates?.[ 0 ]?.groundingMetadata )
123
168
  });
124
169
 
125
170
  // Compute gross + cost from response usage and atomically debit the user.
@@ -131,11 +176,22 @@ const billRequest = async ({
131
176
  db,
132
177
  user,
133
178
  model,
134
- usage
179
+ usage,
180
+ tools
135
181
  }) => {
136
182
 
137
- const gross = priceForRequest( model, usage );
138
- const cogs = costForRequest( model, usage );
183
+ const gross = priceForRequest( model, usage, tools );
184
+ const cogs = costForRequest( model, usage, tools );
185
+
186
+ // Stamp the current trace ID so multiple AI calls inside one HTTP request
187
+ // (or one BullMQ job) share a grouping key — the UI can collapse rows
188
+ // per trace.id to show "this preview cost $0.12 across 5 calls" without
189
+ // the user needing to mentally fold them together. currentTraceId()
190
+ // returns undefined when called outside any traced scope, in which case
191
+ // the field is omitted from the row (additionalProperties:false would
192
+ // reject `trace: undefined`). Nested as `trace.id` to match the `ai` /
193
+ // `balance` sub-object pattern already on this document.
194
+ const id = currentTraceId();
139
195
 
140
196
  await debit({
141
197
  db,
@@ -144,10 +200,12 @@ const billRequest = async ({
144
200
  type : 'ai',
145
201
  category : 'usage',
146
202
  source : 'user',
203
+ ...( id && { trace : { id } }),
147
204
  ai : {
148
205
  name : 'google',
149
206
  model,
150
207
  tokens : usage,
208
+ tools,
151
209
  rates : {
152
210
  wholesale : cost[ model ],
153
211
  retail : pricing[ model ]
@@ -206,7 +264,8 @@ const google = {
206
264
  db,
207
265
  user,
208
266
  model,
209
- usage : tokensFromMetadata( response?.usageMetadata )
267
+ usage : tokensFromMetadata( response?.usageMetadata ),
268
+ tools : toolsFromResponse( response )
210
269
  });
211
270
 
212
271
  }
@@ -251,7 +310,8 @@ const google = {
251
310
  db,
252
311
  user,
253
312
  model,
254
- usage : tokensFromMetadata( response?.usageMetadata )
313
+ usage : tokensFromMetadata( response?.usageMetadata ),
314
+ tools : toolsFromResponse( response )
255
315
  });
256
316
 
257
317
  }
@@ -262,4 +322,4 @@ const google = {
262
322
 
263
323
  };
264
324
 
265
- export { MARKUP, cost, costForRequest, google, priceForRequest, pricing };
325
+ export { MARKUP, cost, costForRequest, google, priceForRequest, pricing, toolCost, toolPricing };
package/dist/ai.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { GoogleGenAI } from '@google/genai';
2
+ import { currentTraceId } from '@drawbridge/drawbridge-telemetry';
2
3
  import { circuit } from './circuit.js';
3
4
  import { debit } from './transactions.js';
4
5
 
@@ -61,6 +62,22 @@ const cost = {
61
62
  // refactor later if tier-based pricing becomes a real need.
62
63
  const MARKUP = 1.3;
63
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
+
64
81
  // Derived retail pricing in cents per 1,000,000 tokens — what users pay.
65
82
  // Rounded so the per-1M rate is a whole-cent value; per-request rounding
66
83
  // happens in priceForRequest via Math.ceil so we never under-bill.
@@ -75,17 +92,33 @@ const pricing = Object.fromEntries(
75
92
  ] )
76
93
  );
77
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.
78
99
  const sumCents = ( rates, tokens ) => Math.ceil(
79
100
  (
80
101
  Number( tokens?.cached || 0 ) * rates.cached +
81
102
  Number( tokens?.input || 0 ) * rates.input +
82
- Number( tokens?.output || 0 ) * rates.output
103
+ (
104
+ Number( tokens?.output || 0 ) +
105
+ Number( tokens?.thinking || 0 )
106
+ ) * rates.output
83
107
  ) / 1_000_000
84
108
  );
85
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
+
86
119
  // What we charge the user, rounded up so we never under-bill. Throws on an
87
120
  // unknown model so a typo doesn't silently fall back to a wrong price.
88
- const priceForRequest = ( model, usage ) => {
121
+ const priceForRequest = ( model, usage, tools ) => {
89
122
 
90
123
  const rates = pricing[ model ];
91
124
 
@@ -95,13 +128,13 @@ const priceForRequest = ( model, usage ) => {
95
128
 
96
129
  }
97
130
 
98
- return sumCents( rates, usage );
131
+ return sumCents( rates, usage ) + Math.ceil( sumToolCents( toolPricing, tools ) );
99
132
 
100
133
  };
101
134
 
102
135
  // What we pay the provider. Recorded on each transaction row so margin is
103
136
  // computable per request without backfilling from git history.
104
- const costForRequest = ( model, usage ) => {
137
+ const costForRequest = ( model, usage, tools ) => {
105
138
 
106
139
  const rates = cost[ model ];
107
140
 
@@ -111,15 +144,27 @@ const costForRequest = ( model, usage ) => {
111
144
 
112
145
  }
113
146
 
114
- return sumCents( rates, usage );
147
+ return sumCents( rates, usage ) + Math.ceil( sumToolCents( toolCost, tools ) );
115
148
 
116
149
  };
117
150
 
118
151
  // Map Google's usageMetadata shape into the pricing.tokens shape.
152
+ // `thinking` is Gemini 2.5's internal reasoning token count — billed at the
153
+ // same rate as `output` but tracked separately so the transaction row shows
154
+ // the visible-vs-reasoning split.
119
155
  const tokensFromMetadata = ( metadata ) => ({
120
156
  cached : Number( metadata?.cachedContentTokenCount ) || 0,
121
157
  input : Number( metadata?.promptTokenCount ) || 0,
122
- output : Number( metadata?.candidatesTokenCount ) || 0
158
+ output : Number( metadata?.candidatesTokenCount ) || 0,
159
+ thinking : Number( metadata?.thoughtsTokenCount ) || 0
160
+ });
161
+
162
+ // Derive which billable tools actually fired from the response. Google bills
163
+ // per-request when the tool was invoked, not when it was merely enabled in
164
+ // config — `groundingMetadata` is only present on candidates when search was
165
+ // used. Add more tools here as Google adds them.
166
+ const toolsFromResponse = ( response ) => ({
167
+ search : Boolean( response?.candidates?.[ 0 ]?.groundingMetadata )
123
168
  });
124
169
 
125
170
  // Compute gross + cost from response usage and atomically debit the user.
@@ -131,11 +176,22 @@ const billRequest = async ({
131
176
  db,
132
177
  user,
133
178
  model,
134
- usage
179
+ usage,
180
+ tools
135
181
  }) => {
136
182
 
137
- const gross = priceForRequest( model, usage );
138
- const cogs = costForRequest( model, usage );
183
+ const gross = priceForRequest( model, usage, tools );
184
+ const cogs = costForRequest( model, usage, tools );
185
+
186
+ // Stamp the current trace ID so multiple AI calls inside one HTTP request
187
+ // (or one BullMQ job) share a grouping key — the UI can collapse rows
188
+ // per trace.id to show "this preview cost $0.12 across 5 calls" without
189
+ // the user needing to mentally fold them together. currentTraceId()
190
+ // returns undefined when called outside any traced scope, in which case
191
+ // the field is omitted from the row (additionalProperties:false would
192
+ // reject `trace: undefined`). Nested as `trace.id` to match the `ai` /
193
+ // `balance` sub-object pattern already on this document.
194
+ const id = currentTraceId();
139
195
 
140
196
  await debit({
141
197
  db,
@@ -144,10 +200,12 @@ const billRequest = async ({
144
200
  type : 'ai',
145
201
  category : 'usage',
146
202
  source : 'user',
203
+ ...( id && { trace : { id } }),
147
204
  ai : {
148
205
  name : 'google',
149
206
  model,
150
207
  tokens : usage,
208
+ tools,
151
209
  rates : {
152
210
  wholesale : cost[ model ],
153
211
  retail : pricing[ model ]
@@ -206,7 +264,8 @@ const google = {
206
264
  db,
207
265
  user,
208
266
  model,
209
- usage : tokensFromMetadata( response?.usageMetadata )
267
+ usage : tokensFromMetadata( response?.usageMetadata ),
268
+ tools : toolsFromResponse( response )
210
269
  });
211
270
 
212
271
  }
@@ -251,7 +310,8 @@ const google = {
251
310
  db,
252
311
  user,
253
312
  model,
254
- usage : tokensFromMetadata( response?.usageMetadata )
313
+ usage : tokensFromMetadata( response?.usageMetadata ),
314
+ tools : toolsFromResponse( response )
255
315
  });
256
316
 
257
317
  }
@@ -262,4 +322,4 @@ const google = {
262
322
 
263
323
  };
264
324
 
265
- export { MARKUP, cost, costForRequest, google, priceForRequest, pricing };
325
+ export { MARKUP, cost, costForRequest, google, priceForRequest, pricing, toolCost, toolPricing };
package/dist/ai.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
 
8
8
  // ai.js
9
9
  import { GoogleGenAI } from "@google/genai";
10
+ import { currentTraceId } from "@drawbridge/drawbridge-telemetry";
10
11
  var google_client = new GoogleGenAI({
11
12
  apiKey: process.env.GOOGLE_API_KEY
12
13
  });
@@ -32,6 +33,15 @@ var cost = {
32
33
  }
33
34
  };
34
35
  var MARKUP = 1.3;
36
+ var toolCost = {
37
+ search: 3.5
38
+ };
39
+ var toolPricing = Object.fromEntries(
40
+ Object.entries(toolCost).map(([tool, value]) => [
41
+ tool,
42
+ Math.ceil(value * MARKUP)
43
+ ])
44
+ );
35
45
  var pricing = Object.fromEntries(
36
46
  Object.entries(cost).map(([model, rates]) => [
37
47
  model,
@@ -43,35 +53,48 @@ var pricing = Object.fromEntries(
43
53
  ])
44
54
  );
45
55
  var sumCents = (rates, tokens) => Math.ceil(
46
- (Number((tokens == null ? void 0 : tokens.cached) || 0) * rates.cached + Number((tokens == null ? void 0 : tokens.input) || 0) * rates.input + Number((tokens == null ? void 0 : tokens.output) || 0) * rates.output) / 1e6
56
+ (Number((tokens == null ? void 0 : tokens.cached) || 0) * rates.cached + Number((tokens == null ? void 0 : tokens.input) || 0) * rates.input + (Number((tokens == null ? void 0 : tokens.output) || 0) + Number((tokens == null ? void 0 : tokens.thinking) || 0)) * rates.output) / 1e6
47
57
  );
48
- var priceForRequest = (model, usage) => {
58
+ var sumToolCents = (table, tools) => Object.entries(tools || {}).reduce(
59
+ (total, [name, used]) => total + (used && table[name] ? table[name] : 0),
60
+ 0
61
+ );
62
+ var priceForRequest = (model, usage, tools) => {
49
63
  const rates = pricing[model];
50
64
  if (!rates) {
51
65
  throw new Error(`Unknown model for pricing: ${model}`);
52
66
  }
53
- return sumCents(rates, usage);
67
+ return sumCents(rates, usage) + Math.ceil(sumToolCents(toolPricing, tools));
54
68
  };
55
- var costForRequest = (model, usage) => {
69
+ var costForRequest = (model, usage, tools) => {
56
70
  const rates = cost[model];
57
71
  if (!rates) {
58
72
  throw new Error(`Unknown model for cost: ${model}`);
59
73
  }
60
- return sumCents(rates, usage);
74
+ return sumCents(rates, usage) + Math.ceil(sumToolCents(toolCost, tools));
61
75
  };
62
76
  var tokensFromMetadata = (metadata) => ({
63
77
  cached: Number(metadata == null ? void 0 : metadata.cachedContentTokenCount) || 0,
64
78
  input: Number(metadata == null ? void 0 : metadata.promptTokenCount) || 0,
65
- output: Number(metadata == null ? void 0 : metadata.candidatesTokenCount) || 0
79
+ output: Number(metadata == null ? void 0 : metadata.candidatesTokenCount) || 0,
80
+ thinking: Number(metadata == null ? void 0 : metadata.thoughtsTokenCount) || 0
66
81
  });
82
+ var toolsFromResponse = (response) => {
83
+ var _a, _b;
84
+ return {
85
+ search: Boolean((_b = (_a = response == null ? void 0 : response.candidates) == null ? void 0 : _a[0]) == null ? void 0 : _b.groundingMetadata)
86
+ };
87
+ };
67
88
  var billRequest = async ({
68
89
  db,
69
90
  user,
70
91
  model,
71
- usage
92
+ usage,
93
+ tools
72
94
  }) => {
73
- const gross = priceForRequest(model, usage);
74
- const cogs = costForRequest(model, usage);
95
+ const gross = priceForRequest(model, usage, tools);
96
+ const cogs = costForRequest(model, usage, tools);
97
+ const id = currentTraceId();
75
98
  await debit({
76
99
  db,
77
100
  user,
@@ -79,10 +102,12 @@ var billRequest = async ({
79
102
  type: "ai",
80
103
  category: "usage",
81
104
  source: "user",
105
+ ...id && { trace: { id } },
82
106
  ai: {
83
107
  name: "google",
84
108
  model,
85
109
  tokens: usage,
110
+ tools,
86
111
  rates: {
87
112
  wholesale: cost[model],
88
113
  retail: pricing[model]
@@ -127,7 +152,8 @@ var google = {
127
152
  db,
128
153
  user,
129
154
  model,
130
- usage: tokensFromMetadata(response == null ? void 0 : response.usageMetadata)
155
+ usage: tokensFromMetadata(response == null ? void 0 : response.usageMetadata),
156
+ tools: toolsFromResponse(response)
131
157
  });
132
158
  }
133
159
  return { content };
@@ -157,7 +183,8 @@ var google = {
157
183
  db,
158
184
  user,
159
185
  model,
160
- usage: tokensFromMetadata(response == null ? void 0 : response.usageMetadata)
186
+ usage: tokensFromMetadata(response == null ? void 0 : response.usageMetadata),
187
+ tools: toolsFromResponse(response)
161
188
  });
162
189
  }
163
190
  return { content };
@@ -169,5 +196,7 @@ export {
169
196
  costForRequest,
170
197
  google,
171
198
  priceForRequest,
172
- pricing
199
+ pricing,
200
+ toolCost,
201
+ toolPricing
173
202
  };
package/package.json CHANGED
@@ -2,6 +2,7 @@
2
2
  "type": "module",
3
3
  "dependencies": {
4
4
  "@drawbridge/drawbridge-agents": "0.0.10",
5
+ "@drawbridge/drawbridge-telemetry": "0.0.13",
5
6
  "@google/genai": "1.30.0",
6
7
  "axios": "1.16.0",
7
8
  "currency-codes": "2.2.0",
@@ -103,5 +104,5 @@
103
104
  "build": "tsup && npm publish"
104
105
  },
105
106
  "types": "dist/index.d.ts",
106
- "version": "0.0.33"
107
+ "version": "0.0.36"
107
108
  }