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