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