@usagetap/sdk 1.3.2 → 1.4.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 +219 -24
  2. package/dist/adapters/anthropic.cjs +990 -24
  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 +990 -25
  7. package/dist/adapters/anthropic.mjs.map +1 -1
  8. package/dist/adapters/openai.cjs +1164 -44
  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 +1164 -45
  13. package/dist/adapters/openai.mjs.map +1 -1
  14. package/dist/adapters/openrouter.cjs +3899 -22
  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 +3897 -23
  19. package/dist/adapters/openrouter.mjs.map +1 -1
  20. package/dist/anthropic/index.cjs +990 -24
  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 +990 -25
  25. package/dist/anthropic/index.mjs.map +1 -1
  26. package/dist/client-CExQ8e1T.d.cts +1225 -0
  27. package/dist/client-CExQ8e1T.d.ts +1225 -0
  28. package/dist/express/index.cjs +421 -29
  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 +421 -29
  33. package/dist/express/index.mjs.map +1 -1
  34. package/dist/index.cjs +680 -15
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +5 -3
  37. package/dist/index.d.ts +5 -3
  38. package/dist/index.mjs +680 -15
  39. package/dist/index.mjs.map +1 -1
  40. package/dist/openai/index.cjs +1165 -45
  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 +1165 -46
  45. package/dist/openai/index.mjs.map +1 -1
  46. package/dist/openrouter/index.cjs +1182 -47
  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 +1180 -46
  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 +1 -1
  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 = withRuntimeCompressionContext(
340
+ resolveBeginRequest(defaultContext, usageContext),
341
+ "openai",
342
+ String(params.model)
343
+ );
263
344
  const wantsStream = isStreamingRequest(params);
264
345
  return usageTap.withUsage(beginRequest, async (ctx) => {
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,13 +361,14 @@ 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
370
  const wrappedStream = wrapStreamForUsageTap(rawStream, async () => {
282
- const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints);
371
+ const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
283
372
  if (usage) {
284
373
  ctx.setUsage(usage);
285
374
  }
@@ -289,9 +378,31 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
289
378
  return wrappedPromise2;
290
379
  }
291
380
  const apiPromise = originalCreate(finalParams, request);
292
- const wrappedPromise = transformApiPromise(apiPromise, (response) => {
293
- tryInferUsage(response, ctx.begin.data.vendorHints, void 0, ctx);
381
+ const wrappedPromise = transformApiPromise(apiPromise, async (response) => {
382
+ tryInferUsage(response, ctx.begin.data.vendorHints, void 0, ctx, provider);
383
+ await captureMeteredOpenAISample({
384
+ usageTap,
385
+ sampling: defaultSampling,
386
+ decision: sampleDecision,
387
+ ctx,
388
+ beginRequest,
389
+ input: params,
390
+ response,
391
+ startedAt: sampleStartedAt
392
+ });
294
393
  return response;
394
+ }, async (error) => {
395
+ await captureMeteredOpenAISample({
396
+ usageTap,
397
+ sampling: defaultSampling,
398
+ decision: sampleDecision,
399
+ ctx,
400
+ beginRequest,
401
+ input: params,
402
+ error,
403
+ startedAt: sampleStartedAt
404
+ });
405
+ throw error;
295
406
  });
296
407
  return wrappedPromise;
297
408
  }, withUsage2);
@@ -306,7 +417,7 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
306
417
  };
307
418
  return new Proxy(resource, handler);
308
419
  }
309
- function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
420
+ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats, defaultSampling, provider) {
310
421
  const originalCreate = resource.create.bind(resource);
311
422
  const streamCandidate = resource.stream;
312
423
  const originalStream = typeof streamCandidate === "function" ? streamCandidate.bind(resource) : void 0;
@@ -317,9 +428,20 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
317
428
  withUsage: withUsage2,
318
429
  promptCompression
319
430
  } = splitUsageOptions(options);
