@usagetap/sdk 1.3.2 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +372 -39
  2. package/dist/adapters/anthropic.cjs +995 -69
  3. package/dist/adapters/anthropic.cjs.map +1 -1
  4. package/dist/adapters/anthropic.d.cts +45 -3
  5. package/dist/adapters/anthropic.d.ts +45 -3
  6. package/dist/adapters/anthropic.mjs +995 -70
  7. package/dist/adapters/anthropic.mjs.map +1 -1
  8. package/dist/adapters/openai.cjs +1208 -106
  9. package/dist/adapters/openai.cjs.map +1 -1
  10. package/dist/adapters/openai.d.cts +46 -3
  11. package/dist/adapters/openai.d.ts +46 -3
  12. package/dist/adapters/openai.mjs +1208 -107
  13. package/dist/adapters/openai.mjs.map +1 -1
  14. package/dist/adapters/openrouter.cjs +3912 -53
  15. package/dist/adapters/openrouter.cjs.map +1 -1
  16. package/dist/adapters/openrouter.d.cts +6 -3
  17. package/dist/adapters/openrouter.d.ts +6 -3
  18. package/dist/adapters/openrouter.mjs +3910 -54
  19. package/dist/adapters/openrouter.mjs.map +1 -1
  20. package/dist/anthropic/index.cjs +995 -69
  21. package/dist/anthropic/index.cjs.map +1 -1
  22. package/dist/anthropic/index.d.cts +2 -2
  23. package/dist/anthropic/index.d.ts +2 -2
  24. package/dist/anthropic/index.mjs +995 -70
  25. package/dist/anthropic/index.mjs.map +1 -1
  26. package/dist/client-C0UiaqVB.d.cts +1305 -0
  27. package/dist/client-C0UiaqVB.d.ts +1305 -0
  28. package/dist/express/index.cjs +399 -64
  29. package/dist/express/index.cjs.map +1 -1
  30. package/dist/express/index.d.cts +2 -2
  31. package/dist/express/index.d.ts +2 -2
  32. package/dist/express/index.mjs +399 -64
  33. package/dist/express/index.mjs.map +1 -1
  34. package/dist/index.cjs +1044 -163
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +16 -5
  37. package/dist/index.d.ts +16 -5
  38. package/dist/index.mjs +1044 -163
  39. package/dist/index.mjs.map +1 -1
  40. package/dist/openai/index.cjs +1209 -107
  41. package/dist/openai/index.cjs.map +1 -1
  42. package/dist/openai/index.d.cts +2 -2
  43. package/dist/openai/index.d.ts +2 -2
  44. package/dist/openai/index.mjs +1209 -108
  45. package/dist/openai/index.mjs.map +1 -1
  46. package/dist/openrouter/index.cjs +1226 -109
  47. package/dist/openrouter/index.cjs.map +1 -1
  48. package/dist/openrouter/index.d.cts +3 -3
  49. package/dist/openrouter/index.d.ts +3 -3
  50. package/dist/openrouter/index.mjs +1224 -108
  51. package/dist/openrouter/index.mjs.map +1 -1
  52. package/dist/react/index.cjs +19 -1
  53. package/dist/react/index.cjs.map +1 -1
  54. package/dist/react/index.d.cts +17 -4
  55. package/dist/react/index.d.ts +17 -4
  56. package/dist/react/index.mjs +19 -1
  57. package/dist/react/index.mjs.map +1 -1
  58. package/package.json +2 -2
  59. package/dist/client-BD8O2J8Z.d.cts +0 -668
  60. package/dist/client-BD8O2J8Z.d.ts +0 -668
@@ -174,6 +174,75 @@ async function pipeToResponse(stream, res, options = {}) {
174
174
  }
175
175
  }
176
176
  var USAGETAP_CORRELATION_HEADER = "x-usage-correlation-id";
