@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
|
@@ -1021,6 +1021,8 @@ var SummarizationResource = class {
|
|
|
1021
1021
|
};
|
|
1022
1022
|
var GatewayResource = class {
|
|
1023
1023
|
chat;
|
|
1024
|
+
/** Buffered OpenAI Responses-compatible requests. */
|
|
1025
|
+
responses;
|
|
1024
1026
|
models;
|
|
1025
1027
|
batches;
|
|
1026
1028
|
transport;
|
|
@@ -1042,6 +1044,15 @@ var GatewayResource = class {
|
|
|
1042
1044
|
})
|
|
1043
1045
|
}
|
|
1044
1046
|
};
|
|
1047
|
+
this.responses = {
|
|
1048
|
+
create: (params, options) => this.transport.request({
|
|
1049
|
+
method: "POST",
|
|
1050
|
+
path: "/v1/responses",
|
|
1051
|
+
body: params,
|
|
1052
|
+
options,
|
|
1053
|
+
response: "json"
|
|
1054
|
+
})
|
|
1055
|
+
};
|
|
1045
1056
|
this.models = {
|
|
1046
1057
|
list: (options) => this.transport.request({
|
|
1047
1058
|
method: "GET",
|
|
@@ -1127,7 +1138,7 @@ var USER_AGENT = "UsageTapClient";
|
|
|
1127
1138
|
var CANONICAL_MEDIA_TYPE2 = "application/vnd.usagetap.v1+json";
|
|
1128
1139
|
var DEFAULT_BASE_URL = "https://api.usagetap.com";
|
|
1129
1140
|
var DEFAULT_RUN_INACTIVITY_MS = 60 * 60 * 1e3;
|
|
1130
|
-
var SDK_VERSION = "1.
|
|
1141
|
+
var SDK_VERSION = "1.7.0" ;
|
|
1131
1142
|
var HAS_WINDOW = typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
|
|
1132
1143
|
var UsageTapClient = class {
|
|
1133
1144
|
/** OpenAI-compatible chat, model, and native batch operations. */
|
|
@@ -1529,9 +1540,18 @@ var UsageTapClient = class {
|
|
|
1529
1540
|
responseTokens: request.responseTokens,
|
|
1530
1541
|
cachedInputTokens: request.cachedInputTokens,
|
|
1531
1542
|
cacheWriteInputTokens: request.cacheWriteInputTokens,
|
|
1543
|
+
cacheWrite5mInputTokens: request.cacheWrite5mInputTokens,
|
|
1544
|
+
cacheWrite1hInputTokens: request.cacheWrite1hInputTokens,
|
|
1532
1545
|
reasoningTokens: request.reasoningTokens,
|
|
1533
1546
|
searches: request.searches,
|
|
1534
1547
|
audioSeconds: request.audioSeconds,
|
|
1548
|
+
imageInputCount: request.imageInputCount,
|
|
1549
|
+
imageInputTokens: request.imageInputTokens,
|
|
1550
|
+
imageOutputCount: request.imageOutputCount,
|
|
1551
|
+
imageOutputTokens: request.imageOutputTokens,
|
|
1552
|
+
audioInputTokens: request.audioInputTokens,
|
|
1553
|
+
cachedAudioInputTokens: request.cachedAudioInputTokens,
|
|
1554
|
+
audioOutputTokens: request.audioOutputTokens,
|
|
1535
1555
|
costUsd: response.data.costUSD
|
|
1536
1556
|
},
|
|
1537
1557
|
correlationId: response.correlationId
|
|
@@ -1618,10 +1638,10 @@ var UsageTapClient = class {
|
|
|
1618
1638
|
"incrementCustomMeter requires meterSlot"
|
|
1619
1639
|
);
|
|
1620
1640
|
}
|
|
1621
|
-
if (!["CUSTOM1", "CUSTOM2"].includes(request.meterSlot)) {
|
|
1641
|
+
if (!["CUSTOM1", "CUSTOM2", "AGENTIC_API"].includes(request.meterSlot)) {
|
|
1622
1642
|
throw new UsageTapError(
|
|
1623
1643
|
"USAGETAP_BAD_REQUEST",
|
|
1624
|
-
"meterSlot must be CUSTOM1 or
|
|
1644
|
+
"meterSlot must be CUSTOM1, CUSTOM2 or AGENTIC_API"
|
|
1625
1645
|
);
|
|
1626
1646
|
}
|
|
1627
1647
|
if (typeof request.amount !== "number" || !Number.isFinite(request.amount) || request.amount <= 0) {
|
|
@@ -1694,6 +1714,28 @@ var UsageTapClient = class {
|
|
|
1694
1714
|
let handlerResult;
|
|
1695
1715
|
let handlerError;
|
|
1696
1716
|
let endCallError;
|
|
1717
|
+
let finalizationDeferred = false;
|
|
1718
|
+
let finalizationPromise;
|
|
1719
|
+
const finalize = () => {
|
|
1720
|
+
if (!finalizationPromise) {
|
|
1721
|
+
finalizationPromise = this.endCall(
|
|
1722
|
+
{
|
|
1723
|
+
callId: beginResponse.data.callId,
|
|
1724
|
+
// Pass context for metric tracking
|
|
1725
|
+
customerId: beginRequest.customerId,
|
|
1726
|
+
feature: beginRequest.feature ?? this.defaultFeature,
|
|
1727
|
+
tags: beginRequest.tags ?? this.defaultTags,
|
|
1728
|
+
...usage,
|
|
1729
|
+
error: errorPayload
|
|
1730
|
+
},
|
|
1731
|
+
{
|
|
1732
|
+
...options,
|
|
1733
|
+
correlationId: beginResponse.correlationId
|
|
1734
|
+
}
|
|
1735
|
+
).then(() => void 0);
|
|
1736
|
+
}
|
|
1737
|
+
return finalizationPromise;
|
|
1738
|
+
};
|
|
1697
1739
|
const context = {
|
|
1698
1740
|
begin: beginResponse,
|
|
1699
1741
|
setUsage: (u) => {
|
|
@@ -1701,6 +1743,10 @@ var UsageTapClient = class {
|
|
|
1701
1743
|
},
|
|
1702
1744
|
setError: (err) => {
|
|
1703
1745
|
errorPayload = err;
|
|
1746
|
+
},
|
|
1747
|
+
deferFinalization: () => {
|
|
1748
|
+
finalizationDeferred = true;
|
|
1749
|
+
return finalize;
|
|
1704
1750
|
}
|
|
1705
1751
|
};
|
|
1706
1752
|
try {
|
|
@@ -1714,24 +1760,12 @@ var UsageTapClient = class {
|
|
|
1714
1760
|
};
|
|
1715
1761
|
}
|
|
1716
1762
|
} finally {
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
feature: beginRequest.feature ?? this.defaultFeature,
|
|
1724
|
-
tags: beginRequest.tags ?? this.defaultTags,
|
|
1725
|
-
...usage,
|
|
1726
|
-
error: errorPayload
|
|
1727
|
-
},
|
|
1728
|
-
{
|
|
1729
|
-
...options,
|
|
1730
|
-
correlationId: beginResponse.correlationId
|
|
1731
|
-
}
|
|
1732
|
-
);
|
|
1733
|
-
} catch (error) {
|
|
1734
|
-
endCallError = error;
|
|
1763
|
+
if (handlerError || !finalizationDeferred) {
|
|
1764
|
+
try {
|
|
1765
|
+
await finalize();
|
|
1766
|
+
} catch (error) {
|
|
1767
|
+
endCallError = error;
|
|
1768
|
+
}
|
|
1735
1769
|
}
|
|
1736
1770
|
}
|
|
1737
1771
|
if (handlerError) {
|
|
@@ -2073,7 +2107,8 @@ var UsageTapClient = class {
|
|
|
2073
2107
|
}
|
|
2074
2108
|
toHttpError(status, payload, correlationId) {
|
|
2075
2109
|
const code = mapStatusToErrorCode(status);
|
|
2076
|
-
const
|
|
2110
|
+
const apiCode = payload?.error?.code ?? payload?.result?.code ?? "UNKNOWN";
|
|
2111
|
+
const retryable = isRetryableStatus(status) || isRetryableApiCode(apiCode);
|
|
2077
2112
|
const message = payload?.error?.message ?? payload?.result?.message ?? `UsageTap responded with HTTP ${status}`;
|
|
2078
2113
|
return new UsageTapError(code, message, {
|
|
2079
2114
|
status,
|
|
@@ -2106,7 +2141,7 @@ function isRetryableStatus(status) {
|
|
|
2106
2141
|
}
|
|
2107
2142
|
function isRetryableApiCode(code) {
|
|
2108
2143
|
const normalized = code.toUpperCase();
|
|
2109
|
-
return normalized.includes("TRANSIENT") || normalized.includes("RETRY") || normalized.includes("TIMEOUT") || normalized.includes("THROTTLE") || normalized.includes("RATE_LIMIT");
|
|
2144
|
+
return normalized === "PAYG_CONFLICT" || normalized.includes("TRANSIENT") || normalized.includes("RETRY") || normalized.includes("TIMEOUT") || normalized.includes("THROTTLE") || normalized.includes("RATE_LIMIT");
|
|
2110
2145
|
}
|
|
2111
2146
|
function mapApiCodeToError(code) {
|
|
2112
2147
|
const normalized = code.toUpperCase();
|
|
@@ -2246,25 +2281,29 @@ function createOpenAIAdapter(init) {
|
|
|
2246
2281
|
const result = await usageTap.withUsage(
|
|
2247
2282
|
params.begin,
|
|
2248
2283
|
async (ctx) => {
|
|
2284
|
+
const settle = deferUsageFinalization(ctx);
|
|
2249
2285
|
ctx.setUsage({ providerUsed: provider });
|
|
2250
2286
|
const { stream, onComplete } = await params.call(client, {
|
|
2251
2287
|
hints: ctx.begin.data.vendorHints,
|
|
2252
2288
|
begin: ctx.begin
|
|
2253
2289
|
});
|
|
2254
|
-
const wrapped = wrapStreamForUsageTap(stream, async () => {
|
|
2255
|
-
if (!onComplete) return;
|
|
2290
|
+
const wrapped = wrapStreamForUsageTap(stream, async (termination) => {
|
|
2256
2291
|
try {
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2292
|
+
if (termination === "complete" && onComplete) {
|
|
2293
|
+
const maybeUsage = await onComplete();
|
|
2294
|
+
if (maybeUsage) {
|
|
2295
|
+
ctx.setUsage(maybeUsage);
|
|
2296
|
+
}
|
|
2260
2297
|
}
|
|
2261
2298
|
} catch (error) {
|
|
2262
2299
|
ctx.setError({
|
|
2263
2300
|
code: "USAGE_FINALIZE_ERROR",
|
|
2264
2301
|
message: error instanceof Error ? error.message : String(error)
|
|
2265
2302
|
});
|
|
2303
|
+
await settle();
|
|
2266
2304
|
throw error;
|
|
2267
2305
|
}
|
|
2306
|
+
await settle();
|
|
2268
2307
|
}, ctx);
|
|
2269
2308
|
const finalize = async () => {
|
|
2270
2309
|
await wrapped.__usageTapFinalize?.();
|
|
@@ -2665,13 +2704,13 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
|
|
|
2665
2704
|
withUsage,
|
|
2666
2705
|
promptCompression
|
|
2667
2706
|
} = splitUsageOptions(options);
|
|
2668
|
-
const beginRequest =
|
|
2707
|
+
const beginRequest = responsesBeginRequest(
|
|
2669
2708
|
resolveBeginRequest(defaultContext, usageContext),
|
|
2670
|
-
|
|
2671
|
-
String(params.model)
|
|
2709
|
+
params
|
|
2672
2710
|
);
|
|
2673
2711
|
const wantsStream = isStreamingRequest(params);
|
|
2674
2712
|
return usageTap.withUsage(beginRequest, async (ctx) => {
|
|
2713
|
+
const settle = wantsStream ? deferUsageFinalization(ctx) : void 0;
|
|
2675
2714
|
const sampleStartedAt = Date.now();
|
|
2676
2715
|
const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredOpenAISampleDecision({
|
|
2677
2716
|
usageTap,
|
|
@@ -2696,12 +2735,31 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
|
|
|
2696
2735
|
const apiPromise2 = originalCreate(finalParams, request);
|
|
2697
2736
|
const wrappedPromise2 = transformApiPromise(apiPromise2, (rawStream) => {
|
|
2698
2737
|
ensureAsyncIterable(rawStream, "responses.create");
|
|
2699
|
-
const wrappedStream = wrapStreamForUsageTap(rawStream, async () => {
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2738
|
+
const wrappedStream = wrapStreamForUsageTap(rawStream, async (termination) => {
|
|
2739
|
+
try {
|
|
2740
|
+
if (termination === "complete") {
|
|
2741
|
+
const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
|
|
2742
|
+
if (usage) {
|
|
2743
|
+
ctx.setUsage(usage);
|
|
2744
|
+
}
|
|
2745
|
+
}
|
|
2746
|
+
} catch (error) {
|
|
2747
|
+
ctx.setError({
|
|
2748
|
+
code: "USAGE_FINALIZE_ERROR",
|
|
2749
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2750
|
+
});
|
|
2751
|
+
throw error;
|
|
2752
|
+
} finally {
|
|
2753
|
+
await settle?.();
|
|
2703
2754
|
}
|
|
2704
|
-
}, ctx)
|
|
2755
|
+
}, ctx, (chunk) => {
|
|
2756
|
+
tryInferUsageFromStreamChunk(
|
|
2757
|
+
chunk,
|
|
2758
|
+
ctx.begin.data.vendorHints,
|
|
2759
|
+
ctx,
|
|
2760
|
+
provider
|
|
2761
|
+
);
|
|
2762
|
+
});
|
|
2705
2763
|
return wrappedStream;
|
|
2706
2764
|
});
|
|
2707
2765
|
return wrappedPromise2;
|
|
@@ -2757,13 +2815,10 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
2757
2815
|
withUsage,
|
|
2758
2816
|
promptCompression
|
|
2759
2817
|
} = splitUsageOptions(options);
|
|
2760
|
-
const beginRequest =
|
|
2761
|
-
resolveBeginRequest(defaultContext, usageContext),
|
|
2762
|
-
"openai",
|
|
2763
|
-
String(params.model)
|
|
2764
|
-
);
|
|
2818
|
+
const beginRequest = resolveBeginRequest(defaultContext, usageContext);
|
|
2765
2819
|
const wantsStream = isStreamingRequest(params);
|
|
2766
2820
|
return usageTap.withUsage(beginRequest, async (ctx) => {
|
|
2821
|
+
const settle = wantsStream ? deferUsageFinalization(ctx) : void 0;
|
|
2767
2822
|
const sampleStartedAt = Date.now();
|
|
2768
2823
|
const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredOpenAISampleDecision({
|
|
2769
2824
|
usageTap,
|
|
@@ -2772,7 +2827,7 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
2772
2827
|
input: params
|
|
2773
2828
|
});
|
|
2774
2829
|
const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
|
|
2775
|
-
const
|
|
2830
|
+
const compressedParams = await compressChatParamsForCall({
|
|
2776
2831
|
params: hintedParams,
|
|
2777
2832
|
usageTap,
|
|
2778
2833
|
ctx,
|
|
@@ -2782,18 +2837,38 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
2782
2837
|
withUsage,
|
|
2783
2838
|
operation: "chat.completions.create"
|
|
2784
2839
|
});
|
|
2840
|
+
const finalParams = wantsStream ? ensureOpenAIStreamUsage(compressedParams) : compressedParams;
|
|
2785
2841
|
ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
|
|
2786
2842
|
const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
|
|
2787
2843
|
if (wantsStream) {
|
|
2788
2844
|
const apiPromise2 = originalCreate(finalParams, request);
|
|
2789
2845
|
const wrappedPromise2 = transformApiPromise(apiPromise2, (rawStream) => {
|
|
2790
2846
|
ensureAsyncIterable(rawStream, "chat.completions.create");
|
|
2791
|
-
const wrappedStream2 = wrapStreamForUsageTap(rawStream, async () => {
|
|
2792
|
-
|
|
2793
|
-
|
|
2794
|
-
|
|
2847
|
+
const wrappedStream2 = wrapStreamForUsageTap(rawStream, async (termination) => {
|
|
2848
|
+
try {
|
|
2849
|
+
if (termination === "complete") {
|
|
2850
|
+
const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
|
|
2851
|
+
if (usage) {
|
|
2852
|
+
ctx.setUsage(usage);
|
|
2853
|
+
}
|
|
2854
|
+
}
|
|
2855
|
+
} catch (error) {
|
|
2856
|
+
ctx.setError({
|
|
2857
|
+
code: "USAGE_FINALIZE_ERROR",
|
|
2858
|
+
message: error instanceof Error ? error.message : String(error)
|
|
2859
|
+
});
|
|
2860
|
+
throw error;
|
|
2861
|
+
} finally {
|
|
2862
|
+
await settle?.();
|
|
2795
2863
|
}
|
|
2796
|
-
}, ctx)
|
|
2864
|
+
}, ctx, (chunk) => {
|
|
2865
|
+
tryInferUsageFromStreamChunk(
|
|
2866
|
+
chunk,
|
|
2867
|
+
ctx.begin.data.vendorHints,
|
|
2868
|
+
ctx,
|
|
2869
|
+
provider
|
|
2870
|
+
);
|
|
2871
|
+
});
|
|
2797
2872
|
return wrappedStream2;
|
|
2798
2873
|
});
|
|
2799
2874
|
return wrappedPromise2;
|
|
@@ -2835,14 +2910,11 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
2835
2910
|
withUsage,
|
|
2836
2911
|
promptCompression
|
|
2837
2912
|
} = splitUsageOptions(options);
|
|
2838
|
-
const beginRequest =
|
|
2839
|
-
resolveBeginRequest(defaultContext, usageContext),
|
|
2840
|
-
"openai",
|
|
2841
|
-
String(params.model)
|
|
2842
|
-
);
|
|
2913
|
+
const beginRequest = resolveBeginRequest(defaultContext, usageContext);
|
|
2843
2914
|
return usageTap.withUsage(beginRequest, async (ctx) => {
|
|
2915
|
+
const settle = deferUsageFinalization(ctx);
|
|
2844
2916
|
const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
|
|
2845
|
-
const
|
|
2917
|
+
const compressedParams = await compressChatParamsForCall({
|
|
2846
2918
|
params: hintedParams,
|
|
2847
2919
|
usageTap,
|
|
2848
2920
|
ctx,
|
|
@@ -2852,21 +2924,41 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
2852
2924
|
withUsage,
|
|
2853
2925
|
operation: "chat.completions.stream"
|
|
2854
2926
|
});
|
|
2927
|
+
const finalParams = ensureOpenAIStreamUsage(compressedParams);
|
|
2855
2928
|
ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
|
|
2856
2929
|
const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
|
|
2857
2930
|
const apiPromise = originalStream(finalParams, request);
|
|
2858
2931
|
const wrappedPromise = transformApiPromise(apiPromise, (rawStream) => {
|
|
2859
2932
|
ensureAsyncIterable(rawStream, "chat.completions.stream");
|
|
2860
|
-
const wrappedStreamInner = wrapStreamForUsageTap(rawStream, async () => {
|
|
2861
|
-
|
|
2862
|
-
|
|
2933
|
+
const wrappedStreamInner = wrapStreamForUsageTap(rawStream, async (termination) => {
|
|
2934
|
+
try {
|
|
2935
|
+
if (termination === "complete") {
|
|
2936
|
+
const usage = await extractUsageFromStream(
|
|
2937
|
+
rawStream,
|
|
2938
|
+
ctx.begin.data.vendorHints,
|
|
2939
|
+
provider
|
|
2940
|
+
);
|
|
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();
|
|
2953
|
+
}
|
|
2954
|
+
}, ctx, (chunk) => {
|
|
2955
|
+
tryInferUsageFromStreamChunk(
|
|
2956
|
+
chunk,
|
|
2863
2957
|
ctx.begin.data.vendorHints,
|
|
2958
|
+
ctx,
|
|
2864
2959
|
provider
|
|
2865
2960
|
);
|
|
2866
|
-
|
|
2867
|
-
ctx.setUsage(usage);
|
|
2868
|
-
}
|
|
2869
|
-
}, ctx);
|
|
2961
|
+
});
|
|
2870
2962
|
return wrappedStreamInner;
|
|
2871
2963
|
});
|
|
2872
2964
|
return wrappedPromise;
|
|
@@ -2886,70 +2978,19 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
2886
2978
|
return new Proxy(resource, handler);
|
|
2887
2979
|
}
|
|
2888
2980
|
async function compressChatParamsForCall(args) {
|
|
2889
|
-
|
|
2981
|
+
const compression = resolveEffectivePromptCompressionOptions(
|
|
2890
2982
|
args.defaultPromptCompression,
|
|
2891
2983
|
args.callPromptCompression
|
|
2892
2984
|
);
|
|
2893
|
-
const runtimePolicy = args.ctx.begin.data.runtimeCompressionPolicy;
|
|
2894
|
-
const policyDriven = !compression && Boolean(runtimePolicy);
|
|
2895
|
-
if (policyDriven && runtimePolicy) {
|
|
2896
|
-
if (!runtimePolicy.selected) {
|
|
2897
|
-
args.ctx.setUsage({
|
|
2898
|
-
runtimeCompression: bypassedRuntimeMeasurement(runtimePolicy)
|
|
2899
|
-
});
|
|
2900
|
-
return args.params;
|
|
2901
|
-
}
|
|
2902
|
-
compression = openAIPolicyCompressionOptions(runtimePolicy);
|
|
2903
|
-
if (runtimePolicy.mode === "SHADOW") {
|
|
2904
|
-
const startedAt2 = Date.now();
|
|
2905
|
-
void compressChatParams(
|
|
2906
|
-
args.params,
|
|
2907
|
-
args.usageTap,
|
|
2908
|
-
compression,
|
|
2909
|
-
args.withUsage?.signal
|
|
2910
|
-
).then((outcome2) => {
|
|
2911
|
-
args.ctx.setUsage({
|
|
2912
|
-
runtimeCompression: openAIRuntimeMeasurement({
|
|
2913
|
-
policy: runtimePolicy,
|
|
2914
|
-
telemetry: buildPromptCompressionTelemetry(outcome2.segments),
|
|
2915
|
-
latencyMs: Date.now() - startedAt2,
|
|
2916
|
-
shadow: true,
|
|
2917
|
-
originalPayload: args.params,
|
|
2918
|
-
resultPayload: outcome2.params
|
|
2919
|
-
})
|
|
2920
|
-
});
|
|
2921
|
-
}).catch(() => {
|
|
2922
|
-
args.ctx.setUsage({
|
|
2923
|
-
runtimeCompression: failedRuntimeMeasurement(
|
|
2924
|
-
runtimePolicy,
|
|
2925
|
-
Date.now() - startedAt2
|
|
2926
|
-
)
|
|
2927
|
-
});
|
|
2928
|
-
});
|
|
2929
|
-
return args.params;
|
|
2930
|
-
}
|
|
2931
|
-
}
|
|
2932
2985
|
if (!compression) {
|
|
2933
2986
|
return args.params;
|
|
2934
2987
|
}
|
|
2935
|
-
const startedAt = Date.now();
|
|
2936
2988
|
const outcome = await compressChatParams(
|
|
2937
2989
|
args.params,
|
|
2938
2990
|
args.usageTap,
|
|
2939
2991
|
compression,
|
|
2940
2992
|
args.withUsage?.signal
|
|
2941
2993
|
);
|
|
2942
|
-
if (policyDriven && runtimePolicy) {
|
|
2943
|
-
const runtimeMeasurement = openAIRuntimeMeasurement({
|
|
2944
|
-
policy: runtimePolicy,
|
|
2945
|
-
telemetry: buildPromptCompressionTelemetry(outcome.segments),
|
|
2946
|
-
latencyMs: Date.now() - startedAt,
|
|
2947
|
-
originalPayload: args.params,
|
|
2948
|
-
resultPayload: outcome.params
|
|
2949
|
-
});
|
|
2950
|
-
args.ctx.setUsage({ runtimeCompression: runtimeMeasurement });
|
|
2951
|
-
if (runtimeMeasurement.decision !== "compressed") return args.params;
|
|
2952
|
-
}
|
|
2953
2994
|
await recordCompressionOutcome({
|
|
2954
2995
|
outcome,
|
|
2955
2996
|
compression,
|
|
@@ -2962,70 +3003,19 @@ async function compressChatParamsForCall(args) {
|
|
|
2962
3003
|
return outcome.params;
|
|
2963
3004
|
}
|
|
2964
3005
|
async function compressResponsesParamsForCall(args) {
|
|
2965
|
-
|
|
3006
|
+
const compression = resolveEffectivePromptCompressionOptions(
|
|
2966
3007
|
args.defaultPromptCompression,
|
|
2967
3008
|
args.callPromptCompression
|
|
2968
3009
|
);
|
|
2969
|
-
const runtimePolicy = args.ctx.begin.data.runtimeCompressionPolicy;
|
|
2970
|
-
const policyDriven = !compression && Boolean(runtimePolicy);
|
|
2971
|
-
if (policyDriven && runtimePolicy) {
|
|
2972
|
-
if (!runtimePolicy.selected) {
|
|
2973
|
-
args.ctx.setUsage({
|
|
2974
|
-
runtimeCompression: bypassedRuntimeMeasurement(runtimePolicy)
|
|
2975
|
-
});
|
|
2976
|
-
return args.params;
|
|
2977
|
-
}
|
|
2978
|
-
compression = openAIPolicyCompressionOptions(runtimePolicy);
|
|
2979
|
-
if (runtimePolicy.mode === "SHADOW") {
|
|
2980
|
-
const startedAt2 = Date.now();
|
|
2981
|
-
void compressResponsesParams(
|
|
2982
|
-
args.params,
|
|
2983
|
-
args.usageTap,
|
|
2984
|
-
compression,
|
|
2985
|
-
args.withUsage?.signal
|
|
2986
|
-
).then((outcome2) => {
|
|
2987
|
-
args.ctx.setUsage({
|
|
2988
|
-
runtimeCompression: openAIRuntimeMeasurement({
|
|
2989
|
-
policy: runtimePolicy,
|
|
2990
|
-
telemetry: buildPromptCompressionTelemetry(outcome2.segments),
|
|
2991
|
-
latencyMs: Date.now() - startedAt2,
|
|
2992
|
-
shadow: true,
|
|
2993
|
-
originalPayload: args.params,
|
|
2994
|
-
resultPayload: outcome2.params
|
|
2995
|
-
})
|
|
2996
|
-
});
|
|
2997
|
-
}).catch(() => {
|
|
2998
|
-
args.ctx.setUsage({
|
|
2999
|
-
runtimeCompression: failedRuntimeMeasurement(
|
|
3000
|
-
runtimePolicy,
|
|
3001
|
-
Date.now() - startedAt2
|
|
3002
|
-
)
|
|
3003
|
-
});
|
|
3004
|
-
});
|
|
3005
|
-
return args.params;
|
|
3006
|
-
}
|
|
3007
|
-
}
|
|
3008
3010
|
if (!compression) {
|
|
3009
3011
|
return args.params;
|
|
3010
3012
|
}
|
|
3011
|
-
const startedAt = Date.now();
|
|
3012
3013
|
const outcome = await compressResponsesParams(
|
|
3013
3014
|
args.params,
|
|
3014
3015
|
args.usageTap,
|
|
3015
3016
|
compression,
|
|
3016
3017
|
args.withUsage?.signal
|
|
3017
3018
|
);
|
|
3018
|
-
if (policyDriven && runtimePolicy) {
|
|
3019
|
-
const runtimeMeasurement = openAIRuntimeMeasurement({
|
|
3020
|
-
policy: runtimePolicy,
|
|
3021
|
-
telemetry: buildPromptCompressionTelemetry(outcome.segments),
|
|
3022
|
-
latencyMs: Date.now() - startedAt,
|
|
3023
|
-
originalPayload: args.params,
|
|
3024
|
-
resultPayload: outcome.params
|
|
3025
|
-
});
|
|
3026
|
-
args.ctx.setUsage({ runtimeCompression: runtimeMeasurement });
|
|
3027
|
-
if (runtimeMeasurement.decision !== "compressed") return args.params;
|
|
3028
|
-
}
|
|
3029
3019
|
await recordCompressionOutcome({
|
|
3030
3020
|
outcome,
|
|
3031
3021
|
compression,
|
|
@@ -3037,79 +3027,6 @@ async function compressResponsesParamsForCall(args) {
|
|
|
3037
3027
|
});
|
|
3038
3028
|
return outcome.params;
|
|
3039
3029
|
}
|
|
3040
|
-
function openAIPolicyCompressionOptions(policy) {
|
|
3041
|
-
const roles = Object.fromEntries(
|
|
3042
|
-
["system", "user", "tool", "assistant"].filter((role) => policy.snapshot.scope.messageRoles.includes(role)).map((role) => [role, true])
|
|
3043
|
-
);
|
|
3044
|
-
return {
|
|
3045
|
-
provider: "usagetap",
|
|
3046
|
-
roles,
|
|
3047
|
-
minContextTokens: policy.snapshot.scope.minimumTokens,
|
|
3048
|
-
failOpen: true
|
|
3049
|
-
};
|
|
3050
|
-
}
|
|
3051
|
-
function bypassedRuntimeMeasurement(policy) {
|
|
3052
|
-
return {
|
|
3053
|
-
policyId: policy.policyId,
|
|
3054
|
-
policyVersionId: policy.policyVersionId,
|
|
3055
|
-
policyVersion: policy.version,
|
|
3056
|
-
rolloutMode: policy.mode,
|
|
3057
|
-
decision: "bypassed",
|
|
3058
|
-
reasonCode: "not_in_rollout_sample",
|
|
3059
|
-
methodsAttempted: [],
|
|
3060
|
-
methodsApplied: [],
|
|
3061
|
-
originalTokens: 0,
|
|
3062
|
-
resultTokens: 0,
|
|
3063
|
-
savedTokens: 0,
|
|
3064
|
-
reductionPercentage: 0,
|
|
3065
|
-
compressionLatencyMs: 0,
|
|
3066
|
-
compressionComputeCostUsd: 0,
|
|
3067
|
-
financialsEstimated: true
|
|
3068
|
-
};
|
|
3069
|
-
}
|
|
3070
|
-
function failedRuntimeMeasurement(policy, latencyMs) {
|
|
3071
|
-
return {
|
|
3072
|
-
...bypassedRuntimeMeasurement(policy),
|
|
3073
|
-
decision: "fallback",
|
|
3074
|
-
reasonCode: "transformation_failed",
|
|
3075
|
-
methodsAttempted: ["deterministic"],
|
|
3076
|
-
compressionLatencyMs: latencyMs
|
|
3077
|
-
};
|
|
3078
|
-
}
|
|
3079
|
-
function openAIRuntimeMeasurement(input) {
|
|
3080
|
-
const telemetry = input.telemetry;
|
|
3081
|
-
const originalTokens = telemetry?.originalTokens ?? estimatePromptTokens(JSON.stringify(input.originalPayload));
|
|
3082
|
-
const resultTokens = telemetry?.compressedTokens ?? originalTokens;
|
|
3083
|
-
const savedTokens = Math.max(0, telemetry?.savedTokens ?? 0);
|
|
3084
|
-
const reductionPercentage = originalTokens > 0 ? savedTokens / originalTokens * 100 : 0;
|
|
3085
|
-
const belowMinimumSize = originalTokens < input.policy.snapshot.scope.minimumTokens;
|
|
3086
|
-
const latencyExceeded = input.latencyMs > input.policy.snapshot.gates.maximumLatencyMs;
|
|
3087
|
-
const belowSavings = savedTokens < input.policy.snapshot.gates.minimumTokensRemoved || reductionPercentage < input.policy.snapshot.gates.minimumReductionPercentage;
|
|
3088
|
-
const accepted = !belowMinimumSize && !latencyExceeded && !belowSavings;
|
|
3089
|
-
const deepText = input.policy.snapshot.methods.deepText.enabled && !input.policy.snapshot.rollout.deterministicOnly && (input.policy.mode !== "CANARY" || input.policy.snapshot.rollout.deepTextCanaryAllowed);
|
|
3090
|
-
const reasonCode = belowMinimumSize ? "below_minimum_size" : latencyExceeded ? "latency_budget_exceeded" : belowSavings ? "below_minimum_savings" : void 0;
|
|
3091
|
-
const measurement = {
|
|
3092
|
-
policyId: input.policy.policyId,
|
|
3093
|
-
policyVersionId: input.policy.policyVersionId,
|
|
3094
|
-
policyVersion: input.policy.version,
|
|
3095
|
-
rolloutMode: input.policy.mode,
|
|
3096
|
-
decision: accepted ? input.shadow ? "bypassed" : "compressed" : latencyExceeded ? "fallback" : "skipped",
|
|
3097
|
-
...reasonCode ? { reasonCode } : {},
|
|
3098
|
-
methodsAttempted: [
|
|
3099
|
-
"deterministic",
|
|
3100
|
-
...deepText ? ["deep_text"] : []
|
|
3101
|
-
],
|
|
3102
|
-
methodsApplied: accepted ? ["deterministic"] : [],
|
|
3103
|
-
originalTokens,
|
|
3104
|
-
resultTokens: accepted ? resultTokens : originalTokens,
|
|
3105
|
-
savedTokens: accepted ? savedTokens : 0,
|
|
3106
|
-
reductionPercentage: accepted ? reductionPercentage : 0,
|
|
3107
|
-
compressionLatencyMs: input.latencyMs,
|
|
3108
|
-
compressionComputeCostUsd: 0,
|
|
3109
|
-
financialsEstimated: true
|
|
3110
|
-
};
|
|
3111
|
-
return measurement;
|
|
3112
|
-
}
|
|
3113
3030
|
async function recordCompressionOutcome(args) {
|
|
3114
3031
|
const telemetry = buildPromptCompressionTelemetry(args.outcome.segments);
|
|
3115
3032
|
if (!telemetry) {
|
|
@@ -3632,10 +3549,6 @@ function resolveBeginRequest(defaults, override) {
|
|
|
3632
3549
|
}
|
|
3633
3550
|
const tags = mergeTags(base.tags, current.tags);
|
|
3634
3551
|
const begin = { customerId };
|
|
3635
|
-
const runtimeCompressionContext = current.runtimeCompressionContext ?? base.runtimeCompressionContext;
|
|
3636
|
-
if (runtimeCompressionContext) {
|
|
3637
|
-
begin.runtimeCompressionContext = runtimeCompressionContext;
|
|
3638
|
-
}
|
|
3639
3552
|
const requested = current.requested ?? base.requested;
|
|
3640
3553
|
if (requested) begin.requested = requested;
|
|
3641
3554
|
const feature = current.feature ?? base.feature;
|
|
@@ -3656,8 +3569,6 @@ function resolveBeginRequest(defaults, override) {
|
|
|
3656
3569
|
if (customerUserEmail) begin.customerUserEmail = customerUserEmail;
|
|
3657
3570
|
const stripeCustomerId = current.stripeCustomerId ?? base.stripeCustomerId;
|
|
3658
3571
|
if (stripeCustomerId) begin.stripeCustomerId = stripeCustomerId;
|
|
3659
|
-
const holdUsd = current.holdUsd ?? base.holdUsd;
|
|
3660
|
-
if (typeof holdUsd === "number") begin.holdUsd = holdUsd;
|
|
3661
3572
|
const batch = current.batch ?? base.batch;
|
|
3662
3573
|
if (typeof batch === "boolean") begin.batch = batch;
|
|
3663
3574
|
const pricingMode = current.pricingMode ?? base.pricingMode;
|
|
@@ -3667,16 +3578,25 @@ function resolveBeginRequest(defaults, override) {
|
|
|
3667
3578
|
}
|
|
3668
3579
|
return begin;
|
|
3669
3580
|
}
|
|
3670
|
-
function
|
|
3581
|
+
function responsesBeginRequest(begin, params) {
|
|
3582
|
+
if (!responsesRequestUsesWebSearch(params)) return begin;
|
|
3671
3583
|
return {
|
|
3672
3584
|
...begin,
|
|
3673
|
-
|
|
3674
|
-
...begin.
|
|
3675
|
-
|
|
3676
|
-
model
|
|
3585
|
+
requested: {
|
|
3586
|
+
...begin.requested ?? {},
|
|
3587
|
+
search: true
|
|
3677
3588
|
}
|
|
3678
3589
|
};
|
|
3679
3590
|
}
|
|
3591
|
+
function responsesRequestUsesWebSearch(params) {
|
|
3592
|
+
if (!params || typeof params !== "object") return false;
|
|
3593
|
+
const tools = params.tools;
|
|
3594
|
+
return Array.isArray(tools) && tools.some((tool) => {
|
|
3595
|
+
if (!tool || typeof tool !== "object") return false;
|
|
3596
|
+
const type = tool.type;
|
|
3597
|
+
return type === "web_search" || type === "web_search_preview";
|
|
3598
|
+
});
|
|
3599
|
+
}
|
|
3680
3600
|
function transformApiPromise(apiPromise, onResolve, onReject) {
|
|
3681
3601
|
const resolvedPromise = Promise.resolve(apiPromise).then(onResolve, onReject);
|
|
3682
3602
|
if (isObjectRecord(apiPromise)) {
|
|
@@ -3907,6 +3827,11 @@ function inferUsageFromResponse(response, hints, provider = "openai") {
|
|
|
3907
3827
|
return void 0;
|
|
3908
3828
|
}
|
|
3909
3829
|
const cachedInputTokens = candidate.usage.prompt_tokens_details?.cached_tokens ?? candidate.usage.input_tokens_details?.cached_tokens ?? candidate.usage.cached_tokens;
|
|
3830
|
+
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;
|
|
3831
|
+
const cacheWrite5mInputTokens = candidate.usage.cache_write_5m_input_tokens ?? candidate.usage.cache_creation?.ephemeral_5m_input_tokens;
|
|
3832
|
+
const cacheWrite1hInputTokens = candidate.usage.cache_write_1h_input_tokens ?? candidate.usage.cache_creation?.ephemeral_1h_input_tokens;
|
|
3833
|
+
const outputSearches = Array.isArray(candidate.output) ? candidate.output.filter((item) => item?.type === "web_search_call").length : 0;
|
|
3834
|
+
const searches = candidate.usage.searches ?? candidate.usage.web_search_queries ?? candidate.usage.server_tool_use?.web_search_requests ?? outputSearches;
|
|
3910
3835
|
const responseEffort = normalizeExecutionReasoningEffort(
|
|
3911
3836
|
candidate.reasoning?.effort
|
|
3912
3837
|
);
|
|
@@ -3916,12 +3841,21 @@ function inferUsageFromResponse(response, hints, provider = "openai") {
|
|
|
3916
3841
|
inputTokens: candidate.usage.prompt_tokens ?? candidate.usage.input_tokens,
|
|
3917
3842
|
responseTokens: candidate.usage.completion_tokens ?? candidate.usage.output_tokens,
|
|
3918
3843
|
cachedInputTokens,
|
|
3844
|
+
cacheWriteInputTokens,
|
|
3845
|
+
cacheWrite5mInputTokens,
|
|
3846
|
+
cacheWrite1hInputTokens,
|
|
3847
|
+
audioInputTokens: candidate.usage.prompt_tokens_details?.audio_tokens ?? candidate.usage.input_tokens_details?.audio_tokens,
|
|
3848
|
+
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,
|
|
3849
|
+
imageInputTokens: candidate.usage.prompt_tokens_details?.image_tokens ?? candidate.usage.input_tokens_details?.image_tokens,
|
|
3850
|
+
imageOutputTokens: candidate.usage.completion_tokens_details?.image_tokens ?? candidate.usage.output_tokens_details?.image_tokens,
|
|
3851
|
+
audioOutputTokens: candidate.usage.completion_tokens_details?.audio_tokens ?? candidate.usage.output_tokens_details?.audio_tokens,
|
|
3919
3852
|
reasoningTokens: candidate.usage.completion_tokens_details?.reasoning_tokens ?? candidate.usage.output_tokens_details?.reasoning_tokens,
|
|
3920
3853
|
...responseEffort ? {
|
|
3921
3854
|
reasoningEffort: responseEffort,
|
|
3922
3855
|
reasoningEffortSource: "provider_response"
|
|
3923
3856
|
} : {},
|
|
3924
|
-
...typeof candidate.reasoning?.type === "string" ? { reasoningMode: candidate.reasoning.type } : typeof candidate.reasoning?.mode === "string" ? { reasoningMode: candidate.reasoning.mode } : {}
|
|
3857
|
+
...typeof candidate.reasoning?.type === "string" ? { reasoningMode: candidate.reasoning.type } : typeof candidate.reasoning?.mode === "string" ? { reasoningMode: candidate.reasoning.mode } : {},
|
|
3858
|
+
...typeof searches === "number" && searches > 0 ? { searches } : {}
|
|
3925
3859
|
};
|
|
3926
3860
|
}
|
|
3927
3861
|
function normalizeExecutionReasoningEffort(value) {
|
|
@@ -3938,29 +3872,64 @@ function openAIRequestExecutionMetadata(params, provider) {
|
|
|
3938
3872
|
const budget = typeof rawBudget === "number" && Number.isInteger(rawBudget) && rawBudget >= 0 ? rawBudget : void 0;
|
|
3939
3873
|
return {
|
|
3940
3874
|
providerUsed: provider,
|
|
3875
|
+
...typeof record.model === "string" ? { modelUsed: record.model } : {},
|
|
3941
3876
|
...effort ? { reasoningEffort: effort, reasoningEffortSource: "provider_request" } : {},
|
|
3942
3877
|
...mode ? { reasoningMode: mode } : {},
|
|
3943
3878
|
...budget !== void 0 ? { reasoningBudgetTokens: budget } : {}
|
|
3944
3879
|
};
|
|
3945
3880
|
}
|
|
3946
|
-
function
|
|
3881
|
+
function tryInferUsageFromStreamChunk(chunk, hints, ctx, provider) {
|
|
3882
|
+
const payload = isObjectRecord(chunk) && isObjectRecord(chunk.response) ? chunk.response : chunk;
|
|
3883
|
+
const inferred = inferUsageFromResponse(payload, hints, provider);
|
|
3884
|
+
if (inferred) {
|
|
3885
|
+
ctx.setUsage(inferred);
|
|
3886
|
+
}
|
|
3887
|
+
}
|
|
3888
|
+
function ensureOpenAIStreamUsage(params) {
|
|
3889
|
+
if (!isObjectRecord(params)) {
|
|
3890
|
+
return params;
|
|
3891
|
+
}
|
|
3892
|
+
const streamOptions = isObjectRecord(params.stream_options) ? params.stream_options : {};
|
|
3893
|
+
return {
|
|
3894
|
+
...params,
|
|
3895
|
+
stream_options: {
|
|
3896
|
+
...streamOptions,
|
|
3897
|
+
include_usage: true
|
|
3898
|
+
}
|
|
3899
|
+
};
|
|
3900
|
+
}
|
|
3901
|
+
function deferUsageFinalization(ctx) {
|
|
3902
|
+
return ctx.deferFinalization?.() ?? (() => Promise.resolve());
|
|
3903
|
+
}
|
|
3904
|
+
function wrapStreamForUsageTap(source, finalize, ctx, onChunk) {
|
|
3947
3905
|
const getIterator = source[Symbol.asyncIterator];
|
|
3948
3906
|
if (typeof getIterator !== "function") {
|
|
3949
3907
|
throw new TypeError("Stream is not async iterable");
|
|
3950
3908
|
}
|
|
3951
3909
|
const iterator = getIterator.call(source);
|
|
3952
3910
|
let completed = false;
|
|
3953
|
-
const invokeFinalize = async () => {
|
|
3911
|
+
const invokeFinalize = async (termination, error) => {
|
|
3954
3912
|
if (completed) return;
|
|
3955
3913
|
completed = true;
|
|
3914
|
+
if (termination === "cancel" || termination === "manual") {
|
|
3915
|
+
ctx.setError({
|
|
3916
|
+
code: "STREAM_ABORTED",
|
|
3917
|
+
message: termination === "cancel" ? "Provider stream consumption was cancelled before completion" : "Provider stream was finalized before completion"
|
|
3918
|
+
});
|
|
3919
|
+
} else if (termination === "error") {
|
|
3920
|
+
ctx.setError({
|
|
3921
|
+
code: "VENDOR_ERROR",
|
|
3922
|
+
message: error instanceof Error ? error.message : String(error)
|
|
3923
|
+
});
|
|
3924
|
+
}
|
|
3956
3925
|
try {
|
|
3957
|
-
await finalize();
|
|
3958
|
-
} catch (
|
|
3926
|
+
await finalize(termination);
|
|
3927
|
+
} catch (error2) {
|
|
3959
3928
|
ctx.setError({
|
|
3960
3929
|
code: "USAGE_FINALIZE_ERROR",
|
|
3961
|
-
message:
|
|
3930
|
+
message: error2 instanceof Error ? error2.message : String(error2)
|
|
3962
3931
|
});
|
|
3963
|
-
throw
|
|
3932
|
+
throw error2;
|
|
3964
3933
|
}
|
|
3965
3934
|
};
|
|
3966
3935
|
const prototype = Object.getPrototypeOf(source) ?? Object.prototype;
|
|
@@ -3984,12 +3953,15 @@ function wrapStreamForUsageTap(source, finalize, ctx) {
|
|
|
3984
3953
|
value: async (...args) => {
|
|
3985
3954
|
try {
|
|
3986
3955
|
const result = await iterator.next(...args);
|
|
3956
|
+
if (!result.done) {
|
|
3957
|
+
onChunk?.(result.value);
|
|
3958
|
+
}
|
|
3987
3959
|
if (result.done) {
|
|
3988
|
-
await invokeFinalize();
|
|
3960
|
+
await invokeFinalize("complete");
|
|
3989
3961
|
}
|
|
3990
3962
|
return result;
|
|
3991
3963
|
} catch (error) {
|
|
3992
|
-
await invokeFinalize().catch(() => void 0);
|
|
3964
|
+
await invokeFinalize("error", error).catch(() => void 0);
|
|
3993
3965
|
throw error;
|
|
3994
3966
|
}
|
|
3995
3967
|
},
|
|
@@ -3998,39 +3970,49 @@ function wrapStreamForUsageTap(source, finalize, ctx) {
|
|
|
3998
3970
|
});
|
|
3999
3971
|
Object.defineProperty(wrapped, "return", {
|
|
4000
3972
|
value: async (value) => {
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
3973
|
+
try {
|
|
3974
|
+
if (typeof iterator.return === "function") {
|
|
3975
|
+
const rawResult = await iterator.return(value);
|
|
3976
|
+
if (!isIteratorResult(rawResult)) {
|
|
3977
|
+
throw new TypeError("Iterator.return() returned an invalid result");
|
|
3978
|
+
}
|
|
3979
|
+
await invokeFinalize("cancel");
|
|
3980
|
+
return rawResult;
|
|
4005
3981
|
}
|
|
4006
|
-
await invokeFinalize();
|
|
4007
|
-
return
|
|
3982
|
+
await invokeFinalize("cancel");
|
|
3983
|
+
return { done: true, value };
|
|
3984
|
+
} catch (error) {
|
|
3985
|
+
await invokeFinalize("error", error).catch(() => void 0);
|
|
3986
|
+
throw error;
|
|
4008
3987
|
}
|
|
4009
|
-
await invokeFinalize();
|
|
4010
|
-
return { done: true, value };
|
|
4011
3988
|
},
|
|
4012
3989
|
configurable: true,
|
|
4013
3990
|
writable: true
|
|
4014
3991
|
});
|
|
4015
3992
|
Object.defineProperty(wrapped, "throw", {
|
|
4016
3993
|
value: async (error) => {
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4020
|
-
|
|
3994
|
+
try {
|
|
3995
|
+
if (typeof iterator.throw === "function") {
|
|
3996
|
+
const rawResult = await iterator.throw(error);
|
|
3997
|
+
if (!isIteratorResult(rawResult)) {
|
|
3998
|
+
throw new TypeError("Iterator.throw() returned an invalid result");
|
|
3999
|
+
}
|
|
4000
|
+
await invokeFinalize("error", error);
|
|
4001
|
+
return rawResult;
|
|
4021
4002
|
}
|
|
4022
|
-
await invokeFinalize();
|
|
4023
|
-
|
|
4003
|
+
await invokeFinalize("error", error);
|
|
4004
|
+
throw error;
|
|
4005
|
+
} catch (thrownError) {
|
|
4006
|
+
await invokeFinalize("error", thrownError).catch(() => void 0);
|
|
4007
|
+
throw thrownError;
|
|
4024
4008
|
}
|
|
4025
|
-
await invokeFinalize();
|
|
4026
|
-
throw error;
|
|
4027
4009
|
},
|
|
4028
4010
|
configurable: true,
|
|
4029
4011
|
writable: true
|
|
4030
4012
|
});
|
|
4031
4013
|
Object.defineProperty(wrapped, "__usageTapFinalize", {
|
|
4032
4014
|
value: async () => {
|
|
4033
|
-
await invokeFinalize();
|
|
4015
|
+
await invokeFinalize("manual");
|
|
4034
4016
|
},
|
|
4035
4017
|
configurable: true
|
|
4036
4018
|
});
|