@usagetap/sdk 1.3.2 → 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 +372 -39
- package/dist/adapters/anthropic.cjs +995 -69
- package/dist/adapters/anthropic.cjs.map +1 -1
- package/dist/adapters/anthropic.d.cts +45 -3
- package/dist/adapters/anthropic.d.ts +45 -3
- package/dist/adapters/anthropic.mjs +995 -70
- package/dist/adapters/anthropic.mjs.map +1 -1
- package/dist/adapters/openai.cjs +1208 -106
- package/dist/adapters/openai.cjs.map +1 -1
- package/dist/adapters/openai.d.cts +46 -3
- package/dist/adapters/openai.d.ts +46 -3
- package/dist/adapters/openai.mjs +1208 -107
- package/dist/adapters/openai.mjs.map +1 -1
- package/dist/adapters/openrouter.cjs +3912 -53
- package/dist/adapters/openrouter.cjs.map +1 -1
- package/dist/adapters/openrouter.d.cts +6 -3
- package/dist/adapters/openrouter.d.ts +6 -3
- package/dist/adapters/openrouter.mjs +3910 -54
- package/dist/adapters/openrouter.mjs.map +1 -1
- package/dist/anthropic/index.cjs +995 -69
- package/dist/anthropic/index.cjs.map +1 -1
- package/dist/anthropic/index.d.cts +2 -2
- package/dist/anthropic/index.d.ts +2 -2
- package/dist/anthropic/index.mjs +995 -70
- package/dist/anthropic/index.mjs.map +1 -1
- package/dist/client-C0UiaqVB.d.cts +1305 -0
- package/dist/client-C0UiaqVB.d.ts +1305 -0
- package/dist/express/index.cjs +399 -64
- package/dist/express/index.cjs.map +1 -1
- package/dist/express/index.d.cts +2 -2
- package/dist/express/index.d.ts +2 -2
- package/dist/express/index.mjs +399 -64
- package/dist/express/index.mjs.map +1 -1
- package/dist/index.cjs +1044 -163
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +16 -5
- package/dist/index.d.ts +16 -5
- package/dist/index.mjs +1044 -163
- package/dist/index.mjs.map +1 -1
- package/dist/openai/index.cjs +1209 -107
- package/dist/openai/index.cjs.map +1 -1
- package/dist/openai/index.d.cts +2 -2
- package/dist/openai/index.d.ts +2 -2
- package/dist/openai/index.mjs +1209 -108
- package/dist/openai/index.mjs.map +1 -1
- package/dist/openrouter/index.cjs +1226 -109
- package/dist/openrouter/index.cjs.map +1 -1
- package/dist/openrouter/index.d.cts +3 -3
- package/dist/openrouter/index.d.ts +3 -3
- package/dist/openrouter/index.mjs +1224 -108
- package/dist/openrouter/index.mjs.map +1 -1
- package/dist/react/index.cjs +19 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.d.cts +17 -4
- package/dist/react/index.d.ts +17 -4
- package/dist/react/index.mjs +19 -1
- package/dist/react/index.mjs.map +1 -1
- package/package.json +2 -2
- package/dist/client-BD8O2J8Z.d.cts +0 -668
- package/dist/client-BD8O2J8Z.d.ts +0 -668
package/dist/express/index.mjs
CHANGED
|
@@ -174,6 +174,75 @@ async function pipeToResponse(stream, res, options = {}) {
|
|
|
174
174
|
}
|
|
175
175
|
}
|
|
176
176
|
var USAGETAP_CORRELATION_HEADER = "x-usage-correlation-id";
|
|
177
|
+
function readString(value) {
|
|
178
|
+
return typeof value === "string" && value.trim() ? value : void 0;
|
|
179
|
+
}
|
|
180
|
+
function serializeSamplingError(error) {
|
|
181
|
+
if (error instanceof Error) {
|
|
182
|
+
return { name: error.name, message: error.message };
|
|
183
|
+
}
|
|
184
|
+
return { message: String(error) };
|
|
185
|
+
}
|
|
186
|
+
function normalizeMeteredOpenAISampling(options) {
|
|
187
|
+
if (!options) return void 0;
|
|
188
|
+
if (options === true) return { provider: "openai" };
|
|
189
|
+
const { provider = "openai", ...policyFields } = options;
|
|
190
|
+
return {
|
|
191
|
+
provider,
|
|
192
|
+
policy: typeof policyFields.rate === "number" ? policyFields : void 0
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function startMeteredOpenAISampleDecision({
|
|
196
|
+
usageTap,
|
|
197
|
+
sampling,
|
|
198
|
+
beginRequest,
|
|
199
|
+
input
|
|
200
|
+
}) {
|
|
201
|
+
if (!sampling) return Promise.resolve(false);
|
|
202
|
+
return usageTap.shouldSampleAsync(
|
|
203
|
+
{
|
|
204
|
+
customerId: beginRequest.customerId,
|
|
205
|
+
feature: beginRequest.feature,
|
|
206
|
+
input
|
|
207
|
+
},
|
|
208
|
+
sampling.policy
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
async function captureMeteredOpenAISample({
|
|
212
|
+
usageTap,
|
|
213
|
+
sampling,
|
|
214
|
+
decision,
|
|
215
|
+
ctx,
|
|
216
|
+
beginRequest,
|
|
217
|
+
input,
|
|
218
|
+
response,
|
|
219
|
+
error,
|
|
220
|
+
startedAt
|
|
221
|
+
}) {
|
|
222
|
+
if (!sampling) return;
|
|
223
|
+
let selected = false;
|
|
224
|
+
try {
|
|
225
|
+
selected = await decision;
|
|
226
|
+
} catch {
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
if (!selected) return;
|
|
230
|
+
const record = isObjectRecord(response) ? response : {};
|
|
231
|
+
await usageTap.captureSample({
|
|
232
|
+
sampleId: ctx.begin.data.callId,
|
|
233
|
+
callId: ctx.begin.data.callId,
|
|
234
|
+
customerId: beginRequest.customerId,
|
|
235
|
+
feature: beginRequest.feature,
|
|
236
|
+
tags: beginRequest.tags,
|
|
237
|
+
provider: sampling.provider,
|
|
238
|
+
model: readString(record.model) ?? readString(input.model),
|
|
239
|
+
input,
|
|
240
|
+
...response === void 0 ? {} : { output: response },
|
|
241
|
+
usage: record.usage,
|
|
242
|
+
latencyMs: Date.now() - startedAt,
|
|
243
|
+
...error === void 0 ? {} : { error: serializeSamplingError(error) }
|
|
244
|
+
}).catch(() => void 0);
|
|
245
|
+
}
|
|
177
246
|
function wrapOpenAI(client, usageTap, options = {}) {
|
|
178
247
|
if (!client) {
|
|
179
248
|
throw new UsageTapError("USAGETAP_BAD_REQUEST", "wrapOpenAI requires an OpenAI client instance");
|
|
@@ -181,6 +250,8 @@ function wrapOpenAI(client, usageTap, options = {}) {
|
|
|
181
250
|
const defaultContext = options.defaultContext;
|
|
182
251
|
const applyVendorHints = options.applyVendorHints !== false;
|
|
183
252
|
const defaultPromptCompression = normalizePromptCompressionOptions(options.promptCompression);
|
|
253
|
+
const defaultSampling = normalizeMeteredOpenAISampling(options.sampling);
|
|
254
|
+
const provider = options.provider ?? "openai";
|
|
184
255
|
const promptCompressionStats = new OpenAIPromptCompressionStats();
|
|
185
256
|
const proxiedChat = client.chat ? createChatProxy(
|
|
186
257
|
client.chat,
|
|
@@ -188,7 +259,9 @@ function wrapOpenAI(client, usageTap, options = {}) {
|
|
|
188
259
|
defaultContext,
|
|
189
260
|
applyVendorHints,
|
|
190
261
|
defaultPromptCompression,
|
|
191
|
-
promptCompressionStats
|
|
262
|
+
promptCompressionStats,
|
|
263
|
+
defaultSampling,
|
|
264
|
+
provider
|
|
192
265
|
) : void 0;
|
|
193
266
|
const proxiedResponses = typeof client.responses !== "undefined" ? createResponsesProxy(
|
|
194
267
|
client.responses,
|
|
@@ -196,7 +269,9 @@ function wrapOpenAI(client, usageTap, options = {}) {
|
|
|
196
269
|
defaultContext,
|
|
197
270
|
applyVendorHints,
|
|
198
271
|
defaultPromptCompression,
|
|
199
|
-
promptCompressionStats
|
|
272
|
+
promptCompressionStats,
|
|
273
|
+
defaultSampling,
|
|
274
|
+
provider
|
|
200
275
|
) : void 0;
|
|
201
276
|
const handler = {
|
|
202
277
|
get(target, prop, receiver) {
|
|
@@ -223,14 +298,16 @@ function wrapOpenAI(client, usageTap, options = {}) {
|
|
|
223
298
|
};
|
|
224
299
|
return new Proxy(client, handler);
|
|
225
300
|
}
|
|
226
|
-
function createChatProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
|
|
301
|
+
function createChatProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats, defaultSampling, provider) {
|
|
227
302
|
const completions = createChatCompletionsProxy(
|
|
228
303
|
resource.completions,
|
|
229
304
|
usageTap,
|
|
230
305
|
defaultContext,
|
|
231
306
|
applyVendorHints,
|
|
232
307
|
defaultPromptCompression,
|
|
233
|
-
promptCompressionStats
|
|
308
|
+
promptCompressionStats,
|
|
309
|
+
defaultSampling,
|
|
310
|
+
provider
|
|
234
311
|
);
|
|
235
312
|
const handler = {
|
|
236
313
|
get(target, prop, receiver) {
|
|
@@ -242,7 +319,7 @@ function createChatProxy(resource, usageTap, defaultContext, applyVendorHints, d
|
|
|
242
319
|
};
|
|
243
320
|
return new Proxy(resource, handler);
|
|
244
321
|
}
|
|
245
|
-
function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
|
|
322
|
+
function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats, defaultSampling, provider) {
|
|
246
323
|
if (!resource || typeof resource !== "object") {
|
|
247
324
|
return void 0;
|
|
248
325
|
}
|
|
@@ -257,9 +334,20 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
|
|
|
257
334
|
withUsage: withUsage2,
|
|
258
335
|
promptCompression
|
|
259
336
|
} = splitUsageOptions(options);
|
|
260
|
-
const beginRequest =
|
|
337
|
+
const beginRequest = responsesBeginRequest(
|
|
338
|
+
resolveBeginRequest(defaultContext, usageContext),
|
|
339
|
+
params
|
|
340
|
+
);
|
|
261
341
|
const wantsStream = isStreamingRequest(params);
|
|
262
342
|
return usageTap.withUsage(beginRequest, async (ctx) => {
|
|
343
|
+
const settle = wantsStream ? deferUsageFinalization(ctx) : void 0;
|
|
344
|
+
const sampleStartedAt = Date.now();
|
|
345
|
+
const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredOpenAISampleDecision({
|
|
346
|
+
usageTap,
|
|
347
|
+
sampling: defaultSampling,
|
|
348
|
+
beginRequest,
|
|
349
|
+
input: params
|
|
350
|
+
});
|
|
263
351
|
const hintedParams = applyVendorHints ? applyResponsesVendorHints(params, ctx.begin.data.vendorHints) : params;
|
|
264
352
|
const finalParams = await compressResponsesParamsForCall({
|
|
265
353
|
params: hintedParams,
|
|
@@ -271,25 +359,67 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
|
|
|
271
359
|
withUsage: withUsage2,
|
|
272
360
|
operation: "responses.create"
|
|
273
361
|
});
|
|
362
|
+
ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
|
|
274
363
|
const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
|
|
275
364
|
if (wantsStream) {
|
|
276
365
|
const apiPromise2 = originalCreate(finalParams, request);
|
|
277
366
|
const wrappedPromise2 = transformApiPromise(apiPromise2, (rawStream) => {
|
|
278
367
|
ensureAsyncIterable(rawStream, "responses.create");
|
|
279
|
-
const wrappedStream = wrapStreamForUsageTap(rawStream, async () => {
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
368
|
+
const wrappedStream = wrapStreamForUsageTap(rawStream, async (termination) => {
|
|
369
|
+
try {
|
|
370
|
+
if (termination === "complete") {
|
|
371
|
+
const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
|
|
372
|
+
if (usage) {
|
|
373
|
+
ctx.setUsage(usage);
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
} catch (error) {
|
|
377
|
+
ctx.setError({
|
|
378
|
+
code: "USAGE_FINALIZE_ERROR",
|
|
379
|
+
message: error instanceof Error ? error.message : String(error)
|
|
380
|
+
});
|
|
381
|
+
throw error;
|
|
382
|
+
} finally {
|
|
383
|
+
await settle?.();
|
|
283
384
|
}
|
|
284
|
-
}, ctx)
|
|
385
|
+
}, ctx, (chunk) => {
|
|
386
|
+
tryInferUsageFromStreamChunk(
|
|
387
|
+
chunk,
|
|
388
|
+
ctx.begin.data.vendorHints,
|
|
389
|
+
ctx,
|
|
390
|
+
provider
|
|
391
|
+
);
|
|
392
|
+
});
|
|
285
393
|
return wrappedStream;
|
|
286
394
|
});
|
|
287
395
|
return wrappedPromise2;
|
|
288
396
|
}
|
|
289
397
|
const apiPromise = originalCreate(finalParams, request);
|
|
290
|
-
const wrappedPromise = transformApiPromise(apiPromise, (response) => {
|
|
291
|
-
tryInferUsage(response, ctx.begin.data.vendorHints, void 0, ctx);
|
|
398
|
+
const wrappedPromise = transformApiPromise(apiPromise, async (response) => {
|
|
399
|
+
tryInferUsage(response, ctx.begin.data.vendorHints, void 0, ctx, provider);
|
|
400
|
+
await captureMeteredOpenAISample({
|
|
401
|
+
usageTap,
|
|
402
|
+
sampling: defaultSampling,
|
|
403
|
+
decision: sampleDecision,
|
|
404
|
+
ctx,
|
|
405
|
+
beginRequest,
|
|
406
|
+
input: params,
|
|
407
|
+
response,
|
|
408
|
+
startedAt: sampleStartedAt
|
|
409
|
+
});
|
|
292
410
|
return response;
|
|
411
|
+
}, async (error) => {
|
|
412
|
+
await captureMeteredOpenAISample({
|
|
413
|
+
usageTap,
|
|
414
|
+
sampling: defaultSampling,
|
|
415
|
+
decision: sampleDecision,
|
|
416
|
+
ctx,
|
|
417
|
+
beginRequest,
|
|
418
|
+
input: params,
|
|
419
|
+
error,
|
|
420
|
+
startedAt: sampleStartedAt
|
|
421
|
+
});
|
|
422
|
+
throw error;
|
|
293
423
|
});
|
|
294
424
|
return wrappedPromise;
|
|
295
425
|
}, withUsage2);
|
|
@@ -304,7 +434,7 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
|
|
|
304
434
|
};
|
|
305
435
|
return new Proxy(resource, handler);
|
|
306
436
|
}
|
|
307
|
-
function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
|
|
437
|
+
function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats, defaultSampling, provider) {
|
|
308
438
|
const originalCreate = resource.create.bind(resource);
|
|
309
439
|
const streamCandidate = resource.stream;
|
|
310
440
|
const originalStream = typeof streamCandidate === "function" ? streamCandidate.bind(resource) : void 0;
|
|
@@ -318,8 +448,16 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
318
448
|
const beginRequest = resolveBeginRequest(defaultContext, usageContext);
|
|
319
449
|
const wantsStream = isStreamingRequest(params);
|
|
320
450
|
return usageTap.withUsage(beginRequest, async (ctx) => {
|
|
451
|
+
const settle = wantsStream ? deferUsageFinalization(ctx) : void 0;
|
|
452
|
+
const sampleStartedAt = Date.now();
|
|
453
|
+
const sampleDecision = wantsStream ? Promise.resolve(false) : startMeteredOpenAISampleDecision({
|
|
454
|
+
usageTap,
|
|
455
|
+
sampling: defaultSampling,
|
|
456
|
+
beginRequest,
|
|
457
|
+
input: params
|
|
458
|
+
});
|
|
321
459
|
const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
|
|
322
|
-
const
|
|
460
|
+
const compressedParams = await compressChatParamsForCall({
|
|
323
461
|
params: hintedParams,
|
|
324
462
|
usageTap,
|
|
325
463
|
ctx,
|
|
@@ -329,25 +467,68 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
329
467
|
withUsage: withUsage2,
|
|
330
468
|
operation: "chat.completions.create"
|
|
331
469
|
});
|
|
470
|
+
const finalParams = wantsStream ? ensureOpenAIStreamUsage(compressedParams) : compressedParams;
|
|
471
|
+
ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
|
|
332
472
|
const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
|
|
333
473
|
if (wantsStream) {
|
|
334
474
|
const apiPromise2 = originalCreate(finalParams, request);
|
|
335
475
|
const wrappedPromise2 = transformApiPromise(apiPromise2, (rawStream) => {
|
|
336
476
|
ensureAsyncIterable(rawStream, "chat.completions.create");
|
|
337
|
-
const wrappedStream2 = wrapStreamForUsageTap(rawStream, async () => {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
477
|
+
const wrappedStream2 = wrapStreamForUsageTap(rawStream, async (termination) => {
|
|
478
|
+
try {
|
|
479
|
+
if (termination === "complete") {
|
|
480
|
+
const usage = await extractUsageFromStream(rawStream, ctx.begin.data.vendorHints, provider);
|
|
481
|
+
if (usage) {
|
|
482
|
+
ctx.setUsage(usage);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
} catch (error) {
|
|
486
|
+
ctx.setError({
|
|
487
|
+
code: "USAGE_FINALIZE_ERROR",
|
|
488
|
+
message: error instanceof Error ? error.message : String(error)
|
|
489
|
+
});
|
|
490
|
+
throw error;
|
|
491
|
+
} finally {
|
|
492
|
+
await settle?.();
|
|
341
493
|
}
|
|
342
|
-
}, ctx)
|
|
494
|
+
}, ctx, (chunk) => {
|
|
495
|
+
tryInferUsageFromStreamChunk(
|
|
496
|
+
chunk,
|
|
497
|
+
ctx.begin.data.vendorHints,
|
|
498
|
+
ctx,
|
|
499
|
+
provider
|
|
500
|
+
);
|
|
501
|
+
});
|
|
343
502
|
return wrappedStream2;
|
|
344
503
|
});
|
|
345
504
|
return wrappedPromise2;
|
|
346
505
|
}
|
|
347
506
|
const apiPromise = originalCreate(finalParams, request);
|
|
348
|
-
const wrappedPromise = transformApiPromise(apiPromise, (response) => {
|
|
349
|
-
tryInferUsage(response, ctx.begin.data.vendorHints, void 0, ctx);
|
|
507
|
+
const wrappedPromise = transformApiPromise(apiPromise, async (response) => {
|
|
508
|
+
tryInferUsage(response, ctx.begin.data.vendorHints, void 0, ctx, provider);
|
|
509
|
+
await captureMeteredOpenAISample({
|
|
510
|
+
usageTap,
|
|
511
|
+
sampling: defaultSampling,
|
|
512
|
+
decision: sampleDecision,
|
|
513
|
+
ctx,
|
|
514
|
+
beginRequest,
|
|
515
|
+
input: params,
|
|
516
|
+
response,
|
|
517
|
+
startedAt: sampleStartedAt
|
|
518
|
+
});
|
|
350
519
|
return response;
|
|
520
|
+
}, async (error) => {
|
|
521
|
+
await captureMeteredOpenAISample({
|
|
522
|
+
usageTap,
|
|
523
|
+
sampling: defaultSampling,
|
|
524
|
+
decision: sampleDecision,
|
|
525
|
+
ctx,
|
|
526
|
+
beginRequest,
|
|
527
|
+
input: params,
|
|
528
|
+
error,
|
|
529
|
+
startedAt: sampleStartedAt
|
|
530
|
+
});
|
|
531
|
+
throw error;
|
|
351
532
|
});
|
|
352
533
|
return wrappedPromise;
|
|
353
534
|
}, withUsage2);
|
|
@@ -361,8 +542,9 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
361
542
|
} = splitUsageOptions(options);
|
|
362
543
|
const beginRequest = resolveBeginRequest(defaultContext, usageContext);
|
|
363
544
|
return usageTap.withUsage(beginRequest, async (ctx) => {
|
|
545
|
+
const settle = deferUsageFinalization(ctx);
|
|
364
546
|
const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
|
|
365
|
-
const
|
|
547
|
+
const compressedParams = await compressChatParamsForCall({
|
|
366
548
|
params: hintedParams,
|
|
367
549
|
usageTap,
|
|
368
550
|
ctx,
|
|
@@ -372,16 +554,41 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
372
554
|
withUsage: withUsage2,
|
|
373
555
|
operation: "chat.completions.stream"
|
|
374
556
|
});
|
|
557
|
+
const finalParams = ensureOpenAIStreamUsage(compressedParams);
|
|
558
|
+
ctx.setUsage(openAIRequestExecutionMetadata(finalParams, provider));
|
|
375
559
|
const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
|
|
376
560
|
const apiPromise = originalStream(finalParams, request);
|
|
377
561
|
const wrappedPromise = transformApiPromise(apiPromise, (rawStream) => {
|
|
378
562
|
ensureAsyncIterable(rawStream, "chat.completions.stream");
|
|
379
|
-
const wrappedStreamInner = wrapStreamForUsageTap(rawStream, async () => {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
563
|
+
const wrappedStreamInner = wrapStreamForUsageTap(rawStream, async (termination) => {
|
|
564
|
+
try {
|
|
565
|
+
if (termination === "complete") {
|
|
566
|
+
const usage = await extractUsageFromStream(
|
|
567
|
+
rawStream,
|
|
568
|
+
ctx.begin.data.vendorHints,
|
|
569
|
+
provider
|
|
570
|
+
);
|
|
571
|
+
if (usage) {
|
|
572
|
+
ctx.setUsage(usage);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
} catch (error) {
|
|
576
|
+
ctx.setError({
|
|
577
|
+
code: "USAGE_FINALIZE_ERROR",
|
|
578
|
+
message: error instanceof Error ? error.message : String(error)
|
|
579
|
+
});
|
|
580
|
+
throw error;
|
|
581
|
+
} finally {
|
|
582
|
+
await settle();
|
|
383
583
|
}
|
|
384
|
-
}, ctx)
|
|
584
|
+
}, ctx, (chunk) => {
|
|
585
|
+
tryInferUsageFromStreamChunk(
|
|
586
|
+
chunk,
|
|
587
|
+
ctx.begin.data.vendorHints,
|
|
588
|
+
ctx,
|
|
589
|
+
provider
|
|
590
|
+
);
|
|
591
|
+
});
|
|
385
592
|
return wrappedStreamInner;
|
|
386
593
|
});
|
|
387
594
|
return wrappedPromise;
|
|
@@ -503,6 +710,10 @@ async function compressChatParams(params, usageTap, compression, signal) {
|
|
|
503
710
|
const result = await usageTap.compressPromptMessages(source, {
|
|
504
711
|
provider: "usagetap",
|
|
505
712
|
failOpen: compression.failOpen,
|
|
713
|
+
mode: compression.mode,
|
|
714
|
+
latencyBudgetMs: compression.latencyBudgetMs,
|
|
715
|
+
compactEmptyUserMessages: compression.compactEmptyUserMessages,
|
|
716
|
+
compactDuplicateUserTextParts: compression.compactDuplicateUserTextParts,
|
|
506
717
|
aggressiveness: resolveMessageEndpointAggressiveness(compression),
|
|
507
718
|
signal
|
|
508
719
|
});
|
|
@@ -972,19 +1183,52 @@ function resolveBeginRequest(defaults, override) {
|
|
|
972
1183
|
if (requested) begin.requested = requested;
|
|
973
1184
|
const feature = current.feature ?? base.feature;
|
|
974
1185
|
if (feature) begin.feature = feature;
|
|
1186
|
+
const runId = current.runId ?? base.runId;
|
|
1187
|
+
if (runId) begin.runId = runId;
|
|
975
1188
|
const idempotency = current.idempotency ?? base.idempotency;
|
|
976
1189
|
if (idempotency) begin.idempotency = idempotency;
|
|
977
1190
|
const customerName = current.customerName ?? base.customerName;
|
|
978
1191
|
if (customerName) begin.customerName = customerName;
|
|
979
1192
|
const customerEmail = current.customerEmail ?? base.customerEmail;
|
|
980
1193
|
if (customerEmail) begin.customerEmail = customerEmail;
|
|
1194
|
+
const customerUserId = current.customerUserId ?? base.customerUserId;
|
|
1195
|
+
if (customerUserId) begin.customerUserId = customerUserId;
|
|
1196
|
+
const customerUserName = current.customerUserName ?? base.customerUserName;
|
|
1197
|
+
if (customerUserName) begin.customerUserName = customerUserName;
|
|
1198
|
+
const customerUserEmail = current.customerUserEmail ?? base.customerUserEmail;
|
|
1199
|
+
if (customerUserEmail) begin.customerUserEmail = customerUserEmail;
|
|
1200
|
+
const stripeCustomerId = current.stripeCustomerId ?? base.stripeCustomerId;
|
|
1201
|
+
if (stripeCustomerId) begin.stripeCustomerId = stripeCustomerId;
|
|
1202
|
+
const batch = current.batch ?? base.batch;
|
|
1203
|
+
if (typeof batch === "boolean") begin.batch = batch;
|
|
1204
|
+
const pricingMode = current.pricingMode ?? base.pricingMode;
|
|
1205
|
+
if (pricingMode) begin.pricingMode = pricingMode;
|
|
981
1206
|
if (tags?.length) {
|
|
982
1207
|
begin.tags = tags;
|
|
983
1208
|
}
|
|
984
1209
|
return begin;
|
|
985
1210
|
}
|
|
986
|
-
function
|
|
987
|
-
|
|
1211
|
+
function responsesBeginRequest(begin, params) {
|
|
1212
|
+
if (!responsesRequestUsesWebSearch(params)) return begin;
|
|
1213
|
+
return {
|
|
1214
|
+
...begin,
|
|
1215
|
+
requested: {
|
|
1216
|
+
...begin.requested ?? {},
|
|
1217
|
+
search: true
|
|
1218
|
+
}
|
|
1219
|
+
};
|
|
1220
|
+
}
|
|
1221
|
+
function responsesRequestUsesWebSearch(params) {
|
|
1222
|
+
if (!params || typeof params !== "object") return false;
|
|
1223
|
+
const tools = params.tools;
|
|
1224
|
+
return Array.isArray(tools) && tools.some((tool) => {
|
|
1225
|
+
if (!tool || typeof tool !== "object") return false;
|
|
1226
|
+
const type = tool.type;
|
|
1227
|
+
return type === "web_search" || type === "web_search_preview";
|
|
1228
|
+
});
|
|
1229
|
+
}
|
|
1230
|
+
function transformApiPromise(apiPromise, onResolve, onReject) {
|
|
1231
|
+
const resolvedPromise = Promise.resolve(apiPromise).then(onResolve, onReject);
|
|
988
1232
|
if (isObjectRecord(apiPromise)) {
|
|
989
1233
|
const proto = Object.getPrototypeOf(apiPromise);
|
|
990
1234
|
if (proto) {
|
|
@@ -1119,12 +1363,12 @@ function applyResponsesVendorHints(params, hints) {
|
|
|
1119
1363
|
}
|
|
1120
1364
|
return next;
|
|
1121
1365
|
}
|
|
1122
|
-
async function extractUsageFromStream(stream, hints) {
|
|
1366
|
+
async function extractUsageFromStream(stream, hints, provider = "openai") {
|
|
1123
1367
|
const finalPayload = await resolveStreamFinalPayload(stream);
|
|
1124
1368
|
if (!finalPayload) {
|
|
1125
1369
|
return void 0;
|
|
1126
1370
|
}
|
|
1127
|
-
return inferUsageFromResponse(finalPayload, hints);
|
|
1371
|
+
return inferUsageFromResponse(finalPayload, hints, provider);
|
|
1128
1372
|
}
|
|
1129
1373
|
async function resolveStreamFinalPayload(stream) {
|
|
1130
1374
|
if (!stream || typeof stream !== "object") {
|
|
@@ -1197,13 +1441,13 @@ function setHeaderIfPossible(res, key, value) {
|
|
|
1197
1441
|
res.setHeader(key, value);
|
|
1198
1442
|
}
|
|
1199
1443
|
}
|
|
1200
|
-
function tryInferUsage(response, hints, extractor, ctx) {
|
|
1201
|
-
const inferred = inferUsageFromResponse(response, hints);
|
|
1444
|
+
function tryInferUsage(response, hints, extractor, ctx, provider = "openai") {
|
|
1445
|
+
const inferred = inferUsageFromResponse(response, hints, provider);
|
|
1202
1446
|
if (inferred) {
|
|
1203
1447
|
ctx.setUsage(inferred);
|
|
1204
1448
|
}
|
|
1205
1449
|
}
|
|
1206
|
-
function inferUsageFromResponse(response, hints) {
|
|
1450
|
+
function inferUsageFromResponse(response, hints, provider = "openai") {
|
|
1207
1451
|
if (!response || typeof response !== "object") {
|
|
1208
1452
|
return void 0;
|
|
1209
1453
|
}
|
|
@@ -1211,32 +1455,110 @@ function inferUsageFromResponse(response, hints) {
|
|
|
1211
1455
|
if (!candidate.usage) {
|
|
1212
1456
|
return void 0;
|
|
1213
1457
|
}
|
|
1214
|
-
const cachedInputTokens = candidate.usage.prompt_tokens_details?.cached_tokens ?? candidate.usage.cached_tokens;
|
|
1458
|
+
const cachedInputTokens = candidate.usage.prompt_tokens_details?.cached_tokens ?? candidate.usage.input_tokens_details?.cached_tokens ?? candidate.usage.cached_tokens;
|
|
1459
|
+
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;
|
|
1460
|
+
const cacheWrite5mInputTokens = candidate.usage.cache_write_5m_input_tokens ?? candidate.usage.cache_creation?.ephemeral_5m_input_tokens;
|
|
1461
|
+
const cacheWrite1hInputTokens = candidate.usage.cache_write_1h_input_tokens ?? candidate.usage.cache_creation?.ephemeral_1h_input_tokens;
|
|
1462
|
+
const outputSearches = Array.isArray(candidate.output) ? candidate.output.filter((item) => item?.type === "web_search_call").length : 0;
|
|
1463
|
+
const searches = candidate.usage.searches ?? candidate.usage.web_search_queries ?? candidate.usage.server_tool_use?.web_search_requests ?? outputSearches;
|
|
1464
|
+
const responseEffort = normalizeExecutionReasoningEffort(
|
|
1465
|
+
candidate.reasoning?.effort
|
|
1466
|
+
);
|
|
1215
1467
|
return {
|
|
1468
|
+
providerUsed: provider,
|
|
1216
1469
|
modelUsed: candidate.model ?? hints?.preferredModel,
|
|
1217
|
-
inputTokens: candidate.usage.prompt_tokens,
|
|
1218
|
-
responseTokens: candidate.usage.completion_tokens,
|
|
1219
|
-
cachedInputTokens
|
|
1470
|
+
inputTokens: candidate.usage.prompt_tokens ?? candidate.usage.input_tokens,
|
|
1471
|
+
responseTokens: candidate.usage.completion_tokens ?? candidate.usage.output_tokens,
|
|
1472
|
+
cachedInputTokens,
|
|
1473
|
+
cacheWriteInputTokens,
|
|
1474
|
+
cacheWrite5mInputTokens,
|
|
1475
|
+
cacheWrite1hInputTokens,
|
|
1476
|
+
audioInputTokens: candidate.usage.prompt_tokens_details?.audio_tokens ?? candidate.usage.input_tokens_details?.audio_tokens,
|
|
1477
|
+
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,
|
|
1478
|
+
imageInputTokens: candidate.usage.prompt_tokens_details?.image_tokens ?? candidate.usage.input_tokens_details?.image_tokens,
|
|
1479
|
+
imageOutputTokens: candidate.usage.completion_tokens_details?.image_tokens ?? candidate.usage.output_tokens_details?.image_tokens,
|
|
1480
|
+
audioOutputTokens: candidate.usage.completion_tokens_details?.audio_tokens ?? candidate.usage.output_tokens_details?.audio_tokens,
|
|
1481
|
+
reasoningTokens: candidate.usage.completion_tokens_details?.reasoning_tokens ?? candidate.usage.output_tokens_details?.reasoning_tokens,
|
|
1482
|
+
...responseEffort ? {
|
|
1483
|
+
reasoningEffort: responseEffort,
|
|
1484
|
+
reasoningEffortSource: "provider_response"
|
|
1485
|
+
} : {},
|
|
1486
|
+
...typeof candidate.reasoning?.type === "string" ? { reasoningMode: candidate.reasoning.type } : typeof candidate.reasoning?.mode === "string" ? { reasoningMode: candidate.reasoning.mode } : {},
|
|
1487
|
+
...typeof searches === "number" && searches > 0 ? { searches } : {}
|
|
1488
|
+
};
|
|
1489
|
+
}
|
|
1490
|
+
function normalizeExecutionReasoningEffort(value) {
|
|
1491
|
+
return value === "none" || value === "minimal" || value === "low" || value === "medium" || value === "high" || value === "xhigh" || value === "max" ? value : void 0;
|
|
1492
|
+
}
|
|
1493
|
+
function openAIRequestExecutionMetadata(params, provider) {
|
|
1494
|
+
const record = params && typeof params === "object" ? params : {};
|
|
1495
|
+
const reasoning = record.reasoning && typeof record.reasoning === "object" ? record.reasoning : void 0;
|
|
1496
|
+
const effort = normalizeExecutionReasoningEffort(
|
|
1497
|
+
record.reasoning_effort ?? reasoning?.effort ?? record.thinking_level
|
|
1498
|
+
);
|
|
1499
|
+
const mode = typeof reasoning?.type === "string" ? reasoning.type : typeof reasoning?.mode === "string" ? reasoning.mode : void 0;
|
|
1500
|
+
const rawBudget = reasoning?.budget_tokens ?? record.thinking_budget ?? record.thinking_budget_tokens;
|
|
1501
|
+
const budget = typeof rawBudget === "number" && Number.isInteger(rawBudget) && rawBudget >= 0 ? rawBudget : void 0;
|
|
1502
|
+
return {
|
|
1503
|
+
providerUsed: provider,
|
|
1504
|
+
...typeof record.model === "string" ? { modelUsed: record.model } : {},
|
|
1505
|
+
...effort ? { reasoningEffort: effort, reasoningEffortSource: "provider_request" } : {},
|
|
1506
|
+
...mode ? { reasoningMode: mode } : {},
|
|
1507
|
+
...budget !== void 0 ? { reasoningBudgetTokens: budget } : {}
|
|
1508
|
+
};
|
|
1509
|
+
}
|
|
1510
|
+
function tryInferUsageFromStreamChunk(chunk, hints, ctx, provider) {
|
|
1511
|
+
const payload = isObjectRecord(chunk) && isObjectRecord(chunk.response) ? chunk.response : chunk;
|
|
1512
|
+
const inferred = inferUsageFromResponse(payload, hints, provider);
|
|
1513
|
+
if (inferred) {
|
|
1514
|
+
ctx.setUsage(inferred);
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
function ensureOpenAIStreamUsage(params) {
|
|
1518
|
+
if (!isObjectRecord(params)) {
|
|
1519
|
+
return params;
|
|
1520
|
+
}
|
|
1521
|
+
const streamOptions = isObjectRecord(params.stream_options) ? params.stream_options : {};
|
|
1522
|
+
return {
|
|
1523
|
+
...params,
|
|
1524
|
+
stream_options: {
|
|
1525
|
+
...streamOptions,
|
|
1526
|
+
include_usage: true
|
|
1527
|
+
}
|
|
1220
1528
|
};
|
|
1221
1529
|
}
|
|
1222
|
-
function
|
|
1530
|
+
function deferUsageFinalization(ctx) {
|
|
1531
|
+
return ctx.deferFinalization?.() ?? (() => Promise.resolve());
|
|
1532
|
+
}
|
|
1533
|
+
function wrapStreamForUsageTap(source, finalize, ctx, onChunk) {
|
|
1223
1534
|
const getIterator = source[Symbol.asyncIterator];
|
|
1224
1535
|
if (typeof getIterator !== "function") {
|
|
1225
1536
|
throw new TypeError("Stream is not async iterable");
|
|
1226
1537
|
}
|
|
1227
1538
|
const iterator = getIterator.call(source);
|
|
1228
1539
|
let completed = false;
|
|
1229
|
-
const invokeFinalize = async () => {
|
|
1540
|
+
const invokeFinalize = async (termination, error) => {
|
|
1230
1541
|
if (completed) return;
|
|
1231
1542
|
completed = true;
|
|
1543
|
+
if (termination === "cancel" || termination === "manual") {
|
|
1544
|
+
ctx.setError({
|
|
1545
|
+
code: "STREAM_ABORTED",
|
|
1546
|
+
message: termination === "cancel" ? "Provider stream consumption was cancelled before completion" : "Provider stream was finalized before completion"
|
|
1547
|
+
});
|
|
1548
|
+
} else if (termination === "error") {
|
|
1549
|
+
ctx.setError({
|
|
1550
|
+
code: "VENDOR_ERROR",
|
|
1551
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1552
|
+
});
|
|
1553
|
+
}
|
|
1232
1554
|
try {
|
|
1233
|
-
await finalize();
|
|
1234
|
-
} catch (
|
|
1555
|
+
await finalize(termination);
|
|
1556
|
+
} catch (error2) {
|
|
1235
1557
|
ctx.setError({
|
|
1236
1558
|
code: "USAGE_FINALIZE_ERROR",
|
|
1237
|
-
message:
|
|
1559
|
+
message: error2 instanceof Error ? error2.message : String(error2)
|
|
1238
1560
|
});
|
|
1239
|
-
throw
|
|
1561
|
+
throw error2;
|
|
1240
1562
|
}
|
|
1241
1563
|
};
|
|
1242
1564
|
const prototype = Object.getPrototypeOf(source) ?? Object.prototype;
|
|
@@ -1260,12 +1582,15 @@ function wrapStreamForUsageTap(source, finalize, ctx) {
|
|
|
1260
1582
|
value: async (...args) => {
|
|
1261
1583
|
try {
|
|
1262
1584
|
const result = await iterator.next(...args);
|
|
1585
|
+
if (!result.done) {
|
|
1586
|
+
onChunk?.(result.value);
|
|
1587
|
+
}
|
|
1263
1588
|
if (result.done) {
|
|
1264
|
-
await invokeFinalize();
|
|
1589
|
+
await invokeFinalize("complete");
|
|
1265
1590
|
}
|
|
1266
1591
|
return result;
|
|
1267
1592
|
} catch (error) {
|
|
1268
|
-
await invokeFinalize().catch(() => void 0);
|
|
1593
|
+
await invokeFinalize("error", error).catch(() => void 0);
|
|
1269
1594
|
throw error;
|
|
1270
1595
|
}
|
|
1271
1596
|
},
|
|
@@ -1274,39 +1599,49 @@ function wrapStreamForUsageTap(source, finalize, ctx) {
|
|
|
1274
1599
|
});
|
|
1275
1600
|
Object.defineProperty(wrapped, "return", {
|
|
1276
1601
|
value: async (value) => {
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1602
|
+
try {
|
|
1603
|
+
if (typeof iterator.return === "function") {
|
|
1604
|
+
const rawResult = await iterator.return(value);
|
|
1605
|
+
if (!isIteratorResult(rawResult)) {
|
|
1606
|
+
throw new TypeError("Iterator.return() returned an invalid result");
|
|
1607
|
+
}
|
|
1608
|
+
await invokeFinalize("cancel");
|
|
1609
|
+
return rawResult;
|
|
1281
1610
|
}
|
|
1282
|
-
await invokeFinalize();
|
|
1283
|
-
return
|
|
1611
|
+
await invokeFinalize("cancel");
|
|
1612
|
+
return { done: true, value };
|
|
1613
|
+
} catch (error) {
|
|
1614
|
+
await invokeFinalize("error", error).catch(() => void 0);
|
|
1615
|
+
throw error;
|
|
1284
1616
|
}
|
|
1285
|
-
await invokeFinalize();
|
|
1286
|
-
return { done: true, value };
|
|
1287
1617
|
},
|
|
1288
1618
|
configurable: true,
|
|
1289
1619
|
writable: true
|
|
1290
1620
|
});
|
|
1291
1621
|
Object.defineProperty(wrapped, "throw", {
|
|
1292
1622
|
value: async (error) => {
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1623
|
+
try {
|
|
1624
|
+
if (typeof iterator.throw === "function") {
|
|
1625
|
+
const rawResult = await iterator.throw(error);
|
|
1626
|
+
if (!isIteratorResult(rawResult)) {
|
|
1627
|
+
throw new TypeError("Iterator.throw() returned an invalid result");
|
|
1628
|
+
}
|
|
1629
|
+
await invokeFinalize("error", error);
|
|
1630
|
+
return rawResult;
|
|
1297
1631
|
}
|
|
1298
|
-
await invokeFinalize();
|
|
1299
|
-
|
|
1632
|
+
await invokeFinalize("error", error);
|
|
1633
|
+
throw error;
|
|
1634
|
+
} catch (thrownError) {
|
|
1635
|
+
await invokeFinalize("error", thrownError).catch(() => void 0);
|
|
1636
|
+
throw thrownError;
|
|
1300
1637
|
}
|
|
1301
|
-
await invokeFinalize();
|
|
1302
|
-
throw error;
|
|
1303
1638
|
},
|
|
1304
1639
|
configurable: true,
|
|
1305
1640
|
writable: true
|
|
1306
1641
|
});
|
|
1307
1642
|
Object.defineProperty(wrapped, "__usageTapFinalize", {
|
|
1308
1643
|
value: async () => {
|
|
1309
|
-
await invokeFinalize();
|
|
1644
|
+
await invokeFinalize("manual");
|
|
1310
1645
|
},
|
|
1311
1646
|
configurable: true
|
|
1312
1647
|
});
|