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