@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
@@ -1024,6 +1024,8 @@ var SummarizationResource = class {
1024
1024
  };
1025
1025
  var GatewayResource = class {
1026
1026
  chat;
1027
+ /** Buffered OpenAI Responses-compatible requests. */
1028
+ responses;
1027
1029
  models;
1028
1030
  batches;
1029
1031
  transport;
@@ -1045,6 +1047,15 @@ var GatewayResource = class {
1045
1047
  })
1046
1048
  }
1047
1049
  };
1050
+ this.responses = {
1051
+ create: (params, options) => this.transport.request({
1052
+ method: "POST",
1053
+ path: "/v1/responses",
1054
+ body: params,
1055
+ options,
1056
+ response: "json"
1057
+ })
1058
+ };
1048
1059
  this.models = {
1049
1060
  list: (options) => this.transport.request({
1050
1061
  method: "GET",
@@ -1130,7 +1141,7 @@ var USER_AGENT = "UsageTapClient";
1130
1141
  var CANONICAL_MEDIA_TYPE2 = "application/vnd.usagetap.v1+json";
1131
1142
  var DEFAULT_BASE_URL = "https://api.usagetap.com";
1132
1143
  var DEFAULT_RUN_INACTIVITY_MS = 60 * 60 * 1e3;
1133
- var SDK_VERSION = "1.4.0" ;
1144
+ var SDK_VERSION = "1.7.0" ;
1134
1145
  var HAS_WINDOW = typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
1135
1146
  var UsageTapClient = class {
1136
1147
  /** OpenAI-compatible chat, model, and native batch operations. */
@@ -1532,9 +1543,18 @@ var UsageTapClient = class {
1532
1543
  responseTokens: request.responseTokens,
1533
1544
  cachedInputTokens: request.cachedInputTokens,
1534
1545
  cacheWriteInputTokens: request.cacheWriteInputTokens,
1546
+ cacheWrite5mInputTokens: request.cacheWrite5mInputTokens,
1547
+ cacheWrite1hInputTokens: request.cacheWrite1hInputTokens,
1535
1548
  reasoningTokens: request.reasoningTokens,
1536
1549
  searches: request.searches,
1537
1550
  audioSeconds: request.audioSeconds,
1551
+ imageInputCount: request.imageInputCount,
1552
+ imageInputTokens: request.imageInputTokens,
1553
+ imageOutputCount: request.imageOutputCount,
1554
+ imageOutputTokens: request.imageOutputTokens,
1555
+ audioInputTokens: request.audioInputTokens,
1556
+ cachedAudioInputTokens: request.cachedAudioInputTokens,
1557
+ audioOutputTokens: request.audioOutputTokens,
1538
1558
  costUsd: response.data.costUSD
1539
1559
  },
1540
1560
  correlationId: response.correlationId
@@ -1621,10 +1641,10 @@ var UsageTapClient = class {
1621
1641
  "incrementCustomMeter requires meterSlot"
1622
1642
  );
1623
1643
  }
1624
- if (!["CUSTOM1", "CUSTOM2"].includes(request.meterSlot)) {
1644
+ if (!["CUSTOM1", "CUSTOM2", "AGENTIC_API"].includes(request.meterSlot)) {
1625
1645
  throw new UsageTapError(
1626
1646
  "USAGETAP_BAD_REQUEST",
1627
- "meterSlot must be CUSTOM1 or CUSTOM2"
1647
+ "meterSlot must be CUSTOM1, CUSTOM2 or AGENTIC_API"
1628
1648
  );
1629
1649
  }
1630
1650
  if (typeof request.amount !== "number" || !Number.isFinite(request.amount) || request.amount <= 0) {
@@ -1697,6 +1717,28 @@ var UsageTapClient = class {
1697
1717
  let handlerResult;
1698
1718
  let handlerError;
1699
1719
  let endCallError;
1720
+ let finalizationDeferred = false;
1721
+ let finalizationPromise;
1722
+ const finalize = () => {
1723
+ if (!finalizationPromise) {
1724
+ finalizationPromise = this.endCall(
1725
+ {
1726
+ callId: beginResponse.data.callId,
1727
+ // Pass context for metric tracking
1728
+ customerId: beginRequest.customerId,
1729
+ feature: beginRequest.feature ?? this.defaultFeature,
1730
+ tags: beginRequest.tags ?? this.defaultTags,
1731
+ ...usage,
1732
+ error: errorPayload
1733
+ },
1734
+ {
1735
+ ...options,
1736
+ correlationId: beginResponse.correlationId
1737
+ }
1738
+ ).then(() => void 0);
1739
+ }
1740
+ return finalizationPromise;
1741
+ };
1700
1742
  const context = {
1701
1743
  begin: beginResponse,
1702
1744
  setUsage: (u) => {
@@ -1704,6 +1746,10 @@ var UsageTapClient = class {
1704
1746
  },
1705
1747
  setError: (err) => {
1706
1748
  errorPayload = err;
1749
+ },
1750
+ deferFinalization: () => {
1751
+ finalizationDeferred = true;
1752
+ return finalize;
1707
1753
  }
1708
1754
  };
1709
1755
  try {
@@ -1717,24 +1763,12 @@ var UsageTapClient = class {
1717
1763
  };
1718
1764
  }
1719
1765
  } finally {
1720
- try {
1721
- await 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
- );
1736
- } catch (error) {
1737
- endCallError = error;
1766
+ if (handlerError || !finalizationDeferred) {
1767
+ try {
1768
+ await finalize();
1769
+ } catch (error) {
1770
+ endCallError = error;
1771
+ }
1738
1772
  }
1739
1773
  }
1740
1774
  if (handlerError) {
@@ -2076,7 +2110,8 @@ var UsageTapClient = class {
2076
2110
  }
2077
2111
  toHttpError(status, payload, correlationId) {
2078
2112
  const code = mapStatusToErrorCode(status);
2079
- const retryable = isRetryableStatus(status);
2113
+ const apiCode = payload?.error?.code ?? payload?.result?.code ?? "UNKNOWN";
2114
+ const retryable = isRetryableStatus(status) || isRetryableApiCode(apiCode);
2080
2115
  const message = payload?.error?.message ?? payload?.result?.message ?? `UsageTap responded with HTTP ${status}`;
2081
2116
  return new UsageTapError(code, message, {
2082
2117
  status,
@@ -2109,7 +2144,7 @@ function isRetryableStatus(status) {
2109
2144
  }
2110
2145
  function isRetryableApiCode(code) {
2111
2146
  const normalized = code.toUpperCase();
2112
- return normalized.includes("TRANSIENT") || normalized.includes("RETRY") || normalized.includes("TIMEOUT") || normalized.includes("THROTTLE") || normalized.includes("RATE_LIMIT");
2147
+ return normalized === "PAYG_CONFLICT" || normalized.includes("TRANSIENT") || normalized.includes("RETRY") || normalized.includes("TIMEOUT") || normalized.includes("THROTTLE") || normalized.includes("RATE_LIMIT");
2113
2148
  }
2114
2149
  function mapApiCodeToError(code) {
2115
2150
  const normalized = code.toUpperCase();
@@ -2249,25 +2284,29 @@ function createOpenAIAdapter(init) {
2249
2284
  const result = await usageTap.withUsage(
2250
2285
  params.begin,
2251
2286
  async (ctx) => {
2287
+ const settle = deferUsageFinalization(ctx);
2252
2288
  ctx.setUsage({ providerUsed: provider });
2253
2289
  const { stream, onComplete } = await params.call(client, {
2254
2290
  hints: ctx.begin.data.vendorHints,
2255
2291
  begin: ctx.begin
2256
2292
  });
2257
- const wrapped = wrapStreamForUsageTap(stream, async () => {
2258
- if (!onComplete) return;
2293
+ const wrapped = wrapStreamForUsageTap(stream, async (termination) => {
2259
2294
  try {
2260
- const maybeUsage = await onComplete();
2261
- if (maybeUsage) {
2262
- ctx.setUsage(maybeUsage);
2295
+ if (termination === "complete" && onComplete) {
2296
+ const maybeUsage = await onComplete();
2297
+ if (maybeUsage) {
2298
+ ctx.setUsage(maybeUsage);
2299
+ }
2263
2300
  }
2264
2301
  } catch (error) {
2265
2302
  ctx.setError({
2266
2303
  code: "USAGE_FINALIZE_ERROR",
2267
2304
  message: error instanceof Error ? error.message : String(error)
2268
2305
  });
2306
+ await settle();
2269
2307
  throw error;
2270
2308
  }
2309
+ await settle();
2271
2310
  }, ctx);
2272
2311
  const finalize = async () => {
2273
2312
  await wrapped.__usageTapFinalize?.();
@@ -2755,13 +2794,13 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
2755
2794
  withUsage,
2756
2795
  promptCompression
2757
2796
  } = splitUsageOptions(options);
2758
- const beginRequest = withRuntimeCompressionContext(
2797
+ const beginRequest = responsesBeginRequest(
2759
2798
  resolveBeginRequest(defaultContext, usageContext),
2760
- "openai",
2761
- String(params.model)
2799
+ params
2762
2800
  );
2763
2801
  const wantsStream = isStreamingRequest(params);
2764
2802
  return usageTap.withUsage(beginRequest, async (ctx) => {
2803
+ const settle = wantsStream ? deferUsageFinalization(ctx) : void 0;
2765
2804
  const sampleStartedAt = Date.now();
2766
2805
  const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredOpenAISampleDecision({
2767
2806
  usageTap,
@@ -2786,12 +2825,31 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
2786
2825
  const apiPromise2 = originalCreate(finalParams, request);
2787
2826
  const wrappedPromise2 = transformApiPromise(apiPromise2, (rawStream) => {
2788
2827
  ensureAsyncIterable(rawStream, "responses.create");
2789
- const wrappedStream = wrapStreamForUsageTap(rawStream, async () => {
2790
- const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
2791
- if (usage) {
2792
- ctx.setUsage(usage);
2828
+ const wrappedStream = wrapStreamForUsageTap(rawStream, async (termination) => {
2829
+ try {
2830
+ if (termination === "complete") {
2831
+ const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
2832
+ if (usage) {
2833
+ ctx.setUsage(usage);
2834
+ }
2835
+ }
2836
+ } catch (error) {
2837
+ ctx.setError({
2838
+ code: "USAGE_FINALIZE_ERROR",
2839
+ message: error instanceof Error ? error.message : String(error)
2840
+ });
2841
+ throw error;
2842
+ } finally {
2843
+ await settle?.();
2793
2844
  }
2794
- }, ctx);
2845
+ }, ctx, (chunk) => {
2846
+ tryInferUsageFromStreamChunk(
2847
+ chunk,
2848
+ ctx.begin.data.vendorHints,
2849
+ ctx,
2850
+ provider
2851
+ );
2852
+ });
2795
2853
  return wrappedStream;
2796
2854
  });
2797
2855
  return wrappedPromise2;
@@ -2847,13 +2905,10 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
2847
2905
  withUsage,
2848
2906
  promptCompression
2849
2907
  } = splitUsageOptions(options);
2850
- const beginRequest = withRuntimeCompressionContext(
2851
- resolveBeginRequest(defaultContext, usageContext),
2852
- "openai",
2853
- String(params.model)
2854
- );
2908
+ const beginRequest = resolveBeginRequest(defaultContext, usageContext);
2855
2909
  const wantsStream = isStreamingRequest(params);
2856
2910
  return usageTap.withUsage(beginRequest, async (ctx) => {
2911
+ const settle = wantsStream ? deferUsageFinalization(ctx) : void 0;
2857
2912
  const sampleStartedAt = Date.now();
2858
2913
  const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredOpenAISampleDecision({
2859
2914
  usageTap,
@@ -2862,7 +2917,7 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
2862
2917
  input: params
2863
2918
  });
2864
2919
  const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
2865
- const finalParams = await compressChatParamsForCall({
2920
+ const compressedParams = await compressChatParamsForCall({
2866
2921
  params: hintedParams,
2867
2922
  usageTap,
2868
2923
  ctx,
@@ -2872,18 +2927,38 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
2872
2927
  withUsage,
2873
2928
  operation: "chat.completions.create"
2874
2929
  });
2930
+ const finalParams = wantsStream ? ensureOpenAIStreamUsage(compressedParams) : compressedParams;
2875
2931
  ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
2876
2932
  const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
2877
2933
  if (wantsStream) {
2878
2934
  const apiPromise2 = originalCreate(finalParams, request);
2879
2935
  const wrappedPromise2 = transformApiPromise(apiPromise2, (rawStream) => {
2880
2936
  ensureAsyncIterable(rawStream, "chat.completions.create");
2881
- const wrappedStream2 = wrapStreamForUsageTap(rawStream, async () => {
2882
- const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
2883
- if (usage) {
2884
- ctx.setUsage(usage);
2937
+ const wrappedStream2 = wrapStreamForUsageTap(rawStream, async (termination) => {
2938
+ try {
2939
+ if (termination === "complete") {
2940
+ const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
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?.();
2885
2953
  }
2886
- }, ctx);
2954
+ }, ctx, (chunk) => {
2955
+ tryInferUsageFromStreamChunk(
2956
+ chunk,
2957
+ ctx.begin.data.vendorHints,
2958
+ ctx,
2959
+ provider
2960
+ );
2961
+ });
2887
2962
  return wrappedStream2;
2888
2963
  });
2889
2964
  return wrappedPromise2;
@@ -2925,14 +3000,11 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
2925
3000
  withUsage,
2926
3001
  promptCompression
2927
3002
  } = splitUsageOptions(options);
2928
- const beginRequest = withRuntimeCompressionContext(
2929
- resolveBeginRequest(defaultContext, usageContext),
2930
- "openai",
2931
- String(params.model)
2932
- );
3003
+ const beginRequest = resolveBeginRequest(defaultContext, usageContext);
2933
3004
  return usageTap.withUsage(beginRequest, async (ctx) => {
3005
+ const settle = deferUsageFinalization(ctx);
2934
3006
  const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
2935
- const finalParams = await compressChatParamsForCall({
3007
+ const compressedParams = await compressChatParamsForCall({
2936
3008
  params: hintedParams,
2937
3009
  usageTap,
2938
3010
  ctx,
@@ -2942,21 +3014,41 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
2942
3014
  withUsage,
2943
3015
  operation: "chat.completions.stream"
2944
3016
  });
3017
+ const finalParams = ensureOpenAIStreamUsage(compressedParams);
2945
3018
  ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
2946
3019
  const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
2947
3020
  const apiPromise = originalStream(finalParams, request);
2948
3021
  const wrappedPromise = transformApiPromise(apiPromise, (rawStream) => {
2949
3022
  ensureAsyncIterable(rawStream, "chat.completions.stream");
2950
- const wrappedStreamInner = wrapStreamForUsageTap(rawStream, async () => {
2951
- const usage = await extractUsageFromStream(
2952
- rawStream,
3023
+ const wrappedStreamInner = wrapStreamForUsageTap(rawStream, async (termination) => {
3024
+ try {
3025
+ if (termination === "complete") {
3026
+ const usage = await extractUsageFromStream(
3027
+ rawStream,
3028
+ ctx.begin.data.vendorHints,
3029
+ provider
3030
+ );
3031
+ if (usage) {
3032
+ ctx.setUsage(usage);
3033
+ }
3034
+ }
3035
+ } catch (error) {
3036
+ ctx.setError({
3037
+ code: "USAGE_FINALIZE_ERROR",
3038
+ message: error instanceof Error ? error.message : String(error)
3039
+ });
3040
+ throw error;
3041
+ } finally {
3042
+ await settle();
3043
+ }
3044
+ }, ctx, (chunk) => {
3045
+ tryInferUsageFromStreamChunk(
3046
+ chunk,
2953
3047
  ctx.begin.data.vendorHints,
3048
+ ctx,
2954
3049
  provider
2955
3050
  );
2956
- if (usage) {
2957
- ctx.setUsage(usage);
2958
- }
2959
- }, ctx);
3051
+ });
2960
3052
  return wrappedStreamInner;
2961
3053
  });
2962
3054
  return wrappedPromise;
@@ -2976,70 +3068,19 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
2976
3068
  return new Proxy(resource, handler);
2977
3069
  }
2978
3070
  async function compressChatParamsForCall(args) {
2979
- let compression = resolveEffectivePromptCompressionOptions(
3071
+ const compression = resolveEffectivePromptCompressionOptions(
2980
3072
  args.defaultPromptCompression,
2981
3073
  args.callPromptCompression
2982
3074
  );
2983
- const runtimePolicy = args.ctx.begin.data.runtimeCompressionPolicy;
2984
- const policyDriven = !compression && Boolean(runtimePolicy);
2985
- if (policyDriven && runtimePolicy) {
2986
- if (!runtimePolicy.selected) {
2987
- args.ctx.setUsage({
2988
- runtimeCompression: bypassedRuntimeMeasurement(runtimePolicy)
2989
- });
2990
- return args.params;
2991
- }
2992
- compression = openAIPolicyCompressionOptions(runtimePolicy);
2993
- if (runtimePolicy.mode === "SHADOW") {
2994
- const startedAt2 = Date.now();
2995
- void compressChatParams(
2996
- args.params,
2997
- args.usageTap,
2998
- compression,
2999
- args.withUsage?.signal
3000
- ).then((outcome2) => {
3001
- args.ctx.setUsage({
3002
- runtimeCompression: openAIRuntimeMeasurement({
3003
- policy: runtimePolicy,
3004
- telemetry: buildPromptCompressionTelemetry(outcome2.segments),
3005
- latencyMs: Date.now() - startedAt2,
3006
- shadow: true,
3007
- originalPayload: args.params,
3008
- resultPayload: outcome2.params
3009
- })
3010
- });
3011
- }).catch(() => {
3012
- args.ctx.setUsage({
3013
- runtimeCompression: failedRuntimeMeasurement(
3014
- runtimePolicy,
3015
- Date.now() - startedAt2
3016
- )
3017
- });
3018
- });
3019
- return args.params;
3020
- }
3021
- }
3022
3075
  if (!compression) {
3023
3076
  return args.params;
3024
3077
  }
3025
- const startedAt = Date.now();
3026
3078
  const outcome = await compressChatParams(
3027
3079
  args.params,
3028
3080
  args.usageTap,
3029
3081
  compression,
3030
3082
  args.withUsage?.signal
3031
3083
  );
3032
- if (policyDriven && runtimePolicy) {
3033
- const runtimeMeasurement = openAIRuntimeMeasurement({
3034
- policy: runtimePolicy,
3035
- telemetry: buildPromptCompressionTelemetry(outcome.segments),
3036
- latencyMs: Date.now() - startedAt,
3037
- originalPayload: args.params,
3038
- resultPayload: outcome.params
3039
- });
3040
- args.ctx.setUsage({ runtimeCompression: runtimeMeasurement });
3041
- if (runtimeMeasurement.decision !== "compressed") return args.params;
3042
- }
3043
3084
  await recordCompressionOutcome({
3044
3085
  outcome,
3045
3086
  compression,
@@ -3052,70 +3093,19 @@ async function compressChatParamsForCall(args) {
3052
3093
  return outcome.params;
3053
3094
  }
3054
3095
  async function compressResponsesParamsForCall(args) {
3055
- let compression = resolveEffectivePromptCompressionOptions(
3096
+ const compression = resolveEffectivePromptCompressionOptions(
3056
3097
  args.defaultPromptCompression,
3057
3098
  args.callPromptCompression
3058
3099
  );
3059
- const runtimePolicy = args.ctx.begin.data.runtimeCompressionPolicy;
3060
- const policyDriven = !compression && Boolean(runtimePolicy);
3061
- if (policyDriven && runtimePolicy) {
3062
- if (!runtimePolicy.selected) {
3063
- args.ctx.setUsage({
3064
- runtimeCompression: bypassedRuntimeMeasurement(runtimePolicy)
3065
- });
3066
- return args.params;
3067
- }
3068
- compression = openAIPolicyCompressionOptions(runtimePolicy);
3069
- if (runtimePolicy.mode === "SHADOW") {
3070
- const startedAt2 = Date.now();
3071
- void compressResponsesParams(
3072
- args.params,
3073
- args.usageTap,
3074
- compression,
3075
- args.withUsage?.signal
3076
- ).then((outcome2) => {
3077
- args.ctx.setUsage({
3078
- runtimeCompression: openAIRuntimeMeasurement({
3079
- policy: runtimePolicy,
3080
- telemetry: buildPromptCompressionTelemetry(outcome2.segments),
3081
- latencyMs: Date.now() - startedAt2,
3082
- shadow: true,
3083
- originalPayload: args.params,
3084
- resultPayload: outcome2.params
3085
- })
3086
- });
3087
- }).catch(() => {
3088
- args.ctx.setUsage({
3089
- runtimeCompression: failedRuntimeMeasurement(
3090
- runtimePolicy,
3091
- Date.now() - startedAt2
3092
- )
3093
- });
3094
- });
3095
- return args.params;
3096
- }
3097
- }
3098
3100
  if (!compression) {
3099
3101
  return args.params;
3100
3102
  }
3101
- const startedAt = Date.now();
3102
3103
  const outcome = await compressResponsesParams(
3103
3104
  args.params,
3104
3105
  args.usageTap,
3105
3106
  compression,
3106
3107
  args.withUsage?.signal
3107
3108
  );
3108
- if (policyDriven && runtimePolicy) {
3109
- const runtimeMeasurement = openAIRuntimeMeasurement({
3110
- policy: runtimePolicy,
3111
- telemetry: buildPromptCompressionTelemetry(outcome.segments),
3112
- latencyMs: Date.now() - startedAt,
3113
- originalPayload: args.params,
3114
- resultPayload: outcome.params
3115
- });
3116
- args.ctx.setUsage({ runtimeCompression: runtimeMeasurement });
3117
- if (runtimeMeasurement.decision !== "compressed") return args.params;
3118
- }
3119
3109
  await recordCompressionOutcome({
3120
3110
  outcome,
3121
3111
  compression,
@@ -3127,79 +3117,6 @@ async function compressResponsesParamsForCall(args) {
3127
3117
  });
3128
3118
  return outcome.params;
3129
3119
  }
3130
- function openAIPolicyCompressionOptions(policy) {
3131
- const roles = Object.fromEntries(
3132
- ["system", "user", "tool", "assistant"].filter((role) => policy.snapshot.scope.messageRoles.includes(role)).map((role) => [role, true])
3133
- );
3134
- return {
3135
- provider: "usagetap",
3136
- roles,
3137
- minContextTokens: policy.snapshot.scope.minimumTokens,
3138
- failOpen: true
3139
- };
3140
- }
3141
- function bypassedRuntimeMeasurement(policy) {
3142
- return {
3143
- policyId: policy.policyId,
3144
- policyVersionId: policy.policyVersionId,
3145
- policyVersion: policy.version,
3146
- rolloutMode: policy.mode,
3147
- decision: "bypassed",
3148
- reasonCode: "not_in_rollout_sample",
3149
- methodsAttempted: [],
3150
- methodsApplied: [],
3151
- originalTokens: 0,
3152
- resultTokens: 0,
3153
- savedTokens: 0,
3154
- reductionPercentage: 0,
3155
- compressionLatencyMs: 0,
3156
- compressionComputeCostUsd: 0,
3157
- financialsEstimated: true
3158
- };
3159
- }
3160
- function failedRuntimeMeasurement(policy, latencyMs) {
3161
- return {
3162
- ...bypassedRuntimeMeasurement(policy),
3163
- decision: "fallback",
3164
- reasonCode: "transformation_failed",
3165
- methodsAttempted: ["deterministic"],
3166
- compressionLatencyMs: latencyMs
3167
- };
3168
- }
3169
- function openAIRuntimeMeasurement(input) {
3170
- const telemetry = input.telemetry;
3171
- const originalTokens = telemetry?.originalTokens ?? estimatePromptTokens(JSON.stringify(input.originalPayload));
3172
- const resultTokens = telemetry?.compressedTokens ?? originalTokens;
3173
- const savedTokens = Math.max(0, telemetry?.savedTokens ?? 0);
3174
- const reductionPercentage = originalTokens > 0 ? savedTokens / originalTokens * 100 : 0;
3175
- const belowMinimumSize = originalTokens < input.policy.snapshot.scope.minimumTokens;
3176
- const latencyExceeded = input.latencyMs > input.policy.snapshot.gates.maximumLatencyMs;
3177
- const belowSavings = savedTokens < input.policy.snapshot.gates.minimumTokensRemoved || reductionPercentage < input.policy.snapshot.gates.minimumReductionPercentage;
3178
- const accepted = !belowMinimumSize && !latencyExceeded && !belowSavings;
3179
- const deepText = input.policy.snapshot.methods.deepText.enabled && !input.policy.snapshot.rollout.deterministicOnly && (input.policy.mode !== "CANARY" || input.policy.snapshot.rollout.deepTextCanaryAllowed);
3180
- const reasonCode = belowMinimumSize ? "below_minimum_size" : latencyExceeded ? "latency_budget_exceeded" : belowSavings ? "below_minimum_savings" : void 0;
3181
- const measurement = {
3182
- policyId: input.policy.policyId,
3183
- policyVersionId: input.policy.policyVersionId,
3184
- policyVersion: input.policy.version,
3185
- rolloutMode: input.policy.mode,
3186
- decision: accepted ? input.shadow ? "bypassed" : "compressed" : latencyExceeded ? "fallback" : "skipped",
3187
- ...reasonCode ? { reasonCode } : {},
3188
- methodsAttempted: [
3189
- "deterministic",
3190
- ...deepText ? ["deep_text"] : []
3191
- ],
3192
- methodsApplied: accepted ? ["deterministic"] : [],
3193
- originalTokens,
3194
- resultTokens: accepted ? resultTokens : originalTokens,
3195
- savedTokens: accepted ? savedTokens : 0,
3196
- reductionPercentage: accepted ? reductionPercentage : 0,
3197
- compressionLatencyMs: input.latencyMs,
3198
- compressionComputeCostUsd: 0,
3199
- financialsEstimated: true
3200
- };
3201
- return measurement;
3202
- }
3203
3120
  async function recordCompressionOutcome(args) {
3204
3121
  const telemetry = buildPromptCompressionTelemetry(args.outcome.segments);
3205
3122
  if (!telemetry) {
@@ -3722,10 +3639,6 @@ function resolveBeginRequest(defaults, override) {
3722
3639
  }
3723
3640
  const tags = mergeTags(base.tags, current.tags);
3724
3641
  const begin = { customerId };
3725
- const runtimeCompressionContext = current.runtimeCompressionContext ?? base.runtimeCompressionContext;
3726
- if (runtimeCompressionContext) {
3727
- begin.runtimeCompressionContext = runtimeCompressionContext;
3728
- }
3729
3642
  const requested = current.requested ?? base.requested;
3730
3643
  if (requested) begin.requested = requested;
3731
3644
  const feature = current.feature ?? base.feature;
@@ -3746,8 +3659,6 @@ function resolveBeginRequest(defaults, override) {
3746
3659
  if (customerUserEmail) begin.customerUserEmail = customerUserEmail;
3747
3660
  const stripeCustomerId = current.stripeCustomerId ?? base.stripeCustomerId;
3748
3661
  if (stripeCustomerId) begin.stripeCustomerId = stripeCustomerId;
3749
- const holdUsd = current.holdUsd ?? base.holdUsd;
3750
- if (typeof holdUsd === "number") begin.holdUsd = holdUsd;
3751
3662
  const batch = current.batch ?? base.batch;
3752
3663
  if (typeof batch === "boolean") begin.batch = batch;
3753
3664
  const pricingMode = current.pricingMode ?? base.pricingMode;
@@ -3757,16 +3668,25 @@ function resolveBeginRequest(defaults, override) {
3757
3668
  }
3758
3669
  return begin;
3759
3670
  }
3760
- function withRuntimeCompressionContext(begin, provider, model) {
3671
+ function responsesBeginRequest(begin, params) {
3672
+ if (!responsesRequestUsesWebSearch(params)) return begin;
3761
3673
  return {
3762
3674
  ...begin,
3763
- runtimeCompressionContext: {
3764
- ...begin.runtimeCompressionContext ?? {},
3765
- provider,
3766
- model
3675
+ requested: {
3676
+ ...begin.requested ?? {},
3677
+ search: true
3767
3678
  }
3768
3679
  };
3769
3680
  }
3681
+ function responsesRequestUsesWebSearch(params) {
3682
+ if (!params || typeof params !== "object") return false;
3683
+ const tools = params.tools;
3684
+ return Array.isArray(tools) && tools.some((tool) => {
3685
+ if (!tool || typeof tool !== "object") return false;
3686
+ const type = tool.type;
3687
+ return type === "web_search" || type === "web_search_preview";
3688
+ });
3689
+ }
3770
3690
  function transformApiPromise(apiPromise, onResolve, onReject) {
3771
3691
  const resolvedPromise = Promise.resolve(apiPromise).then(onResolve, onReject);
3772
3692
  if (isObjectRecord(apiPromise)) {
@@ -3997,6 +3917,11 @@ function inferUsageFromResponse(response, hints, provider = "openai") {
3997
3917
  return void 0;
3998
3918
  }
3999
3919
  const cachedInputTokens = candidate.usage.prompt_tokens_details?.cached_tokens ?? candidate.usage.input_tokens_details?.cached_tokens ?? candidate.usage.cached_tokens;
3920
+ 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;
3921
+ const cacheWrite5mInputTokens = candidate.usage.cache_write_5m_input_tokens ?? candidate.usage.cache_creation?.ephemeral_5m_input_tokens;
3922
+ const cacheWrite1hInputTokens = candidate.usage.cache_write_1h_input_tokens ?? candidate.usage.cache_creation?.ephemeral_1h_input_tokens;
3923
+ const outputSearches = Array.isArray(candidate.output) ? candidate.output.filter((item) => item?.type === "web_search_call").length : 0;
3924
+ const searches = candidate.usage.searches ?? candidate.usage.web_search_queries ?? candidate.usage.server_tool_use?.web_search_requests ?? outputSearches;
4000
3925
  const responseEffort = normalizeExecutionReasoningEffort(
4001
3926
  candidate.reasoning?.effort
4002
3927
  );
@@ -4006,12 +3931,21 @@ function inferUsageFromResponse(response, hints, provider = "openai") {
4006
3931
  inputTokens: candidate.usage.prompt_tokens ?? candidate.usage.input_tokens,
4007
3932
  responseTokens: candidate.usage.completion_tokens ?? candidate.usage.output_tokens,
4008
3933
  cachedInputTokens,
3934
+ cacheWriteInputTokens,
3935
+ cacheWrite5mInputTokens,
3936
+ cacheWrite1hInputTokens,
3937
+ audioInputTokens: candidate.usage.prompt_tokens_details?.audio_tokens ?? candidate.usage.input_tokens_details?.audio_tokens,
3938
+ 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,
3939
+ imageInputTokens: candidate.usage.prompt_tokens_details?.image_tokens ?? candidate.usage.input_tokens_details?.image_tokens,
3940
+ imageOutputTokens: candidate.usage.completion_tokens_details?.image_tokens ?? candidate.usage.output_tokens_details?.image_tokens,
3941
+ audioOutputTokens: candidate.usage.completion_tokens_details?.audio_tokens ?? candidate.usage.output_tokens_details?.audio_tokens,
4009
3942
  reasoningTokens: candidate.usage.completion_tokens_details?.reasoning_tokens ?? candidate.usage.output_tokens_details?.reasoning_tokens,
4010
3943
  ...responseEffort ? {
4011
3944
  reasoningEffort: responseEffort,
4012
3945
  reasoningEffortSource: "provider_response"
4013
3946
  } : {},
4014
- ...typeof candidate.reasoning?.type === "string" ? { reasoningMode: candidate.reasoning.type } : typeof candidate.reasoning?.mode === "string" ? { reasoningMode: candidate.reasoning.mode } : {}
3947
+ ...typeof candidate.reasoning?.type === "string" ? { reasoningMode: candidate.reasoning.type } : typeof candidate.reasoning?.mode === "string" ? { reasoningMode: candidate.reasoning.mode } : {},
3948
+ ...typeof searches === "number" && searches > 0 ? { searches } : {}
4015
3949
  };
4016
3950
  }
4017
3951
  function normalizeExecutionReasoningEffort(value) {
@@ -4028,29 +3962,64 @@ function openAIRequestExecutionMetadata(params, provider) {
4028
3962
  const budget = typeof rawBudget === "number" && Number.isInteger(rawBudget) && rawBudget >= 0 ? rawBudget : void 0;
4029
3963
  return {
4030
3964
  providerUsed: provider,
3965
+ ...typeof record.model === "string" ? { modelUsed: record.model } : {},
4031
3966
  ...effort ? { reasoningEffort: effort, reasoningEffortSource: "provider_request" } : {},
4032
3967
  ...mode ? { reasoningMode: mode } : {},
4033
3968
  ...budget !== void 0 ? { reasoningBudgetTokens: budget } : {}
4034
3969
  };
4035
3970
  }
4036
- function wrapStreamForUsageTap(source, finalize, ctx) {
3971
+ function tryInferUsageFromStreamChunk(chunk, hints, ctx, provider) {
3972
+ const payload = isObjectRecord(chunk) && isObjectRecord(chunk.response) ? chunk.response : chunk;
3973
+ const inferred = inferUsageFromResponse(payload, hints, provider);
3974
+ if (inferred) {
3975
+ ctx.setUsage(inferred);
3976
+ }
3977
+ }
3978
+ function ensureOpenAIStreamUsage(params) {
3979
+ if (!isObjectRecord(params)) {
3980
+ return params;
3981
+ }
3982
+ const streamOptions = isObjectRecord(params.stream_options) ? params.stream_options : {};
3983
+ return {
3984
+ ...params,
3985
+ stream_options: {
3986
+ ...streamOptions,
3987
+ include_usage: true
3988
+ }
3989
+ };
3990
+ }
3991
+ function deferUsageFinalization(ctx) {
3992
+ return ctx.deferFinalization?.() ?? (() => Promise.resolve());
3993
+ }
3994
+ function wrapStreamForUsageTap(source, finalize, ctx, onChunk) {
4037
3995
  const getIterator = source[Symbol.asyncIterator];
4038
3996
  if (typeof getIterator !== "function") {
4039
3997
  throw new TypeError("Stream is not async iterable");
4040
3998
  }
4041
3999
  const iterator = getIterator.call(source);
4042
4000
  let completed = false;
4043
- const invokeFinalize = async () => {
4001
+ const invokeFinalize = async (termination, error) => {
4044
4002
  if (completed) return;
4045
4003
  completed = true;
4004
+ if (termination === "cancel" || termination === "manual") {
4005
+ ctx.setError({
4006
+ code: "STREAM_ABORTED",
4007
+ message: termination === "cancel" ? "Provider stream consumption was cancelled before completion" : "Provider stream was finalized before completion"
4008
+ });
4009
+ } else if (termination === "error") {
4010
+ ctx.setError({
4011
+ code: "VENDOR_ERROR",
4012
+ message: error instanceof Error ? error.message : String(error)
4013
+ });
4014
+ }
4046
4015
  try {
4047
- await finalize();
4048
- } catch (error) {
4016
+ await finalize(termination);
4017
+ } catch (error2) {
4049
4018
  ctx.setError({
4050
4019
  code: "USAGE_FINALIZE_ERROR",
4051
- message: error instanceof Error ? error.message : String(error)
4020
+ message: error2 instanceof Error ? error2.message : String(error2)
4052
4021
  });
4053
- throw error;
4022
+ throw error2;
4054
4023
  }
4055
4024
  };
4056
4025
  const prototype = Object.getPrototypeOf(source) ?? Object.prototype;
@@ -4074,12 +4043,15 @@ function wrapStreamForUsageTap(source, finalize, ctx) {
4074
4043
  value: async (...args) => {
4075
4044
  try {
4076
4045
  const result = await iterator.next(...args);
4046
+ if (!result.done) {
4047
+ onChunk?.(result.value);
4048
+ }
4077
4049
  if (result.done) {
4078
- await invokeFinalize();
4050
+ await invokeFinalize("complete");
4079
4051
  }
4080
4052
  return result;
4081
4053
  } catch (error) {
4082
- await invokeFinalize().catch(() => void 0);
4054
+ await invokeFinalize("error", error).catch(() => void 0);
4083
4055
  throw error;
4084
4056
  }
4085
4057
  },
@@ -4088,39 +4060,49 @@ function wrapStreamForUsageTap(source, finalize, ctx) {
4088
4060
  });
4089
4061
  Object.defineProperty(wrapped, "return", {
4090
4062
  value: async (value) => {
4091
- if (typeof iterator.return === "function") {
4092
- const rawResult = await iterator.return(value);
4093
- if (!isIteratorResult(rawResult)) {
4094
- throw new TypeError("Iterator.return() returned an invalid result");
4063
+ try {
4064
+ if (typeof iterator.return === "function") {
4065
+ const rawResult = await iterator.return(value);
4066
+ if (!isIteratorResult(rawResult)) {
4067
+ throw new TypeError("Iterator.return() returned an invalid result");
4068
+ }
4069
+ await invokeFinalize("cancel");
4070
+ return rawResult;
4095
4071
  }
4096
- await invokeFinalize();
4097
- return rawResult;
4072
+ await invokeFinalize("cancel");
4073
+ return { done: true, value };
4074
+ } catch (error) {
4075
+ await invokeFinalize("error", error).catch(() => void 0);
4076
+ throw error;
4098
4077
  }
4099
- await invokeFinalize();
4100
- return { done: true, value };
4101
4078
  },
4102
4079
  configurable: true,
4103
4080
  writable: true
4104
4081
  });
4105
4082
  Object.defineProperty(wrapped, "throw", {
4106
4083
  value: async (error) => {
4107
- if (typeof iterator.throw === "function") {
4108
- const rawResult = await iterator.throw(error);
4109
- if (!isIteratorResult(rawResult)) {
4110
- throw new TypeError("Iterator.throw() returned an invalid result");
4084
+ try {
4085
+ if (typeof iterator.throw === "function") {
4086
+ const rawResult = await iterator.throw(error);
4087
+ if (!isIteratorResult(rawResult)) {
4088
+ throw new TypeError("Iterator.throw() returned an invalid result");
4089
+ }
4090
+ await invokeFinalize("error", error);
4091
+ return rawResult;
4111
4092
  }
4112
- await invokeFinalize();
4113
- return rawResult;
4093
+ await invokeFinalize("error", error);
4094
+ throw error;
4095
+ } catch (thrownError) {
4096
+ await invokeFinalize("error", thrownError).catch(() => void 0);
4097
+ throw thrownError;
4114
4098
  }
4115
- await invokeFinalize();
4116
- throw error;
4117
4099
  },
4118
4100
  configurable: true,
4119
4101
  writable: true
4120
4102
  });
4121
4103
  Object.defineProperty(wrapped, "__usageTapFinalize", {
4122
4104
  value: async () => {
4123
- await invokeFinalize();
4105
+ await invokeFinalize("manual");
4124
4106
  },
4125
4107
  configurable: true
4126
4108
  });