@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
@@ -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 proxiedChat = client.chat ? createChatProxy(client.chat, usageTap, defaultContext, applyVendorHints) : void 0;
188
- const proxiedResponses = typeof client.responses !== "undefined" ? createResponsesProxy(client.responses, usageTap, defaultContext, applyVendorHints) : void 0;
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 { requestOptions, usageContext, withUsage } = splitUsageOptions(options);
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 finalParams = applyVendorHints ? applyResponsesVendorHints(params, ctx.begin.data.vendorHints) : params;
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 { requestOptions, usageContext, withUsage } = splitUsageOptions(options);
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 finalParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
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 { requestOptions, usageContext, withUsage } = splitUsageOptions(options);
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 finalParams = applyVendorHints ? applyChatVendorHints(params, ctx.begin.data.vendorHints) : params;
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) {
@@ -759,6 +1408,6 @@ function isIteratorResult(value) {
759
1408
  return isObjectRecord(value) && "done" in value;
760
1409
  }
761
1410
 
762
- export { createOpenAIAdapter, pipeToResponse, streamOpenAIRoute, toNextResponse, wrapOpenAI };
1411
+ export { OpenAIPromptCompressionStats, createOpenAIAdapter, pipeToResponse, streamOpenAIRoute, toNextResponse, wrapOpenAI };
763
1412
  //# sourceMappingURL=openai.mjs.map
764
1413
  //# sourceMappingURL=openai.mjs.map