@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.
Files changed (44) hide show
  1. package/README.md +77 -16
  2. package/dist/adapters/anthropic.cjs +1009 -0
  3. package/dist/adapters/anthropic.cjs.map +1 -0
  4. package/dist/adapters/anthropic.d.cts +90 -0
  5. package/dist/adapters/anthropic.d.ts +90 -0
  6. package/dist/adapters/anthropic.mjs +1006 -0
  7. package/dist/adapters/anthropic.mjs.map +1 -0
  8. package/dist/adapters/openai.cjs +667 -17
  9. package/dist/adapters/openai.cjs.map +1 -1
  10. package/dist/adapters/openai.d.cts +66 -2
  11. package/dist/adapters/openai.d.ts +66 -2
  12. package/dist/adapters/openai.mjs +667 -18
  13. package/dist/adapters/openai.mjs.map +1 -1
  14. package/dist/adapters/openrouter.cjs.map +1 -1
  15. package/dist/adapters/openrouter.d.cts +1 -1
  16. package/dist/adapters/openrouter.d.ts +1 -1
  17. package/dist/adapters/openrouter.mjs.map +1 -1
  18. package/dist/anthropic/index.cjs +1009 -0
  19. package/dist/anthropic/index.cjs.map +1 -0
  20. package/dist/anthropic/index.d.cts +2 -0
  21. package/dist/anthropic/index.d.ts +2 -0
  22. package/dist/anthropic/index.mjs +1006 -0
  23. package/dist/anthropic/index.mjs.map +1 -0
  24. package/dist/{client-BHNMYvlO.d.cts → client-EMXt9_fA.d.cts} +106 -6
  25. package/dist/{client-BHNMYvlO.d.ts → client-EMXt9_fA.d.ts} +106 -6
  26. package/dist/express/index.cjs +663 -17
  27. package/dist/express/index.cjs.map +1 -1
  28. package/dist/express/index.d.cts +1 -1
  29. package/dist/express/index.d.ts +1 -1
  30. package/dist/express/index.mjs +663 -17
  31. package/dist/express/index.mjs.map +1 -1
  32. package/dist/index.cjs +287 -24
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.d.cts +2 -2
  35. package/dist/index.d.ts +2 -2
  36. package/dist/index.mjs +285 -25
  37. package/dist/index.mjs.map +1 -1
  38. package/dist/openai/index.cjs +667 -17
  39. package/dist/openai/index.cjs.map +1 -1
  40. package/dist/openai/index.d.cts +2 -2
  41. package/dist/openai/index.d.ts +2 -2
  42. package/dist/openai/index.mjs +667 -18
  43. package/dist/openai/index.mjs.map +1 -1
  44. package/package.json +21 -1
@@ -29,7 +29,62 @@ var UsageTapError = class extends Error {
29
29
  }
30
30
  };
31
31
 
32
+ // src/prompt-compression.ts
33
+ function estimatePromptTokens(input) {
34
+ const text = typeof input === "string" ? input : stableStringifyInput(input);
35
+ return text.match(/[\p{L}\p{N}]+|[^\s]/gu)?.length ?? 0;
36
+ }
37
+ function stableStringifyInput(input) {
38
+ if (typeof input === "string") return input;
39
+ return JSON.stringify(input) ?? String(input);
40
+ }
41
+
32
42
  // src/adapters/openai.ts
