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