@usagetap/sdk 1.0.0 → 1.2.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 +77 -16
- package/dist/adapters/anthropic.cjs +1009 -0
- package/dist/adapters/anthropic.cjs.map +1 -0
- package/dist/adapters/anthropic.d.cts +90 -0
- package/dist/adapters/anthropic.d.ts +90 -0
- package/dist/adapters/anthropic.mjs +1006 -0
- package/dist/adapters/anthropic.mjs.map +1 -0
- package/dist/adapters/openai.cjs +667 -17
- package/dist/adapters/openai.cjs.map +1 -1
- package/dist/adapters/openai.d.cts +66 -2
- package/dist/adapters/openai.d.ts +66 -2
- package/dist/adapters/openai.mjs +667 -18
- package/dist/adapters/openai.mjs.map +1 -1
- 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.map +1 -1
- package/dist/anthropic/index.cjs +1009 -0
- package/dist/anthropic/index.cjs.map +1 -0
- package/dist/anthropic/index.d.cts +2 -0
- package/dist/anthropic/index.d.ts +2 -0
- package/dist/anthropic/index.mjs +1006 -0
- package/dist/anthropic/index.mjs.map +1 -0
- package/dist/{client-BHNMYvlO.d.cts → client-EMXt9_fA.d.cts} +106 -6
- package/dist/{client-BHNMYvlO.d.ts → client-EMXt9_fA.d.ts} +106 -6
- package/dist/express/index.cjs +663 -17
- 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 +663 -17
- package/dist/express/index.mjs.map +1 -1
- package/dist/index.cjs +287 -24
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.mjs +285 -25
- package/dist/index.mjs.map +1 -1
- package/dist/openai/index.cjs +667 -17
- 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 +667 -18
- package/dist/openai/index.mjs.map +1 -1
- package/package.json +21 -1
package/dist/openai/index.mjs
CHANGED
|
@@ -27,7 +27,62 @@ var UsageTapError = class extends Error {
|
|
|
27
27
|
}
|
|
28
28
|
};
|
|
29
29
|
|
|
30
|
+
// src/prompt-compression.ts
|
|
31
|
+
function estimatePromptTokens(input) {
|
|
32
|
+
const text = typeof input === "string" ? input : stableStringifyInput(input);
|
|
33
|
+
return text.match(/[\p{L}\p{N}]+|[^\s]/gu)?.length ?? 0;
|
|
34
|
+
}
|
|
35
|
+
function stableStringifyInput(input) {
|
|
36
|
+
if (typeof input === "string") return input;
|
|
37
|
+
return JSON.stringify(input) ?? String(input);
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
// src/adapters/openai.ts
|
|
41
|
+
var OpenAIPromptCompressionStats = class {
|
|
42
|
+
history = [];
|
|
43
|
+
failures = [];
|
|
44
|
+
_record(turn) {
|
|
45
|
+
this.history.push(turn);
|
|
46
|
+
}
|
|
47
|
+
_recordFailure(failure) {
|
|
48
|
+
this.failures.push(failure);
|
|
49
|
+
}
|
|
50
|
+
get totalOriginalTokens() {
|
|
51
|
+
return this.history.reduce((sum, turn) => sum + (turn.originalTokens ?? 0), 0);
|
|
52
|
+
}
|
|
53
|
+
get totalCompressedTokens() {
|
|
54
|
+
return this.history.reduce((sum, turn) => sum + (turn.compressedTokens ?? 0), 0);
|
|
55
|
+
}
|
|
56
|
+
get totalTokensSaved() {
|
|
57
|
+
return this.history.reduce((sum, turn) => sum + (turn.savedTokens ?? 0), 0);
|
|
58
|
+
}
|
|
59
|
+
get totalOriginalCharacters() {
|
|
60
|
+
return this.history.reduce((sum, turn) => sum + turn.originalCharacters, 0);
|
|
61
|
+
}
|
|
62
|
+
get totalCompressedCharacters() {
|
|
63
|
+
return this.history.reduce((sum, turn) => sum + turn.compressedCharacters, 0);
|
|
64
|
+
}
|
|
65
|
+
get totalCharactersSaved() {
|
|
66
|
+
return this.history.reduce((sum, turn) => sum + turn.savedCharacters, 0);
|
|
67
|
+
}
|
|
68
|
+
get calls() {
|
|
69
|
+
return this.history.length;
|
|
70
|
+
}
|
|
71
|
+
get telemetryFailures() {
|
|
72
|
+
return this.failures.length;
|
|
73
|
+
}
|
|
74
|
+
get failOpenEvents() {
|
|
75
|
+
return this.history.filter(
|
|
76
|
+
(turn) => turn.techniques.includes("compression-error") || turn.techniques.includes("fallback-original")
|
|
77
|
+
).length;
|
|
78
|
+
}
|
|
79
|
+
get tokenSavingsRatio() {
|
|
80
|
+
return this.totalOriginalTokens > 0 ? this.totalTokensSaved / this.totalOriginalTokens : 0;
|
|
81
|
+
}
|
|
82
|
+
get savingsRatio() {
|
|
83
|
+
return this.totalOriginalCharacters > 0 ? this.totalCharactersSaved / this.totalOriginalCharacters : 0;
|
|
84
|
+
}
|
|
85
|
+
};
|
|
31
86
|
function createOpenAIAdapter(init) {
|
|
32
87
|
const { client, usageTap } = init;
|
|
33
88
|
return {
|
|
@@ -184,8 +239,24 @@ function wrapOpenAI(client, usageTap, options = {}) {
|
|
|
184
239
|
}
|
|
185
240
|
const defaultContext = options.defaultContext;
|
|
186
241
|
const applyVendorHints = options.applyVendorHints !== false;
|
|
187
|
-
const
|
|
188
|
-
const
|
|
242
|
+
const defaultPromptCompression = normalizePromptCompressionOptions(options.promptCompression);
|
|
243
|
+
const promptCompressionStats = new OpenAIPromptCompressionStats();
|
|
244
|
+
const proxiedChat = client.chat ? createChatProxy(
|
|
245
|
+
client.chat,
|
|
246
|
+
usageTap,
|
|
247
|
+
defaultContext,
|
|
248
|
+
applyVendorHints,
|
|
249
|
+
defaultPromptCompression,
|
|
250
|
+
promptCompressionStats
|
|
251
|
+
) : void 0;
|
|
252
|
+
const proxiedResponses = typeof client.responses !== "undefined" ? createResponsesProxy(
|
|
253
|
+
client.responses,
|
|
254
|
+
usageTap,
|
|
255
|
+
defaultContext,
|
|
256
|
+
applyVendorHints,
|
|
257
|
+
defaultPromptCompression,
|
|
258
|
+
promptCompressionStats
|
|
259
|
+
) : void 0;
|
|
189
260
|
const handler = {
|
|
190
261
|
get(target, prop, receiver) {
|
|
191
262
|
if (prop === "chat" && proxiedChat) {
|
|
@@ -200,6 +271,9 @@ function wrapOpenAI(client, usageTap, options = {}) {
|
|
|
200
271
|
if (prop === "pipeToResponse") {
|
|
201
272
|
return pipeToResponse;
|
|
202
273
|
}
|
|
274
|
+
if (prop === "promptCompression") {
|
|
275
|
+
return promptCompressionStats;
|
|
276
|
+
}
|
|
203
277
|
if (prop === "unwrap") {
|
|
204
278
|
return () => target;
|
|
205
279
|
}
|
|
@@ -230,6 +304,9 @@ function streamOpenAIRoute(usageTap, openai, options) {
|
|
|
230
304
|
if (requestConfig.withUsage) {
|
|
231
305
|
callOptions.withUsage = requestConfig.withUsage;
|
|
232
306
|
}
|
|
307
|
+
if (requestConfig.promptCompression !== void 0) {
|
|
308
|
+
callOptions.promptCompression = requestConfig.promptCompression;
|
|
309
|
+
}
|
|
233
310
|
const stream = await wrappedClient.chat.completions.create(
|
|
234
311
|
mergedParams,
|
|
235
312
|
Object.keys(callOptions).length ? callOptions : void 0
|
|
@@ -256,12 +333,14 @@ function streamOpenAIRoute(usageTap, openai, options) {
|
|
|
256
333
|
});
|
|
257
334
|
};
|
|
258
335
|
}
|
|
259
|
-
function createChatProxy(resource, usageTap, defaultContext, applyVendorHints) {
|
|
336
|
+
function createChatProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
|
|
260
337
|
const completions = createChatCompletionsProxy(
|
|
261
338
|
resource.completions,
|
|
262
339
|
usageTap,
|
|
263
340
|
defaultContext,
|
|
264
|
-
applyVendorHints
|
|
341
|
+
applyVendorHints,
|
|
342
|
+
defaultPromptCompression,
|
|
343
|
+
promptCompressionStats
|
|
265
344
|
);
|
|
266
345
|
const handler = {
|
|
267
346
|
get(target, prop, receiver) {
|
|
@@ -273,7 +352,7 @@ function createChatProxy(resource, usageTap, defaultContext, applyVendorHints) {
|
|
|
273
352
|
};
|
|
274
353
|
return new Proxy(resource, handler);
|
|
275
354
|
}
|
|
276
|
-
function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHints) {
|
|
355
|
+
function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
|
|
277
356
|
if (!resource || typeof resource !== "object") {
|
|
278
357
|
return void 0;
|
|
279
358
|
}
|
|
@@ -282,11 +361,26 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
|
|
|
282
361
|
}
|
|
283
362
|
const originalCreate = resource.create.bind(resource);
|
|
284
363
|
const wrappedCreate = (params, options) => {
|
|
285
|
-
const {
|
|
364
|
+
const {
|
|
365
|
+
requestOptions,
|
|
366
|
+
usageContext,
|
|
367
|
+
withUsage,
|
|
368
|
+
promptCompression
|
|
369
|
+
} = splitUsageOptions(options);
|
|
286
370
|
const beginRequest = resolveBeginRequest(defaultContext, usageContext);
|
|
287
371
|
const wantsStream = isStreamingRequest(params);
|
|
288
|
-
return usageTap.withUsage(beginRequest, (ctx) => {
|
|
289
|
-
const
|
|
372
|
+
return usageTap.withUsage(beginRequest, async (ctx) => {
|
|
373
|
+
const hintedParams = applyVendorHints ? applyResponsesVendorHints(params, ctx.begin.data.vendorHints) : params;
|
|
374
|
+
const finalParams = await compressResponsesParamsForCall({
|
|
375
|
+
params: hintedParams,
|
|
376
|
+
usageTap,
|
|
377
|
+
ctx,
|
|
378
|
+
defaultPromptCompression,
|
|
379
|
+
callPromptCompression: promptCompression,
|
|
380
|
+
stats: promptCompressionStats,
|
|
381
|
+
withUsage,
|
|
382
|
+
operation: "responses.create"
|
|
383
|
+
});
|
|
290
384
|
const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
|
|
291
385
|
if (wantsStream) {
|
|
292
386
|
const apiPromise2 = originalCreate(finalParams, request);
|
|
@@ -320,16 +414,31 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
|
|
|
320
414
|
};
|
|
321
415
|
return new Proxy(resource, handler);
|
|
322
416
|
}
|
|
323
|
-
function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVendorHints) {
|
|
417
|
+
function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
|
|
324
418
|
const originalCreate = resource.create.bind(resource);
|
|
325
419
|
const streamCandidate = resource.stream;
|
|
326
420
|
const originalStream = typeof streamCandidate === "function" ? streamCandidate.bind(resource) : void 0;
|
|
327
421
|
const wrappedCreate = (params, options) => {
|
|
328
|
-
const {
|
|
422
|
+
const {
|
|
423
|
+
requestOptions,
|
|
424
|
+
usageContext,
|
|
425
|
+
withUsage,
|
|
426
|
+
promptCompression
|
|
427
|
+
} = splitUsageOptions(options);
|
|
329
428
|
const beginRequest = resolveBeginRequest(defaultContext, usageContext);
|
|
330
429
|
const wantsStream = isStreamingRequest(params);
|
|
331
|
-
return usageTap.withUsage(beginRequest, (ctx) => {
|
|
332
|
-
const
|
|
430
|
+
return usageTap.withUsage(beginRequest, async (ctx) => {
|
|
431
|
+
const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
|
|
432
|
+
const finalParams = await compressChatParamsForCall({
|
|
433
|
+
params: hintedParams,
|
|
434
|
+
usageTap,
|
|
435
|
+
ctx,
|
|
436
|
+
defaultPromptCompression,
|
|
437
|
+
callPromptCompression: promptCompression,
|
|
438
|
+
stats: promptCompressionStats,
|
|
439
|
+
withUsage,
|
|
440
|
+
operation: "chat.completions.create"
|
|
441
|
+
});
|
|
333
442
|
const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
|
|
334
443
|
if (wantsStream) {
|
|
335
444
|
const apiPromise2 = originalCreate(finalParams, request);
|
|
@@ -354,10 +463,25 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
354
463
|
}, withUsage);
|
|
355
464
|
};
|
|
356
465
|
const wrappedStream = originalStream ? (params, options) => {
|
|
357
|
-
const {
|
|
466
|
+
const {
|
|
467
|
+
requestOptions,
|
|
468
|
+
usageContext,
|
|
469
|
+
withUsage,
|
|
470
|
+
promptCompression
|
|
471
|
+
} = splitUsageOptions(options);
|
|
358
472
|
const beginRequest = resolveBeginRequest(defaultContext, usageContext);
|
|
359
|
-
return usageTap.withUsage(beginRequest, (ctx) => {
|
|
360
|
-
const
|
|
473
|
+
return usageTap.withUsage(beginRequest, async (ctx) => {
|
|
474
|
+
const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
|
|
475
|
+
const finalParams = await compressChatParamsForCall({
|
|
476
|
+
params: hintedParams,
|
|
477
|
+
usageTap,
|
|
478
|
+
ctx,
|
|
479
|
+
defaultPromptCompression,
|
|
480
|
+
callPromptCompression: promptCompression,
|
|
481
|
+
stats: promptCompressionStats,
|
|
482
|
+
withUsage,
|
|
483
|
+
operation: "chat.completions.stream"
|
|
484
|
+
});
|
|
361
485
|
const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
|
|
362
486
|
const apiPromise = originalStream(finalParams, request);
|
|
363
487
|
const wrappedPromise = transformApiPromise(apiPromise, (rawStream) => {
|
|
@@ -386,16 +510,541 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
|
|
|
386
510
|
};
|
|
387
511
|
return new Proxy(resource, handler);
|
|
388
512
|
}
|
|
513
|
+
async function compressChatParamsForCall(args) {
|
|
514
|
+
const compression = resolveEffectivePromptCompressionOptions(
|
|
515
|
+
args.defaultPromptCompression,
|
|
516
|
+
args.callPromptCompression
|
|
517
|
+
);
|
|
518
|
+
if (!compression) {
|
|
519
|
+
return args.params;
|
|
520
|
+
}
|
|
521
|
+
const outcome = await compressChatParams(
|
|
522
|
+
args.params,
|
|
523
|
+
args.usageTap,
|
|
524
|
+
compression,
|
|
525
|
+
args.withUsage?.signal
|
|
526
|
+
);
|
|
527
|
+
await recordCompressionOutcome({
|
|
528
|
+
outcome,
|
|
529
|
+
compression,
|
|
530
|
+
usageTap: args.usageTap,
|
|
531
|
+
ctx: args.ctx,
|
|
532
|
+
stats: args.stats,
|
|
533
|
+
withUsage: args.withUsage,
|
|
534
|
+
operation: args.operation
|
|
535
|
+
});
|
|
536
|
+
return outcome.params;
|
|
537
|
+
}
|
|
538
|
+
async function compressResponsesParamsForCall(args) {
|
|
539
|
+
const compression = resolveEffectivePromptCompressionOptions(
|
|
540
|
+
args.defaultPromptCompression,
|
|
541
|
+
args.callPromptCompression
|
|
542
|
+
);
|
|
543
|
+
if (!compression) {
|
|
544
|
+
return args.params;
|
|
545
|
+
}
|
|
546
|
+
const outcome = await compressResponsesParams(
|
|
547
|
+
args.params,
|
|
548
|
+
args.usageTap,
|
|
549
|
+
compression,
|
|
550
|
+
args.withUsage?.signal
|
|
551
|
+
);
|
|
552
|
+
await recordCompressionOutcome({
|
|
553
|
+
outcome,
|
|
554
|
+
compression,
|
|
555
|
+
usageTap: args.usageTap,
|
|
556
|
+
ctx: args.ctx,
|
|
557
|
+
stats: args.stats,
|
|
558
|
+
withUsage: args.withUsage,
|
|
559
|
+
operation: args.operation
|
|
560
|
+
});
|
|
561
|
+
return outcome.params;
|
|
562
|
+
}
|
|
563
|
+
async function recordCompressionOutcome(args) {
|
|
564
|
+
const telemetry = buildPromptCompressionTelemetry(args.outcome.segments);
|
|
565
|
+
if (!telemetry) {
|
|
566
|
+
return;
|
|
567
|
+
}
|
|
568
|
+
const turn = {
|
|
569
|
+
...telemetry,
|
|
570
|
+
callId: args.ctx.begin.data.callId,
|
|
571
|
+
operation: args.operation,
|
|
572
|
+
messagesCompressed: args.outcome.segments.length,
|
|
573
|
+
timestamp: Date.now()
|
|
574
|
+
};
|
|
575
|
+
args.stats._record(turn);
|
|
576
|
+
try {
|
|
577
|
+
await args.usageTap.recordPromptCompression(
|
|
578
|
+
{
|
|
579
|
+
callId: args.ctx.begin.data.callId,
|
|
580
|
+
promptCompression: telemetry
|
|
581
|
+
},
|
|
582
|
+
promptCompressionRequestOptions(args.withUsage, args.ctx.begin.correlationId)
|
|
583
|
+
);
|
|
584
|
+
} catch (error) {
|
|
585
|
+
args.stats._recordFailure({
|
|
586
|
+
callId: args.ctx.begin.data.callId,
|
|
587
|
+
operation: args.operation,
|
|
588
|
+
stage: "telemetry",
|
|
589
|
+
message: error instanceof Error ? error.message : String(error),
|
|
590
|
+
timestamp: Date.now()
|
|
591
|
+
});
|
|
592
|
+
if (args.compression.failOpen === false) {
|
|
593
|
+
throw error;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
async function compressChatParams(params, usageTap, compression, signal) {
|
|
598
|
+
if (!params || typeof params !== "object") {
|
|
599
|
+
return { params, segments: [] };
|
|
600
|
+
}
|
|
601
|
+
const source = cloneRecord(params);
|
|
602
|
+
const messages = Array.isArray(source.messages) ? source.messages : void 0;
|
|
603
|
+
if (!messages) {
|
|
604
|
+
return { params, segments: [] };
|
|
605
|
+
}
|
|
606
|
+
if (shouldUseUsageTapMessageEndpoint(compression)) {
|
|
607
|
+
const result = await usageTap.compressPromptMessages(source, {
|
|
608
|
+
provider: "usagetap",
|
|
609
|
+
failOpen: compression.failOpen,
|
|
610
|
+
aggressiveness: resolveMessageEndpointAggressiveness(compression),
|
|
611
|
+
signal
|
|
612
|
+
});
|
|
613
|
+
return {
|
|
614
|
+
params: result.compressedInput,
|
|
615
|
+
segments: [{ role: "user", result }]
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
const messageResults = await Promise.all(
|
|
619
|
+
messages.map(
|
|
620
|
+
(message) => compressOpenAIMessage(message, usageTap, compression, signal)
|
|
621
|
+
)
|
|
622
|
+
);
|
|
623
|
+
return {
|
|
624
|
+
params: {
|
|
625
|
+
...source,
|
|
626
|
+
messages: messageResults.map((result) => result.value)
|
|
627
|
+
},
|
|
628
|
+
segments: messageResults.flatMap((result) => result.segments)
|
|
629
|
+
};
|
|
630
|
+
}
|
|
631
|
+
async function compressResponsesParams(params, usageTap, compression, signal) {
|
|
632
|
+
if (!params || typeof params !== "object") {
|
|
633
|
+
return { params, segments: [] };
|
|
634
|
+
}
|
|
635
|
+
const source = cloneRecord(params);
|
|
636
|
+
const segments = [];
|
|
637
|
+
if (typeof source.instructions === "string") {
|
|
638
|
+
const compressed = await compressTextForRole(
|
|
639
|
+
source.instructions,
|
|
640
|
+
"system",
|
|
641
|
+
usageTap,
|
|
642
|
+
compression,
|
|
643
|
+
signal
|
|
644
|
+
);
|
|
645
|
+
if (compressed) {
|
|
646
|
+
source.instructions = compressed.text;
|
|
647
|
+
segments.push(compressed.segment);
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
if (typeof source.input === "string") {
|
|
651
|
+
const compressed = await compressTextForRole(
|
|
652
|
+
source.input,
|
|
653
|
+
"user",
|
|
654
|
+
usageTap,
|
|
655
|
+
compression,
|
|
656
|
+
signal
|
|
657
|
+
);
|
|
658
|
+
if (compressed) {
|
|
659
|
+
source.input = compressed.text;
|
|
660
|
+
segments.push(compressed.segment);
|
|
661
|
+
}
|
|
662
|
+
} else if (Array.isArray(source.input)) {
|
|
663
|
+
const inputResults = await Promise.all(
|
|
664
|
+
source.input.map(
|
|
665
|
+
(item) => compressResponsesInputItem(item, usageTap, compression, signal)
|
|
666
|
+
)
|
|
667
|
+
);
|
|
668
|
+
source.input = inputResults.map((result) => result.value);
|
|
669
|
+
segments.push(...inputResults.flatMap((result) => result.segments));
|
|
670
|
+
}
|
|
671
|
+
return {
|
|
672
|
+
params: source,
|
|
673
|
+
segments
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
async function compressOpenAIMessage(message, usageTap, compression, signal) {
|
|
677
|
+
if (!isObjectRecord(message)) {
|
|
678
|
+
return { value: message, segments: [] };
|
|
679
|
+
}
|
|
680
|
+
const role = mapOpenAIRole(message.role);
|
|
681
|
+
if (!role) {
|
|
682
|
+
return { value: message, segments: [] };
|
|
683
|
+
}
|
|
684
|
+
const content = message.content;
|
|
685
|
+
if (typeof content === "string") {
|
|
686
|
+
const compressed = await compressTextForRole(
|
|
687
|
+
content,
|
|
688
|
+
role,
|
|
689
|
+
usageTap,
|
|
690
|
+
compression,
|
|
691
|
+
signal
|
|
692
|
+
);
|
|
693
|
+
if (!compressed) {
|
|
694
|
+
return { value: message, segments: [] };
|
|
695
|
+
}
|
|
696
|
+
return {
|
|
697
|
+
value: { ...message, content: compressed.text },
|
|
698
|
+
segments: [compressed.segment]
|
|
699
|
+
};
|
|
700
|
+
}
|
|
701
|
+
if (Array.isArray(content)) {
|
|
702
|
+
const blockResults = await Promise.all(
|
|
703
|
+
content.map(
|
|
704
|
+
(block) => compressOpenAITextBlock(block, role, usageTap, compression, signal)
|
|
705
|
+
)
|
|
706
|
+
);
|
|
707
|
+
const segments = blockResults.flatMap(
|
|
708
|
+
(result) => result.segment ? [result.segment] : []
|
|
709
|
+
);
|
|
710
|
+
return {
|
|
711
|
+
value: segments.length ? { ...message, content: blockResults.map((result) => result.value) } : message,
|
|
712
|
+
segments
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
return { value: message, segments: [] };
|
|
716
|
+
}
|
|
717
|
+
async function compressOpenAITextBlock(block, role, usageTap, compression, signal) {
|
|
718
|
+
if (!isObjectRecord(block) || block.type !== "text" || typeof block.text !== "string") {
|
|
719
|
+
return { value: block };
|
|
720
|
+
}
|
|
721
|
+
const compressed = await compressTextForRole(
|
|
722
|
+
block.text,
|
|
723
|
+
role,
|
|
724
|
+
usageTap,
|
|
725
|
+
compression,
|
|
726
|
+
signal
|
|
727
|
+
);
|
|
728
|
+
if (!compressed) {
|
|
729
|
+
return { value: block };
|
|
730
|
+
}
|
|
731
|
+
return {
|
|
732
|
+
value: { ...block, text: compressed.text },
|
|
733
|
+
segment: compressed.segment
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
async function compressResponsesInputItem(item, usageTap, compression, signal) {
|
|
737
|
+
if (!isObjectRecord(item)) {
|
|
738
|
+
return { value: item, segments: [] };
|
|
739
|
+
}
|
|
740
|
+
const specialToolRole = mapResponsesItemTypeToRole(item.type);
|
|
741
|
+
const role = specialToolRole ?? mapOpenAIRole(item.role);
|
|
742
|
+
const segments = [];
|
|
743
|
+
let next = item;
|
|
744
|
+
if (role && typeof item.content === "string") {
|
|
745
|
+
const compressed = await compressTextForRole(
|
|
746
|
+
item.content,
|
|
747
|
+
role,
|
|
748
|
+
usageTap,
|
|
749
|
+
compression,
|
|
750
|
+
signal
|
|
751
|
+
);
|
|
752
|
+
if (compressed) {
|
|
753
|
+
next = { ...next, content: compressed.text };
|
|
754
|
+
segments.push(compressed.segment);
|
|
755
|
+
}
|
|
756
|
+
} else if (role && Array.isArray(item.content)) {
|
|
757
|
+
const contentResults = await Promise.all(
|
|
758
|
+
item.content.map(
|
|
759
|
+
(block) => compressResponsesContentBlock(block, role, usageTap, compression, signal)
|
|
760
|
+
)
|
|
761
|
+
);
|
|
762
|
+
segments.push(
|
|
763
|
+
...contentResults.flatMap(
|
|
764
|
+
(result) => result.segment ? [result.segment] : []
|
|
765
|
+
)
|
|
766
|
+
);
|
|
767
|
+
if (segments.length) {
|
|
768
|
+
next = {
|
|
769
|
+
...next,
|
|
770
|
+
content: contentResults.map((result) => result.value)
|
|
771
|
+
};
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
if (specialToolRole && typeof item.output === "string") {
|
|
775
|
+
const compressed = await compressTextForRole(
|
|
776
|
+
item.output,
|
|
777
|
+
specialToolRole,
|
|
778
|
+
usageTap,
|
|
779
|
+
compression,
|
|
780
|
+
signal
|
|
781
|
+
);
|
|
782
|
+
if (compressed) {
|
|
783
|
+
next = { ...next, output: compressed.text };
|
|
784
|
+
segments.push(compressed.segment);
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
return { value: next, segments };
|
|
788
|
+
}
|
|
789
|
+
async function compressResponsesContentBlock(block, role, usageTap, compression, signal) {
|
|
790
|
+
if (!isObjectRecord(block)) {
|
|
791
|
+
return { value: block };
|
|
792
|
+
}
|
|
793
|
+
if ((block.type === "input_text" || block.type === "text") && typeof block.text === "string") {
|
|
794
|
+
const compressed = await compressTextForRole(
|
|
795
|
+
block.text,
|
|
796
|
+
role,
|
|
797
|
+
usageTap,
|
|
798
|
+
compression,
|
|
799
|
+
signal
|
|
800
|
+
);
|
|
801
|
+
if (compressed) {
|
|
802
|
+
return {
|
|
803
|
+
value: { ...block, text: compressed.text },
|
|
804
|
+
segment: compressed.segment
|
|
805
|
+
};
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
if (role === "tool" && typeof block.output === "string") {
|
|
809
|
+
const compressed = await compressTextForRole(
|
|
810
|
+
block.output,
|
|
811
|
+
role,
|
|
812
|
+
usageTap,
|
|
813
|
+
compression,
|
|
814
|
+
signal
|
|
815
|
+
);
|
|
816
|
+
if (compressed) {
|
|
817
|
+
return {
|
|
818
|
+
value: { ...block, output: compressed.text },
|
|
819
|
+
segment: compressed.segment
|
|
820
|
+
};
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
return { value: block };
|
|
824
|
+
}
|
|
825
|
+
async function compressTextForRole(text, role, usageTap, compression, signal) {
|
|
826
|
+
if (!text.trim()) {
|
|
827
|
+
return void 0;
|
|
828
|
+
}
|
|
829
|
+
const roleOptions = resolveRoleCompressionOptions(compression, role);
|
|
830
|
+
if (!roleOptions) {
|
|
831
|
+
return void 0;
|
|
832
|
+
}
|
|
833
|
+
const estimatedTokens = estimatePromptTokens(text);
|
|
834
|
+
if (typeof roleOptions.minTokens === "number" && estimatedTokens < roleOptions.minTokens) {
|
|
835
|
+
return void 0;
|
|
836
|
+
}
|
|
837
|
+
const result = await usageTap.compressPromptInput(text, {
|
|
838
|
+
provider: roleOptions.provider,
|
|
839
|
+
failOpen: roleOptions.failOpen,
|
|
840
|
+
tokenCompanyModel: roleOptions.tokenCompanyModel,
|
|
841
|
+
aggressiveness: roleOptions.aggressiveness,
|
|
842
|
+
tokenCompanyAggressiveness: roleOptions.tokenCompanyAggressiveness,
|
|
843
|
+
tokenCompanyAppId: roleOptions.tokenCompanyAppId,
|
|
844
|
+
usageTapCompressionModel: roleOptions.usageTapCompressionModel,
|
|
845
|
+
usageTapCompressionAggressiveness: roleOptions.usageTapCompressionAggressiveness,
|
|
846
|
+
signal
|
|
847
|
+
});
|
|
848
|
+
const compressedText = typeof result.compressedInput === "string" ? result.compressedInput : String(result.compressedInput);
|
|
849
|
+
return {
|
|
850
|
+
text: compressedText,
|
|
851
|
+
segment: { role, result: { ...result, compressedInput: compressedText } }
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
function normalizePromptCompressionOptions(options) {
|
|
855
|
+
if (!options) {
|
|
856
|
+
return void 0;
|
|
857
|
+
}
|
|
858
|
+
if (options === true) {
|
|
859
|
+
return {};
|
|
860
|
+
}
|
|
861
|
+
if (options.enabled === false) {
|
|
862
|
+
return void 0;
|
|
863
|
+
}
|
|
864
|
+
return options;
|
|
865
|
+
}
|
|
866
|
+
function resolveEffectivePromptCompressionOptions(defaults, override) {
|
|
867
|
+
if (override === false) {
|
|
868
|
+
return void 0;
|
|
869
|
+
}
|
|
870
|
+
if (override === void 0) {
|
|
871
|
+
return defaults;
|
|
872
|
+
}
|
|
873
|
+
if (override === true) {
|
|
874
|
+
return defaults ?? {};
|
|
875
|
+
}
|
|
876
|
+
const merged = {
|
|
877
|
+
...defaults ?? {},
|
|
878
|
+
...override,
|
|
879
|
+
roles: override.roles ?? defaults?.roles
|
|
880
|
+
};
|
|
881
|
+
return normalizePromptCompressionOptions(merged);
|
|
882
|
+
}
|
|
883
|
+
function resolveRoleCompressionOptions(compression, role) {
|
|
884
|
+
const hasExplicitRoles = compression.roles !== void 0;
|
|
885
|
+
const setting = compression.roles?.[role];
|
|
886
|
+
if (hasExplicitRoles && setting === void 0) {
|
|
887
|
+
return void 0;
|
|
888
|
+
}
|
|
889
|
+
if (!hasExplicitRoles && role === "assistant") {
|
|
890
|
+
return void 0;
|
|
891
|
+
}
|
|
892
|
+
if (setting === false) {
|
|
893
|
+
return void 0;
|
|
894
|
+
}
|
|
895
|
+
const roleOptions = typeof setting === "object" ? setting : void 0;
|
|
896
|
+
if (roleOptions?.enabled === false) {
|
|
897
|
+
return void 0;
|
|
898
|
+
}
|
|
899
|
+
return {
|
|
900
|
+
provider: roleOptions?.provider ?? compression.provider,
|
|
901
|
+
minTokens: roleOptions?.minTokens ?? compression.minTokens,
|
|
902
|
+
failOpen: compression.failOpen,
|
|
903
|
+
tokenCompanyModel: compression.tokenCompanyModel,
|
|
904
|
+
aggressiveness: roleOptions?.aggressiveness ?? resolveAggressiveness(compression, role),
|
|
905
|
+
tokenCompanyAggressiveness: roleOptions?.tokenCompanyAggressiveness ?? resolveTokenCompanyAggressiveness(compression, role),
|
|
906
|
+
tokenCompanyAppId: compression.tokenCompanyAppId,
|
|
907
|
+
usageTapCompressionModel: compression.usageTapCompressionModel,
|
|
908
|
+
usageTapCompressionAggressiveness: roleOptions?.usageTapCompressionAggressiveness ?? resolveUsageTapCompressionAggressiveness(compression, role)
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
function resolveAggressiveness(compression, role) {
|
|
912
|
+
if (typeof compression.aggressiveness === "number") {
|
|
913
|
+
return compression.aggressiveness;
|
|
914
|
+
}
|
|
915
|
+
return compression.aggressiveness?.[role];
|
|
916
|
+
}
|
|
917
|
+
function resolveTokenCompanyAggressiveness(compression, role) {
|
|
918
|
+
if (typeof compression.tokenCompanyAggressiveness === "number") {
|
|
919
|
+
return compression.tokenCompanyAggressiveness;
|
|
920
|
+
}
|
|
921
|
+
return compression.tokenCompanyAggressiveness?.[role];
|
|
922
|
+
}
|
|
923
|
+
function resolveUsageTapCompressionAggressiveness(compression, role) {
|
|
924
|
+
if (typeof compression.usageTapCompressionAggressiveness === "number") {
|
|
925
|
+
return compression.usageTapCompressionAggressiveness;
|
|
926
|
+
}
|
|
927
|
+
return compression.usageTapCompressionAggressiveness?.[role];
|
|
928
|
+
}
|
|
929
|
+
function shouldUseUsageTapMessageEndpoint(compression) {
|
|
930
|
+
if (compression.provider !== "usagetap") {
|
|
931
|
+
return false;
|
|
932
|
+
}
|
|
933
|
+
return Object.values(compression.roles ?? {}).every((setting) => {
|
|
934
|
+
if (typeof setting !== "object" || setting === null) {
|
|
935
|
+
return true;
|
|
936
|
+
}
|
|
937
|
+
return setting.provider === void 0 || setting.provider === "usagetap";
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
function resolveMessageEndpointAggressiveness(compression) {
|
|
941
|
+
const base = compression.aggressiveness ?? compression.usageTapCompressionAggressiveness ?? compression.tokenCompanyAggressiveness;
|
|
942
|
+
if (typeof base === "number" || base === void 0) {
|
|
943
|
+
return hasExplicitEnabledRoles(compression) ? buildRoleAggressiveness(compression, base) : base;
|
|
944
|
+
}
|
|
945
|
+
return buildRoleAggressiveness(compression, void 0, base);
|
|
946
|
+
}
|
|
947
|
+
function hasExplicitEnabledRoles(compression) {
|
|
948
|
+
return Object.values(compression.roles ?? {}).some((setting) => setting !== false);
|
|
949
|
+
}
|
|
950
|
+
function buildRoleAggressiveness(compression, fallback, base = {}) {
|
|
951
|
+
const roles = ["system", "user", "tool", "assistant"];
|
|
952
|
+
const result = {};
|
|
953
|
+
for (const role of roles) {
|
|
954
|
+
const roleOptions = resolveRoleCompressionOptions(compression, role);
|
|
955
|
+
if (!roleOptions) {
|
|
956
|
+
continue;
|
|
957
|
+
}
|
|
958
|
+
const roleAggressiveness = roleOptions.aggressiveness ?? roleOptions.usageTapCompressionAggressiveness ?? roleOptions.tokenCompanyAggressiveness ?? base[role] ?? fallback;
|
|
959
|
+
if (roleAggressiveness !== void 0) {
|
|
960
|
+
result[role] = roleAggressiveness;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
return result;
|
|
964
|
+
}
|
|
965
|
+
function buildPromptCompressionTelemetry(segments) {
|
|
966
|
+
if (!segments.length) {
|
|
967
|
+
return void 0;
|
|
968
|
+
}
|
|
969
|
+
const originalCharacters = segments.reduce(
|
|
970
|
+
(sum, segment) => sum + segment.result.originalCharacters,
|
|
971
|
+
0
|
|
972
|
+
);
|
|
973
|
+
const compressedCharacters = segments.reduce(
|
|
974
|
+
(sum, segment) => sum + segment.result.compressedCharacters,
|
|
975
|
+
0
|
|
976
|
+
);
|
|
977
|
+
const originalTokens = segments.reduce(
|
|
978
|
+
(sum, segment) => sum + segment.result.originalTokens,
|
|
979
|
+
0
|
|
980
|
+
);
|
|
981
|
+
const compressedTokens = segments.reduce(
|
|
982
|
+
(sum, segment) => sum + segment.result.compressedTokens,
|
|
983
|
+
0
|
|
984
|
+
);
|
|
985
|
+
const savedCharacters = Math.max(0, originalCharacters - compressedCharacters);
|
|
986
|
+
const savedTokens = Math.max(0, originalTokens - compressedTokens);
|
|
987
|
+
const providers = dedupeStrings(segments.map((segment) => segment.result.provider));
|
|
988
|
+
const roles = dedupeStrings(segments.map((segment) => `role:${segment.role}`));
|
|
989
|
+
const techniques = dedupeStrings([
|
|
990
|
+
"openai-wrapper",
|
|
991
|
+
...roles,
|
|
992
|
+
...segments.flatMap((segment) => segment.result.techniques),
|
|
993
|
+
...providers.length > 1 ? ["mixed-providers"] : []
|
|
994
|
+
]);
|
|
995
|
+
return {
|
|
996
|
+
provider: segments[0]?.result.provider ?? "heuristic",
|
|
997
|
+
originalCharacters,
|
|
998
|
+
compressedCharacters,
|
|
999
|
+
savedCharacters,
|
|
1000
|
+
originalTokens,
|
|
1001
|
+
compressedTokens,
|
|
1002
|
+
savedTokens,
|
|
1003
|
+
tokenSavingsRatio: originalTokens > 0 ? savedTokens / originalTokens : 0,
|
|
1004
|
+
savingsRatio: originalCharacters > 0 ? savedCharacters / originalCharacters : 0,
|
|
1005
|
+
techniques
|
|
1006
|
+
};
|
|
1007
|
+
}
|
|
1008
|
+
function promptCompressionRequestOptions(withUsage, correlationId) {
|
|
1009
|
+
return {
|
|
1010
|
+
signal: withUsage?.signal,
|
|
1011
|
+
headers: withUsage?.headers,
|
|
1012
|
+
retries: withUsage?.retries,
|
|
1013
|
+
correlationId
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
function mapOpenAIRole(role) {
|
|
1017
|
+
if (role === "system" || role === "developer") {
|
|
1018
|
+
return "system";
|
|
1019
|
+
}
|
|
1020
|
+
if (role === "user") {
|
|
1021
|
+
return "user";
|
|
1022
|
+
}
|
|
1023
|
+
if (role === "tool" || role === "function") {
|
|
1024
|
+
return "tool";
|
|
1025
|
+
}
|
|
1026
|
+
if (role === "assistant") {
|
|
1027
|
+
return "assistant";
|
|
1028
|
+
}
|
|
1029
|
+
return void 0;
|
|
1030
|
+
}
|
|
1031
|
+
function mapResponsesItemTypeToRole(type) {
|
|
1032
|
+
if (type === "function_call_output" || type === "tool_result" || type === "computer_call_output") {
|
|
1033
|
+
return "tool";
|
|
1034
|
+
}
|
|
1035
|
+
return void 0;
|
|
1036
|
+
}
|
|
389
1037
|
function splitUsageOptions(options) {
|
|
390
1038
|
if (!options || typeof options !== "object") {
|
|
391
1039
|
return {};
|
|
392
1040
|
}
|
|
393
|
-
const { usageTap, withUsage, ...rest } = options;
|
|
1041
|
+
const { usageTap, withUsage, promptCompression, ...rest } = options;
|
|
394
1042
|
const requestOptions = Object.keys(rest).length ? cloneRequestOptions(rest) : void 0;
|
|
395
1043
|
return {
|
|
396
1044
|
requestOptions,
|
|
397
1045
|
usageContext: usageTap,
|
|
398
|
-
withUsage
|
|
1046
|
+
withUsage,
|
|
1047
|
+
promptCompression
|
|
399
1048
|
};
|
|
400
1049
|
}
|
|
401
1050
|
function resolveBeginRequest(defaults, override) {
|
|
@@ -764,6 +1413,6 @@ function createOpenRouterAdapter(init) {
|
|
|
764
1413
|
return createOpenAIAdapter(init);
|
|
765
1414
|
}
|
|
766
1415
|
|
|
767
|
-
export { createOpenAIAdapter, createOpenRouterAdapter, pipeToResponse, streamOpenAIRoute, toNextResponse, wrapOpenAI };
|
|
1416
|
+
export { OpenAIPromptCompressionStats, createOpenAIAdapter, createOpenRouterAdapter, pipeToResponse, streamOpenAIRoute, toNextResponse, wrapOpenAI };
|
|
768
1417
|
//# sourceMappingURL=index.mjs.map
|
|
769
1418
|
//# sourceMappingURL=index.mjs.map
|