@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
|
@@ -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?.();
|
|
@@ -2755,13 +2794,13 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
|
|
|
2755
2794
|
withUsage,
|
|
2756
2795
|
promptCompression
|
|
2757
2796
|
} = splitUsageOptions(options);
|
|
2758
|
-
const beginRequest =
|
|
2797
|
+
const beginRequest = responsesBeginRequest(
|
|
2759
2798
|
resolveBeginRequest(defaultContext, usageContext),
|
|
2760
|
-
|
|
2761
|
-
String(params.model)
|
|
2799
|
+
params
|
|
2762
2800
|
);
|
|
2763
2801
|
const wantsStream = isStreamingRequest(params);
|
|
2764
2802
|
return usageTap.withUsage(beginRequest, async (ctx) => {
|
|
2803
|
+
const settle = wantsStream ? deferUsageFinalization(ctx) : void 0;
|
|
2765
2804
|
const sampleStartedAt = Date.now();
|
|
2766
2805
|
const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredOpenAISampleDecision({
|
|
2767
2806
|
usageTap,
|
|
@@ -2786,12 +2825,31 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
|
|
|
2786
2825
|
const apiPromise2 = originalCreate(finalParams, request);
|
|
2787
2826
|
const wrappedPromise2 = transformApiPromise(apiPromise2, (rawStream) => {
|
|
2788
2827
|
ensureAsyncIterable(rawStream, "responses.create");
|
|
2789
|
-
const wrappedStream = wrapStreamForUsageTap(rawStream, async () => {
|
|
2790
|
-
|
|
2791
|
-
|
|
2792
|
-
|
|
2828
|
+
const wrappedStream = wrapStreamForUsageTap(rawStream, async (termination) => {
|
|
2829
|
+
try {
|
|
2830
|
+
if (termination === "complete") {
|
|
2831
|
+
const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
|
|
2832
|
+
if (usage) {
|
|
2833
|
+
ctx.setUsage(usage);
|
|
2834
|
+
}
|
|
2835
|
+
}
|
|
2836
|
+
} catch (error) {
|
|
2837
|
+
ctx.setError({
|
|
2838
|
+
code: "USAGE_FINALIZE_ERROR",
|
|
2839
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2840
|
+
});
|
|
2841
|
+
throw error;
|
|
2842
|
+
} finally {
|
|
2843
|
+
await settle?.();
|
|
2793
2844
|
}
|
|
2794
|
-
}, ctx)
|
|
2845
|
+
}, ctx, (chunk) => {
|
|
2846
|
+
tryInferUsageFromStreamChunk(
|
|
2847
|
+
chunk,
|
|
2848
|
+
ctx.begin.data.vendorHints,
|
|
2849
|
+
ctx,
|
|
2850
|
+
provider
|
|
2851
|
+
);
|
|
2852
|
+
});
|
|
2795
2853
|
return wrappedStream;
|
|
2796
2854
|
});
|
|
2797
2855
|
return wrappedPromise2;
|
|
@@ -2847,13 +2905,10 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
2847
2905
|
withUsage,
|
|
2848
2906
|
promptCompression
|
|
2849
2907
|
} = splitUsageOptions(options);
|
|
2850
|
-
const beginRequest =
|
|
2851
|
-
resolveBeginRequest(defaultContext, usageContext),
|
|
2852
|
-
"openai",
|
|
2853
|
-
String(params.model)
|
|
2854
|
-
);
|
|
2908
|
+
const beginRequest = resolveBeginRequest(defaultContext, usageContext);
|
|
2855
2909
|
const wantsStream = isStreamingRequest(params);
|
|
2856
2910
|
return usageTap.withUsage(beginRequest, async (ctx) => {
|
|
2911
|
+
const settle = wantsStream ? deferUsageFinalization(ctx) : void 0;
|
|
2857
2912
|
const sampleStartedAt = Date.now();
|
|
2858
2913
|
const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredOpenAISampleDecision({
|
|
2859
2914
|
usageTap,
|
|
@@ -2862,7 +2917,7 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
2862
2917
|
input: params
|
|
2863
2918
|
});
|
|
2864
2919
|
const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
|
|
2865
|
-
const
|
|
2920
|
+
const compressedParams = await compressChatParamsForCall({
|
|
2866
2921
|
params: hintedParams,
|
|
2867
2922
|
usageTap,
|
|
2868
2923
|
ctx,
|
|
@@ -2872,18 +2927,38 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
2872
2927
|
withUsage,
|
|
2873
2928
|
operation: "chat.completions.create"
|
|
2874
2929
|
});
|
|
2930
|
+
const finalParams = wantsStream ? ensureOpenAIStreamUsage(compressedParams) : compressedParams;
|
|
2875
2931
|
ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
|
|
2876
2932
|
const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
|
|
2877
2933
|
if (wantsStream) {
|
|
2878
2934
|
const apiPromise2 = originalCreate(finalParams, request);
|
|
2879
2935
|
const wrappedPromise2 = transformApiPromise(apiPromise2, (rawStream) => {
|
|
2880
2936
|
ensureAsyncIterable(rawStream, "chat.completions.create");
|
|
2881
|
-
const wrappedStream2 = wrapStreamForUsageTap(rawStream, async () => {
|
|
2882
|
-
|
|
2883
|
-
|
|
2884
|
-
|
|
2937
|
+
const wrappedStream2 = wrapStreamForUsageTap(rawStream, async (termination) => {
|
|
2938
|
+
try {
|
|
2939
|
+
if (termination === "complete") {
|
|
2940
|
+
const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
|
|
2941
|
+
if (usage) {
|
|
2942
|
+
ctx.setUsage(usage);
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
} catch (error) {
|
|
2946
|
+
ctx.setError({
|
|
2947
|
+
code: "USAGE_FINALIZE_ERROR",
|
|
2948
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2949
|
+
});
|
|
2950
|
+
throw error;
|
|
2951
|
+
} finally {
|
|
2952
|
+
await settle?.();
|
|
2885
2953
|
}
|
|
2886
|
-
}, ctx)
|
|
2954
|
+
}, ctx, (chunk) => {
|
|
2955
|
+
tryInferUsageFromStreamChunk(
|
|
2956
|
+
chunk,
|
|
2957
|
+
ctx.begin.data.vendorHints,
|
|
2958
|
+
ctx,
|
|
2959
|
+
provider
|
|
2960
|
+
);
|
|
2961
|
+
});
|
|
2887
2962
|
return wrappedStream2;
|
|
2888
2963
|
});
|
|
2889
2964
|
return wrappedPromise2;
|
|
@@ -2925,14 +3000,11 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
2925
3000
|
withUsage,
|
|
2926
3001
|
promptCompression
|
|
2927
3002
|
} = splitUsageOptions(options);
|
|
2928
|
-
const beginRequest =
|
|
2929
|
-
resolveBeginRequest(defaultContext, usageContext),
|
|
2930
|
-
"openai",
|
|
2931
|
-
String(params.model)
|
|
2932
|
-
);
|
|
3003
|
+
const beginRequest = resolveBeginRequest(defaultContext, usageContext);
|
|
2933
3004
|
return usageTap.withUsage(beginRequest, async (ctx) => {
|
|
3005
|
+
const settle = deferUsageFinalization(ctx);
|
|
2934
3006
|
const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
|
|
2935
|
-
const
|
|
3007
|
+
const compressedParams = await compressChatParamsForCall({
|
|
2936
3008
|
params: hintedParams,
|
|
2937
3009
|
usageTap,
|
|
2938
3010
|
ctx,
|
|
@@ -2942,21 +3014,41 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
2942
3014
|
withUsage,
|
|
2943
3015
|
operation: "chat.completions.stream"
|
|
2944
3016
|
});
|
|
3017
|
+
const finalParams = ensureOpenAIStreamUsage(compressedParams);
|
|
2945
3018
|
ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
|
|
2946
3019
|
const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
|
|
2947
3020
|
const apiPromise = originalStream(finalParams, request);
|
|
2948
3021
|
const wrappedPromise = transformApiPromise(apiPromise, (rawStream) => {
|
|
2949
3022
|
ensureAsyncIterable(rawStream, "chat.completions.stream");
|
|
2950
|
-
const wrappedStreamInner = wrapStreamForUsageTap(rawStream, async () => {
|
|
2951
|
-
|
|
2952
|
-
|
|
3023
|
+
const wrappedStreamInner = wrapStreamForUsageTap(rawStream, async (termination) => {
|
|
3024
|
+
try {
|
|
3025
|
+
if (termination === "complete") {
|
|
3026
|
+
const usage = await extractUsageFromStream(
|
|
3027
|
+
rawStream,
|
|
3028
|
+
ctx.begin.data.vendorHints,
|
|
3029
|
+
provider
|
|
3030
|
+
);
|
|
3031
|
+
if (usage) {
|
|
3032
|
+
ctx.setUsage(usage);
|
|
3033
|
+
}
|
|
3034
|
+
}
|
|
3035
|
+
} catch (error) {
|
|
3036
|
+
ctx.setError({
|
|
3037
|
+
code: "USAGE_FINALIZE_ERROR",
|
|
3038
|
+
message: error instanceof Error ? error.message : String(error)
|
|
3039
|
+
});
|
|
3040
|
+
throw error;
|
|
3041
|
+
} finally {
|
|
3042
|
+
await settle();
|
|
3043
|
+
}
|
|
3044
|
+
}, ctx, (chunk) => {
|
|
3045
|
+
tryInferUsageFromStreamChunk(
|
|
3046
|
+
chunk,
|
|
2953
3047
|
ctx.begin.data.vendorHints,
|
|
3048
|
+
ctx,
|
|
2954
3049
|
provider
|
|
2955
3050
|
);
|
|
2956
|
-
|
|
2957
|
-
ctx.setUsage(usage);
|
|
2958
|
-
}
|
|
2959
|
-
}, ctx);
|
|
3051
|
+
});
|
|
2960
3052
|
return wrappedStreamInner;
|
|
2961
3053
|
});
|
|
2962
3054
|
return wrappedPromise;
|
|
@@ -2976,70 +3068,19 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
2976
3068
|
return new Proxy(resource, handler);
|
|
2977
3069
|
}
|
|
2978
3070
|
async function compressChatParamsForCall(args) {
|
|
2979
|
-
|
|
3071
|
+
const compression = resolveEffectivePromptCompressionOptions(
|
|
2980
3072
|
args.defaultPromptCompression,
|
|
2981
3073
|
args.callPromptCompression
|
|
2982
3074
|
);
|
|
2983
|
-
const runtimePolicy = args.ctx.begin.data.runtimeCompressionPolicy;
|
|
2984
|
-
const policyDriven = !compression && Boolean(runtimePolicy);
|
|
2985
|
-
if (policyDriven && runtimePolicy) {
|
|
2986
|
-
if (!runtimePolicy.selected) {
|
|
2987
|
-
args.ctx.setUsage({
|
|
2988
|
-
runtimeCompression: bypassedRuntimeMeasurement(runtimePolicy)
|
|
2989
|
-
});
|
|
2990
|
-
return args.params;
|
|
2991
|
-
}
|
|
2992
|
-
compression = openAIPolicyCompressionOptions(runtimePolicy);
|
|
2993
|
-
if (runtimePolicy.mode === "SHADOW") {
|
|
2994
|
-
const startedAt2 = Date.now();
|
|
2995
|
-
void compressChatParams(
|
|
2996
|
-
args.params,
|
|
2997
|
-
args.usageTap,
|
|
2998
|
-
compression,
|
|
2999
|
-
args.withUsage?.signal
|
|
3000
|
-
).then((outcome2) => {
|
|
3001
|
-
args.ctx.setUsage({
|
|
3002
|
-
runtimeCompression: openAIRuntimeMeasurement({
|
|
3003
|
-
policy: runtimePolicy,
|
|
3004
|
-
telemetry: buildPromptCompressionTelemetry(outcome2.segments),
|
|
3005
|
-
latencyMs: Date.now() - startedAt2,
|
|
3006
|
-
shadow: true,
|
|
3007
|
-
originalPayload: args.params,
|
|
3008
|
-
resultPayload: outcome2.params
|
|
3009
|
-
})
|
|
3010
|
-
});
|
|
3011
|
-
}).catch(() => {
|
|
3012
|
-
args.ctx.setUsage({
|
|
3013
|
-
runtimeCompression: failedRuntimeMeasurement(
|
|
3014
|
-
runtimePolicy,
|
|
3015
|
-
Date.now() - startedAt2
|
|
3016
|
-
)
|
|
3017
|
-
});
|
|
3018
|
-
});
|
|
3019
|
-
return args.params;
|
|
3020
|
-
}
|
|
3021
|
-
}
|
|
3022
3075
|
if (!compression) {
|
|
3023
3076
|
return args.params;
|
|
3024
3077
|
}
|
|
3025
|
-
const startedAt = Date.now();
|
|
3026
3078
|
const outcome = await compressChatParams(
|
|
3027
3079
|
args.params,
|
|
3028
3080
|
args.usageTap,
|
|
3029
3081
|
compression,
|
|
3030
3082
|
args.withUsage?.signal
|
|
3031
3083
|
);
|
|
3032
|
-
if (policyDriven && runtimePolicy) {
|
|
3033
|
-
const runtimeMeasurement = openAIRuntimeMeasurement({
|
|
3034
|
-
policy: runtimePolicy,
|
|
3035
|
-
telemetry: buildPromptCompressionTelemetry(outcome.segments),
|
|
3036
|
-
latencyMs: Date.now() - startedAt,
|
|
3037
|
-
originalPayload: args.params,
|
|
3038
|
-
resultPayload: outcome.params
|
|
3039
|
-
});
|
|
3040
|
-
args.ctx.setUsage({ runtimeCompression: runtimeMeasurement });
|
|
3041
|
-
if (runtimeMeasurement.decision !== "compressed") return args.params;
|
|
3042
|
-
}
|
|
3043
3084
|
await recordCompressionOutcome({
|
|
3044
3085
|
outcome,
|
|
3045
3086
|
compression,
|
|
@@ -3052,70 +3093,19 @@ async function compressChatParamsForCall(args) {
|
|
|
3052
3093
|
return outcome.params;
|
|
3053
3094
|
}
|
|
3054
3095
|
async function compressResponsesParamsForCall(args) {
|
|
3055
|
-
|
|
3096
|
+
const compression = resolveEffectivePromptCompressionOptions(
|
|
3056
3097
|
args.defaultPromptCompression,
|
|
3057
3098
|
args.callPromptCompression
|
|
3058
3099
|
);
|
|
3059
|
-
const runtimePolicy = args.ctx.begin.data.runtimeCompressionPolicy;
|
|
3060
|
-
const policyDriven = !compression && Boolean(runtimePolicy);
|
|
3061
|
-
if (policyDriven && runtimePolicy) {
|
|
3062
|
-
if (!runtimePolicy.selected) {
|
|
3063
|
-
args.ctx.setUsage({
|
|
3064
|
-
runtimeCompression: bypassedRuntimeMeasurement(runtimePolicy)
|
|
3065
|
-
});
|
|
3066
|
-
return args.params;
|
|
3067
|
-
}
|
|
3068
|
-
compression = openAIPolicyCompressionOptions(runtimePolicy);
|
|
3069
|
-
if (runtimePolicy.mode === "SHADOW") {
|
|
3070
|
-
const startedAt2 = Date.now();
|
|
3071
|
-
void compressResponsesParams(
|
|
3072
|
-
args.params,
|
|
3073
|
-
args.usageTap,
|
|
3074
|
-
compression,
|
|
3075
|
-
args.withUsage?.signal
|
|
3076
|
-
).then((outcome2) => {
|
|
3077
|
-
args.ctx.setUsage({
|
|
3078
|
-
runtimeCompression: openAIRuntimeMeasurement({
|
|
3079
|
-
policy: runtimePolicy,
|
|
3080
|
-
telemetry: buildPromptCompressionTelemetry(outcome2.segments),
|
|
3081
|
-
latencyMs: Date.now() - startedAt2,
|
|
3082
|
-
shadow: true,
|
|
3083
|
-
originalPayload: args.params,
|
|
3084
|
-
resultPayload: outcome2.params
|
|
3085
|
-
})
|
|
3086
|
-
});
|
|
3087
|
-
}).catch(() => {
|
|
3088
|
-
args.ctx.setUsage({
|
|
3089
|
-
runtimeCompression: failedRuntimeMeasurement(
|
|
3090
|
-
runtimePolicy,
|
|
3091
|
-
Date.now() - startedAt2
|
|
3092
|
-
)
|
|
3093
|
-
});
|
|
3094
|
-
});
|
|
3095
|
-
return args.params;
|
|
3096
|
-
}
|
|
3097
|
-
}
|
|
3098
3100
|
if (!compression) {
|
|
3099
3101
|
return args.params;
|
|
3100
3102
|
}
|
|
3101
|
-
const startedAt = Date.now();
|
|
3102
3103
|
const outcome = await compressResponsesParams(
|
|
3103
3104
|
args.params,
|
|
3104
3105
|
args.usageTap,
|
|
3105
3106
|
compression,
|
|
3106
3107
|
args.withUsage?.signal
|
|
3107
3108
|
);
|
|
3108
|
-
if (policyDriven && runtimePolicy) {
|
|
3109
|
-
const runtimeMeasurement = openAIRuntimeMeasurement({
|
|
3110
|
-
policy: runtimePolicy,
|
|
3111
|
-
telemetry: buildPromptCompressionTelemetry(outcome.segments),
|
|
3112
|
-
latencyMs: Date.now() - startedAt,
|
|
3113
|
-
originalPayload: args.params,
|
|
3114
|
-
resultPayload: outcome.params
|
|
3115
|
-
});
|
|
3116
|
-
args.ctx.setUsage({ runtimeCompression: runtimeMeasurement });
|
|
3117
|
-
if (runtimeMeasurement.decision !== "compressed") return args.params;
|
|
3118
|
-
}
|
|
3119
3109
|
await recordCompressionOutcome({
|
|
3120
3110
|
outcome,
|
|
3121
3111
|
compression,
|
|
@@ -3127,79 +3117,6 @@ async function compressResponsesParamsForCall(args) {
|
|
|
3127
3117
|
});
|
|
3128
3118
|
return outcome.params;
|
|
3129
3119
|
}
|
|
3130
|
-
function openAIPolicyCompressionOptions(policy) {
|
|
3131
|
-
const roles = Object.fromEntries(
|
|
3132
|
-
["system", "user", "tool", "assistant"].filter((role) => policy.snapshot.scope.messageRoles.includes(role)).map((role) => [role, true])
|
|
3133
|
-
);
|
|
3134
|
-
return {
|
|
3135
|
-
provider: "usagetap",
|
|
3136
|
-
roles,
|
|
3137
|
-
minContextTokens: policy.snapshot.scope.minimumTokens,
|
|
3138
|
-
failOpen: true
|
|
3139
|
-
};
|
|
3140
|
-
}
|
|
3141
|
-
function bypassedRuntimeMeasurement(policy) {
|
|
3142
|
-
return {
|
|
3143
|
-
policyId: policy.policyId,
|
|
3144
|
-
policyVersionId: policy.policyVersionId,
|
|
3145
|
-
policyVersion: policy.version,
|
|
3146
|
-
rolloutMode: policy.mode,
|
|
3147
|
-
decision: "bypassed",
|
|
3148
|
-
reasonCode: "not_in_rollout_sample",
|
|
3149
|
-
methodsAttempted: [],
|
|
3150
|
-
methodsApplied: [],
|
|
3151
|
-
originalTokens: 0,
|
|
3152
|
-
resultTokens: 0,
|
|
3153
|
-
savedTokens: 0,
|
|
3154
|
-
reductionPercentage: 0,
|
|
3155
|
-
compressionLatencyMs: 0,
|
|
3156
|
-
compressionComputeCostUsd: 0,
|
|
3157
|
-
financialsEstimated: true
|
|
3158
|
-
};
|
|
3159
|
-
}
|
|
3160
|
-
function failedRuntimeMeasurement(policy, latencyMs) {
|
|
3161
|
-
return {
|
|
3162
|
-
...bypassedRuntimeMeasurement(policy),
|
|
3163
|
-
decision: "fallback",
|
|
3164
|
-
reasonCode: "transformation_failed",
|
|
3165
|
-
methodsAttempted: ["deterministic"],
|
|
3166
|
-
compressionLatencyMs: latencyMs
|
|
3167
|
-
};
|
|
3168
|
-
}
|
|
3169
|
-
function openAIRuntimeMeasurement(input) {
|
|
3170
|
-
const telemetry = input.telemetry;
|
|
3171
|
-
const originalTokens = telemetry?.originalTokens ?? estimatePromptTokens(JSON.stringify(input.originalPayload));
|
|
3172
|
-
const resultTokens = telemetry?.compressedTokens ?? originalTokens;
|
|
3173
|
-
const savedTokens = Math.max(0, telemetry?.savedTokens ?? 0);
|
|
3174
|
-
const reductionPercentage = originalTokens > 0 ? savedTokens / originalTokens * 100 : 0;
|
|
3175
|
-
const belowMinimumSize = originalTokens < input.policy.snapshot.scope.minimumTokens;
|
|
3176
|
-
const latencyExceeded = input.latencyMs > input.policy.snapshot.gates.maximumLatencyMs;
|
|
3177
|
-
const belowSavings = savedTokens < input.policy.snapshot.gates.minimumTokensRemoved || reductionPercentage < input.policy.snapshot.gates.minimumReductionPercentage;
|
|
3178
|
-
const accepted = !belowMinimumSize && !latencyExceeded && !belowSavings;
|
|
3179
|
-
const deepText = input.policy.snapshot.methods.deepText.enabled && !input.policy.snapshot.rollout.deterministicOnly && (input.policy.mode !== "CANARY" || input.policy.snapshot.rollout.deepTextCanaryAllowed);
|
|
3180
|
-
const reasonCode = belowMinimumSize ? "below_minimum_size" : latencyExceeded ? "latency_budget_exceeded" : belowSavings ? "below_minimum_savings" : void 0;
|
|
3181
|
-
const measurement = {
|
|
3182
|
-
policyId: input.policy.policyId,
|
|
3183
|
-
policyVersionId: input.policy.policyVersionId,
|
|
3184
|
-
policyVersion: input.policy.version,
|
|
3185
|
-
rolloutMode: input.policy.mode,
|
|
3186
|
-
decision: accepted ? input.shadow ? "bypassed" : "compressed" : latencyExceeded ? "fallback" : "skipped",
|
|
3187
|
-
...reasonCode ? { reasonCode } : {},
|
|
3188
|
-
methodsAttempted: [
|
|
3189
|
-
"deterministic",
|
|
3190
|
-
...deepText ? ["deep_text"] : []
|
|
3191
|
-
],
|
|
3192
|
-
methodsApplied: accepted ? ["deterministic"] : [],
|
|
3193
|
-
originalTokens,
|
|
3194
|
-
resultTokens: accepted ? resultTokens : originalTokens,
|
|
3195
|
-
savedTokens: accepted ? savedTokens : 0,
|
|
3196
|
-
reductionPercentage: accepted ? reductionPercentage : 0,
|
|
3197
|
-
compressionLatencyMs: input.latencyMs,
|
|
3198
|
-
compressionComputeCostUsd: 0,
|
|
3199
|
-
financialsEstimated: true
|
|
3200
|
-
};
|
|
3201
|
-
return measurement;
|
|
3202
|
-
}
|
|
3203
3120
|
async function recordCompressionOutcome(args) {
|
|
3204
3121
|
const telemetry = buildPromptCompressionTelemetry(args.outcome.segments);
|
|
3205
3122
|
if (!telemetry) {
|
|
@@ -3722,10 +3639,6 @@ function resolveBeginRequest(defaults, override) {
|
|
|
3722
3639
|
}
|
|
3723
3640
|
const tags = mergeTags(base.tags, current.tags);
|
|
3724
3641
|
const begin = { customerId };
|
|
3725
|
-
const runtimeCompressionContext = current.runtimeCompressionContext ?? base.runtimeCompressionContext;
|
|
3726
|
-
if (runtimeCompressionContext) {
|
|
3727
|
-
begin.runtimeCompressionContext = runtimeCompressionContext;
|
|
3728
|
-
}
|
|
3729
3642
|
const requested = current.requested ?? base.requested;
|
|
3730
3643
|
if (requested) begin.requested = requested;
|
|
3731
3644
|
const feature = current.feature ?? base.feature;
|
|
@@ -3746,8 +3659,6 @@ function resolveBeginRequest(defaults, override) {
|
|
|
3746
3659
|
if (customerUserEmail) begin.customerUserEmail = customerUserEmail;
|
|
3747
3660
|
const stripeCustomerId = current.stripeCustomerId ?? base.stripeCustomerId;
|
|
3748
3661
|
if (stripeCustomerId) begin.stripeCustomerId = stripeCustomerId;
|
|
3749
|
-
const holdUsd = current.holdUsd ?? base.holdUsd;
|
|
3750
|
-
if (typeof holdUsd === "number") begin.holdUsd = holdUsd;
|
|
3751
3662
|
const batch = current.batch ?? base.batch;
|
|
3752
3663
|
if (typeof batch === "boolean") begin.batch = batch;
|
|
3753
3664
|
const pricingMode = current.pricingMode ?? base.pricingMode;
|
|
@@ -3757,16 +3668,25 @@ function resolveBeginRequest(defaults, override) {
|
|
|
3757
3668
|
}
|
|
3758
3669
|
return begin;
|
|
3759
3670
|
}
|
|
3760
|
-
function
|
|
3671
|
+
function responsesBeginRequest(begin, params) {
|
|
3672
|
+
if (!responsesRequestUsesWebSearch(params)) return begin;
|
|
3761
3673
|
return {
|
|
3762
3674
|
...begin,
|
|
3763
|
-
|
|
3764
|
-
...begin.
|
|
3765
|
-
|
|
3766
|
-
model
|
|
3675
|
+
requested: {
|
|
3676
|
+
...begin.requested ?? {},
|
|
3677
|
+
search: true
|
|
3767
3678
|
}
|
|
3768
3679
|
};
|
|
3769
3680
|
}
|
|
3681
|
+
function responsesRequestUsesWebSearch(params) {
|
|
3682
|
+
if (!params || typeof params !== "object") return false;
|
|
3683
|
+
const tools = params.tools;
|
|
3684
|
+
return Array.isArray(tools) && tools.some((tool) => {
|
|
3685
|
+
if (!tool || typeof tool !== "object") return false;
|
|
3686
|
+
const type = tool.type;
|
|
3687
|
+
return type === "web_search" || type === "web_search_preview";
|
|
3688
|
+
});
|
|
3689
|
+
}
|
|
3770
3690
|
function transformApiPromise(apiPromise, onResolve, onReject) {
|
|
3771
3691
|
const resolvedPromise = Promise.resolve(apiPromise).then(onResolve, onReject);
|
|
3772
3692
|
if (isObjectRecord(apiPromise)) {
|
|
@@ -3997,6 +3917,11 @@ function inferUsageFromResponse(response, hints, provider = "openai") {
|
|
|
3997
3917
|
return void 0;
|
|
3998
3918
|
}
|
|
3999
3919
|
const cachedInputTokens = candidate.usage.prompt_tokens_details?.cached_tokens ?? candidate.usage.input_tokens_details?.cached_tokens ?? candidate.usage.cached_tokens;
|
|
3920
|
+
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;
|
|
3921
|
+
const cacheWrite5mInputTokens = candidate.usage.cache_write_5m_input_tokens ?? candidate.usage.cache_creation?.ephemeral_5m_input_tokens;
|
|
3922
|
+
const cacheWrite1hInputTokens = candidate.usage.cache_write_1h_input_tokens ?? candidate.usage.cache_creation?.ephemeral_1h_input_tokens;
|
|
3923
|
+
const outputSearches = Array.isArray(candidate.output) ? candidate.output.filter((item) => item?.type === "web_search_call").length : 0;
|
|
3924
|
+
const searches = candidate.usage.searches ?? candidate.usage.web_search_queries ?? candidate.usage.server_tool_use?.web_search_requests ?? outputSearches;
|
|
4000
3925
|
const responseEffort = normalizeExecutionReasoningEffort(
|
|
4001
3926
|
candidate.reasoning?.effort
|
|
4002
3927
|
);
|
|
@@ -4006,12 +3931,21 @@ function inferUsageFromResponse(response, hints, provider = "openai") {
|
|
|
4006
3931
|
inputTokens: candidate.usage.prompt_tokens ?? candidate.usage.input_tokens,
|
|
4007
3932
|
responseTokens: candidate.usage.completion_tokens ?? candidate.usage.output_tokens,
|
|
4008
3933
|
cachedInputTokens,
|
|
3934
|
+
cacheWriteInputTokens,
|
|
3935
|
+
cacheWrite5mInputTokens,
|
|
3936
|
+
cacheWrite1hInputTokens,
|
|
3937
|
+
audioInputTokens: candidate.usage.prompt_tokens_details?.audio_tokens ?? candidate.usage.input_tokens_details?.audio_tokens,
|
|
3938
|
+
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,
|
|
3939
|
+
imageInputTokens: candidate.usage.prompt_tokens_details?.image_tokens ?? candidate.usage.input_tokens_details?.image_tokens,
|
|
3940
|
+
imageOutputTokens: candidate.usage.completion_tokens_details?.image_tokens ?? candidate.usage.output_tokens_details?.image_tokens,
|
|
3941
|
+
audioOutputTokens: candidate.usage.completion_tokens_details?.audio_tokens ?? candidate.usage.output_tokens_details?.audio_tokens,
|
|
4009
3942
|
reasoningTokens: candidate.usage.completion_tokens_details?.reasoning_tokens ?? candidate.usage.output_tokens_details?.reasoning_tokens,
|
|
4010
3943
|
...responseEffort ? {
|
|
4011
3944
|
reasoningEffort: responseEffort,
|
|
4012
3945
|
reasoningEffortSource: "provider_response"
|
|
4013
3946
|
} : {},
|
|
4014
|
-
...typeof candidate.reasoning?.type === "string" ? { reasoningMode: candidate.reasoning.type } : typeof candidate.reasoning?.mode === "string" ? { reasoningMode: candidate.reasoning.mode } : {}
|
|
3947
|
+
...typeof candidate.reasoning?.type === "string" ? { reasoningMode: candidate.reasoning.type } : typeof candidate.reasoning?.mode === "string" ? { reasoningMode: candidate.reasoning.mode } : {},
|
|
3948
|
+
...typeof searches === "number" && searches > 0 ? { searches } : {}
|
|
4015
3949
|
};
|
|
4016
3950
|
}
|
|
4017
3951
|
function normalizeExecutionReasoningEffort(value) {
|
|
@@ -4028,29 +3962,64 @@ function openAIRequestExecutionMetadata(params, provider) {
|
|
|
4028
3962
|
const budget = typeof rawBudget === "number" && Number.isInteger(rawBudget) && rawBudget >= 0 ? rawBudget : void 0;
|
|
4029
3963
|
return {
|
|
4030
3964
|
providerUsed: provider,
|
|
3965
|
+
...typeof record.model === "string" ? { modelUsed: record.model } : {},
|
|
4031
3966
|
...effort ? { reasoningEffort: effort, reasoningEffortSource: "provider_request" } : {},
|
|
4032
3967
|
...mode ? { reasoningMode: mode } : {},
|
|
4033
3968
|
...budget !== void 0 ? { reasoningBudgetTokens: budget } : {}
|
|
4034
3969
|
};
|
|
4035
3970
|
}
|
|
4036
|
-
function
|
|
3971
|
+
function tryInferUsageFromStreamChunk(chunk, hints, ctx, provider) {
|
|
3972
|
+
const payload = isObjectRecord(chunk) && isObjectRecord(chunk.response) ? chunk.response : chunk;
|
|
3973
|
+
const inferred = inferUsageFromResponse(payload, hints, provider);
|
|
3974
|
+
if (inferred) {
|
|
3975
|
+
ctx.setUsage(inferred);
|
|
3976
|
+
}
|
|
3977
|
+
}
|
|
3978
|
+
function ensureOpenAIStreamUsage(params) {
|
|
3979
|
+
if (!isObjectRecord(params)) {
|
|
3980
|
+
return params;
|
|
3981
|
+
}
|
|
3982
|
+
const streamOptions = isObjectRecord(params.stream_options) ? params.stream_options : {};
|
|
3983
|
+
return {
|
|
3984
|
+
...params,
|
|
3985
|
+
stream_options: {
|
|
3986
|
+
...streamOptions,
|
|
3987
|
+
include_usage: true
|
|
3988
|
+
}
|
|
3989
|
+
};
|
|
3990
|
+
}
|
|
3991
|
+
function deferUsageFinalization(ctx) {
|
|
3992
|
+
return ctx.deferFinalization?.() ?? (() => Promise.resolve());
|
|
3993
|
+
}
|
|
3994
|
+
function wrapStreamForUsageTap(source, finalize, ctx, onChunk) {
|
|
4037
3995
|
const getIterator = source[Symbol.asyncIterator];
|
|
4038
3996
|
if (typeof getIterator !== "function") {
|
|
4039
3997
|
throw new TypeError("Stream is not async iterable");
|
|
4040
3998
|
}
|
|
4041
3999
|
const iterator = getIterator.call(source);
|
|
4042
4000
|
let completed = false;
|
|
4043
|
-
const invokeFinalize = async () => {
|
|
4001
|
+
const invokeFinalize = async (termination, error) => {
|
|
4044
4002
|
if (completed) return;
|
|
4045
4003
|
completed = true;
|
|
4004
|
+
if (termination === "cancel" || termination === "manual") {
|
|
4005
|
+
ctx.setError({
|
|
4006
|
+
code: "STREAM_ABORTED",
|
|
4007
|
+
message: termination === "cancel" ? "Provider stream consumption was cancelled before completion" : "Provider stream was finalized before completion"
|
|
4008
|
+
});
|
|
4009
|
+
} else if (termination === "error") {
|
|
4010
|
+
ctx.setError({
|
|
4011
|
+
code: "VENDOR_ERROR",
|
|
4012
|
+
message: error instanceof Error ? error.message : String(error)
|
|
4013
|
+
});
|
|
4014
|
+
}
|
|
4046
4015
|
try {
|
|
4047
|
-
await finalize();
|
|
4048
|
-
} catch (
|
|
4016
|
+
await finalize(termination);
|
|
4017
|
+
} catch (error2) {
|
|
4049
4018
|
ctx.setError({
|
|
4050
4019
|
code: "USAGE_FINALIZE_ERROR",
|
|
4051
|
-
message:
|
|
4020
|
+
message: error2 instanceof Error ? error2.message : String(error2)
|
|
4052
4021
|
});
|
|
4053
|
-
throw
|
|
4022
|
+
throw error2;
|
|
4054
4023
|
}
|
|
4055
4024
|
};
|
|
4056
4025
|
const prototype = Object.getPrototypeOf(source) ?? Object.prototype;
|
|
@@ -4074,12 +4043,15 @@ function wrapStreamForUsageTap(source, finalize, ctx) {
|
|
|
4074
4043
|
value: async (...args) => {
|
|
4075
4044
|
try {
|
|
4076
4045
|
const result = await iterator.next(...args);
|
|
4046
|
+
if (!result.done) {
|
|
4047
|
+
onChunk?.(result.value);
|
|
4048
|
+
}
|
|
4077
4049
|
if (result.done) {
|
|
4078
|
-
await invokeFinalize();
|
|
4050
|
+
await invokeFinalize("complete");
|
|
4079
4051
|
}
|
|
4080
4052
|
return result;
|
|
4081
4053
|
} catch (error) {
|
|
4082
|
-
await invokeFinalize().catch(() => void 0);
|
|
4054
|
+
await invokeFinalize("error", error).catch(() => void 0);
|
|
4083
4055
|
throw error;
|
|
4084
4056
|
}
|
|
4085
4057
|
},
|
|
@@ -4088,39 +4060,49 @@ function wrapStreamForUsageTap(source, finalize, ctx) {
|
|
|
4088
4060
|
});
|
|
4089
4061
|
Object.defineProperty(wrapped, "return", {
|
|
4090
4062
|
value: async (value) => {
|
|
4091
|
-
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4063
|
+
try {
|
|
4064
|
+
if (typeof iterator.return === "function") {
|
|
4065
|
+
const rawResult = await iterator.return(value);
|
|
4066
|
+
if (!isIteratorResult(rawResult)) {
|
|
4067
|
+
throw new TypeError("Iterator.return() returned an invalid result");
|
|
4068
|
+
}
|
|
4069
|
+
await invokeFinalize("cancel");
|
|
4070
|
+
return rawResult;
|
|
4095
4071
|
}
|
|
4096
|
-
await invokeFinalize();
|
|
4097
|
-
return
|
|
4072
|
+
await invokeFinalize("cancel");
|
|
4073
|
+
return { done: true, value };
|
|
4074
|
+
} catch (error) {
|
|
4075
|
+
await invokeFinalize("error", error).catch(() => void 0);
|
|
4076
|
+
throw error;
|
|
4098
4077
|
}
|
|
4099
|
-
await invokeFinalize();
|
|
4100
|
-
return { done: true, value };
|
|
4101
4078
|
},
|
|
4102
4079
|
configurable: true,
|
|
4103
4080
|
writable: true
|
|
4104
4081
|
});
|
|
4105
4082
|
Object.defineProperty(wrapped, "throw", {
|
|
4106
4083
|
value: async (error) => {
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4084
|
+
try {
|
|
4085
|
+
if (typeof iterator.throw === "function") {
|
|
4086
|
+
const rawResult = await iterator.throw(error);
|
|
4087
|
+
if (!isIteratorResult(rawResult)) {
|
|
4088
|
+
throw new TypeError("Iterator.throw() returned an invalid result");
|
|
4089
|
+
}
|
|
4090
|
+
await invokeFinalize("error", error);
|
|
4091
|
+
return rawResult;
|
|
4111
4092
|
}
|
|
4112
|
-
await invokeFinalize();
|
|
4113
|
-
|
|
4093
|
+
await invokeFinalize("error", error);
|
|
4094
|
+
throw error;
|
|
4095
|
+
} catch (thrownError) {
|
|
4096
|
+
await invokeFinalize("error", thrownError).catch(() => void 0);
|
|
4097
|
+
throw thrownError;
|
|
4114
4098
|
}
|
|
4115
|
-
await invokeFinalize();
|
|
4116
|
-
throw error;
|
|
4117
4099
|
},
|
|
4118
4100
|
configurable: true,
|
|
4119
4101
|
writable: true
|
|
4120
4102
|
});
|
|
4121
4103
|
Object.defineProperty(wrapped, "__usageTapFinalize", {
|
|
4122
4104
|
value: async () => {
|
|
4123
|
-
await invokeFinalize();
|
|
4105
|
+
await invokeFinalize("manual");
|
|
4124
4106
|
},
|
|
4125
4107
|
configurable: true
|
|
4126
4108
|
});
|