320
- const beginRequest = resolveBeginRequest(defaultContext, usageContext);
431
+ const beginRequest = withRuntimeCompressionContext(
432
+ resolveBeginRequest(defaultContext, usageContext),
433
+ "openai",
434
+ String(params.model)
435
+ );
321
436
  const wantsStream = isStreamingRequest(params);
322
437
  return usageTap.withUsage(beginRequest, async (ctx) => {
438
+ const sampleStartedAt = Date.now();
439
+ const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredOpenAISampleDecision({
440
+ usageTap,
441
+ sampling: defaultSampling,
442
+ beginRequest,
443
+ input: params
444
+ });
323
445
  const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
324
446
  const finalParams = await compressChatParamsForCall({
325
447
  params: hintedParams,
@@ -331,13 +453,14 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
331
453
  withUsage: withUsage2,
332
454
  operation: "chat.completions.create"
333
455
  });
456
+ ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
334
457
  const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
335
458
  if (wantsStream) {
336
459
  const apiPromise2 = originalCreate(finalParams, request);
337
460
  const wrappedPromise2 = transformApiPromise(apiPromise2, (rawStream) => {
338
461
  ensureAsyncIterable(rawStream, "chat.completions.create");
339
462
  const wrappedStream2 = wrapStreamForUsageTap(rawStream, async () => {
340
- const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints);
463
+ const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
341
464
  if (usage) {
342
465
  ctx.setUsage(usage);
343
466
  }
@@ -347,9 +470,31 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
347
470
  return wrappedPromise2;
348
471
  }
349
472
  const apiPromise = originalCreate(finalParams, request);
350
- const wrappedPromise = transformApiPromise(apiPromise, (response) => {
351
- tryInferUsage(response, ctx.begin.data.vendorHints, void 0, ctx);
473
+ const wrappedPromise = transformApiPromise(apiPromise, async (response) => {
474
+ tryInferUsage(response, ctx.begin.data.vendorHints, void 0, ctx, provider);
475
+ await captureMeteredOpenAISample({
476
+ usageTap,
477
+ sampling: defaultSampling,
478
+ decision: sampleDecision,
479
+ ctx,
480
+ beginRequest,
481
+ input: params,
482
+ response,
483
+ startedAt: sampleStartedAt
484
+ });
352
485
  return response;
486
+ }, async (error) => {
487
+ await captureMeteredOpenAISample({
488
+ usageTap,
489
+ sampling: defaultSampling,
490
+ decision: sampleDecision,
491
+ ctx,
492
+ beginRequest,
493
+ input: params,
494
+ error,
495
+ startedAt: sampleStartedAt
496
+ });
497
+ throw error;
353
498
  });
354
499
  return wrappedPromise;
355
500
  }, withUsage2);
@@ -361,7 +506,11 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
361
506
  withUsage: withUsage2,
362
507
  promptCompression
363
508
  } = splitUsageOptions(options);
364
- const beginRequest = resolveBeginRequest(defaultContext, usageContext);
509
+ const beginRequest = withRuntimeCompressionContext(
510
+ resolveBeginRequest(defaultContext, usageContext),
511
+ "openai",
512
+ String(params.model)
513
+ );
365
514
  return usageTap.withUsage(beginRequest, async (ctx) => {
366
515
  const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
367
516
  const finalParams = await compressChatParamsForCall({
@@ -374,12 +523,17 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
374
523
  withUsage: withUsage2,
375
524
  operation: "chat.completions.stream"
376
525
  });
526
+ ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
377
527
  const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
378
528
  const apiPromise = originalStream(finalParams, request);
379
529
  const wrappedPromise = transformApiPromise(apiPromise, (rawStream) => {
380
530
  ensureAsyncIterable(rawStream, "chat.completions.stream");
381
531
  const wrappedStreamInner = wrapStreamForUsageTap(rawStream, async () => {
382
- const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints);
532
+ const usage = await extractUsageFromStream(
533
+ rawStream,
534
+ ctx.begin.data.vendorHints,
535
+ provider
536
+ );
383
537
  if (usage) {
384
538
  ctx.setUsage(usage);
385
539
  }
@@ -403,19 +557,70 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
403
557
  return new Proxy(resource, handler);
404
558
  }
405
559
  async function compressChatParamsForCall(args) {
406
- const compression = resolveEffectivePromptCompressionOptions(
560
+ let compression = resolveEffectivePromptCompressionOptions(
407
561
  args.defaultPromptCompression,
408
562
  args.callPromptCompression
409
563
  );
564
+ const runtimePolicy = args.ctx.begin.data.runtimeCompressionPolicy;
565
+ const policyDriven = !compression && Boolean(runtimePolicy);
566
+ if (policyDriven && runtimePolicy) {
567
+ if (!runtimePolicy.selected) {
568
+ args.ctx.setUsage({
569
+ runtimeCompression: bypassedRuntimeMeasurement(runtimePolicy)
570
+ });
571
+ return args.params;
572
+ }
573
+ compression = openAIPolicyCompressionOptions(runtimePolicy);
574
+ if (runtimePolicy.mode === "SHADOW") {
575
+ const startedAt2 = Date.now();
576
+ void compressChatParams(
577
+ args.params,
578
+ args.usageTap,
579
+ compression,
580
+ args.withUsage?.signal
581
+ ).then((outcome2) => {
582
+ args.ctx.setUsage({
583
+ runtimeCompression: openAIRuntimeMeasurement({
584
+ policy: runtimePolicy,
585
+ telemetry: buildPromptCompressionTelemetry(outcome2.segments),
586
+ latencyMs: Date.now() - startedAt2,
587
+ shadow: true,
588
+ originalPayload: args.params,
589
+ resultPayload: outcome2.params
590
+ })
591
+ });
592
+ }).catch(() => {
593
+ args.ctx.setUsage({
594
+ runtimeCompression: failedRuntimeMeasurement(
595
+ runtimePolicy,
596
+ Date.now() - startedAt2
597
+ )
598
+ });
599
+ });
600
+ return args.params;
601
+ }
602
+ }
410
603
  if (!compression) {
411
604
  return args.params;
412
605
  }
606
+ const startedAt = Date.now();
413
607
  const outcome = await compressChatParams(
414
608
  args.params,
415
609
  args.usageTap,
416
610
  compression,
417
611
  args.withUsage?.signal
418
612
  );
613
+ if (policyDriven && runtimePolicy) {
614
+ const runtimeMeasurement = openAIRuntimeMeasurement({
615
+ policy: runtimePolicy,
616
+ telemetry: buildPromptCompressionTelemetry(outcome.segments),
617
+ latencyMs: Date.now() - startedAt,
618
+ originalPayload: args.params,
619
+ resultPayload: outcome.params
620
+ });
621
+ args.ctx.setUsage({ runtimeCompression: runtimeMeasurement });
622
+ if (runtimeMeasurement.decision !== "compressed") return args.params;
623
+ }
419
624
  await recordCompressionOutcome({
420
625
  outcome,
421
626
  compression,
@@ -428,19 +633,70 @@ async function compressChatParamsForCall(args) {
428
633
  return outcome.params;
429
634
  }
430
635
  async function compressResponsesParamsForCall(args) {
431
- const compression = resolveEffectivePromptCompressionOptions(
636
+ let compression = resolveEffectivePromptCompressionOptions(
432
637
  args.defaultPromptCompression,
433
638
  args.callPromptCompression
434
639
  );
640
+ const runtimePolicy = args.ctx.begin.data.runtimeCompressionPolicy;
641
+ const policyDriven = !compression && Boolean(runtimePolicy);
642
+ if (policyDriven && runtimePolicy) {
643
+ if (!runtimePolicy.selected) {
644
+ args.ctx.setUsage({
645
+ runtimeCompression: bypassedRuntimeMeasurement(runtimePolicy)
646
+ });
647
+ return args.params;
648
+ }
649
+ compression = openAIPolicyCompressionOptions(runtimePolicy);
650
+ if (runtimePolicy.mode === "SHADOW") {
651
+ const startedAt2 = Date.now();
652
+ void compressResponsesParams(
653
+ args.params,
654
+ args.usageTap,
655
+ compression,
656
+ args.withUsage?.signal
657
+ ).then((outcome2) => {
658
+ args.ctx.setUsage({
659
+ runtimeCompression: openAIRuntimeMeasurement({
660
+ policy: runtimePolicy,
661
+ telemetry: buildPromptCompressionTelemetry(outcome2.segments),
662
+ latencyMs: Date.now() - startedAt2,
663
+ shadow: true,
664
+ originalPayload: args.params,
665
+ resultPayload: outcome2.params
666
+ })
667
+ });
668
+ }).catch(() => {
669
+ args.ctx.setUsage({
670
+ runtimeCompression: failedRuntimeMeasurement(
671
+ runtimePolicy,
672
+ Date.now() - startedAt2
673
+ )
674
+ });
675
+ });
676
+ return args.params;
677
+ }
678
+ }
435
679
  if (!compression) {
436
680
  return args.params;
437
681
  }
682
+ const startedAt = Date.now();
438
683
  const outcome = await compressResponsesParams(
439
684
  args.params,
440
685
  args.usageTap,
441
686
  compression,
442
687
  args.withUsage?.signal
443
688
  );
689
+ if (policyDriven && runtimePolicy) {
690
+ const runtimeMeasurement = openAIRuntimeMeasurement({
691
+ policy: runtimePolicy,
692
+ telemetry: buildPromptCompressionTelemetry(outcome.segments),
693
+ latencyMs: Date.now() - startedAt,
694
+ originalPayload: args.params,
695
+ resultPayload: outcome.params
696
+ });
697
+ args.ctx.setUsage({ runtimeCompression: runtimeMeasurement });
698
+ if (runtimeMeasurement.decision !== "compressed") return args.params;
699
+ }
444
700
  await recordCompressionOutcome({
445
701
  outcome,
446
702
  compression,
@@ -452,6 +708,79 @@ async function compressResponsesParamsForCall(args) {
452
708
  });
453
709
  return outcome.params;
454
710
  }
711
+ function openAIPolicyCompressionOptions(policy) {
712
+ const roles = Object.fromEntries(
713
+ ["system", "user", "tool", "assistant"].filter((role) => policy.snapshot.scope.messageRoles.includes(role)).map((role) => [role, true])
714
+ );
715
+ return {
716
+ provider: "usagetap",
717
+ roles,
718
+ minContextTokens: policy.snapshot.scope.minimumTokens,
719
+ failOpen: true
720
+ };
721
+ }
722
+ function bypassedRuntimeMeasurement(policy) {
723
+ return {
724
+ policyId: policy.policyId,
725
+ policyVersionId: policy.policyVersionId,
726
+ policyVersion: policy.version,
727
+ rolloutMode: policy.mode,
728
+ decision: "bypassed",
729
+ reasonCode: "not_in_rollout_sample",
730
+ methodsAttempted: [],
731
+ methodsApplied: [],
732
+ originalTokens: 0,
733
+ resultTokens: 0,
734
+ savedTokens: 0,
735
+ reductionPercentage: 0,
736
+ compressionLatencyMs: 0,
737
+ compressionComputeCostUsd: 0,
738
+ financialsEstimated: true
739
+ };
740
+ }
741
+ function failedRuntimeMeasurement(policy, latencyMs) {
742
+ return {
743
+ ...bypassedRuntimeMeasurement(policy),
744
+ decision: "fallback",
745
+ reasonCode: "transformation_failed",
746
+ methodsAttempted: ["deterministic"],
747
+ compressionLatencyMs: latencyMs
748
+ };
749
+ }
750
+ function openAIRuntimeMeasurement(input) {
751
+ const telemetry = input.telemetry;
752
+ const originalTokens = telemetry?.originalTokens ?? estimatePromptTokens(JSON.stringify(input.originalPayload));
753
+ const resultTokens = telemetry?.compressedTokens ?? originalTokens;
754
+ const savedTokens = Math.max(0, telemetry?.savedTokens ?? 0);
755
+ const reductionPercentage = originalTokens > 0 ? savedTokens / originalTokens * 100 : 0;
756
+ const belowMinimumSize = originalTokens < input.policy.snapshot.scope.minimumTokens;
757
+ const latencyExceeded = input.latencyMs > input.policy.snapshot.gates.maximumLatencyMs;
758
+ const belowSavings = savedTokens < input.policy.snapshot.gates.minimumTokensRemoved || reductionPercentage < input.policy.snapshot.gates.minimumReductionPercentage;
759
+ const accepted = !belowMinimumSize && !latencyExceeded && !belowSavings;
760
+ const deepText = input.policy.snapshot.methods.deepText.enabled && !input.policy.snapshot.rollout.deterministicOnly && (input.policy.mode !== "CANARY" || input.policy.snapshot.rollout.deepTextCanaryAllowed);
761
+ const reasonCode = belowMinimumSize ? "below_minimum_size" : latencyExceeded ? "latency_budget_exceeded" : belowSavings ? "below_minimum_savings" : void 0;
762
+ const measurement = {
763
+ policyId: input.policy.policyId,
764
+ policyVersionId: input.policy.policyVersionId,
765
+ policyVersion: input.policy.version,
766
+ rolloutMode: input.policy.mode,
767
+ decision: accepted ? input.shadow ? "bypassed" : "compressed" : latencyExceeded ? "fallback" : "skipped",
768
+ ...reasonCode ? { reasonCode } : {},
769
+ methodsAttempted: [
770
+ "deterministic",
771
+ ...deepText ? ["deep_text"] : []
772
+ ],
773
+ methodsApplied: accepted ? ["deterministic"] : [],
774
+ originalTokens,
775
+ resultTokens: accepted ? resultTokens : originalTokens,
776
+ savedTokens: accepted ? savedTokens : 0,
777
+ reductionPercentage: accepted ? reductionPercentage : 0,
778
+ compressionLatencyMs: input.latencyMs,
779
+ compressionComputeCostUsd: 0,
780
+ financialsEstimated: true
781
+ };
782
+ return measurement;
783
+ }
455
784
  async function recordCompressionOutcome(args) {
456
785
  const telemetry = buildPromptCompressionTelemetry(args.outcome.segments);
457
786
  if (!telemetry) {
@@ -505,6 +834,10 @@ async function compressChatParams(params, usageTap, compression, signal) {
505
834
  const result = await usageTap.compressPromptMessages(source, {
506
835
  provider: "usagetap",
507
836
  failOpen: compression.failOpen,
837
+ mode: compression.mode,
838
+ latencyBudgetMs: compression.latencyBudgetMs,
839
+ compactEmptyUserMessages: compression.compactEmptyUserMessages,
840
+ compactDuplicateUserTextParts: compression.compactDuplicateUserTextParts,
508
841
  aggressiveness: resolveMessageEndpointAggressiveness(compression),
509
842
  signal
510
843
  });
@@ -970,23 +1303,53 @@ function resolveBeginRequest(defaults, override) {
970
1303
  }
971
1304
  const tags = mergeTags(base.tags, current.tags);
972
1305
  const begin = { customerId };
1306
+ const runtimeCompressionContext = current.runtimeCompressionContext ?? base.runtimeCompressionContext;
1307
+ if (runtimeCompressionContext) {
1308
+ begin.runtimeCompressionContext = runtimeCompressionContext;
1309
+ }
973
1310
  const requested = current.requested ?? base.requested;
974
1311
  if (requested) begin.requested = requested;
975
1312
  const feature = current.feature ?? base.feature;
976
1313
  if (feature) begin.feature = feature;
1314
+ const runId = current.runId ?? base.runId;
1315
+ if (runId) begin.runId = runId;
977
1316
  const idempotency = current.idempotency ?? base.idempotency;
978
1317
  if (idempotency) begin.idempotency = idempotency;
979
1318
  const customerName = current.customerName ?? base.customerName;
980
1319
  if (customerName) begin.customerName = customerName;
981
1320
  const customerEmail = current.customerEmail ?? base.customerEmail;
982
1321
  if (customerEmail) begin.customerEmail = customerEmail;
1322
+ const customerUserId = current.customerUserId ?? base.customerUserId;
1323
+ if (customerUserId) begin.customerUserId = customerUserId;
1324
+ const customerUserName = current.customerUserName ?? base.customerUserName;
1325
+ if (customerUserName) begin.customerUserName = customerUserName;
1326
+ const customerUserEmail = current.customerUserEmail ?? base.customerUserEmail;
1327
+ if (customerUserEmail) begin.customerUserEmail = customerUserEmail;
1328
+ const stripeCustomerId = current.stripeCustomerId ?? base.stripeCustomerId;
1329
+ if (stripeCustomerId) begin.stripeCustomerId = stripeCustomerId;
1330
+ const holdUsd = current.holdUsd ?? base.holdUsd;
1331
+ if (typeof holdUsd === "number") begin.holdUsd = holdUsd;
1332
+ const batch = current.batch ?? base.batch;
1333
+ if (typeof batch === "boolean") begin.batch = batch;
1334
+ const pricingMode = current.pricingMode ?? base.pricingMode;
1335
+ if (pricingMode) begin.pricingMode = pricingMode;
983
1336
  if (tags?.length) {
984
1337
  begin.tags = tags;
985
1338
  }
986
1339
  return begin;
987
1340
  }
988
- function transformApiPromise(apiPromise, onResolve) {
989
- const resolvedPromise = Promise.resolve(apiPromise).then(onResolve);
1341
+ function withRuntimeCompressionContext(begin, provider, model) {
1342
+ return {
1343
+ ...begin,
1344
+ runtimeCompressionContext: {
1345
+ ...begin.runtimeCompressionContext ?? {},
1346
+ provider,
1347
+ model
1348
+ }
1349
+ };
1350
+ }
1351
+ function transformApiPromise(apiPromise, onResolve, onReject) {
1352
+ const resolvedPromise = Promise.resolve(apiPromise).then(onResolve, onReject);
990
1353
  if (isObjectRecord(apiPromise)) {
991
1354
  const proto = Object.getPrototypeOf(apiPromise);
992
1355
  if (proto) {
@@ -1121,12 +1484,12 @@ function applyResponsesVendorHints(params, hints) {
1121
1484
  }
1122
1485
  return next;
1123
1486
  }
1124
- async function extractUsageFromStream(stream, hints) {
1487
+ async function extractUsageFromStream(stream, hints, provider = "openai") {
1125
1488
  const finalPayload = await resolveStreamFinalPayload(stream);
1126
1489
  if (!finalPayload) {
1127
1490
  return void 0;
1128
1491
  }
1129
- return inferUsageFromResponse(finalPayload, hints);
1492
+ return inferUsageFromResponse(finalPayload, hints, provider);
1130
1493
  }
1131
1494
  async function resolveStreamFinalPayload(stream) {
1132
1495
  if (!stream || typeof stream !== "object") {
@@ -1199,13 +1562,13 @@ function setHeaderIfPossible(res, key, value) {
1199
1562
  res.setHeader(key, value);
1200
1563
  }
1201
1564
  }
1202
- function tryInferUsage(response, hints, extractor, ctx) {
1203
- const inferred = inferUsageFromResponse(response, hints);
1565
+ function tryInferUsage(response, hints, extractor, ctx, provider = "openai") {
1566
+ const inferred = inferUsageFromResponse(response, hints, provider);
1204
1567
  if (inferred) {
1205
1568
  ctx.setUsage(inferred);
1206
1569
  }
1207
1570
  }
1208
- function inferUsageFromResponse(response, hints) {
1571
+ function inferUsageFromResponse(response, hints, provider = "openai") {
1209
1572
  if (!response || typeof response !== "object") {
1210
1573
  return void 0;
1211
1574
  }
@@ -1213,12 +1576,41 @@ function inferUsageFromResponse(response, hints) {
1213
1576
  if (!candidate.usage) {
1214
1577
  return void 0;
1215
1578
  }
1216
- const cachedInputTokens = candidate.usage.prompt_tokens_details?.cached_tokens ?? candidate.usage.cached_tokens;
1579
+ const cachedInputTokens = candidate.usage.prompt_tokens_details?.cached_tokens ?? candidate.usage.input_tokens_details?.cached_tokens ?? candidate.usage.cached_tokens;
1580
+ const responseEffort = normalizeExecutionReasoningEffort(
1581
+ candidate.reasoning?.effort
1582
+ );
1217
1583
  return {
1584
+ providerUsed: provider,
1218
1585
  modelUsed: candidate.model ?? hints?.preferredModel,
1219
- inputTokens: candidate.usage.prompt_tokens,
1220
- responseTokens: candidate.usage.completion_tokens,
1221
- cachedInputTokens
1586
+ inputTokens: candidate.usage.prompt_tokens ?? candidate.usage.input_tokens,
1587
+ responseTokens: candidate.usage.completion_tokens ?? candidate.usage.output_tokens,
1588
+ cachedInputTokens,
1589
+ reasoningTokens: candidate.usage.completion_tokens_details?.reasoning_tokens ?? candidate.usage.output_tokens_details?.reasoning_tokens,
1590
+ ...responseEffort ? {
1591
+ reasoningEffort: responseEffort,
1592
+ reasoningEffortSource: "provider_response"
1593
+ } : {},
1594
+ ...typeof candidate.reasoning?.type === "string" ? { reasoningMode: candidate.reasoning.type } : typeof candidate.reasoning?.mode === "string" ? { reasoningMode: candidate.reasoning.mode } : {}
1595
+ };
1596
+ }
1597
+ function normalizeExecutionReasoningEffort(value) {
1598
+ return value === "none" || value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh" || value === "max" ? value : void 0;
1599
+ }
1600
+ function openAIRequestExecutionMetadata(params, provider) {
1601
+ const record = params && typeof params === "object" ? params : {};
1602
+ const reasoning = record.reasoning && typeof record.reasoning === "object" ? record.reasoning : void 0;
1603
+ const effort = normalizeExecutionReasoningEffort(
1604
+ record.reasoning_effort ?? reasoning?.effort ?? record.thinking_level
1605
+ );
1606
+ const mode = typeof reasoning?.type === "string" ? reasoning.type : typeof reasoning?.mode === "string" ? reasoning.mode : void 0;
1607
+ const rawBudget = reasoning?.budget_tokens ?? record.thinking_budget ?? record.thinking_budget_tokens;
1608
+ const budget = typeof rawBudget === "number" && Number.isInteger(rawBudget) && rawBudget >= 0 ? rawBudget : void 0;
1609
+ return {
1610
+ providerUsed: provider,
1611
+ ...effort ? { reasoningEffort: effort, reasoningEffortSource: "provider_request" } : {},
1612
+ ...mode ? { reasoningMode: mode } : {},
1613
+ ...budget !== void 0 ? { reasoningBudgetTokens: budget } : {}
1222
1614
  };
1223
1615
  }
1224
1616
  function wrapStreamForUsageTap(source, finalize, ctx) {