@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?.();
@@ -2806,13 +2845,13 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
2806
2845
  withUsage,
2807
2846
  promptCompression
2808
2847
  } = splitUsageOptions(options);
2809
- const beginRequest = withRuntimeCompressionContext(
2848
+ const beginRequest = responsesBeginRequest(
2810
2849
  resolveBeginRequest(defaultContext, usageContext),
2811
- "openai",
2812
- String(params.model)
2850
+ params
2813
2851
  );
2814
2852
  const wantsStream = isStreamingRequest(params);
2815
2853
  return usageTap.withUsage(beginRequest, async (ctx) => {
2854
+ const settle = wantsStream ? deferUsageFinalization(ctx) : void 0;
2816
2855
  const sampleStartedAt = Date.now();
2817
2856
  const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredOpenAISampleDecision({
2818
2857
  usageTap,
@@ -2837,12 +2876,31 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
2837
2876
  const apiPromise2 = originalCreate(finalParams, request);
2838
2877
  const wrappedPromise2 = transformApiPromise(apiPromise2, (rawStream) => {
2839
2878
  ensureAsyncIterable(rawStream, "responses.create");
2840
- const wrappedStream = wrapStreamForUsageTap(rawStream, async () => {
2841
- const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
2842
- if (usage) {
2843
- ctx.setUsage(usage);
2879
+ const wrappedStream = wrapStreamForUsageTap(rawStream, async (termination) => {
2880
+ try {
2881
+ if (termination === "complete") {
2882
+ const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
2883
+ if (usage) {
2884
+ ctx.setUsage(usage);
2885
+ }
2886
+ }
2887
+ } catch (error) {
2888
+ ctx.setError({
2889
+ code: "USAGE_FINALIZE_ERROR",
2890
+ message: error instanceof Error ? error.message : String(error)
2891
+ });
2892
+ throw error;
2893
+ } finally {
2894
+ await settle?.();
2844
2895
  }
2845
- }, ctx);
2896
+ }, ctx, (chunk) => {
2897
+ tryInferUsageFromStreamChunk(
2898
+ chunk,
2899
+ ctx.begin.data.vendorHints,
2900
+ ctx,
2901
+ provider
2902
+ );
2903
+ });
2846
2904
  return wrappedStream;
2847
2905
  });
2848
2906
  return wrappedPromise2;
@@ -2898,13 +2956,10 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
2898
2956
  withUsage,
2899
2957
  promptCompression
2900
2958
  } = splitUsageOptions(options);
2901
- const beginRequest = withRuntimeCompressionContext(
2902
- resolveBeginRequest(defaultContext, usageContext),
2903
- "openai",
2904
- String(params.model)
2905
- );
2959
+ const beginRequest = resolveBeginRequest(defaultContext, usageContext);
2906
2960
  const wantsStream = isStreamingRequest(params);
2907
2961
  return usageTap.withUsage(beginRequest, async (ctx) => {
2962
+ const settle = wantsStream ? deferUsageFinalization(ctx) : void 0;
2908
2963
  const sampleStartedAt = Date.now();
2909
2964
  const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredOpenAISampleDecision({
2910
2965
  usageTap,
@@ -2913,7 +2968,7 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
2913
2968
  input: params
2914
2969
  });
2915
2970
  const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
2916
- const finalParams = await compressChatParamsForCall({
2971
+ const compressedParams = await compressChatParamsForCall({
2917
2972
  params: hintedParams,
2918
2973
  usageTap,
2919
2974
  ctx,
@@ -2923,18 +2978,38 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
2923
2978
  withUsage,
2924
2979
  operation: "chat.completions.create"
2925
2980
  });
2981
+ const finalParams = wantsStream ? ensureOpenAIStreamUsage(compressedParams) : compressedParams;
2926
2982
  ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
2927
2983
  const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
2928
2984
  if (wantsStream) {
2929
2985
  const apiPromise2 = originalCreate(finalParams, request);
2930
2986
  const wrappedPromise2 = transformApiPromise(apiPromise2, (rawStream) => {
2931
2987
  ensureAsyncIterable(rawStream, "chat.completions.create");
2932
- const wrappedStream2 = wrapStreamForUsageTap(rawStream, async () => {
2933
- const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
2934
- if (usage) {
2935
- ctx.setUsage(usage);
2988
+ const wrappedStream2 = wrapStreamForUsageTap(rawStream, async (termination) => {
2989
+ try {
2990
+ if (termination === "complete") {
2991
+ const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
2992
+ if (usage) {
2993
+ ctx.setUsage(usage);
2994
+ }
2995
+ }
2996
+ } catch (error) {
2997
+ ctx.setError({
2998
+ code: "USAGE_FINALIZE_ERROR",
2999
+ message: error instanceof Error ? error.message : String(error)
3000
+ });
3001
+ throw error;
3002
+ } finally {
3003
+ await settle?.();
2936
3004
  }
2937
- }, ctx);
3005
+ }, ctx, (chunk) => {
3006
+ tryInferUsageFromStreamChunk(
3007
+ chunk,
3008
+ ctx.begin.data.vendorHints,
3009
+ ctx,
3010
+ provider
3011
+ );
3012
+ });
2938
3013
  return wrappedStream2;
2939
3014
  });
2940
3015
  return wrappedPromise2;
@@ -2976,14 +3051,11 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
2976
3051
  withUsage,
2977
3052
  promptCompression
2978
3053
  } = splitUsageOptions(options);
2979
- const beginRequest = withRuntimeCompressionContext(
2980
- resolveBeginRequest(defaultContext, usageContext),
2981
- "openai",
2982
- String(params.model)
2983
- );
3054
+ const beginRequest = resolveBeginRequest(defaultContext, usageContext);
2984
3055
  return usageTap.withUsage(beginRequest, async (ctx) => {
3056
+ const settle = deferUsageFinalization(ctx);
2985
3057
  const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
2986
- const finalParams = await compressChatParamsForCall({
3058
+ const compressedParams = await compressChatParamsForCall({
2987
3059
  params: hintedParams,
2988
3060
  usageTap,
2989
3061
  ctx,
@@ -2993,21 +3065,41 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
2993
3065
  withUsage,
2994
3066
  operation: "chat.completions.stream"
2995
3067
  });
3068
+ const finalParams = ensureOpenAIStreamUsage(compressedParams);
2996
3069
  ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
2997
3070
  const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
2998
3071
  const apiPromise = originalStream(finalParams, request);
2999
3072
  const wrappedPromise = transformApiPromise(apiPromise, (rawStream) => {
3000
3073
  ensureAsyncIterable(rawStream, "chat.completions.stream");
3001
- const wrappedStreamInner = wrapStreamForUsageTap(rawStream, async () => {
3002
- const usage = await extractUsageFromStream(
3003
- rawStream,
3074
+ const wrappedStreamInner = wrapStreamForUsageTap(rawStream, async (termination) => {
3075
+ try {
3076
+ if (termination === "complete") {
3077
+ const usage = await extractUsageFromStream(
3078
+ rawStream,
3079
+ ctx.begin.data.vendorHints,
3080
+ provider
3081
+ );
3082
+ if (usage) {
3083
+ ctx.setUsage(usage);
3084
+ }
3085
+ }
3086
+ } catch (error) {
3087
+ ctx.setError({
3088
+ code: "USAGE_FINALIZE_ERROR",
3089
+ message: error instanceof Error ? error.message : String(error)
3090
+ });
3091
+ throw error;
3092
+ } finally {
3093
+ await settle();
3094
+ }
3095
+ }, ctx, (chunk) => {
3096
+ tryInferUsageFromStreamChunk(
3097
+ chunk,
3004
3098
  ctx.begin.data.vendorHints,
3099
+ ctx,
3005
3100
  provider
3006
3101
  );
3007
- if (usage) {
3008
- ctx.setUsage(usage);
3009
- }
3010
- }, ctx);
3102
+ });
3011
3103
  return wrappedStreamInner;
3012
3104
  });
3013
3105
  return wrappedPromise;
@@ -3027,70 +3119,19 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
3027
3119
  return new Proxy(resource, handler);
3028
3120
  }
3029
3121
  async function compressChatParamsForCall(args) {
3030
- let compression = resolveEffectivePromptCompressionOptions(
3122
+ const compression = resolveEffectivePromptCompressionOptions(
3031
3123
  args.defaultPromptCompression,
3032
3124
  args.callPromptCompression
3033
3125
  );
3034
- const runtimePolicy = args.ctx.begin.data.runtimeCompressionPolicy;
3035
- const policyDriven = !compression && Boolean(runtimePolicy);
3036
- if (policyDriven && runtimePolicy) {
3037
- if (!runtimePolicy.selected) {
3038
- args.ctx.setUsage({
3039
- runtimeCompression: bypassedRuntimeMeasurement(runtimePolicy)
3040
- });
3041
- return args.params;
3042
- }
3043
- compression = openAIPolicyCompressionOptions(runtimePolicy);
3044
- if (runtimePolicy.mode === "SHADOW") {
3045
- const startedAt2 = Date.now();
3046
- void compressChatParams(
3047
- args.params,
3048
- args.usageTap,
3049
- compression,
3050
- args.withUsage?.signal
3051
- ).then((outcome2) => {
3052
- args.ctx.setUsage({
3053
- runtimeCompression: openAIRuntimeMeasurement({
3054
- policy: runtimePolicy,
3055
- telemetry: buildPromptCompressionTelemetry(outcome2.segments),
3056
- latencyMs: Date.now() - startedAt2,
3057
- shadow: true,
3058
- originalPayload: args.params,
3059
- resultPayload: outcome2.params
3060
- })
3061
- });
3062
- }).catch(() => {
3063
- args.ctx.setUsage({
3064
- runtimeCompression: failedRuntimeMeasurement(
3065
- runtimePolicy,
3066
- Date.now() - startedAt2
3067
- )
3068
- });
3069
- });
3070
- return args.params;
3071
- }
3072
- }
3073
3126
  if (!compression) {
3074
3127
  return args.params;
3075
3128
  }
3076
- const startedAt = Date.now();
3077
3129
  const outcome = await compressChatParams(
3078
3130
  args.params,
3079
3131
  args.usageTap,
3080
3132
  compression,
3081
3133
  args.withUsage?.signal
3082
3134
  );
3083
- if (policyDriven && runtimePolicy) {
3084
- const runtimeMeasurement = openAIRuntimeMeasurement({
3085
- policy: runtimePolicy,
3086
- telemetry: buildPromptCompressionTelemetry(outcome.segments),
3087
- latencyMs: Date.now() - startedAt,
3088
- originalPayload: args.params,
3089
- resultPayload: outcome.params
3090
- });
3091
- args.ctx.setUsage({ runtimeCompression: runtimeMeasurement });
3092
- if (runtimeMeasurement.decision !== "compressed") return args.params;
3093
- }
3094
3135
  await recordCompressionOutcome({
3095
3136
  outcome,
3096
3137
  compression,
@@ -3103,70 +3144,19 @@ async function compressChatParamsForCall(args) {
3103
3144
  return outcome.params;
3104
3145
  }
3105
3146
  async function compressResponsesParamsForCall(args) {
3106
- let compression = resolveEffectivePromptCompressionOptions(
3147
+ const compression = resolveEffectivePromptCompressionOptions(
3107
3148
  args.defaultPromptCompression,
3108
3149
  args.callPromptCompression
3109
3150
  );
3110
- const runtimePolicy = args.ctx.begin.data.runtimeCompressionPolicy;
3111
- const policyDriven = !compression && Boolean(runtimePolicy);
3112
- if (policyDriven && runtimePolicy) {
3113
- if (!runtimePolicy.selected) {
3114
- args.ctx.setUsage({
3115
- runtimeCompression: bypassedRuntimeMeasurement(runtimePolicy)
3116
- });
3117
- return args.params;
3118
- }
3119
- compression = openAIPolicyCompressionOptions(runtimePolicy);
3120
- if (runtimePolicy.mode === "SHADOW") {
3121
- const startedAt2 = Date.now();
3122
- void compressResponsesParams(
3123
- args.params,
3124
- args.usageTap,
3125
- compression,
3126
- args.withUsage?.signal
3127
- ).then((outcome2) => {
3128
- args.ctx.setUsage({
3129
- runtimeCompression: openAIRuntimeMeasurement({
3130
- policy: runtimePolicy,
3131
- telemetry: buildPromptCompressionTelemetry(outcome2.segments),
3132
- latencyMs: Date.now() - startedAt2,
3133
- shadow: true,
3134
- originalPayload: args.params,
3135
- resultPayload: outcome2.params
3136
- })
3137
- });
3138
- }).catch(() => {
3139
- args.ctx.setUsage({
3140
- runtimeCompression: failedRuntimeMeasurement(
3141
- runtimePolicy,
3142
- Date.now() - startedAt2
3143
- )
3144
- });
3145
- });
3146
- return args.params;
3147
- }
3148
- }
3149
3151
  if (!compression) {
3150
3152
  return args.params;
3151
3153
  }
3152
- const startedAt = Date.now();
3153
3154
  const outcome = await compressResponsesParams(
3154
3155
  args.params,
3155
3156
  args.usageTap,
3156
3157
  compression,
3157
3158
  args.withUsage?.signal
3158
3159
  );
3159
- if (policyDriven && runtimePolicy) {
3160
- const runtimeMeasurement = openAIRuntimeMeasurement({
3161
- policy: runtimePolicy,
3162
- telemetry: buildPromptCompressionTelemetry(outcome.segments),
3163
- latencyMs: Date.now() - startedAt,
3164
- originalPayload: args.params,
3165
- resultPayload: outcome.params
3166
- });
3167
- args.ctx.setUsage({ runtimeCompression: runtimeMeasurement });
3168
- if (runtimeMeasurement.decision !== "compressed") return args.params;
3169
- }
3170
3160
  await recordCompressionOutcome({
3171
3161
  outcome,
3172
3162
  compression,
@@ -3178,79 +3168,6 @@ async function compressResponsesParamsForCall(args) {
3178
3168
  });
3179
3169
  return outcome.params;
3180
3170
  }
3181
- function openAIPolicyCompressionOptions(policy) {
3182
- const roles = Object.fromEntries(
3183
- ["system", "user", "tool", "assistant"].filter((role) => policy.snapshot.scope.messageRoles.includes(role)).map((role) => [role, true])
3184
- );
3185
- return {
3186
- provider: "usagetap",
3187
- roles,
3188
- minContextTokens: policy.snapshot.scope.minimumTokens,
3189
- failOpen: true
3190
- };
3191
- }
3192
- function bypassedRuntimeMeasurement(policy) {
3193
- return {
3194
- policyId: policy.policyId,
3195
- policyVersionId: policy.policyVersionId,
3196
- policyVersion: policy.version,
3197
- rolloutMode: policy.mode,
3198
- decision: "bypassed",
3199
- reasonCode: "not_in_rollout_sample",
3200
- methodsAttempted: [],
3201
- methodsApplied: [],
3202
- originalTokens: 0,
3203
- resultTokens: 0,
3204
- savedTokens: 0,
3205
- reductionPercentage: 0,
3206
- compressionLatencyMs: 0,
3207
- compressionComputeCostUsd: 0,
3208
- financialsEstimated: true
3209
- };
3210
- }
3211
- function failedRuntimeMeasurement(policy, latencyMs) {
3212
- return {
3213
- ...bypassedRuntimeMeasurement(policy),
3214
- decision: "fallback",
3215
- reasonCode: "transformation_failed",
3216
- methodsAttempted: ["deterministic"],
3217
- compressionLatencyMs: latencyMs
3218
- };
3219
- }
3220
- function openAIRuntimeMeasurement(input) {
3221
- const telemetry = input.telemetry;
3222
- const originalTokens = telemetry?.originalTokens ?? estimatePromptTokens(JSON.stringify(input.originalPayload));
3223
- const resultTokens = telemetry?.compressedTokens ?? originalTokens;
3224
- const savedTokens = Math.max(0, telemetry?.savedTokens ?? 0);
3225
- const reductionPercentage = originalTokens > 0 ? savedTokens / originalTokens * 100 : 0;
3226
- const belowMinimumSize = originalTokens < input.policy.snapshot.scope.minimumTokens;
3227
- const latencyExceeded = input.latencyMs > input.policy.snapshot.gates.maximumLatencyMs;
3228
- const belowSavings = savedTokens < input.policy.snapshot.gates.minimumTokensRemoved || reductionPercentage < input.policy.snapshot.gates.minimumReductionPercentage;
3229
- const accepted = !belowMinimumSize && !latencyExceeded && !belowSavings;
3230
- const deepText = input.policy.snapshot.methods.deepText.enabled && !input.policy.snapshot.rollout.deterministicOnly && (input.policy.mode !== "CANARY" || input.policy.snapshot.rollout.deepTextCanaryAllowed);
3231
- const reasonCode = belowMinimumSize ? "below_minimum_size" : latencyExceeded ? "latency_budget_exceeded" : belowSavings ? "below_minimum_savings" : void 0;
3232
- const measurement = {
3233
- policyId: input.policy.policyId,
3234
- policyVersionId: input.policy.policyVersionId,
3235
- policyVersion: input.policy.version,
3236
- rolloutMode: input.policy.mode,
3237
- decision: accepted ? input.shadow ? "bypassed" : "compressed" : latencyExceeded ? "fallback" : "skipped",
3238
- ...reasonCode ? { reasonCode } : {},
3239
- methodsAttempted: [
3240
- "deterministic",
3241
- ...deepText ? ["deep_text"] : []
3242
- ],
3243
- methodsApplied: accepted ? ["deterministic"] : [],
3244
- originalTokens,
3245
- resultTokens: accepted ? resultTokens : originalTokens,
3246
- savedTokens: accepted ? savedTokens : 0,
3247
- reductionPercentage: accepted ? reductionPercentage : 0,
3248
- compressionLatencyMs: input.latencyMs,
3249
- compressionComputeCostUsd: 0,
3250
- financialsEstimated: true
3251
- };
3252
- return measurement;
3253
- }
3254
3171
  async function recordCompressionOutcome(args) {
3255
3172
  const telemetry = buildPromptCompressionTelemetry(args.outcome.segments);
3256
3173
  if (!telemetry) {
@@ -3773,10 +3690,6 @@ function resolveBeginRequest(defaults, override) {
3773
3690
  }
3774
3691
  const tags = mergeTags(base.tags, current.tags);
3775
3692
  const begin = { customerId };
3776
- const runtimeCompressionContext = current.runtimeCompressionContext ?? base.runtimeCompressionContext;
3777
- if (runtimeCompressionContext) {
3778
- begin.runtimeCompressionContext = runtimeCompressionContext;
3779
- }
3780
3693
  const requested = current.requested ?? base.requested;
3781
3694
  if (requested) begin.requested = requested;
3782
3695
  const feature = current.feature ?? base.feature;
@@ -3797,8 +3710,6 @@ function resolveBeginRequest(defaults, override) {
3797
3710
  if (customerUserEmail) begin.customerUserEmail = customerUserEmail;
3798
3711
  const stripeCustomerId = current.stripeCustomerId ?? base.stripeCustomerId;
3799
3712
  if (stripeCustomerId) begin.stripeCustomerId = stripeCustomerId;
3800
- const holdUsd = current.holdUsd ?? base.holdUsd;
3801
- if (typeof holdUsd === "number") begin.holdUsd = holdUsd;
3802
3713
  const batch = current.batch ?? base.batch;
3803
3714
  if (typeof batch === "boolean") begin.batch = batch;
3804
3715
  const pricingMode = current.pricingMode ?? base.pricingMode;
@@ -3808,16 +3719,25 @@ function resolveBeginRequest(defaults, override) {
3808
3719
  }
3809
3720
  return begin;
3810
3721
  }
3811
- function withRuntimeCompressionContext(begin, provider, model) {
3722
+ function responsesBeginRequest(begin, params) {
3723
+ if (!responsesRequestUsesWebSearch(params)) return begin;
3812
3724
  return {
3813
3725
  ...begin,
3814
- runtimeCompressionContext: {
3815
- ...begin.runtimeCompressionContext ?? {},
3816
- provider,
3817
- model
3726
+ requested: {
3727
+ ...begin.requested ?? {},
3728
+ search: true
3818
3729
  }
3819
3730
  };
3820
3731
  }
3732
+ function responsesRequestUsesWebSearch(params) {
3733
+ if (!params || typeof params !== "object") return false;
3734
+ const tools = params.tools;
3735
+ return Array.isArray(tools) && tools.some((tool) => {
3736
+ if (!tool || typeof tool !== "object") return false;
3737
+ const type = tool.type;
3738
+ return type === "web_search" || type === "web_search_preview";
3739
+ });
3740
+ }
3821
3741
  function transformApiPromise(apiPromise, onResolve, onReject) {
3822
3742
  const resolvedPromise = Promise.resolve(apiPromise).then(onResolve, onReject);
3823
3743
  if (isObjectRecord(apiPromise)) {
@@ -4048,6 +3968,11 @@ function inferUsageFromResponse(response, hints, provider = "openai") {
4048
3968
  return void 0;
4049
3969
  }
4050
3970
  const cachedInputTokens = candidate.usage.prompt_tokens_details?.cached_tokens ?? candidate.usage.input_tokens_details?.cached_tokens ?? candidate.usage.cached_tokens;
3971
+ 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;
3972
+ const cacheWrite5mInputTokens = candidate.usage.cache_write_5m_input_tokens ?? candidate.usage.cache_creation?.ephemeral_5m_input_tokens;
3973
+ const cacheWrite1hInputTokens = candidate.usage.cache_write_1h_input_tokens ?? candidate.usage.cache_creation?.ephemeral_1h_input_tokens;
3974
+ const outputSearches = Array.isArray(candidate.output) ? candidate.output.filter((item) => item?.type === "web_search_call").length : 0;
3975
+ const searches = candidate.usage.searches ?? candidate.usage.web_search_queries ?? candidate.usage.server_tool_use?.web_search_requests ?? outputSearches;
4051
3976
  const responseEffort = normalizeExecutionReasoningEffort(
4052
3977
  candidate.reasoning?.effort
4053
3978
  );
@@ -4057,12 +3982,21 @@ function inferUsageFromResponse(response, hints, provider = "openai") {
4057
3982
  inputTokens: candidate.usage.prompt_tokens ?? candidate.usage.input_tokens,
4058
3983
  responseTokens: candidate.usage.completion_tokens ?? candidate.usage.output_tokens,
4059
3984
  cachedInputTokens,
3985
+ cacheWriteInputTokens,
3986
+ cacheWrite5mInputTokens,
3987
+ cacheWrite1hInputTokens,
3988
+ audioInputTokens: candidate.usage.prompt_tokens_details?.audio_tokens ?? candidate.usage.input_tokens_details?.audio_tokens,
3989
+ 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,
3990
+ imageInputTokens: candidate.usage.prompt_tokens_details?.image_tokens ?? candidate.usage.input_tokens_details?.image_tokens,
3991
+ imageOutputTokens: candidate.usage.completion_tokens_details?.image_tokens ?? candidate.usage.output_tokens_details?.image_tokens,
3992
+ audioOutputTokens: candidate.usage.completion_tokens_details?.audio_tokens ?? candidate.usage.output_tokens_details?.audio_tokens,
4060
3993
  reasoningTokens: candidate.usage.completion_tokens_details?.reasoning_tokens ?? candidate.usage.output_tokens_details?.reasoning_tokens,
4061
3994
  ...responseEffort ? {
4062
3995
  reasoningEffort: responseEffort,
4063
3996
  reasoningEffortSource: "provider_response"
4064
3997
  } : {},
4065
- ...typeof candidate.reasoning?.type === "string" ? { reasoningMode: candidate.reasoning.type } : typeof candidate.reasoning?.mode === "string" ? { reasoningMode: candidate.reasoning.mode } : {}
3998
+ ...typeof candidate.reasoning?.type === "string" ? { reasoningMode: candidate.reasoning.type } : typeof candidate.reasoning?.mode === "string" ? { reasoningMode: candidate.reasoning.mode } : {},
3999
+ ...typeof searches === "number" && searches > 0 ? { searches } : {}
4066
4000
  };
4067
4001
  }
4068
4002
  function normalizeExecutionReasoningEffort(value) {
@@ -4079,29 +4013,64 @@ function openAIRequestExecutionMetadata(params, provider) {
4079
4013
  const budget = typeof rawBudget === "number" && Number.isInteger(rawBudget) && rawBudget >= 0 ? rawBudget : void 0;
4080
4014
  return {
4081
4015
  providerUsed: provider,
4016
+ ...typeof record.model === "string" ? { modelUsed: record.model } : {},
4082
4017
  ...effort ? { reasoningEffort: effort, reasoningEffortSource: "provider_request" } : {},
4083
4018
  ...mode ? { reasoningMode: mode } : {},
4084
4019
  ...budget !== void 0 ? { reasoningBudgetTokens: budget } : {}
4085
4020
  };
4086
4021
  }
4087
- function wrapStreamForUsageTap(source, finalize, ctx) {
4022
+ function tryInferUsageFromStreamChunk(chunk, hints, ctx, provider) {
4023
+ const payload = isObjectRecord(chunk) && isObjectRecord(chunk.response) ? chunk.response : chunk;
4024
+ const inferred = inferUsageFromResponse(payload, hints, provider);
4025
+ if (inferred) {
4026
+ ctx.setUsage(inferred);
4027
+ }
4028
+ }
4029
+ function ensureOpenAIStreamUsage(params) {
4030
+ if (!isObjectRecord(params)) {
4031
+ return params;
4032
+ }
4033
+ const streamOptions = isObjectRecord(params.stream_options) ? params.stream_options : {};
4034
+ return {
4035
+ ...params,
4036
+ stream_options: {
4037
+ ...streamOptions,
4038
+ include_usage: true
4039
+ }
4040
+ };
4041
+ }
4042
+ function deferUsageFinalization(ctx) {
4043
+ return ctx.deferFinalization?.() ?? (() => Promise.resolve());
4044
+ }
4045
+ function wrapStreamForUsageTap(source, finalize, ctx, onChunk) {
4088
4046
  const getIterator = source[Symbol.asyncIterator];
4089
4047
  if (typeof getIterator !== "function") {
4090
4048
  throw new TypeError("Stream is not async iterable");
4091
4049
  }
4092
4050
  const iterator = getIterator.call(source);
4093
4051
  let completed = false;
4094
- const invokeFinalize = async () => {
4052
+ const invokeFinalize = async (termination, error) => {
4095
4053
  if (completed) return;
4096
4054
  completed = true;
4055
+ if (termination === "cancel" || termination === "manual") {
4056
+ ctx.setError({
4057
+ code: "STREAM_ABORTED",
4058
+ message: termination === "cancel" ? "Provider stream consumption was cancelled before completion" : "Provider stream was finalized before completion"
4059
+ });
4060
+ } else if (termination === "error") {
4061
+ ctx.setError({
4062
+ code: "VENDOR_ERROR",
4063
+ message: error instanceof Error ? error.message : String(error)
4064
+ });
4065
+ }
4097
4066
  try {
4098
- await finalize();
4099
- } catch (error) {
4067
+ await finalize(termination);
4068
+ } catch (error2) {
4100
4069
  ctx.setError({
4101
4070
  code: "USAGE_FINALIZE_ERROR",
4102
- message: error instanceof Error ? error.message : String(error)
4071
+ message: error2 instanceof Error ? error2.message : String(error2)
4103
4072
  });
4104
- throw error;
4073
+ throw error2;
4105
4074
  }
4106
4075
  };
4107
4076
  const prototype = Object.getPrototypeOf(source) ?? Object.prototype;
@@ -4125,12 +4094,15 @@ function wrapStreamForUsageTap(source, finalize, ctx) {
4125
4094
  value: async (...args) => {
4126
4095
  try {
4127
4096
  const result = await iterator.next(...args);
4097
+ if (!result.done) {
4098
+ onChunk?.(result.value);
4099
+ }
4128
4100
  if (result.done) {
4129
- await invokeFinalize();
4101
+ await invokeFinalize("complete");
4130
4102
  }
4131
4103
  return result;
4132
4104
  } catch (error) {
4133
- await invokeFinalize().catch(() => void 0);
4105
+ await invokeFinalize("error", error).catch(() => void 0);
4134
4106
  throw error;
4135
4107
  }
4136
4108
  },
@@ -4139,39 +4111,49 @@ function wrapStreamForUsageTap(source, finalize, ctx) {
4139
4111
  });
4140
4112
  Object.defineProperty(wrapped, "return", {
4141
4113
  value: async (value) => {
4142
- if (typeof iterator.return === "function") {
4143
- const rawResult = await iterator.return(value);
4144
- if (!isIteratorResult(rawResult)) {
4145
- throw new TypeError("Iterator.return() returned an invalid result");
4114
+ try {
4115
+ if (typeof iterator.return === "function") {
4116
+ const rawResult = await iterator.return(value);
4117
+ if (!isIteratorResult(rawResult)) {
4118
+ throw new TypeError("Iterator.return() returned an invalid result");
4119
+ }
4120
+ await invokeFinalize("cancel");
4121
+ return rawResult;
4146
4122
  }
4147
- await invokeFinalize();
4148
- return rawResult;
4123
+ await invokeFinalize("cancel");
4124
+ return { done: true, value };
4125
+ } catch (error) {
4126
+ await invokeFinalize("error", error).catch(() => void 0);
4127
+ throw error;
4149
4128
  }
4150
- await invokeFinalize();
4151
- return { done: true, value };
4152
4129
  },
4153
4130
  configurable: true,
4154
4131
  writable: true
4155
4132
  });
4156
4133
  Object.defineProperty(wrapped, "throw", {
4157
4134
  value: async (error) => {
4158
- if (typeof iterator.throw === "function") {
4159
- const rawResult = await iterator.throw(error);
4160
- if (!isIteratorResult(rawResult)) {
4161
- throw new TypeError("Iterator.throw() returned an invalid result");
4135
+ try {
4136
+ if (typeof iterator.throw === "function") {
4137
+ const rawResult = await iterator.throw(error);
4138
+ if (!isIteratorResult(rawResult)) {
4139
+ throw new TypeError("Iterator.throw() returned an invalid result");
4140
+ }
4141
+ await invokeFinalize("error", error);
4142
+ return rawResult;
4162
4143
  }
4163
- await invokeFinalize();
4164
- return rawResult;
4144
+ await invokeFinalize("error", error);
4145
+ throw error;
4146
+ } catch (thrownError) {
4147
+ await invokeFinalize("error", thrownError).catch(() => void 0);
4148
+ throw thrownError;
4165
4149
  }
4166
- await invokeFinalize();
4167
- throw error;
4168
4150
  },
4169
4151
  configurable: true,
4170
4152
  writable: true
4171
4153
  });
4172
4154
  Object.defineProperty(wrapped, "__usageTapFinalize", {
4173
4155
  value: async () => {
4174
- await invokeFinalize();
4156
+ await invokeFinalize("manual");
4175
4157
  },
4176
4158
  configurable: true
4177
4159
  });