@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.
- package/README.md +156 -18
- package/dist/adapters/anthropic.cjs +195 -235
- package/dist/adapters/anthropic.cjs.map +1 -1
- package/dist/adapters/anthropic.d.cts +1 -1
- package/dist/adapters/anthropic.d.ts +1 -1
- package/dist/adapters/anthropic.mjs +195 -235
- package/dist/adapters/anthropic.mjs.map +1 -1
- package/dist/adapters/openai.cjs +256 -274
- package/dist/adapters/openai.cjs.map +1 -1
- package/dist/adapters/openai.d.cts +1 -1
- package/dist/adapters/openai.d.ts +1 -1
- package/dist/adapters/openai.mjs +256 -274
- package/dist/adapters/openai.mjs.map +1 -1
- package/dist/adapters/openrouter.cjs +256 -274
- package/dist/adapters/openrouter.cjs.map +1 -1
- package/dist/adapters/openrouter.d.cts +1 -1
- package/dist/adapters/openrouter.d.ts +1 -1
- package/dist/adapters/openrouter.mjs +256 -274
- package/dist/adapters/openrouter.mjs.map +1 -1
- package/dist/anthropic/index.cjs +195 -235
- package/dist/anthropic/index.cjs.map +1 -1
- package/dist/anthropic/index.d.cts +1 -1
- package/dist/anthropic/index.d.ts +1 -1
- package/dist/anthropic/index.mjs +195 -235
- package/dist/anthropic/index.mjs.map +1 -1
- package/dist/{client-CExQ8e1T.d.cts → client-C0UiaqVB.d.cts} +141 -61
- package/dist/{client-CExQ8e1T.d.ts → client-C0UiaqVB.d.ts} +141 -61
- package/dist/express/index.cjs +189 -246
- package/dist/express/index.cjs.map +1 -1
- package/dist/express/index.d.cts +1 -1
- package/dist/express/index.d.ts +1 -1
- package/dist/express/index.mjs +189 -246
- package/dist/express/index.mjs.map +1 -1
- package/dist/index.cjs +391 -175
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -4
- package/dist/index.d.ts +13 -4
- package/dist/index.mjs +391 -175
- package/dist/index.mjs.map +1 -1
- package/dist/openai/index.cjs +256 -274
- package/dist/openai/index.cjs.map +1 -1
- package/dist/openai/index.d.cts +1 -1
- package/dist/openai/index.d.ts +1 -1
- package/dist/openai/index.mjs +256 -274
- package/dist/openai/index.mjs.map +1 -1
- package/dist/openrouter/index.cjs +256 -274
- package/dist/openrouter/index.cjs.map +1 -1
- package/dist/openrouter/index.d.cts +1 -1
- package/dist/openrouter/index.d.ts +1 -1
- package/dist/openrouter/index.mjs +256 -274
- package/dist/openrouter/index.mjs.map +1 -1
- package/package.json +2 -2
package/dist/adapters/openai.mjs
CHANGED
|
@@ -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.
|
|
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
|
|
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
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
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
|
|
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
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
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 =
|
|
2846
|
+
const beginRequest = responsesBeginRequest(
|
|
2808
2847
|
resolveBeginRequest(defaultContext, usageContext),
|
|
2809
|
-
|
|
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
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
2932
|
-
|
|
2933
|
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
3001
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
3720
|
+
function responsesBeginRequest(begin, params) {
|
|
3721
|
+
if (!responsesRequestUsesWebSearch(params)) return begin;
|
|
3810
3722
|
return {
|
|
3811
3723
|
...begin,
|
|
3812
|
-
|
|
3813
|
-
...begin.
|
|
3814
|
-
|
|
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
|
|
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 (
|
|
4065
|
+
await finalize(termination);
|
|
4066
|
+
} catch (error2) {
|
|
4098
4067
|
ctx.setError({
|
|
4099
4068
|
code: "USAGE_FINALIZE_ERROR",
|
|
4100
|
-
message:
|
|
4069
|
+
message: error2 instanceof Error ? error2.message : String(error2)
|
|
4101
4070
|
});
|
|
4102
|
-
throw
|
|
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
|
-
|
|
4141
|
-
|
|
4142
|
-
|
|
4143
|
-
|
|
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
|
|
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
|
-
|
|
4157
|
-
|
|
4158
|
-
|
|
4159
|
-
|
|
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
|
-
|
|
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
|
});
|