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