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