@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/openai/index.cjs
CHANGED
|
@@ -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.
|
|
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
|
|
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
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
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
|
|
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
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
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 =
|
|
2848
|
+
const beginRequest = responsesBeginRequest(
|
|
2810
2849
|
resolveBeginRequest(defaultContext, usageContext),
|
|
2811
|
-
|
|
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
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
2934
|
-
|
|
2935
|
-
|
|
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 =
|
|
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
|
|
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
|
-
|
|
3003
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
3722
|
+
function responsesBeginRequest(begin, params) {
|
|
3723
|
+
if (!responsesRequestUsesWebSearch(params)) return begin;
|
|
3812
3724
|
return {
|
|
3813
3725
|
...begin,
|
|
3814
|
-
|
|
3815
|
-
...begin.
|
|
3816
|
-
|
|
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
|
|
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 (
|
|
4067
|
+
await finalize(termination);
|
|
4068
|
+
} catch (error2) {
|
|
4100
4069
|
ctx.setError({
|
|
4101
4070
|
code: "USAGE_FINALIZE_ERROR",
|
|
4102
|
-
message:
|
|
4071
|
+
message: error2 instanceof Error ? error2.message : String(error2)
|
|
4103
4072
|
});
|
|
4104
|
-
throw
|
|
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
|
-
|
|
4143
|
-
|
|
4144
|
-
|
|
4145
|
-
|
|
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
|
|
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
|
-
|
|
4159
|
-
|
|
4160
|
-
|
|
4161
|
-
|
|
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
|
-
|
|
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
|
});
|