43
+ var OpenAIPromptCompressionStats = class {
44
+ history = [];
45
+ failures = [];
46
+ _record(turn) {
47
+ this.history.push(turn);
48
+ }
49
+ _recordFailure(failure) {
50
+ this.failures.push(failure);
51
+ }
52
+ get totalOriginalTokens() {
53
+ return this.history.reduce((sum, turn) => sum + (turn.originalTokens ?? 0), 0);
54
+ }
55
+ get totalCompressedTokens() {
56
+ return this.history.reduce((sum, turn) => sum + (turn.compressedTokens ?? 0), 0);
57
+ }
58
+ get totalTokensSaved() {
59
+ return this.history.reduce((sum, turn) => sum + (turn.savedTokens ?? 0), 0);
60
+ }
61
+ get totalOriginalCharacters() {
62
+ return this.history.reduce((sum, turn) => sum + turn.originalCharacters, 0);
63
+ }
64
+ get totalCompressedCharacters() {
65
+ return this.history.reduce((sum, turn) => sum + turn.compressedCharacters, 0);
66
+ }
67
+ get totalCharactersSaved() {
68
+ return this.history.reduce((sum, turn) => sum + turn.savedCharacters, 0);
69
+ }
70
+ get calls() {
71
+ return this.history.length;
72
+ }
73
+ get telemetryFailures() {
74
+ return this.failures.length;
75
+ }
76
+ get failOpenEvents() {
77
+ return this.history.filter(
78
+ (turn) => turn.techniques.includes("compression-error") || turn.techniques.includes("fallback-original")
79
+ ).length;
80
+ }
81
+ get tokenSavingsRatio() {
82
+ return this.totalOriginalTokens > 0 ? this.totalTokensSaved / this.totalOriginalTokens : 0;
83
+ }
84
+ get savingsRatio() {
85
+ return this.totalOriginalCharacters > 0 ? this.totalCharactersSaved / this.totalOriginalCharacters : 0;
86
+ }
87
+ };
33
88
  function toNextResponse(stream, options = {}) {
34
89
  const mode = options.mode ?? "text";
35
90
  const headers = new Headers(options.headers ?? {});
@@ -127,8 +182,24 @@ function wrapOpenAI(client, usageTap, options = {}) {
127
182
  }
128
183
  const defaultContext = options.defaultContext;
129
184
  const applyVendorHints = options.applyVendorHints !== false;
130
- const proxiedChat = client.chat ? createChatProxy(client.chat, usageTap, defaultContext, applyVendorHints) : void 0;
131
- const proxiedResponses = typeof client.responses !== "undefined" ? createResponsesProxy(client.responses, usageTap, defaultContext, applyVendorHints) : void 0;
185
+ const defaultPromptCompression = normalizePromptCompressionOptions(options.promptCompression);
186
+ const promptCompressionStats = new OpenAIPromptCompressionStats();
187
+ const proxiedChat = client.chat ? createChatProxy(
188
+ client.chat,
189
+ usageTap,
190
+ defaultContext,
191
+ applyVendorHints,
192
+ defaultPromptCompression,
193
+ promptCompressionStats
194
+ ) : void 0;
195
+ const proxiedResponses = typeof client.responses !== "undefined" ? createResponsesProxy(
196
+ client.responses,
197
+ usageTap,
198
+ defaultContext,
199
+ applyVendorHints,
200
+ defaultPromptCompression,
201
+ promptCompressionStats
202
+ ) : void 0;
132
203
  const handler = {
133
204
  get(target, prop, receiver) {
134
205
  if (prop === "chat" && proxiedChat) {
@@ -143,6 +214,9 @@ function wrapOpenAI(client, usageTap, options = {}) {
143
214
  if (prop === "pipeToResponse") {
144
215
  return pipeToResponse;
145
216
  }
217
+ if (prop === "promptCompression") {
218
+ return promptCompressionStats;
219
+ }
146
220
  if (prop === "unwrap") {
147
221
  return () => target;
148
222
  }
@@ -151,12 +225,14 @@ function wrapOpenAI(client, usageTap, options = {}) {
151
225
  };
152
226
  return new Proxy(client, handler);
153
227
  }
154
- function createChatProxy(resource, usageTap, defaultContext, applyVendorHints) {
228
+ function createChatProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
155
229
  const completions = createChatCompletionsProxy(
156
230
  resource.completions,
157
231
  usageTap,
158
232
  defaultContext,
159
- applyVendorHints
233
+ applyVendorHints,
234
+ defaultPromptCompression,
235
+ promptCompressionStats
160
236
  );
161
237
  const handler = {
162
238
  get(target, prop, receiver) {
@@ -168,7 +244,7 @@ function createChatProxy(resource, usageTap, defaultContext, applyVendorHints) {
168
244
  };
169
245
  return new Proxy(resource, handler);
170
246
  }
171
- function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHints) {
247
+ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
172
248
  if (!resource || typeof resource !== "object") {
173
249
  return void 0;
174
250
  }
@@ -177,11 +253,26 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
177
253
  }
178
254
  const originalCreate = resource.create.bind(resource);
179
255
  const wrappedCreate = (params, options) => {
180
- const { requestOptions, usageContext, withUsage: withUsage2 } = splitUsageOptions(options);
256
+ const {
257
+ requestOptions,
258
+ usageContext,
259
+ withUsage: withUsage2,
260
+ promptCompression
261
+ } = splitUsageOptions(options);
181
262
  const beginRequest = resolveBeginRequest(defaultContext, usageContext);
182
263
  const wantsStream = isStreamingRequest(params);
183
- return usageTap.withUsage(beginRequest, (ctx) => {
184
- const finalParams = applyVendorHints ? applyResponsesVendorHints(params, ctx.begin.data.vendorHints) : params;
264
+ return usageTap.withUsage(beginRequest, async (ctx) => {
265
+ const hintedParams = applyVendorHints ? applyResponsesVendorHints(params, ctx.begin.data.vendorHints) : params;
266
+ const finalParams = await compressResponsesParamsForCall({
267
+ params: hintedParams,
268
+ usageTap,
269
+ ctx,
270
+ defaultPromptCompression,
271
+ callPromptCompression: promptCompression,
272
+ stats: promptCompressionStats,
273
+ withUsage: withUsage2,
274
+ operation: "responses.create"
275
+ });
185
276
  const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
186
277
  if (wantsStream) {
187
278
  const apiPromise2 = originalCreate(finalParams, request);
@@ -215,16 +306,31 @@ function createResponsesProxy(resource, usageTap, defaultContext, applyVendorHin
215
306
  };
216
307
  return new Proxy(resource, handler);
217
308
  }
218
- function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVendorHints) {
309
+ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
219
310
  const originalCreate = resource.create.bind(resource);
220
311
  const streamCandidate = resource.stream;
221
312
  const originalStream = typeof streamCandidate === "function" ? streamCandidate.bind(resource) : void 0;
222
313
  const wrappedCreate = (params, options) => {
223
- const { requestOptions, usageContext, withUsage: withUsage2 } = splitUsageOptions(options);
314
+ const {
315
+ requestOptions,
316
+ usageContext,
317
+ withUsage: withUsage2,
318
+ promptCompression
319
+ } = splitUsageOptions(options);
224
320
  const beginRequest = resolveBeginRequest(defaultContext, usageContext);
225
321
  const wantsStream = isStreamingRequest(params);
226
- return usageTap.withUsage(beginRequest, (ctx) => {
227
- const finalParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
322
+ return usageTap.withUsage(beginRequest, async (ctx) => {
323
+ const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
324
+ const finalParams = await compressChatParamsForCall({
325
+ params: hintedParams,
326
+ usageTap,
327
+ ctx,
328
+ defaultPromptCompression,
329
+ callPromptCompression: promptCompression,
330
+ stats: promptCompressionStats,
331
+ withUsage: withUsage2,
332
+ operation: "chat.completions.create"
333
+ });
228
334
  const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
229
335
  if (wantsStream) {
230
336
  const apiPromise2 = originalCreate(finalParams, request);
@@ -249,10 +355,25 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
249
355
  }, withUsage2);
250
356
  };
251
357
  const wrappedStream = originalStream ? (params, options) => {
252
- const { requestOptions, usageContext, withUsage: withUsage2 } = splitUsageOptions(options);
358
+ const {
359
+ requestOptions,
360
+ usageContext,
361
+ withUsage: withUsage2,
362
+ promptCompression
363
+ } = splitUsageOptions(options);
253
364
  const beginRequest = resolveBeginRequest(defaultContext, usageContext);
254
- return usageTap.withUsage(beginRequest, (ctx) => {
255
- const finalParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
365
+ return usageTap.withUsage(beginRequest, async (ctx) => {
366
+ const hintedParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
367
+ const finalParams = await compressChatParamsForCall({
368
+ params: hintedParams,
369
+ usageTap,
370
+ ctx,
371
+ defaultPromptCompression,
372
+ callPromptCompression: promptCompression,
373
+ stats: promptCompressionStats,
374
+ withUsage: withUsage2,
375
+ operation: "chat.completions.stream"
376
+ });
256
377
  const request = attachCorrelationHeader(requestOptions, ctx.begin.correlationId);
257
378
  const apiPromise = originalStream(finalParams, request);
258
379
  const wrappedPromise = transformApiPromise(apiPromise, (rawStream) => {
@@ -281,16 +402,541 @@ function createChatCompletionsProxy(resource, usageTap, defaultContext, applyVen
281
402
  };
282
403
  return new Proxy(resource, handler);
283
404
  }
405
+ async function compressChatParamsForCall(args) {
406
+ const compression = resolveEffectivePromptCompressionOptions(
407
+ args.defaultPromptCompression,
408
+ args.callPromptCompression
409
+ );
410
+ if (!compression) {
411
+ return args.params;
412
+ }
413
+ const outcome = await compressChatParams(
414
+ args.params,
415
+ args.usageTap,
416
+ compression,
417
+ args.withUsage?.signal
418
+ );
419
+ await recordCompressionOutcome({
420
+ outcome,
421
+ compression,
422
+ usageTap: args.usageTap,
423
+ ctx: args.ctx,
424
+ stats: args.stats,
425
+ withUsage: args.withUsage,
426
+ operation: args.operation
427
+ });
428
+ return outcome.params;
429
+ }
430
+ async function compressResponsesParamsForCall(args) {
431
+ const compression = resolveEffectivePromptCompressionOptions(
432
+ args.defaultPromptCompression,
433
+ args.callPromptCompression
434
+ );
435
+ if (!compression) {
436
+ return args.params;
437
+ }
438
+ const outcome = await compressResponsesParams(
439
+ args.params,
440
+ args.usageTap,
441
+ compression,
442
+ args.withUsage?.signal
443
+ );
444
+ await recordCompressionOutcome({
445
+ outcome,
446
+ compression,
447
+ usageTap: args.usageTap,
448
+ ctx: args.ctx,
449
+ stats: args.stats,
450
+ withUsage: args.withUsage,
451
+ operation: args.operation
452
+ });
453
+ return outcome.params;
454
+ }
455
+ async function recordCompressionOutcome(args) {
456
+ const telemetry = buildPromptCompressionTelemetry(args.outcome.segments);
457
+ if (!telemetry) {
458
+ return;
459
+ }
460
+ const turn = {
461
+ ...telemetry,
462
+ callId: args.ctx.begin.data.callId,
463
+ operation: args.operation,
464
+ messagesCompressed: args.outcome.segments.length,
465
+ timestamp: Date.now()
466
+ };
467
+ args.stats._record(turn);
468
+ try {
469
+ await args.usageTap.recordPromptCompression(
470
+ {
471
+ callId: args.ctx.begin.data.callId,
472
+ promptCompression: telemetry
473
+ },
474
+ promptCompressionRequestOptions(args.withUsage, args.ctx.begin.correlationId)
475
+ );
476
+ } catch (error) {
477
+ args.stats._recordFailure({
478
+ callId: args.ctx.begin.data.callId,
479
+ operation: args.operation,
480
+ stage: "telemetry",
481
+ message: error instanceof Error ? error.message : String(error),
482
+ timestamp: Date.now()
483
+ });
484
+ if (args.compression.failOpen === false) {
485
+ throw error;
486
+ }
487
+ }
488
+ }
489
+ async function compressChatParams(params, usageTap, compression, signal) {
490
+ if (!params || typeof params !== "object") {
491
+ return { params, segments: [] };
492
+ }
493
+ const source = cloneRecord(params);
494
+ const messages = Array.isArray(source.messages) ? source.messages : void 0;
495
+ if (!messages) {
496
+ return { params, segments: [] };
497
+ }
498
+ if (shouldUseUsageTapMessageEndpoint(compression)) {
499
+ const result = await usageTap.compressPromptMessages(source, {
500
+ provider: "usagetap",
501
+ failOpen: compression.failOpen,
502
+ aggressiveness: resolveMessageEndpointAggressiveness(compression),
503
+ signal
504
+ });
505
+ return {
506
+ params: result.compressedInput,
507
+ segments: [{ role: "user", result }]
508
+ };
509
+ }
510
+ const messageResults = await Promise.all(
511
+ messages.map(
512
+ (message) => compressOpenAIMessage(message, usageTap, compression, signal)
513
+ )
514
+ );
515
+ return {
516
+ params: {
517
+ ...source,
518
+ messages: messageResults.map((result) => result.value)
519
+ },
520
+ segments: messageResults.flatMap((result) => result.segments)
521
+ };
522
+ }
523
+ async function compressResponsesParams(params, usageTap, compression, signal) {
524
+ if (!params || typeof params !== "object") {
525
+ return { params, segments: [] };
526
+ }
527
+ const source = cloneRecord(params);
528
+ const segments = [];
529
+ if (typeof source.instructions === "string") {
530
+ const compressed = await compressTextForRole(
531
+ source.instructions,
532
+ "system",
533
+ usageTap,
534
+ compression,
535
+ signal
536
+ );
537
+ if (compressed) {
538
+ source.instructions = compressed.text;
539
+ segments.push(compressed.segment);
540
+ }
541
+ }
542
+ if (typeof source.input === "string") {
543
+ const compressed = await compressTextForRole(
544
+ source.input,
545
+ "user",
546
+ usageTap,
547
+ compression,
548
+ signal
549
+ );
550
+ if (compressed) {
551
+ source.input = compressed.text;
552
+ segments.push(compressed.segment);
553
+ }
554
+ } else if (Array.isArray(source.input)) {
555
+ const inputResults = await Promise.all(
556
+ source.input.map(
557
+ (item) => compressResponsesInputItem(item, usageTap, compression, signal)
558
+ )
559
+ );
560
+ source.input = inputResults.map((result) => result.value);
561
+ segments.push(...inputResults.flatMap((result) => result.segments));
562
+ }
563
+ return {
564
+ params: source,
565
+ segments
566
+ };
567
+ }
568
+ async function compressOpenAIMessage(message, usageTap, compression, signal) {
569
+ if (!isObjectRecord(message)) {
570
+ return { value: message, segments: [] };
571
+ }
572
+ const role = mapOpenAIRole(message.role);
573
+ if (!role) {
574
+ return { value: message, segments: [] };
575
+ }
576
+ const content = message.content;
577
+ if (typeof content === "string") {
578
+ const compressed = await compressTextForRole(
579
+ content,
580
+ role,
581
+ usageTap,
582
+ compression,
583
+ signal
584
+ );
585
+ if (!compressed) {
586
+ return { value: message, segments: [] };
587
+ }
588
+ return {
589
+ value: { ...message, content: compressed.text },
590
+ segments: [compressed.segment]
591
+ };
592
+ }
593
+ if (Array.isArray(content)) {
594
+ const blockResults = await Promise.all(
595
+ content.map(
596
+ (block) => compressOpenAITextBlock(block, role, usageTap, compression, signal)
597
+ )
598
+ );
599
+ const segments = blockResults.flatMap(
600
+ (result) => result.segment ? [result.segment] : []
601
+ );
602
+ return {
603
+ value: segments.length ? { ...message, content: blockResults.map((result) => result.value) } : message,
604
+ segments
605
+ };
606
+ }
607
+ return { value: message, segments: [] };
608
+ }
609
+ async function compressOpenAITextBlock(block, role, usageTap, compression, signal) {
610
+ if (!isObjectRecord(block) || block.type !== "text" || typeof block.text !== "string") {
611
+ return { value: block };
612
+ }
613
+ const compressed = await compressTextForRole(
614
+ block.text,
615
+ role,
616
+ usageTap,
617
+ compression,
618
+ signal
619
+ );
620
+ if (!compressed) {
621
+ return { value: block };
622
+ }
623
+ return {
624
+ value: { ...block, text: compressed.text },
625
+ segment: compressed.segment
626
+ };
627
+ }
628
+ async function compressResponsesInputItem(item, usageTap, compression, signal) {
629
+ if (!isObjectRecord(item)) {
630
+ return { value: item, segments: [] };
631
+ }
632
+ const specialToolRole = mapResponsesItemTypeToRole(item.type);
633
+ const role = specialToolRole ?? mapOpenAIRole(item.role);
634
+ const segments = [];
635
+ let next = item;
636
+ if (role && typeof item.content === "string") {
637
+ const compressed = await compressTextForRole(
638
+ item.content,
639
+ role,
640
+ usageTap,
641
+ compression,
642
+ signal
643
+ );
644
+ if (compressed) {
645
+ next = { ...next, content: compressed.text };
646
+ segments.push(compressed.segment);
647
+ }
648
+ } else if (role && Array.isArray(item.content)) {
649
+ const contentResults = await Promise.all(
650
+ item.content.map(
651
+ (block) => compressResponsesContentBlock(block, role, usageTap, compression, signal)
652
+ )
653
+ );
654
+ segments.push(
655
+ ...contentResults.flatMap(
656
+ (result) => result.segment ? [result.segment] : []
657
+ )
658
+ );
659
+ if (segments.length) {
660
+ next = {
661
+ ...next,
662
+ content: contentResults.map((result) => result.value)
663
+ };
664
+ }
665
+ }
666
+ if (specialToolRole && typeof item.output === "string") {
667
+ const compressed = await compressTextForRole(
668
+ item.output,
669
+ specialToolRole,
670
+ usageTap,
671
+ compression,
672
+ signal
673
+ );
674
+ if (compressed) {
675
+ next = { ...next, output: compressed.text };
676
+ segments.push(compressed.segment);
677
+ }
678
+ }
679
+ return { value: next, segments };
680
+ }
681
+ async function compressResponsesContentBlock(block, role, usageTap, compression, signal) {
682
+ if (!isObjectRecord(block)) {
683
+ return { value: block };
684
+ }
685
+ if ((block.type === "input_text" || block.type === "text") && typeof block.text === "string") {
686
+ const compressed = await compressTextForRole(
687
+ block.text,
688
+ role,
689
+ usageTap,
690
+ compression,
691
+ signal
692
+ );
693
+ if (compressed) {
694
+ return {
695
+ value: { ...block, text: compressed.text },
696
+ segment: compressed.segment
697
+ };
698
+ }
699
+ }
700
+ if (role === "tool" && typeof block.output === "string") {
701
+ const compressed = await compressTextForRole(
702
+ block.output,
703
+ role,
704
+ usageTap,
705
+ compression,
706
+ signal
707
+ );
708
+ if (compressed) {
709
+ return {
710
+ value: { ...block, output: compressed.text },
711
+ segment: compressed.segment
712
+ };
713
+ }
714
+ }
715
+ return { value: block };
716
+ }
717
+ async function compressTextForRole(text, role, usageTap, compression, signal) {
718
+ if (!text.trim()) {
719
+ return void 0;
720
+ }
721
+ const roleOptions = resolveRoleCompressionOptions(compression, role);
722
+ if (!roleOptions) {
723
+ return void 0;
724
+ }
725
+ const estimatedTokens = estimatePromptTokens(text);
726
+ if (typeof roleOptions.minTokens === "number" && estimatedTokens < roleOptions.minTokens) {
727
+ return void 0;
728
+ }
729
+ const result = await usageTap.compressPromptInput(text, {
730
+ provider: roleOptions.provider,
731
+ failOpen: roleOptions.failOpen,
732
+ tokenCompanyModel: roleOptions.tokenCompanyModel,
733
+ aggressiveness: roleOptions.aggressiveness,
734
+ tokenCompanyAggressiveness: roleOptions.tokenCompanyAggressiveness,
735
+ tokenCompanyAppId: roleOptions.tokenCompanyAppId,
736
+ usageTapCompressionModel: roleOptions.usageTapCompressionModel,
737
+ usageTapCompressionAggressiveness: roleOptions.usageTapCompressionAggressiveness,
738
+ signal
739
+ });
740
+ const compressedText = typeof result.compressedInput === "string" ? result.compressedInput : String(result.compressedInput);
741
+ return {
742
+ text: compressedText,
743
+ segment: { role, result: { ...result, compressedInput: compressedText } }
744
+ };
745
+ }
746
+ function normalizePromptCompressionOptions(options) {
747
+ if (!options) {
748
+ return void 0;
749
+ }
750
+ if (options === true) {
751
+ return {};
752
+ }
753
+ if (options.enabled === false) {
754
+ return void 0;
755
+ }
756
+ return options;
757
+ }
758
+ function resolveEffectivePromptCompressionOptions(defaults, override) {
759
+ if (override === false) {
760
+ return void 0;
761
+ }
762
+ if (override === void 0) {
763
+ return defaults;
764
+ }
765
+ if (override === true) {
766
+ return defaults ?? {};
767
+ }
768
+ const merged = {
769
+ ...defaults ?? {},
770
+ ...override,
771
+ roles: override.roles ?? defaults?.roles
772
+ };
773
+ return normalizePromptCompressionOptions(merged);
774
+ }
775
+ function resolveRoleCompressionOptions(compression, role) {
776
+ const hasExplicitRoles = compression.roles !== void 0;
777
+ const setting = compression.roles?.[role];
778
+ if (hasExplicitRoles && setting === void 0) {
779
+ return void 0;
780
+ }
781
+ if (!hasExplicitRoles && role === "assistant") {
782
+ return void 0;
783
+ }
784
+ if (setting === false) {
785
+ return void 0;
786
+ }
787
+ const roleOptions = typeof setting === "object" ? setting : void 0;
788
+ if (roleOptions?.enabled === false) {
789
+ return void 0;
790
+ }
791
+ return {
792
+ provider: roleOptions?.provider ?? compression.provider,
793
+ minTokens: roleOptions?.minTokens ?? compression.minTokens,
794
+ failOpen: compression.failOpen,
795
+ tokenCompanyModel: compression.tokenCompanyModel,
796
+ aggressiveness: roleOptions?.aggressiveness ?? resolveAggressiveness(compression, role),
797
+ tokenCompanyAggressiveness: roleOptions?.tokenCompanyAggressiveness ?? resolveTokenCompanyAggressiveness(compression, role),
798
+ tokenCompanyAppId: compression.tokenCompanyAppId,
799
+ usageTapCompressionModel: compression.usageTapCompressionModel,
800
+ usageTapCompressionAggressiveness: roleOptions?.usageTapCompressionAggressiveness ?? resolveUsageTapCompressionAggressiveness(compression, role)
801
+ };
802
+ }
803
+ function resolveAggressiveness(compression, role) {
804
+ if (typeof compression.aggressiveness === "number") {
805
+ return compression.aggressiveness;
806
+ }
807
+ return compression.aggressiveness?.[role];
808
+ }
809
+ function resolveTokenCompanyAggressiveness(compression, role) {
810
+ if (typeof compression.tokenCompanyAggressiveness === "number") {
811
+ return compression.tokenCompanyAggressiveness;
812
+ }
813
+ return compression.tokenCompanyAggressiveness?.[role];
814
+ }
815
+ function resolveUsageTapCompressionAggressiveness(compression, role) {
816
+ if (typeof compression.usageTapCompressionAggressiveness === "number") {
817
+ return compression.usageTapCompressionAggressiveness;
818
+ }
819
+ return compression.usageTapCompressionAggressiveness?.[role];
820
+ }
821
+ function shouldUseUsageTapMessageEndpoint(compression) {
822
+ if (compression.provider !== "usagetap") {
823
+ return false;
824
+ }
825
+ return Object.values(compression.roles ?? {}).every((setting) => {
826
+ if (typeof setting !== "object" || setting === null) {
827
+ return true;
828
+ }
829
+ return setting.provider === void 0 || setting.provider === "usagetap";
830
+ });
831
+ }
832
+ function resolveMessageEndpointAggressiveness(compression) {
833
+ const base = compression.aggressiveness ?? compression.usageTapCompressionAggressiveness ?? compression.tokenCompanyAggressiveness;
834
+ if (typeof base === "number" || base === void 0) {
835
+ return hasExplicitEnabledRoles(compression) ? buildRoleAggressiveness(compression, base) : base;
836
+ }
837
+ return buildRoleAggressiveness(compression, void 0, base);
838
+ }
839
+ function hasExplicitEnabledRoles(compression) {
840
+ return Object.values(compression.roles ?? {}).some((setting) => setting !== false);
841
+ }
842
+ function buildRoleAggressiveness(compression, fallback, base = {}) {
843
+ const roles = ["system", "user", "tool", "assistant"];
844
+ const result = {};
845
+ for (const role of roles) {
846
+ const roleOptions = resolveRoleCompressionOptions(compression, role);
847
+ if (!roleOptions) {
848
+ continue;
849
+ }
850
+ const roleAggressiveness = roleOptions.aggressiveness ?? roleOptions.usageTapCompressionAggressiveness ?? roleOptions.tokenCompanyAggressiveness ?? base[role] ?? fallback;
851
+ if (roleAggressiveness !== void 0) {
852
+ result[role] = roleAggressiveness;
853
+ }
854
+ }
855
+ return result;
856
+ }
857
+ function buildPromptCompressionTelemetry(segments) {
858
+ if (!segments.length) {
859
+ return void 0;
860
+ }
861
+ const originalCharacters = segments.reduce(
862
+ (sum, segment) => sum + segment.result.originalCharacters,
863
+ 0
864
+ );
865
+ const compressedCharacters = segments.reduce(
866
+ (sum, segment) => sum + segment.result.compressedCharacters,
867
+ 0
868
+ );
869
+ const originalTokens = segments.reduce(
870
+ (sum, segment) => sum + segment.result.originalTokens,
871
+ 0
872
+ );
873
+ const compressedTokens = segments.reduce(
874
+ (sum, segment) => sum + segment.result.compressedTokens,
875
+ 0
876
+ );
877
+ const savedCharacters = Math.max(0, originalCharacters - compressedCharacters);
878
+ const savedTokens = Math.max(0, originalTokens - compressedTokens);
879
+ const providers = dedupeStrings(segments.map((segment) => segment.result.provider));
880
+ const roles = dedupeStrings(segments.map((segment) => `role:${segment.role}`));
881
+ const techniques = dedupeStrings([
882
+ "openai-wrapper",
883
+ ...roles,
884
+ ...segments.flatMap((segment) => segment.result.techniques),
885
+ ...providers.length > 1 ? ["mixed-providers"] : []
886
+ ]);
887
+ return {
888
+ provider: segments[0]?.result.provider ?? "heuristic",
889
+ originalCharacters,
890
+ compressedCharacters,
891
+ savedCharacters,
892
+ originalTokens,
893
+ compressedTokens,
894
+ savedTokens,
895
+ tokenSavingsRatio: originalTokens > 0 ? savedTokens / originalTokens : 0,
896
+ savingsRatio: originalCharacters > 0 ? savedCharacters / originalCharacters : 0,
897
+ techniques
898
+ };
899
+ }
900
+ function promptCompressionRequestOptions(withUsage2, correlationId) {
901
+ return {
902
+ signal: withUsage2?.signal,
903
+ headers: withUsage2?.headers,
904
+ retries: withUsage2?.retries,
905
+ correlationId
906
+ };
907
+ }
908
+ function mapOpenAIRole(role) {
909
+ if (role === "system" || role === "developer") {
910
+ return "system";
911
+ }
912
+ if (role === "user") {
913
+ return "user";
914
+ }
915
+ if (role === "tool" || role === "function") {
916
+ return "tool";
917
+ }
918
+ if (role === "assistant") {
919
+ return "assistant";
920
+ }
921
+ return void 0;
922
+ }
923
+ function mapResponsesItemTypeToRole(type) {
924
+ if (type === "function_call_output" || type === "tool_result" || type === "computer_call_output") {
925
+ return "tool";
926
+ }
927
+ return void 0;
928
+ }
284
929
  function splitUsageOptions(options) {
285
930
  if (!options || typeof options !== "object") {
286
931
  return {};
287
932
  }
288
- const { usageTap, withUsage: withUsage2, ...rest } = options;
933
+ const { usageTap, withUsage: withUsage2, promptCompression, ...rest } = options;
289
934
  const requestOptions = Object.keys(rest).length ? cloneRequestOptions(rest) : void 0;
290
935
  return {
291
936
  requestOptions,
292
937
  usageContext: usageTap,
293
- withUsage: withUsage2
938
+ withUsage: withUsage2,
939
+ promptCompression
294
940
  };
295
941
  }
296
942
  function resolveBeginRequest(defaults, override) {