@usagetap/sdk 1.4.0 → 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 (52) hide show
  1. package/README.md +156 -18
  2. package/dist/adapters/anthropic.cjs +195 -235
  3. package/dist/adapters/anthropic.cjs.map +1 -1
  4. package/dist/adapters/anthropic.d.cts +1 -1
  5. package/dist/adapters/anthropic.d.ts +1 -1
  6. package/dist/adapters/anthropic.mjs +195 -235
  7. package/dist/adapters/anthropic.mjs.map +1 -1
  8. package/dist/adapters/openai.cjs +256 -274
  9. package/dist/adapters/openai.cjs.map +1 -1
  10. package/dist/adapters/openai.d.cts +1 -1
  11. package/dist/adapters/openai.d.ts +1 -1
  12. package/dist/adapters/openai.mjs +256 -274
  13. package/dist/adapters/openai.mjs.map +1 -1
  14. package/dist/adapters/openrouter.cjs +256 -274
  15. package/dist/adapters/openrouter.cjs.map +1 -1
  16. package/dist/adapters/openrouter.d.cts +1 -1
  17. package/dist/adapters/openrouter.d.ts +1 -1
  18. package/dist/adapters/openrouter.mjs +256 -274
  19. package/dist/adapters/openrouter.mjs.map +1 -1
  20. package/dist/anthropic/index.cjs +195 -235
  21. package/dist/anthropic/index.cjs.map +1 -1
  22. package/dist/anthropic/index.d.cts +1 -1
  23. package/dist/anthropic/index.d.ts +1 -1
  24. package/dist/anthropic/index.mjs +195 -235
  25. package/dist/anthropic/index.mjs.map +1 -1
  26. package/dist/{client-CExQ8e1T.d.cts → client-C0UiaqVB.d.cts} +141 -61
  27. package/dist/{client-CExQ8e1T.d.ts → client-C0UiaqVB.d.ts} +141 -61
  28. package/dist/express/index.cjs +189 -246
  29. package/dist/express/index.cjs.map +1 -1
  30. package/dist/express/index.d.cts +1 -1
  31. package/dist/express/index.d.ts +1 -1
  32. package/dist/express/index.mjs +189 -246
  33. package/dist/express/index.mjs.map +1 -1
  34. package/dist/index.cjs +391 -175
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +13 -4
  37. package/dist/index.d.ts +13 -4
  38. package/dist/index.mjs +391 -175
  39. package/dist/index.mjs.map +1 -1
  40. package/dist/openai/index.cjs +256 -274
  41. package/dist/openai/index.cjs.map +1 -1
  42. package/dist/openai/index.d.cts +1 -1
  43. package/dist/openai/index.d.ts +1 -1
  44. package/dist/openai/index.mjs +256 -274
  45. package/dist/openai/index.mjs.map +1 -1
  46. package/dist/openrouter/index.cjs +256 -274
  47. package/dist/openrouter/index.cjs.map +1 -1
  48. package/dist/openrouter/index.d.cts +1 -1
  49. package/dist/openrouter/index.d.ts +1 -1
  50. package/dist/openrouter/index.mjs +256 -274
  51. package/dist/openrouter/index.mjs.map +1 -1
  52. package/package.json +2 -2
@@ -1021,6 +1021,8 @@ var SummarizationResource = class {
1021
1021
  };
1022
1022
  var GatewayResource = class {
1023
1023
  chat;
1024
+ /** Buffered OpenAI Responses-compatible requests. */
1025
+ responses;
1024
1026
  models;
1025
1027
  batches;
1026
1028
  transport;
@@ -1042,6 +1044,15 @@ var GatewayResource = class {
1042
1044
  })
1043
1045
  }
1044
1046
  };