177
+ function readString(value) {
178
+ return typeof value === "string" && value.trim() ? value : void 0;
179
+ }
180
+ function serializeSamplingError(error) {
181
+ if (error instanceof Error) {
182
+ return { name: error.name, message: error.message };
183
+ }
184
+ return { message: String(error) };
185
+ }
186
+ function normalizeMeteredOpenAISampling(options) {
187
+ if (!options) return void 0;
188
+ if (options === true) return { provider: "openai" };
189
+ const { provider = "openai", ...policyFields } = options;
190
+ return {
191
+ provider,
192
+ policy: typeof policyFields.rate === "number" ? policyFields : void 0
193
+ };
194
+ }
195
+ function startMeteredOpenAISampleDecision({
196
+ usageTap,
197
+ sampling,
198
+ beginRequest,
199
+ input
200
+ }) {
201
+ if (!sampling) return Promise.resolve(false);
202
+ return usageTap.shouldSampleAsync(
203
+ {
204
+ customerId: beginRequest.customerId,
205
+ feature: beginRequest.feature,
206
+ input
207
+ },
208
+ sampling.policy
209
+ );
210
+ }
211
+ async function captureMeteredOpenAISample({
212
+ usageTap,
213
+ sampling,
214
+ decision,
215
+ ctx,
216
+ beginRequest,
217
+ input,
218
+ response,
219
+ error,
220
+ startedAt
221
+ }) {
222
+ if (!sampling) return;
223
+ let selected = false;
224
+ try {
225
+ selected = await decision;
226
+ } catch {
227
+ return;
228
+ }
229
+ if (!selected) return;
230
+ const record = isObjectRecord(response) ? response : {};
231
+ await usageTap.captureSample({
232
+ sampleId: ctx.begin.data.callId,
233
+ callId: ctx.begin.data.callId,
234
+ customerId: beginRequest.customerId,
235
+ feature: beginRequest.feature,
236
+ tags: beginRequest.tags,
237
+ provider: sampling.provider,
238
+ model: readString(record.model) ?? readString(input.model),
239
+ input,
240
+ ...response === void 0 ? {} : { output: response },
241
+ usage: record.usage,
242
+ latencyMs: Date.now() - startedAt,
243
+ ...error === void 0 ? {} : { error: serializeSamplingError(error) }
244
+ }).catch(() => void 0);
245
+ }
177
246
  function wrapOpenAI(client, usageTap, options = {}) {
178
247
  if (!client) {
179
248
  throw new UsageTapError("USAGETAP_BAD_REQUEST", "wrapOpenAI requires an OpenAI client instance");
@@ -181,6 +250,8 @@ function wrapOpenAI(client, usageTap, options = {}) {
181
250
  const defaultContext = options.defaultContext;
182
251
  const applyVendorHints = options.applyVendorHints !== false;
183
252
  const defaultPromptCompression = normalizePromptCompressionOptions(options.promptCompression);
253
+ const defaultSampling = normalizeMeteredOpenAISampling(options.sampling);
254
+ const provider = options.provider ?? "openai";
184
255
  const promptCompressionStats = new OpenAIPromptCompressionStats();
185
256
  const proxiedChat = client.chat ? createChatProxy(
186
257
  client.chat,
@@ -188,7 +259,9 @@ function wrapOpenAI(client, usageTap, options = {}) {
188
259
  defaultContext,
189
260
  applyVendorHints,
190
261
  defaultPromptCompression,
191
- promptCompressionStats
262
+ promptCompressionStats,
263
+ defaultSampling,
264
+ provider
192
265
  ) : void 0;
193
266
  const proxiedResponses = typeof client.responses !== "undefined" ? createResponsesProxy(
194
267
  client.responses,
@@ -196,7 +269,9 @@ function wrapOpenAI(client, usageTap, options = {}) {
196
269
  defaultContext,
197
270
  applyVendorHints,
198
271
  defaultPromptCompression,
199
- promptCompressionStats
272
+ promptCompressionStats,
273
+ defaultSampling,
274
+ provider
200
275
  ) : void 0;
201
276
  const handler = {
202
277
  get(target, prop, receiver) {
@@ -223,14 +298,16 @@ function wrapOpenAI(client, usageTap, options = {}) {
223
298
  };
224
299
  return new Proxy(client, handler);
225
300
  }
226
- function createChatProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
301
+ function createChatProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats, defaultSampling, provider) {
227
302
  const completions = createChatCompletionsProxy(
228
303
  resource.completions,
229
304
  usageTap,
230
305
  defaultContext,
231
306
  applyVendorHints,
232
307
  defaultPromptCompression,
233
- promptCompressionStats
308
+ promptCompressionStats,
309
+ defaultSampling,
310
+ provider
234
311
  );
235
312
  const handler = {
236
313
  get(target, prop, receiver) {
@@ -242,7 +319,7 @@ function createChatProxy(resource, usageTap, defaultContext, applyVendorHints, d
242
319
  };
243
320
  return new Proxy(resource, handler);
244
321
  }
245
- function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
322
+ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats, defaultSampling, provider) {
246
323
  if (!resource || typeof resource !== "object") {
247
324
  return void 0;
248
325
  }
@@ -257,9 +334,20 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
257
334
  withUsage: withUsage2,
258
335
  promptCompression
259
336
  } = splitUsageOptions(options);
260
- const beginRequest = resolveBeginRequest(defaultContext, usageContext);
337
+ const beginRequest = responsesBeginRequest(
338
+ resolveBeginRequest(defaultContext, usageContext),
339
+ params
340
+ );
261
341
  const wantsStream = isStreamingRequest(params);
262
342
  return usageTap.withUsage(beginRequest, async (ctx) => {
343
+ const settle = wantsStream ? deferUsageFinalization(ctx) : void 0;
344
+ const sampleStartedAt = Date.now();
345
+ const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredOpenAISampleDecision({
346
+ usageTap,
347
+ sampling: defaultSampling,
348
+ beginRequest,
349
+ input: params
350
+ });
263
351
  const hintedParams = applyVendorHints ? applyResponsesVendorHints(params, ctx.begin.data.vendorHints) : params;
264
352
  const finalParams = await compressResponsesParamsForCall({
265
353
  params: hintedParams,
@@ -271,25 +359,67 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
271
359
  withUsage: withUsage2,
272
360
  operation: "responses.create"
273
361
  });
362
+ ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
274
363
  const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
275
364
  if (wantsStream) {
276
365
  const apiPromise2 = originalCreate(finalParams, request);
277
366
  const wrappedPromise2 = transformApiPromise(apiPromise2, (rawStream) => {
278
367
  ensureAsyncIterable(rawStream, "responses.create");
279
- const wrappedStream = wrapStreamForUsageTap(rawStream, async () => {
280
- const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints);
281
- if (usage) {
282
- ctx.setUsage(usage);
368
+ const wrappedStream = wrapStreamForUsageTap(rawStream, async (termination) => {
369
+ try {
370
+ if (termination === "complete") {
371
+ const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
372
+ if (usage) {
373
+ ctx.setUsage(usage);
374
+ }
375
+ }
376
+ } catch (error) {
377
+ ctx.setError({
378
+ code: "USAGE_FINALIZE_ERROR",
379
+ message: error instanceof Error ? error.message : String(error)
380
+ });
381
+ throw error;
382
+ } finally {
383
+ await settle?.();
283
384
  }
284
- }, ctx);
385
+ }, ctx, (chunk) => {
386
+ tryInferUsageFromStreamChunk(
387
+ chunk,
388
+ ctx.begin.data.vendorHints,
389
+ ctx,
390
+ provider
391
+ );
392
+ });
285
393
  return wrappedStream;
286
394
  });
287
395
  return wrappedPromise2;
288
396
  }
289
397
  const apiPromise = originalCreate(finalParams, request);
290
- const wrappedPromise = transformApiPromise(apiPromise, (response) => {
291
- tryInferUsage(response, ctx.begin.data.vendorHints, void 0, ctx);
398
+ const wrappedPromise = transformApiPromise(apiPromise, async (response) => {
399
+ tryInferUsage(response, ctx.begin.data.vendorHints, void 0, ctx, provider);
400
+ await captureMeteredOpenAISample({
401
+ usageTap,
402
+ sampling: defaultSampling,
403
+ decision: sampleDecision,
404
+ ctx,
405
+ beginRequest,
406
+ input: params,
407
+ response,
408
+ startedAt: sampleStartedAt
409
+ });
292
410
  return response;
411
+ }, async (error) => {
412
+ await captureMeteredOpenAISample({
413
+ usageTap,
414
+ sampling: defaultSampling,
415
+ decision: sampleDecision,
416
+ ctx,
417
+ beginRequest,
418
+ input: params,
419
+ error,
420
+ startedAt: sampleStartedAt
421
+ });
422
+ throw error;
293
423
  });
294
424
  return wrappedPromise;
295
425
  }, withUsage2);
@@ -304,7 +434,7 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
304
434
  };
305
435
  return new Proxy(resource, handler);
306
436
  }
307
- function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
437
+ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats, defaultSampling, provider) {
308
438
  const originalCreate = resource.create.bind(resource);
309
439
  const streamCandidate = resource.stream;
310
440
  const originalStream = typeof streamCandidate === "function" ? streamCandidate.bind(resource) : void 0;
@@ -318,8 +448,16 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
318
448
  const beginRequest = resolveBeginRequest(defaultContext, usageContext);
319
449
  const wantsStream = isStreamingRequest(params);
320
450
  return usageTap.withUsage(beginRequest, async (ctx) => {
451
+ const settle = wantsStream ? deferUsageFinalization(ctx) : void 0;
452
+ const sampleStartedAt = Date.now();
453
+ const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredOpenAISampleDecision({
454
+ usageTap,
455
+ sampling: defaultSampling,
456
+ beginRequest,
457
+ input: params
458
+ });
321
459
  const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
322
- const finalParams = await compressChatParamsForCall({
460
+ const compressedParams = await compressChatParamsForCall({
323
461
  params: hintedParams,
324
462
  usageTap,
325
463
  ctx,
@@ -329,25 +467,68 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
329
467
  withUsage: withUsage2,
330
468
  operation: "chat.completions.create"
331
469
  });
470
+ const finalParams = wantsStream ? ensureOpenAIStreamUsage(compressedParams) : compressedParams;
471
+ ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
332
472
  const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
333
473
  if (wantsStream) {
334
474
  const apiPromise2 = originalCreate(finalParams, request);
335
475
  const wrappedPromise2 = transformApiPromise(apiPromise2, (rawStream) => {
336
476
  ensureAsyncIterable(rawStream, "chat.completions.create");
337
- const wrappedStream2 = wrapStreamForUsageTap(rawStream, async () => {
338
- const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints);
339
- if (usage) {
340
- ctx.setUsage(usage);
477
+ const wrappedStream2 = wrapStreamForUsageTap(rawStream, async (termination) => {
478
+ try {
479
+ if (termination === "complete") {
480
+ const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
481
+ if (usage) {
482
+ ctx.setUsage(usage);
483
+ }
484
+ }
485
+ } catch (error) {
486
+ ctx.setError({
487
+ code: "USAGE_FINALIZE_ERROR",
488
+ message: error instanceof Error ? error.message : String(error)
489
+ });
490
+ throw error;
491
+ } finally {
492
+ await settle?.();
341
493
  }
342
- }, ctx);
494
+ }, ctx, (chunk) => {
495
+ tryInferUsageFromStreamChunk(
496
+ chunk,
497
+ ctx.begin.data.vendorHints,
498
+ ctx,
499
+ provider
500
+ );
501
+ });
343
502
  return wrappedStream2;
344
503
  });
345
504
  return wrappedPromise2;
346
505
  }
347
506
  const apiPromise = originalCreate(finalParams, request);
348
- const wrappedPromise = transformApiPromise(apiPromise, (response) => {
349
- tryInferUsage(response, ctx.begin.data.vendorHints, void 0, ctx);
507
+ const wrappedPromise = transformApiPromise(apiPromise, async (response) => {
508
+ tryInferUsage(response, ctx.begin.data.vendorHints, void 0, ctx, provider);
509
+ await captureMeteredOpenAISample({
510
+ usageTap,
511
+ sampling: defaultSampling,
512
+ decision: sampleDecision,
513
+ ctx,
514
+ beginRequest,
515
+ input: params,
516
+ response,
517
+ startedAt: sampleStartedAt
518
+ });
350
519
  return response;
520
+ }, async (error) => {
521
+ await captureMeteredOpenAISample({
522
+ usageTap,
523
+ sampling: defaultSampling,
524
+ decision: sampleDecision,
525
+ ctx,
526
+ beginRequest,
527
+ input: params,
528
+ error,
529
+ startedAt: sampleStartedAt
530
+ });
531
+ throw error;
351
532
  });
352
533
  return wrappedPromise;
353
534
  }, withUsage2);
@@ -361,8 +542,9 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
361
542
  } = splitUsageOptions(options);
362
543
  const beginRequest = resolveBeginRequest(defaultContext, usageContext);
363
544
  return usageTap.withUsage(beginRequest, async (ctx) => {
545
+ const settle = deferUsageFinalization(ctx);
364
546
  const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
365
- const finalParams = await compressChatParamsForCall({
547
+ const compressedParams = await compressChatParamsForCall({
366
548
  params: hintedParams,
367
549
  usageTap,
368
550
  ctx,
@@ -372,16 +554,41 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
372
554
  withUsage: withUsage2,
373
555
  operation: "chat.completions.stream"
374
556
  });
557
+ const finalParams = ensureOpenAIStreamUsage(compressedParams);
558
+ ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
375
559
  const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
376
560
  const apiPromise = originalStream(finalParams, request);
377
561
  const wrappedPromise = transformApiPromise(apiPromise, (rawStream) => {
378
562
  ensureAsyncIterable(rawStream, "chat.completions.stream");
379
- const wrappedStreamInner = wrapStreamForUsageTap(rawStream, async () => {
380
- const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints);
381
- if (usage) {
382
- ctx.setUsage(usage);
563
+ const wrappedStreamInner = wrapStreamForUsageTap(rawStream, async (termination) => {
564
+ try {
565
+ if (termination === "complete") {
566
+ const usage = await extractUsageFromStream(
567
+ rawStream,
568
+ ctx.begin.data.vendorHints,
569
+ provider
570
+ );
571
+ if (usage) {
572
+ ctx.setUsage(usage);
573
+ }
574
+ }
575
+ } catch (error) {
576
+ ctx.setError({
577
+ code: "USAGE_FINALIZE_ERROR",
578
+ message: error instanceof Error ? error.message : String(error)
579
+ });
580
+ throw error;
581
+ } finally {
582
+ await settle();
383
583
  }
384
- }, ctx);
584
+ }, ctx, (chunk) => {
585
+ tryInferUsageFromStreamChunk(
586
+ chunk,
587
+ ctx.begin.data.vendorHints,
588
+ ctx,
589
+ provider
590
+ );
591
+ });
385
592
  return wrappedStreamInner;
386
593
  });
387
594
  return wrappedPromise;
@@ -503,6 +710,10 @@ async function compressChatParams(params, usageTap, compression, signal) {
503
710
  const result = await usageTap.compressPromptMessages(source, {
504
711
  provider: "usagetap",
505
712
  failOpen: compression.failOpen,
713
+ mode: compression.mode,
714
+ latencyBudgetMs: compression.latencyBudgetMs,
715
+ compactEmptyUserMessages: compression.compactEmptyUserMessages,
716
+ compactDuplicateUserTextParts: compression.compactDuplicateUserTextParts,
506
717
  aggressiveness: resolveMessageEndpointAggressiveness(compression),
507
718
  signal
508
719
  });
@@ -972,19 +1183,52 @@ function resolveBeginRequest(defaults, override) {
972
1183
  if (requested) begin.requested = requested;
973
1184
  const feature = current.feature ?? base.feature;
974
1185
  if (feature) begin.feature = feature;
1186
+ const runId = current.runId ?? base.runId;
1187
+ if (runId) begin.runId = runId;
975
1188
  const idempotency = current.idempotency ?? base.idempotency;
976
1189
  if (idempotency) begin.idempotency = idempotency;
977
1190
  const customerName = current.customerName ?? base.customerName;
978
1191
  if (customerName) begin.customerName = customerName;
979
1192
  const customerEmail = current.customerEmail ?? base.customerEmail;
980
1193
  if (customerEmail) begin.customerEmail = customerEmail;
1194
+ const customerUserId = current.customerUserId ?? base.customerUserId;
1195
+ if (customerUserId) begin.customerUserId = customerUserId;
1196
+ const customerUserName = current.customerUserName ?? base.customerUserName;
1197
+ if (customerUserName) begin.customerUserName = customerUserName;
1198
+ const customerUserEmail = current.customerUserEmail ?? base.customerUserEmail;
1199
+ if (customerUserEmail) begin.customerUserEmail = customerUserEmail;
1200
+ const stripeCustomerId = current.stripeCustomerId ?? base.stripeCustomerId;
1201
+ if (stripeCustomerId) begin.stripeCustomerId = stripeCustomerId;
1202
+ const batch = current.batch ?? base.batch;
1203
+ if (typeof batch === "boolean") begin.batch = batch;
1204
+ const pricingMode = current.pricingMode ?? base.pricingMode;
1205
+ if (pricingMode) begin.pricingMode = pricingMode;
981
1206
  if (tags?.length) {
982
1207
  begin.tags = tags;
983
1208
  }
984
1209
  return begin;
985
1210
  }
986
- function transformApiPromise(apiPromise, onResolve) {
987
- const resolvedPromise = Promise.resolve(apiPromise).then(onResolve);
1211
+ function responsesBeginRequest(begin, params) {
1212
+ if (!responsesRequestUsesWebSearch(params)) return begin;
1213
+ return {
1214
+ ...begin,
1215
+ requested: {
1216
+ ...begin.requested ?? {},
1217
+ search: true
1218
+ }
1219
+ };
1220
+ }
1221
+ function responsesRequestUsesWebSearch(params) {
1222
+ if (!params || typeof params !== "object") return false;
1223
+ const tools = params.tools;
1224
+ return Array.isArray(tools) && tools.some((tool) => {
1225
+ if (!tool || typeof tool !== "object") return false;
1226
+ const type = tool.type;
1227
+ return type === "web_search" || type === "web_search_preview";
1228
+ });
1229
+ }
1230
+ function transformApiPromise(apiPromise, onResolve, onReject) {
1231
+ const resolvedPromise = Promise.resolve(apiPromise).then(onResolve, onReject);
988
1232
  if (isObjectRecord(apiPromise)) {
989
1233
  const proto = Object.getPrototypeOf(apiPromise);
990
1234
  if (proto) {
@@ -1119,12 +1363,12 @@ function applyResponsesVendorHints(params, hints) {
1119
1363
  }
1120
1364
  return next;
1121
1365
  }
1122
- async function extractUsageFromStream(stream, hints) {
1366
+ async function extractUsageFromStream(stream, hints, provider = "openai") {
1123
1367
  const finalPayload = await resolveStreamFinalPayload(stream);
1124
1368
  if (!finalPayload) {
1125
1369
  return void 0;
1126
1370
  }
1127
- return inferUsageFromResponse(finalPayload, hints);
1371
+ return inferUsageFromResponse(finalPayload, hints, provider);
1128
1372
  }
1129
1373
  async function resolveStreamFinalPayload(stream) {
1130
1374
  if (!stream || typeof stream !== "object") {
@@ -1197,13 +1441,13 @@ function setHeaderIfPossible(res, key, value) {
1197
1441
  res.setHeader(key, value);
1198
1442
  }
1199
1443
  }
1200
- function tryInferUsage(response, hints, extractor, ctx) {
1201
- const inferred = inferUsageFromResponse(response, hints);
1444
+ function tryInferUsage(response, hints, extractor, ctx, provider = "openai") {
1445
+ const inferred = inferUsageFromResponse(response, hints, provider);
1202
1446
  if (inferred) {
1203
1447
  ctx.setUsage(inferred);
1204
1448
  }
1205
1449
  }
1206
- function inferUsageFromResponse(response, hints) {
1450
+ function inferUsageFromResponse(response, hints, provider = "openai") {
1207
1451
  if (!response || typeof response !== "object") {
1208
1452
  return void 0;
1209
1453
  }
@@ -1211,32 +1455,110 @@ function inferUsageFromResponse(response, hints) {
1211
1455
  if (!candidate.usage) {
1212
1456
  return void 0;
1213
1457
  }
1214
- const cachedInputTokens = candidate.usage.prompt_tokens_details?.cached_tokens ?? candidate.usage.cached_tokens;
1458
+ const cachedInputTokens = candidate.usage.prompt_tokens_details?.cached_tokens ?? candidate.usage.input_tokens_details?.cached_tokens ?? candidate.usage.cached_tokens;
1459
+ const cacheWriteInputTokens = candidate.usage.prompt_tokens_details?.cache_write_tokens ?? candidate.usage.prompt_tokens_details?.cache_creation_tokens ?? candidate.usage.input_tokens_details?.cache_write_tokens ?? candidate.usage.input_tokens_details?.cache_creation_tokens ?? candidate.usage.cache_creation_input_tokens ?? candidate.usage.cache_write_input_tokens ?? candidate.usage.cache_write_tokens;
1460
+ const cacheWrite5mInputTokens = candidate.usage.cache_write_5m_input_tokens ?? candidate.usage.cache_creation?.ephemeral_5m_input_tokens;
1461
+ const cacheWrite1hInputTokens = candidate.usage.cache_write_1h_input_tokens ?? candidate.usage.cache_creation?.ephemeral_1h_input_tokens;
1462
+ const outputSearches = Array.isArray(candidate.output) ? candidate.output.filter((item) => item?.type === "web_search_call").length : 0;
1463
+ const searches = candidate.usage.searches ?? candidate.usage.web_search_queries ?? candidate.usage.server_tool_use?.web_search_requests ?? outputSearches;
1464
+ const responseEffort = normalizeExecutionReasoningEffort(
1465
+ candidate.reasoning?.effort
1466
+ );
1215
1467
  return {
1468
+ providerUsed: provider,
1216
1469
  modelUsed: candidate.model ?? hints?.preferredModel,
1217
- inputTokens: candidate.usage.prompt_tokens,
1218
- responseTokens: candidate.usage.completion_tokens,
1219
- cachedInputTokens
1470
+ inputTokens: candidate.usage.prompt_tokens ?? candidate.usage.input_tokens,
1471
+ responseTokens: candidate.usage.completion_tokens ?? candidate.usage.output_tokens,
1472
+ cachedInputTokens,
1473
+ cacheWriteInputTokens,
1474
+ cacheWrite5mInputTokens,
1475
+ cacheWrite1hInputTokens,
1476
+ audioInputTokens: candidate.usage.prompt_tokens_details?.audio_tokens ?? candidate.usage.input_tokens_details?.audio_tokens,
1477
+ cachedAudioInputTokens: candidate.usage.prompt_tokens_details?.cached_audio_tokens ?? candidate.usage.input_tokens_details?.cached_audio_tokens ?? candidate.usage.prompt_tokens_details?.cached_tokens_details?.audio_tokens ?? candidate.usage.input_tokens_details?.cached_tokens_details?.audio_tokens,
1478
+ imageInputTokens: candidate.usage.prompt_tokens_details?.image_tokens ?? candidate.usage.input_tokens_details?.image_tokens,
1479
+ imageOutputTokens: candidate.usage.completion_tokens_details?.image_tokens ?? candidate.usage.output_tokens_details?.image_tokens,
1480
+ audioOutputTokens: candidate.usage.completion_tokens_details?.audio_tokens ?? candidate.usage.output_tokens_details?.audio_tokens,
1481
+ reasoningTokens: candidate.usage.completion_tokens_details?.reasoning_tokens ?? candidate.usage.output_tokens_details?.reasoning_tokens,
1482
+ ...responseEffort ? {
1483
+ reasoningEffort: responseEffort,
1484
+ reasoningEffortSource: "provider_response"
1485
+ } : {},
1486
+ ...typeof candidate.reasoning?.type === "string" ? { reasoningMode: candidate.reasoning.type } : typeof candidate.reasoning?.mode === "string" ? { reasoningMode: candidate.reasoning.mode } : {},
1487
+ ...typeof searches === "number" && searches > 0 ? { searches } : {}
1488
+ };
1489
+ }
1490
+ function normalizeExecutionReasoningEffort(value) {
1491
+ return value === "none" || value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh" || value === "max" ? value : void 0;
1492
+ }
1493
+ function openAIRequestExecutionMetadata(params, provider) {
1494
+ const record = params && typeof params === "object" ? params : {};
1495
+ const reasoning = record.reasoning && typeof record.reasoning === "object" ? record.reasoning : void 0;
1496
+ const effort = normalizeExecutionReasoningEffort(
1497
+ record.reasoning_effort ?? reasoning?.effort ?? record.thinking_level
1498
+ );
1499
+ const mode = typeof reasoning?.type === "string" ? reasoning.type : typeof reasoning?.mode === "string" ? reasoning.mode : void 0;
1500
+ const rawBudget = reasoning?.budget_tokens ?? record.thinking_budget ?? record.thinking_budget_tokens;
1501
+ const budget = typeof rawBudget === "number" && Number.isInteger(rawBudget) && rawBudget >= 0 ? rawBudget : void 0;
1502
+ return {
1503
+ providerUsed: provider,
1504
+ ...typeof record.model === "string" ? { modelUsed: record.model } : {},
1505
+ ...effort ? { reasoningEffort: effort, reasoningEffortSource: "provider_request" } : {},
1506
+ ...mode ? { reasoningMode: mode } : {},
1507
+ ...budget !== void 0 ? { reasoningBudgetTokens: budget } : {}
1508
+ };
1509
+ }
1510
+ function tryInferUsageFromStreamChunk(chunk, hints, ctx, provider) {
1511
+ const payload = isObjectRecord(chunk) && isObjectRecord(chunk.response) ? chunk.response : chunk;
1512
+ const inferred = inferUsageFromResponse(payload, hints, provider);
1513
+ if (inferred) {
1514
+ ctx.setUsage(inferred);
1515
+ }
1516
+ }
1517
+ function ensureOpenAIStreamUsage(params) {
1518
+ if (!isObjectRecord(params)) {
1519
+ return params;
1520
+ }
1521
+ const streamOptions = isObjectRecord(params.stream_options) ? params.stream_options : {};
1522
+ return {
1523
+ ...params,
1524
+ stream_options: {
1525
+ ...streamOptions,
1526
+ include_usage: true
1527
+ }
1220
1528
  };
1221
1529
  }
1222
- function wrapStreamForUsageTap(source, finalize, ctx) {
1530
+ function deferUsageFinalization(ctx) {
1531
+ return ctx.deferFinalization?.() ?? (() => Promise.resolve());
1532
+ }
1533
+ function wrapStreamForUsageTap(source, finalize, ctx, onChunk) {
1223
1534
  const getIterator = source[Symbol.asyncIterator];
1224
1535
  if (typeof getIterator !== "function") {
1225
1536
  throw new TypeError("Stream is not async iterable");
1226
1537
  }
1227
1538
  const iterator = getIterator.call(source);
1228
1539
  let completed = false;
1229
- const invokeFinalize = async () => {
1540
+ const invokeFinalize = async (termination, error) => {
1230
1541
  if (completed) return;
1231
1542
  completed = true;
1543
+ if (termination === "cancel" || termination === "manual") {
1544
+ ctx.setError({
1545
+ code: "STREAM_ABORTED",
1546
+ message: termination === "cancel" ? "Provider stream consumption was cancelled before completion" : "Provider stream was finalized before completion"
1547
+ });
1548
+ } else if (termination === "error") {
1549
+ ctx.setError({
1550
+ code: "VENDOR_ERROR",
1551
+ message: error instanceof Error ? error.message : String(error)
1552
+ });
1553
+ }
1232
1554
  try {
1233
- await finalize();
1234
- } catch (error) {
1555
+ await finalize(termination);
1556
+ } catch (error2) {
1235
1557
  ctx.setError({
1236
1558
  code: "USAGE_FINALIZE_ERROR",
1237
- message: error instanceof Error ? error.message : String(error)
1559
+ message: error2 instanceof Error ? error2.message : String(error2)
1238
1560
  });
1239
- throw error;
1561
+ throw error2;
1240
1562
  }
1241
1563
  };
1242
1564
  const prototype = Object.getPrototypeOf(source) ?? Object.prototype;
@@ -1260,12 +1582,15 @@ function wrapStreamForUsageTap(source, finalize, ctx) {
1260
1582
  value: async (...args) => {
1261
1583
  try {
1262
1584
  const result = await iterator.next(...args);
1585
+ if (!result.done) {
1586
+ onChunk?.(result.value);
1587
+ }
1263
1588
  if (result.done) {
1264
- await invokeFinalize();
1589
+ await invokeFinalize("complete");
1265
1590
  }
1266
1591
  return result;
1267
1592
  } catch (error) {
1268
- await invokeFinalize().catch(() => void 0);
1593
+ await invokeFinalize("error", error).catch(() => void 0);
1269
1594
  throw error;
1270
1595
  }
1271
1596
  },
@@ -1274,39 +1599,49 @@ function wrapStreamForUsageTap(source, finalize, ctx) {
1274
1599
  });
1275
1600
  Object.defineProperty(wrapped, "return", {
1276
1601
  value: async (value) => {
1277
- if (typeof iterator.return === "function") {
1278
- const rawResult = await iterator.return(value);
1279
- if (!isIteratorResult(rawResult)) {
1280
- throw new TypeError("Iterator.return() returned an invalid result");
1602
+ try {
1603
+ if (typeof iterator.return === "function") {
1604
+ const rawResult = await iterator.return(value);
1605
+ if (!isIteratorResult(rawResult)) {
1606
+ throw new TypeError("Iterator.return() returned an invalid result");
1607
+ }
1608
+ await invokeFinalize("cancel");
1609
+ return rawResult;
1281
1610
  }
1282
- await invokeFinalize();
1283
- return rawResult;
1611
+ await invokeFinalize("cancel");
1612
+ return { done: true, value };
1613
+ } catch (error) {
1614
+ await invokeFinalize("error", error).catch(() => void 0);
1615
+ throw error;
1284
1616
  }
1285
- await invokeFinalize();
1286
- return { done: true, value };
1287
1617
  },
1288
1618
  configurable: true,
1289
1619
  writable: true
1290
1620
  });
1291
1621
  Object.defineProperty(wrapped, "throw", {
1292
1622
  value: async (error) => {
1293
- if (typeof iterator.throw === "function") {
1294
- const rawResult = await iterator.throw(error);
1295
- if (!isIteratorResult(rawResult)) {
1296
- throw new TypeError("Iterator.throw() returned an invalid result");
1623
+ try {
1624
+ if (typeof iterator.throw === "function") {
1625
+ const rawResult = await iterator.throw(error);
1626
+ if (!isIteratorResult(rawResult)) {
1627
+ throw new TypeError("Iterator.throw() returned an invalid result");
1628
+ }
1629
+ await invokeFinalize("error", error);
1630
+ return rawResult;
1297
1631
  }
1298
- await invokeFinalize();
1299
- return rawResult;
1632
+ await invokeFinalize("error", error);
1633
+ throw error;
1634
+ } catch (thrownError) {
1635
+ await invokeFinalize("error", thrownError).catch(() => void 0);
1636
+ throw thrownError;
1300
1637
  }
1301
- await invokeFinalize();
1302
- throw error;
1303
1638
  },
1304
1639
  configurable: true,
1305
1640
  writable: true
1306
1641
  });
1307
1642
  Object.defineProperty(wrapped, "__usageTapFinalize", {
1308
1643
  value: async () => {
1309
- await invokeFinalize();
1644
+ await invokeFinalize("manual");
1310
1645
  },
1311
1646
  configurable: true
1312
1647
  });