1047
+ this.responses = {
1048
+ create: (params, options) => this.transport.request({
1049
+ method: "POST",
1050
+ path: "/v1/responses",
1051
+ body: params,
1052
+ options,
1053
+ response: "json"
1054
+ })
1055
+ };
1045
1056
  this.models = {
1046
1057
  list: (options) => this.transport.request({
1047
1058
  method: "GET",
@@ -1127,7 +1138,7 @@ var USER_AGENT = "UsageTapClient";
1127
1138
  var CANONICAL_MEDIA_TYPE2 = "application/vnd.usagetap.v1+json";
1128
1139
  var DEFAULT_BASE_URL = "https://api.usagetap.com";
1129
1140
  var DEFAULT_RUN_INACTIVITY_MS = 60 * 60 * 1e3;
1130
- var SDK_VERSION = "1.4.0" ;
1141
+ var SDK_VERSION = "1.7.0" ;
1131
1142
  var HAS_WINDOW = typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
1132
1143
  var UsageTapClient = class {
1133
1144
  /** OpenAI-compatible chat, model, and native batch operations. */
@@ -1529,9 +1540,18 @@ var UsageTapClient = class {
1529
1540
  responseTokens: request.responseTokens,
1530
1541
  cachedInputTokens: request.cachedInputTokens,
1531
1542
  cacheWriteInputTokens: request.cacheWriteInputTokens,
1543
+ cacheWrite5mInputTokens: request.cacheWrite5mInputTokens,
1544
+ cacheWrite1hInputTokens: request.cacheWrite1hInputTokens,
1532
1545
  reasoningTokens: request.reasoningTokens,
1533
1546
  searches: request.searches,
1534
1547
  audioSeconds: request.audioSeconds,
1548
+ imageInputCount: request.imageInputCount,
1549
+ imageInputTokens: request.imageInputTokens,
1550
+ imageOutputCount: request.imageOutputCount,
1551
+ imageOutputTokens: request.imageOutputTokens,
1552
+ audioInputTokens: request.audioInputTokens,
1553
+ cachedAudioInputTokens: request.cachedAudioInputTokens,
1554
+ audioOutputTokens: request.audioOutputTokens,
1535
1555
  costUsd: response.data.costUSD
1536
1556
  },
1537
1557
  correlationId: response.correlationId
@@ -1618,10 +1638,10 @@ var UsageTapClient = class {
1618
1638
  "incrementCustomMeter requires meterSlot"
1619
1639
  );
1620
1640
  }
1621
- if (!["CUSTOM1", "CUSTOM2"].includes(request.meterSlot)) {
1641
+ if (!["CUSTOM1", "CUSTOM2", "AGENTIC_API"].includes(request.meterSlot)) {
1622
1642
  throw new UsageTapError(
1623
1643
  "USAGETAP_BAD_REQUEST",
1624
- "meterSlot must be CUSTOM1 or CUSTOM2"
1644
+ "meterSlot must be CUSTOM1, CUSTOM2 or AGENTIC_API"
1625
1645
  );
1626
1646
  }
1627
1647
  if (typeof request.amount !== "number" || !Number.isFinite(request.amount) || request.amount <= 0) {
@@ -1694,6 +1714,28 @@ var UsageTapClient = class {
1694
1714
  let handlerResult;
1695
1715
  let handlerError;
1696
1716
  let endCallError;
1717
+ let finalizationDeferred = false;
1718
+ let finalizationPromise;
1719
+ const finalize = () => {
1720
+ if (!finalizationPromise) {
1721
+ finalizationPromise = this.endCall(
1722
+ {
1723
+ callId: beginResponse.data.callId,
1724
+ // Pass context for metric tracking
1725
+ customerId: beginRequest.customerId,
1726
+ feature: beginRequest.feature ?? this.defaultFeature,
1727
+ tags: beginRequest.tags ?? this.defaultTags,
1728
+ ...usage,
1729
+ error: errorPayload
1730
+ },
1731
+ {
1732
+ ...options,
1733
+ correlationId: beginResponse.correlationId
1734
+ }
1735
+ ).then(() => void 0);
1736
+ }
1737
+ return finalizationPromise;
1738
+ };
1697
1739
  const context = {
1698
1740
  begin: beginResponse,
1699
1741
  setUsage: (u) => {
@@ -1701,6 +1743,10 @@ var UsageTapClient = class {
1701
1743
  },
1702
1744
  setError: (err) => {
1703
1745
  errorPayload = err;
1746
+ },
1747
+ deferFinalization: () => {
1748
+ finalizationDeferred = true;
1749
+ return finalize;
1704
1750
  }
1705
1751
  };
1706
1752
  try {
@@ -1714,24 +1760,12 @@ var UsageTapClient = class {
1714
1760
  };
1715
1761
  }
1716
1762
  } finally {
1717
- try {
1718
- await this.endCall(
1719
- {
1720
- callId: beginResponse.data.callId,
1721
- // Pass context for metric tracking
1722
- customerId: beginRequest.customerId,
1723
- feature: beginRequest.feature ?? this.defaultFeature,
1724
- tags: beginRequest.tags ?? this.defaultTags,
1725
- ...usage,
1726
- error: errorPayload
1727
- },
1728
- {
1729
- ...options,
1730
- correlationId: beginResponse.correlationId
1731
- }
1732
- );
1733
- } catch (error) {
1734
- endCallError = error;
1763
+ if (handlerError || !finalizationDeferred) {
1764
+ try {
1765
+ await finalize();
1766
+ } catch (error) {
1767
+ endCallError = error;
1768
+ }
1735
1769
  }
1736
1770
  }
1737
1771
  if (handlerError) {
@@ -2073,7 +2107,8 @@ var UsageTapClient = class {
2073
2107
  }
2074
2108
  toHttpError(status, payload, correlationId) {
2075
2109
  const code = mapStatusToErrorCode(status);
2076
- const retryable = isRetryableStatus(status);
2110
+ const apiCode = payload?.error?.code ?? payload?.result?.code ?? "UNKNOWN";
2111
+ const retryable = isRetryableStatus(status) || isRetryableApiCode(apiCode);
2077
2112
  const message = payload?.error?.message ?? payload?.result?.message ?? `UsageTap responded with HTTP ${status}`;
2078
2113
  return new UsageTapError(code, message, {
2079
2114
  status,
@@ -2106,7 +2141,7 @@ function isRetryableStatus(status) {
2106
2141
  }
2107
2142
  function isRetryableApiCode(code) {
2108
2143
  const normalized = code.toUpperCase();
2109
- return normalized.includes("TRANSIENT") || normalized.includes("RETRY") || normalized.includes("TIMEOUT") || normalized.includes("THROTTLE") || normalized.includes("RATE_LIMIT");
2144
+ return normalized === "PAYG_CONFLICT" || normalized.includes("TRANSIENT") || normalized.includes("RETRY") || normalized.includes("TIMEOUT") || normalized.includes("THROTTLE") || normalized.includes("RATE_LIMIT");
2110
2145
  }
2111
2146
  function mapApiCodeToError(code) {
2112
2147
  const normalized = code.toUpperCase();
@@ -2246,25 +2281,29 @@ function createOpenAIAdapter(init) {
2246
2281
  const result = await usageTap.withUsage(
2247
2282
  params.begin,
2248
2283
  async (ctx) => {
2284
+ const settle = deferUsageFinalization(ctx);
2249
2285
  ctx.setUsage({ providerUsed: provider });
2250
2286
  const { stream, onComplete } = await params.call(client, {
2251
2287
  hints: ctx.begin.data.vendorHints,
2252
2288
  begin: ctx.begin
2253
2289
  });
2254
- const wrapped = wrapStreamForUsageTap(stream, async () => {
2255
- if (!onComplete) return;
2290
+ const wrapped = wrapStreamForUsageTap(stream, async (termination) => {
2256
2291
  try {
2257
- const maybeUsage = await onComplete();
2258
- if (maybeUsage) {
2259
- ctx.setUsage(maybeUsage);
2292
+ if (termination === "complete" && onComplete) {
2293
+ const maybeUsage = await onComplete();
2294
+ if (maybeUsage) {
2295
+ ctx.setUsage(maybeUsage);
2296
+ }
2260
2297
  }
2261
2298
  } catch (error) {
2262
2299
  ctx.setError({
2263
2300
  code: "USAGE_FINALIZE_ERROR",
2264
2301
  message: error instanceof Error ? error.message : String(error)
2265
2302
  });
2303
+ await settle();
2266
2304
  throw error;
2267
2305
  }
2306
+ await settle();
2268
2307
  }, ctx);
2269
2308
  const finalize = async () => {
2270
2309
  await wrapped.__usageTapFinalize?.();
@@ -2665,13 +2704,13 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
2665
2704
  withUsage,
2666
2705
  promptCompression
2667
2706
  } = splitUsageOptions(options);
2668
- const beginRequest = withRuntimeCompressionContext(
2707
+ const beginRequest = responsesBeginRequest(
2669
2708
  resolveBeginRequest(defaultContext, usageContext),
2670
- "openai",
2671
- String(params.model)
2709
+ params
2672
2710
  );
2673
2711
  const wantsStream = isStreamingRequest(params);
2674
2712
  return usageTap.withUsage(beginRequest, async (ctx) => {
2713
+ const settle = wantsStream ? deferUsageFinalization(ctx) : void 0;
2675
2714
  const sampleStartedAt = Date.now();
2676
2715
  const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredOpenAISampleDecision({
2677
2716
  usageTap,
@@ -2696,12 +2735,31 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
2696
2735
  const apiPromise2 = originalCreate(finalParams, request);
2697
2736
  const wrappedPromise2 = transformApiPromise(apiPromise2, (rawStream) => {
2698
2737
  ensureAsyncIterable(rawStream, "responses.create");
2699
- const wrappedStream = wrapStreamForUsageTap(rawStream, async () => {
2700
- const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
2701
- if (usage) {
2702
- ctx.setUsage(usage);
2738
+ const wrappedStream = wrapStreamForUsageTap(rawStream, async (termination) => {
2739
+ try {
2740
+ if (termination === "complete") {
2741
+ const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
2742
+ if (usage) {
2743
+ ctx.setUsage(usage);
2744
+ }
2745
+ }
2746
+ } catch (error) {
2747
+ ctx.setError({
2748
+ code: "USAGE_FINALIZE_ERROR",
2749
+ message: error instanceof Error ? error.message : String(error)
2750
+ });
2751
+ throw error;
2752
+ } finally {
2753
+ await settle?.();
2703
2754
  }
2704
- }, ctx);
2755
+ }, ctx, (chunk) => {
2756
+ tryInferUsageFromStreamChunk(
2757
+ chunk,
2758
+ ctx.begin.data.vendorHints,
2759
+ ctx,
2760
+ provider
2761
+ );
2762
+ });
2705
2763
  return wrappedStream;
2706
2764
  });
2707
2765
  return wrappedPromise2;
@@ -2757,13 +2815,10 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
2757
2815
  withUsage,
2758
2816
  promptCompression
2759
2817
  } = splitUsageOptions(options);
2760
- const beginRequest = withRuntimeCompressionContext(
2761
- resolveBeginRequest(defaultContext, usageContext),
2762
- "openai",
2763
- String(params.model)
2764
- );
2818
+ const beginRequest = resolveBeginRequest(defaultContext, usageContext);
2765
2819
  const wantsStream = isStreamingRequest(params);
2766
2820
  return usageTap.withUsage(beginRequest, async (ctx) => {
2821
+ const settle = wantsStream ? deferUsageFinalization(ctx) : void 0;
2767
2822
  const sampleStartedAt = Date.now();
2768
2823
  const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredOpenAISampleDecision({
2769
2824
  usageTap,
@@ -2772,7 +2827,7 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
2772
2827
  input: params
2773
2828
  });
2774
2829
  const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
2775
- const finalParams = await compressChatParamsForCall({
2830
+ const compressedParams = await compressChatParamsForCall({
2776
2831
  params: hintedParams,
2777
2832
  usageTap,
2778
2833
  ctx,
@@ -2782,18 +2837,38 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
2782
2837
  withUsage,
2783
2838
  operation: "chat.completions.create"
2784
2839
  });
2840
+ const finalParams = wantsStream ? ensureOpenAIStreamUsage(compressedParams) : compressedParams;
2785
2841
  ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
2786
2842
  const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
2787
2843
  if (wantsStream) {
2788
2844
  const apiPromise2 = originalCreate(finalParams, request);
2789
2845
  const wrappedPromise2 = transformApiPromise(apiPromise2, (rawStream) => {
2790
2846
  ensureAsyncIterable(rawStream, "chat.completions.create");
2791
- const wrappedStream2 = wrapStreamForUsageTap(rawStream, async () => {
2792
- const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
2793
- if (usage) {
2794
- ctx.setUsage(usage);
2847
+ const wrappedStream2 = wrapStreamForUsageTap(rawStream, async (termination) => {
2848
+ try {
2849
+ if (termination === "complete") {
2850
+ const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
2851
+ if (usage) {
2852
+ ctx.setUsage(usage);
2853
+ }
2854
+ }
2855
+ } catch (error) {
2856
+ ctx.setError({
2857
+ code: "USAGE_FINALIZE_ERROR",
2858
+ message: error instanceof Error ? error.message : String(error)
2859
+ });
2860
+ throw error;
2861
+ } finally {
2862
+ await settle?.();
2795
2863
  }
2796
- }, ctx);
2864
+ }, ctx, (chunk) => {
2865
+ tryInferUsageFromStreamChunk(
2866
+ chunk,
2867
+ ctx.begin.data.vendorHints,
2868
+ ctx,
2869
+ provider
2870
+ );
2871
+ });
2797
2872
  return wrappedStream2;
2798
2873
  });
2799
2874
  return wrappedPromise2;
@@ -2835,14 +2910,11 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
2835
2910
  withUsage,
2836
2911
  promptCompression
2837
2912
  } = splitUsageOptions(options);
2838
- const beginRequest = withRuntimeCompressionContext(
2839
- resolveBeginRequest(defaultContext, usageContext),
2840
- "openai",
2841
- String(params.model)
2842
- );
2913
+ const beginRequest = resolveBeginRequest(defaultContext, usageContext);
2843
2914
  return usageTap.withUsage(beginRequest, async (ctx) => {
2915
+ const settle = deferUsageFinalization(ctx);
2844
2916
  const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
2845
- const finalParams = await compressChatParamsForCall({
2917
+ const compressedParams = await compressChatParamsForCall({
2846
2918
  params: hintedParams,
2847
2919
  usageTap,
2848
2920
  ctx,
@@ -2852,21 +2924,41 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
2852
2924
  withUsage,
2853
2925
  operation: "chat.completions.stream"
2854
2926
  });
2927
+ const finalParams = ensureOpenAIStreamUsage(compressedParams);
2855
2928
  ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
2856
2929
  const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
2857
2930
  const apiPromise = originalStream(finalParams, request);
2858
2931
  const wrappedPromise = transformApiPromise(apiPromise, (rawStream) => {
2859
2932
  ensureAsyncIterable(rawStream, "chat.completions.stream");
2860
- const wrappedStreamInner = wrapStreamForUsageTap(rawStream, async () => {
2861
- const usage = await extractUsageFromStream(
2862
- rawStream,
2933
+ const wrappedStreamInner = wrapStreamForUsageTap(rawStream, async (termination) => {
2934
+ try {
2935
+ if (termination === "complete") {
2936
+ const usage = await extractUsageFromStream(
2937
+ rawStream,
2938
+ ctx.begin.data.vendorHints,
2939
+ provider
2940
+ );
2941
+ if (usage) {
2942
+ ctx.setUsage(usage);
2943
+ }
2944
+ }
2945
+ } catch (error) {
2946
+ ctx.setError({
2947
+ code: "USAGE_FINALIZE_ERROR",
2948
+ message: error instanceof Error ? error.message : String(error)
2949
+ });
2950
+ throw error;
2951
+ } finally {
2952
+ await settle();
2953
+ }
2954
+ }, ctx, (chunk) => {
2955
+ tryInferUsageFromStreamChunk(
2956
+ chunk,
2863
2957
  ctx.begin.data.vendorHints,
2958
+ ctx,
2864
2959
  provider
2865
2960
  );
2866
- if (usage) {
2867
- ctx.setUsage(usage);
2868
- }
2869
- }, ctx);
2961
+ });
2870
2962
  return wrappedStreamInner;
2871
2963
  });
2872
2964
  return wrappedPromise;
@@ -2886,70 +2978,19 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
2886
2978
  return new Proxy(resource, handler);
2887
2979
  }
2888
2980
  async function compressChatParamsForCall(args) {
2889
- let compression = resolveEffectivePromptCompressionOptions(
2981
+ const compression = resolveEffectivePromptCompressionOptions(
2890
2982
  args.defaultPromptCompression,
2891
2983
  args.callPromptCompression
2892
2984
  );
2893
- const runtimePolicy = args.ctx.begin.data.runtimeCompressionPolicy;
2894
- const policyDriven = !compression && Boolean(runtimePolicy);
2895
- if (policyDriven && runtimePolicy) {
2896
- if (!runtimePolicy.selected) {
2897
- args.ctx.setUsage({
2898
- runtimeCompression: bypassedRuntimeMeasurement(runtimePolicy)
2899
- });
2900
- return args.params;
2901
- }
2902
- compression = openAIPolicyCompressionOptions(runtimePolicy);
2903
- if (runtimePolicy.mode === "SHADOW") {
2904
- const startedAt2 = Date.now();
2905
- void compressChatParams(
2906
- args.params,
2907
- args.usageTap,
2908
- compression,
2909
- args.withUsage?.signal
2910
- ).then((outcome2) => {
2911
- args.ctx.setUsage({
2912
- runtimeCompression: openAIRuntimeMeasurement({
2913
- policy: runtimePolicy,
2914
- telemetry: buildPromptCompressionTelemetry(outcome2.segments),
2915
- latencyMs: Date.now() - startedAt2,
2916
- shadow: true,
2917
- originalPayload: args.params,
2918
- resultPayload: outcome2.params
2919
- })
2920
- });
2921
- }).catch(() => {
2922
- args.ctx.setUsage({
2923
- runtimeCompression: failedRuntimeMeasurement(
2924
- runtimePolicy,
2925
- Date.now() - startedAt2
2926
- )
2927
- });
2928
- });
2929
- return args.params;
2930
- }
2931
- }
2932
2985
  if (!compression) {
2933
2986
  return args.params;
2934
2987
  }
2935
- const startedAt = Date.now();
2936
2988
  const outcome = await compressChatParams(
2937
2989
  args.params,
2938
2990
  args.usageTap,
2939
2991
  compression,
2940
2992
  args.withUsage?.signal
2941
2993
  );
2942
- if (policyDriven && runtimePolicy) {
2943
- const runtimeMeasurement = openAIRuntimeMeasurement({
2944
- policy: runtimePolicy,
2945
- telemetry: buildPromptCompressionTelemetry(outcome.segments),
2946
- latencyMs: Date.now() - startedAt,
2947
- originalPayload: args.params,
2948
- resultPayload: outcome.params
2949
- });
2950
- args.ctx.setUsage({ runtimeCompression: runtimeMeasurement });
2951
- if (runtimeMeasurement.decision !== "compressed") return args.params;
2952
- }
2953
2994
  await recordCompressionOutcome({
2954
2995
  outcome,
2955
2996
  compression,
@@ -2962,70 +3003,19 @@ async function compressChatParamsForCall(args) {
2962
3003
  return outcome.params;
2963
3004
  }
2964
3005
  async function compressResponsesParamsForCall(args) {
2965
- let compression = resolveEffectivePromptCompressionOptions(
3006
+ const compression = resolveEffectivePromptCompressionOptions(
2966
3007
  args.defaultPromptCompression,
2967
3008
  args.callPromptCompression
2968
3009
  );
2969
- const runtimePolicy = args.ctx.begin.data.runtimeCompressionPolicy;
2970
- const policyDriven = !compression && Boolean(runtimePolicy);
2971
- if (policyDriven && runtimePolicy) {
2972
- if (!runtimePolicy.selected) {
2973
- args.ctx.setUsage({
2974
- runtimeCompression: bypassedRuntimeMeasurement(runtimePolicy)
2975
- });
2976
- return args.params;
2977
- }
2978
- compression = openAIPolicyCompressionOptions(runtimePolicy);
2979
- if (runtimePolicy.mode === "SHADOW") {
2980
- const startedAt2 = Date.now();
2981
- void compressResponsesParams(
2982
- args.params,
2983
- args.usageTap,
2984
- compression,
2985
- args.withUsage?.signal
2986
- ).then((outcome2) => {
2987
- args.ctx.setUsage({
2988
- runtimeCompression: openAIRuntimeMeasurement({
2989
- policy: runtimePolicy,
2990
- telemetry: buildPromptCompressionTelemetry(outcome2.segments),
2991
- latencyMs: Date.now() - startedAt2,
2992
- shadow: true,
2993
- originalPayload: args.params,
2994
- resultPayload: outcome2.params
2995
- })
2996
- });
2997
- }).catch(() => {
2998
- args.ctx.setUsage({
2999
- runtimeCompression: failedRuntimeMeasurement(
3000
- runtimePolicy,
3001
- Date.now() - startedAt2
3002
- )
3003
- });
3004
- });
3005
- return args.params;
3006
- }
3007
- }
3008
3010
  if (!compression) {
3009
3011
  return args.params;
3010
3012
  }
3011
- const startedAt = Date.now();
3012
3013
  const outcome = await compressResponsesParams(
3013
3014
  args.params,
3014
3015
  args.usageTap,
3015
3016
  compression,
3016
3017
  args.withUsage?.signal
3017
3018
  );
3018
- if (policyDriven && runtimePolicy) {
3019
- const runtimeMeasurement = openAIRuntimeMeasurement({
3020
- policy: runtimePolicy,
3021
- telemetry: buildPromptCompressionTelemetry(outcome.segments),
3022
- latencyMs: Date.now() - startedAt,
3023
- originalPayload: args.params,
3024
- resultPayload: outcome.params
3025
- });
3026
- args.ctx.setUsage({ runtimeCompression: runtimeMeasurement });
3027
- if (runtimeMeasurement.decision !== "compressed") return args.params;
3028
- }
3029
3019
  await recordCompressionOutcome({
3030
3020
  outcome,
3031
3021
  compression,
@@ -3037,79 +3027,6 @@ async function compressResponsesParamsForCall(args) {
3037
3027
  });
3038
3028
  return outcome.params;
3039
3029
  }
3040
- function openAIPolicyCompressionOptions(policy) {
3041
- const roles = Object.fromEntries(
3042
- ["system", "user", "tool", "assistant"].filter((role) => policy.snapshot.scope.messageRoles.includes(role)).map((role) => [role, true])
3043
- );
3044
- return {
3045
- provider: "usagetap",
3046
- roles,
3047
- minContextTokens: policy.snapshot.scope.minimumTokens,
3048
- failOpen: true
3049
- };
3050
- }
3051
- function bypassedRuntimeMeasurement(policy) {
3052
- return {
3053
- policyId: policy.policyId,
3054
- policyVersionId: policy.policyVersionId,
3055
- policyVersion: policy.version,
3056
- rolloutMode: policy.mode,
3057
- decision: "bypassed",
3058
- reasonCode: "not_in_rollout_sample",
3059
- methodsAttempted: [],
3060
- methodsApplied: [],
3061
- originalTokens: 0,
3062
- resultTokens: 0,
3063
- savedTokens: 0,
3064
- reductionPercentage: 0,
3065
- compressionLatencyMs: 0,
3066
- compressionComputeCostUsd: 0,
3067
- financialsEstimated: true
3068
- };
3069
- }
3070
- function failedRuntimeMeasurement(policy, latencyMs) {
3071
- return {
3072
- ...bypassedRuntimeMeasurement(policy),
3073
- decision: "fallback",
3074
- reasonCode: "transformation_failed",
3075
- methodsAttempted: ["deterministic"],
3076
- compressionLatencyMs: latencyMs
3077
- };
3078
- }
3079
- function openAIRuntimeMeasurement(input) {
3080
- const telemetry = input.telemetry;
3081
- const originalTokens = telemetry?.originalTokens ?? estimatePromptTokens(JSON.stringify(input.originalPayload));
3082
- const resultTokens = telemetry?.compressedTokens ?? originalTokens;
3083
- const savedTokens = Math.max(0, telemetry?.savedTokens ?? 0);
3084
- const reductionPercentage = originalTokens > 0 ? savedTokens / originalTokens * 100 : 0;
3085
- const belowMinimumSize = originalTokens < input.policy.snapshot.scope.minimumTokens;
3086
- const latencyExceeded = input.latencyMs > input.policy.snapshot.gates.maximumLatencyMs;
3087
- const belowSavings = savedTokens < input.policy.snapshot.gates.minimumTokensRemoved || reductionPercentage < input.policy.snapshot.gates.minimumReductionPercentage;
3088
- const accepted = !belowMinimumSize && !latencyExceeded && !belowSavings;
3089
- const deepText = input.policy.snapshot.methods.deepText.enabled && !input.policy.snapshot.rollout.deterministicOnly && (input.policy.mode !== "CANARY" || input.policy.snapshot.rollout.deepTextCanaryAllowed);
3090
- const reasonCode = belowMinimumSize ? "below_minimum_size" : latencyExceeded ? "latency_budget_exceeded" : belowSavings ? "below_minimum_savings" : void 0;
3091
- const measurement = {
3092
- policyId: input.policy.policyId,
3093
- policyVersionId: input.policy.policyVersionId,
3094
- policyVersion: input.policy.version,
3095
- rolloutMode: input.policy.mode,
3096
- decision: accepted ? input.shadow ? "bypassed" : "compressed" : latencyExceeded ? "fallback" : "skipped",
3097
- ...reasonCode ? { reasonCode } : {},
3098
- methodsAttempted: [
3099
- "deterministic",
3100
- ...deepText ? ["deep_text"] : []
3101
- ],
3102
- methodsApplied: accepted ? ["deterministic"] : [],
3103
- originalTokens,
3104
- resultTokens: accepted ? resultTokens : originalTokens,
3105
- savedTokens: accepted ? savedTokens : 0,
3106
- reductionPercentage: accepted ? reductionPercentage : 0,
3107
- compressionLatencyMs: input.latencyMs,
3108
- compressionComputeCostUsd: 0,
3109
- financialsEstimated: true
3110
- };
3111
- return measurement;
3112
- }
3113
3030
  async function recordCompressionOutcome(args) {
3114
3031
  const telemetry = buildPromptCompressionTelemetry(args.outcome.segments);
3115
3032
  if (!telemetry) {
@@ -3632,10 +3549,6 @@ function resolveBeginRequest(defaults, override) {
3632
3549
  }
3633
3550
  const tags = mergeTags(base.tags, current.tags);
3634
3551
  const begin = { customerId };
3635
- const runtimeCompressionContext = current.runtimeCompressionContext ?? base.runtimeCompressionContext;
3636
- if (runtimeCompressionContext) {
3637
- begin.runtimeCompressionContext = runtimeCompressionContext;
3638
- }
3639
3552
  const requested = current.requested ?? base.requested;
3640
3553
  if (requested) begin.requested = requested;
3641
3554
  const feature = current.feature ?? base.feature;
@@ -3656,8 +3569,6 @@ function resolveBeginRequest(defaults, override) {
3656
3569
  if (customerUserEmail) begin.customerUserEmail = customerUserEmail;
3657
3570
  const stripeCustomerId = current.stripeCustomerId ?? base.stripeCustomerId;
3658
3571
  if (stripeCustomerId) begin.stripeCustomerId = stripeCustomerId;
3659
- const holdUsd = current.holdUsd ?? base.holdUsd;
3660
- if (typeof holdUsd === "number") begin.holdUsd = holdUsd;
3661
3572
  const batch = current.batch ?? base.batch;
3662
3573
  if (typeof batch === "boolean") begin.batch = batch;
3663
3574
  const pricingMode = current.pricingMode ?? base.pricingMode;
@@ -3667,16 +3578,25 @@ function resolveBeginRequest(defaults, override) {
3667
3578
  }
3668
3579
  return begin;
3669
3580
  }
3670
- function withRuntimeCompressionContext(begin, provider, model) {
3581
+ function responsesBeginRequest(begin, params) {
3582
+ if (!responsesRequestUsesWebSearch(params)) return begin;
3671
3583
  return {
3672
3584
  ...begin,
3673
- runtimeCompressionContext: {
3674
- ...begin.runtimeCompressionContext ?? {},
3675
- provider,
3676
- model
3585
+ requested: {
3586
+ ...begin.requested ?? {},
3587
+ search: true
3677
3588
  }
3678
3589
  };
3679
3590
  }
3591
+ function responsesRequestUsesWebSearch(params) {
3592
+ if (!params || typeof params !== "object") return false;
3593
+ const tools = params.tools;
3594
+ return Array.isArray(tools) && tools.some((tool) => {
3595
+ if (!tool || typeof tool !== "object") return false;
3596
+ const type = tool.type;
3597
+ return type === "web_search" || type === "web_search_preview";
3598
+ });
3599
+ }
3680
3600
  function transformApiPromise(apiPromise, onResolve, onReject) {
3681
3601
  const resolvedPromise = Promise.resolve(apiPromise).then(onResolve, onReject);
3682
3602
  if (isObjectRecord(apiPromise)) {
@@ -3907,6 +3827,11 @@ function inferUsageFromResponse(response, hints, provider = "openai") {
3907
3827
  return void 0;
3908
3828
  }
3909
3829
  const cachedInputTokens = candidate.usage.prompt_tokens_details?.cached_tokens ?? candidate.usage.input_tokens_details?.cached_tokens ?? candidate.usage.cached_tokens;
3830
+ 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;
3831
+ const cacheWrite5mInputTokens = candidate.usage.cache_write_5m_input_tokens ?? candidate.usage.cache_creation?.ephemeral_5m_input_tokens;
3832
+ const cacheWrite1hInputTokens = candidate.usage.cache_write_1h_input_tokens ?? candidate.usage.cache_creation?.ephemeral_1h_input_tokens;
3833
+ const outputSearches = Array.isArray(candidate.output) ? candidate.output.filter((item) => item?.type === "web_search_call").length : 0;
3834
+ const searches = candidate.usage.searches ?? candidate.usage.web_search_queries ?? candidate.usage.server_tool_use?.web_search_requests ?? outputSearches;
3910
3835
  const responseEffort = normalizeExecutionReasoningEffort(
3911
3836
  candidate.reasoning?.effort
3912
3837
  );
@@ -3916,12 +3841,21 @@ function inferUsageFromResponse(response, hints, provider = "openai") {
3916
3841
  inputTokens: candidate.usage.prompt_tokens ?? candidate.usage.input_tokens,
3917
3842
  responseTokens: candidate.usage.completion_tokens ?? candidate.usage.output_tokens,
3918
3843
  cachedInputTokens,
3844
+ cacheWriteInputTokens,
3845
+ cacheWrite5mInputTokens,
3846
+ cacheWrite1hInputTokens,
3847
+ audioInputTokens: candidate.usage.prompt_tokens_details?.audio_tokens ?? candidate.usage.input_tokens_details?.audio_tokens,
3848
+ 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,
3849
+ imageInputTokens: candidate.usage.prompt_tokens_details?.image_tokens ?? candidate.usage.input_tokens_details?.image_tokens,
3850
+ imageOutputTokens: candidate.usage.completion_tokens_details?.image_tokens ?? candidate.usage.output_tokens_details?.image_tokens,
3851
+ audioOutputTokens: candidate.usage.completion_tokens_details?.audio_tokens ?? candidate.usage.output_tokens_details?.audio_tokens,
3919
3852
  reasoningTokens: candidate.usage.completion_tokens_details?.reasoning_tokens ?? candidate.usage.output_tokens_details?.reasoning_tokens,
3920
3853
  ...responseEffort ? {
3921
3854
  reasoningEffort: responseEffort,
3922
3855
  reasoningEffortSource: "provider_response"
3923
3856
  } : {},
3924
- ...typeof candidate.reasoning?.type === "string" ? { reasoningMode: candidate.reasoning.type } : typeof candidate.reasoning?.mode === "string" ? { reasoningMode: candidate.reasoning.mode } : {}
3857
+ ...typeof candidate.reasoning?.type === "string" ? { reasoningMode: candidate.reasoning.type } : typeof candidate.reasoning?.mode === "string" ? { reasoningMode: candidate.reasoning.mode } : {},
3858
+ ...typeof searches === "number" && searches > 0 ? { searches } : {}
3925
3859
  };
3926
3860
  }
3927
3861
  function normalizeExecutionReasoningEffort(value) {
@@ -3938,29 +3872,64 @@ function openAIRequestExecutionMetadata(params, provider) {
3938
3872
  const budget = typeof rawBudget === "number" && Number.isInteger(rawBudget) && rawBudget >= 0 ? rawBudget : void 0;
3939
3873
  return {
3940
3874
  providerUsed: provider,
3875
+ ...typeof record.model === "string" ? { modelUsed: record.model } : {},
3941
3876
  ...effort ? { reasoningEffort: effort, reasoningEffortSource: "provider_request" } : {},
3942
3877
  ...mode ? { reasoningMode: mode } : {},
3943
3878
  ...budget !== void 0 ? { reasoningBudgetTokens: budget } : {}
3944
3879
  };
3945
3880
  }
3946
- function wrapStreamForUsageTap(source, finalize, ctx) {
3881
+ function tryInferUsageFromStreamChunk(chunk, hints, ctx, provider) {
3882
+ const payload = isObjectRecord(chunk) && isObjectRecord(chunk.response) ? chunk.response : chunk;
3883
+ const inferred = inferUsageFromResponse(payload, hints, provider);
3884
+ if (inferred) {
3885
+ ctx.setUsage(inferred);
3886
+ }
3887
+ }
3888
+ function ensureOpenAIStreamUsage(params) {
3889
+ if (!isObjectRecord(params)) {
3890
+ return params;
3891
+ }
3892
+ const streamOptions = isObjectRecord(params.stream_options) ? params.stream_options : {};
3893
+ return {
3894
+ ...params,
3895
+ stream_options: {
3896
+ ...streamOptions,
3897
+ include_usage: true
3898
+ }
3899
+ };
3900
+ }
3901
+ function deferUsageFinalization(ctx) {
3902
+ return ctx.deferFinalization?.() ?? (() => Promise.resolve());
3903
+ }
3904
+ function wrapStreamForUsageTap(source, finalize, ctx, onChunk) {
3947
3905
  const getIterator = source[Symbol.asyncIterator];
3948
3906
  if (typeof getIterator !== "function") {
3949
3907
  throw new TypeError("Stream is not async iterable");
3950
3908
  }
3951
3909
  const iterator = getIterator.call(source);
3952
3910
  let completed = false;
3953
- const invokeFinalize = async () => {
3911
+ const invokeFinalize = async (termination, error) => {
3954
3912
  if (completed) return;
3955
3913
  completed = true;
3914
+ if (termination === "cancel" || termination === "manual") {
3915
+ ctx.setError({
3916
+ code: "STREAM_ABORTED",
3917
+ message: termination === "cancel" ? "Provider stream consumption was cancelled before completion" : "Provider stream was finalized before completion"
3918
+ });
3919
+ } else if (termination === "error") {
3920
+ ctx.setError({
3921
+ code: "VENDOR_ERROR",
3922
+ message: error instanceof Error ? error.message : String(error)
3923
+ });
3924
+ }
3956
3925
  try {
3957
- await finalize();
3958
- } catch (error) {
3926
+ await finalize(termination);
3927
+ } catch (error2) {
3959
3928
  ctx.setError({
3960
3929
  code: "USAGE_FINALIZE_ERROR",
3961
- message: error instanceof Error ? error.message : String(error)
3930
+ message: error2 instanceof Error ? error2.message : String(error2)
3962
3931
  });
3963
- throw error;
3932
+ throw error2;
3964
3933
  }
3965
3934
  };
3966
3935
  const prototype = Object.getPrototypeOf(source) ?? Object.prototype;
@@ -3984,12 +3953,15 @@ function wrapStreamForUsageTap(source, finalize, ctx) {
3984
3953
  value: async (...args) => {
3985
3954
  try {
3986
3955
  const result = await iterator.next(...args);
3956
+ if (!result.done) {
3957
+ onChunk?.(result.value);
3958
+ }
3987
3959
  if (result.done) {
3988
- await invokeFinalize();
3960
+ await invokeFinalize("complete");
3989
3961
  }
3990
3962
  return result;
3991
3963
  } catch (error) {
3992
- await invokeFinalize().catch(() => void 0);
3964
+ await invokeFinalize("error", error).catch(() => void 0);
3993
3965
  throw error;
3994
3966
  }
3995
3967
  },
@@ -3998,39 +3970,49 @@ function wrapStreamForUsageTap(source, finalize, ctx) {
3998
3970
  });
3999
3971
  Object.defineProperty(wrapped, "return", {
4000
3972
  value: async (value) => {
4001
- if (typeof iterator.return === "function") {
4002
- const rawResult = await iterator.return(value);
4003
- if (!isIteratorResult(rawResult)) {
4004
- throw new TypeError("Iterator.return() returned an invalid result");
3973
+ try {
3974
+ if (typeof iterator.return === "function") {
3975
+ const rawResult = await iterator.return(value);
3976
+ if (!isIteratorResult(rawResult)) {
3977
+ throw new TypeError("Iterator.return() returned an invalid result");
3978
+ }
3979
+ await invokeFinalize("cancel");
3980
+ return rawResult;
4005
3981
  }
4006
- await invokeFinalize();
4007
- return rawResult;
3982
+ await invokeFinalize("cancel");
3983
+ return { done: true, value };
3984
+ } catch (error) {
3985
+ await invokeFinalize("error", error).catch(() => void 0);
3986
+ throw error;
4008
3987
  }
4009
- await invokeFinalize();
4010
- return { done: true, value };
4011
3988
  },
4012
3989
  configurable: true,
4013
3990
  writable: true
4014
3991
  });
4015
3992
  Object.defineProperty(wrapped, "throw", {
4016
3993
  value: async (error) => {
4017
- if (typeof iterator.throw === "function") {
4018
- const rawResult = await iterator.throw(error);
4019
- if (!isIteratorResult(rawResult)) {
4020
- throw new TypeError("Iterator.throw() returned an invalid result");
3994
+ try {
3995
+ if (typeof iterator.throw === "function") {
3996
+ const rawResult = await iterator.throw(error);
3997
+ if (!isIteratorResult(rawResult)) {
3998
+ throw new TypeError("Iterator.throw() returned an invalid result");
3999
+ }
4000
+ await invokeFinalize("error", error);
4001
+ return rawResult;
4021
4002
  }
4022
- await invokeFinalize();
4023
- return rawResult;
4003
+ await invokeFinalize("error", error);
4004
+ throw error;
4005
+ } catch (thrownError) {
4006
+ await invokeFinalize("error", thrownError).catch(() => void 0);
4007
+ throw thrownError;
4024
4008
  }
4025
- await invokeFinalize();
4026
- throw error;
4027
4009
  },
4028
4010
  configurable: true,
4029
4011
  writable: true
4030
4012
  });
4031
4013
  Object.defineProperty(wrapped, "__usageTapFinalize", {
4032
4014
  value: async () => {
4033
- await invokeFinalize();
4015
+ await invokeFinalize("manual");
4034
4016
  },
4035
4017
  configurable: true
4036
4018
  });