@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
package/dist/index.cjs CHANGED
@@ -117,21 +117,34 @@ async function runWithRetry(operation, options, shouldRetry, onSchedule, signal)
117
117
 
118
118
  // src/prompt-compression.ts
119
119
  var DEFAULT_TTC_ENDPOINT = "https://api.thetokencompany.com/v1/compress";
120
+ var DEFAULT_TTC_MODEL = "bear-2";
121
+ var DEFAULT_TTC_AGGRESSIVENESS = 0.2;
122
+ var DEFAULT_USAGETAP_COMPRESSION_ENDPOINT = "https://compress.usagetap.com/v1/compress";
123
+ var DEFAULT_USAGETAP_MESSAGES_COMPRESSION_ENDPOINT = "https://compress.usagetap.com/v1/messages/compress";
124
+ var PROTECTED_TEXT_PATTERN = /<ttc_safe>[\s\S]*?<\/ttc_safe>|<usagetap_safe>[\s\S]*?<\/usagetap_safe>/g;
125
+ function protectPromptText(text) {
126
+ return `<ttc_safe>${text}</ttc_safe>`;
127
+ }
128
+ var protect = protectPromptText;
120
129
  async function compressPrompt(options) {
130
+ const input = resolvePromptCompressionInput(options);
121
131
  try {
132
+ if (options.provider === "usagetap") {
133
+ return await compressWithUsageTap(options);
134
+ }
122
135
  if (options.provider === "thetokencompany" || options.tokenCompanyApiKey) {
123
136
  return await compressWithTheTokenCompany(options);
124
137
  }
125
138
  if (options.provider === "toon") {
126
- return compressPromptToon(options.input);
139
+ return compressPromptToon(input);
127
140
  }
128
- return compressPromptHeuristic(options.input);
141
+ return compressPromptHeuristic(input);
129
142
  } catch (error) {
130
143
  if (options.failOpen === false) {
131
144
  throw error;
132
145
  }
133
146
  return createPromptCompressionFallback(
134
- options.input,
147
+ input,
135
148
  options.provider ?? (options.tokenCompanyApiKey ? "thetokencompany" : "heuristic"),
136
149
  error
137
150
  );
@@ -165,6 +178,20 @@ function compressPromptToon(input) {
165
178
  "json-minify"
166
179
  ]);
167
180
  }
181
+ async function compressPromptMessages(options) {
182
+ try {
183
+ return await compressMessagesWithUsageTap(options);
184
+ } catch (error) {
185
+ if (options.failOpen === false) {
186
+ throw error;
187
+ }
188
+ return createPromptCompressionFallback(
189
+ options.input,
190
+ options.provider ?? "usagetap",
191
+ error
192
+ );
193
+ }
194
+ }
168
195
  async function compressWithTheTokenCompany(options) {
169
196
  if (!options.tokenCompanyApiKey) {
170
197
  throw new Error(
@@ -177,30 +204,131 @@ async function compressWithTheTokenCompany(options) {
177
204
  "A fetch implementation is required for The Token Company compression"
178
205
  );
179
206
  }
207
+ return compressWithCompatibleRemoteProvider({
208
+ options,
209
+ provider: "thetokencompany",
210
+ endpoint: options.tokenCompanyEndpoint ?? DEFAULT_TTC_ENDPOINT,
211
+ model: options.model ?? options.tokenCompanyModel ?? DEFAULT_TTC_MODEL,
212
+ aggressiveness: options.aggressiveness ?? options.tokenCompanyAggressiveness ?? DEFAULT_TTC_AGGRESSIVENESS,
213
+ apiKey: options.tokenCompanyApiKey,
214
+ appId: options.tokenCompanyAppId,
215
+ providerLabel: "The Token Company"
216
+ });
217
+ }
218
+ async function compressWithUsageTap(options) {
219
+ const fetchCandidate = options.fetchImpl ?? globalThis.fetch;
220
+ if (typeof fetchCandidate !== "function") {
221
+ throw new Error(
222
+ "A fetch implementation is required for UsageTap prompt compression"
223
+ );
224
+ }
225
+ return compressWithCompatibleRemoteProvider({
226
+ options,
227
+ provider: "usagetap",
228
+ endpoint: options.usageTapCompressionEndpoint ?? DEFAULT_USAGETAP_COMPRESSION_ENDPOINT,
229
+ model: options.model ?? options.usageTapCompressionModel ?? options.tokenCompanyModel ?? DEFAULT_TTC_MODEL,
230
+ aggressiveness: options.aggressiveness ?? options.usageTapCompressionAggressiveness ?? options.tokenCompanyAggressiveness ?? DEFAULT_TTC_AGGRESSIVENESS,
231
+ apiKey: options.usageTapCompressionApiKey,
232
+ appId: options.tokenCompanyAppId,
233
+ providerLabel: "UsageTap prompt compression"
234
+ });
235
+ }
236
+ async function compressMessagesWithUsageTap(options) {
237
+ const fetchCandidate = options.fetchImpl ?? globalThis.fetch;
238
+ if (typeof fetchCandidate !== "function") {
239
+ throw new Error(
240
+ "A fetch implementation is required for UsageTap prompt message compression"
241
+ );
242
+ }
243
+ const aggressiveness = options.aggressiveness ?? options.usageTapCompressionAggressiveness ?? DEFAULT_TTC_AGGRESSIVENESS;
244
+ validateAggressiveness(
245
+ aggressiveness,
246
+ "UsageTap prompt message compression"
247
+ );
180
248
  const original = stableStringifyInput(options.input);
181
- const heuristic = compressPromptHeuristic(options.input);
249
+ const headers = {
250
+ "content-type": "application/json"
251
+ };
252
+ if (options.usageTapCompressionApiKey) {
253
+ headers.authorization = `Bearer ${options.usageTapCompressionApiKey}`;
254
+ }
182
255
  const response = await fetchCandidate(
183
- options.tokenCompanyEndpoint ?? DEFAULT_TTC_ENDPOINT,
256
+ options.usageTapCompressionMessagesEndpoint ?? DEFAULT_USAGETAP_MESSAGES_COMPRESSION_ENDPOINT,
184
257
  {
185
258
  method: "POST",
186
- headers: {
187
- authorization: `Bearer ${options.tokenCompanyApiKey}`,
188
- "content-type": "application/json"
189
- },
190
- body: JSON.stringify({ input: heuristic.compressedInput }),
259
+ headers,
260
+ body: JSON.stringify({
261
+ ...cloneInputRecord(options.input),
262
+ compression_settings: { aggressiveness }
263
+ }),
191
264
  signal: options.signal
192
265
  }
193
266
  );
194
267
  if (!response.ok) {
195
268
  throw new Error(
196
- `The Token Company compression failed with HTTP ${response.status}`
269
+ `UsageTap prompt message compression failed with HTTP ${response.status}`
270
+ );
271
+ }
272
+ const payload = await response.json();
273
+ const compressedInput = payload.compressed_request ?? payload.compressedInput ?? payload.compressed ?? (payload.messages !== void 0 ? { ...cloneInputRecord(options.input), messages: payload.messages } : void 0);
274
+ if (compressedInput === void 0) {
275
+ throw new Error(
276
+ "UsageTap prompt message compression response did not include compressed content"
277
+ );
278
+ }
279
+ const compressed = stableStringifyInput(compressedInput);
280
+ const tokenCounts = normalizeCompatibleTokenCounts(payload);
281
+ return buildResult(
282
+ options.input,
283
+ compressedInput,
284
+ "usagetap",
285
+ original,
286
+ compressed,
287
+ ["usagetap", "messages-endpoint"],
288
+ tokenCounts
289
+ );
290
+ }
291
+ async function compressWithCompatibleRemoteProvider(args) {
292
+ const { options, provider, endpoint, model, aggressiveness, apiKey, appId, providerLabel } = args;
293
+ const fetchCandidate = options.fetchImpl ?? globalThis.fetch;
294
+ const sourceInput = resolvePromptCompressionInput(options);
295
+ const original = stableStringifyInput(sourceInput);
296
+ const heuristic = compressPromptHeuristic(sourceInput);
297
+ const input = typeof heuristic.compressedInput === "string" ? heuristic.compressedInput : stableStringifyInput(heuristic.compressedInput);
298
+ if (!isValidAggressiveness(aggressiveness)) {
299
+ throw new Error(`${providerLabel} aggressiveness must be between 0.0 and 1.0`);
300
+ }
301
+ const headers = {
302
+ "content-type": "application/json"
303
+ };
304
+ if (apiKey) {
305
+ headers.authorization = `Bearer ${apiKey}`;
306
+ }
307
+ const response = await fetchCandidate(
308
+ endpoint,
309
+ {
310
+ method: "POST",
311
+ headers,
312
+ body: JSON.stringify({
313
+ model,
314
+ input,
315
+ ...provider === "usagetap" ? { text: input } : {},
316
+ compression_settings: { aggressiveness },
317
+ ...appId ? { app_id: appId } : {}
318
+ }),
319
+ signal: options.signal
320
+ }
321
+ );
322
+ if (!response.ok) {
323
+ throw new Error(
324
+ `${providerLabel} failed with HTTP ${response.status}`
197
325
  );
198
326
  }
199
327
  const payload = await response.json();
200
328
  const tokenCompanyResult = normalizeTheTokenCompanyCompressResponse(payload);
201
329
  const compressedInput = payload.compressedInput ?? payload.compressed ?? tokenCompanyResult?.output ?? payload.output ?? payload.text;
202
330
  if (compressedInput === void 0) {
203
- throw new Error("The Token Company response did not include compressed content");
331
+ throw new Error(`${providerLabel} response did not include compressed content`);
204
332
  }
205
333
  const compressed = stableStringifyInput(compressedInput);
206
334
  const tokenCounts = tokenCompanyResult ? {
@@ -209,15 +337,55 @@ async function compressWithTheTokenCompany(options) {
209
337
  savedTokens: tokenCompanyResult.tokens_saved
210
338
  } : void 0;
211
339
  return buildResult(
212
- options.input,
340
+ sourceInput,
213
341
  compressedInput,
214
- "thetokencompany",
342
+ provider,
215
343
  original,
216
344
  compressed,
217
- [...heuristic.techniques, "thetokencompany"],
345
+ [...heuristic.techniques, provider],
218
346
  tokenCounts
219
347
  );
220
348
  }
349
+ function resolvePromptCompressionInput(options) {
350
+ if (options.input !== void 0) {
351
+ return options.input;
352
+ }
353
+ if (options.text !== void 0) {
354
+ return options.text;
355
+ }
356
+ throw new Error("Prompt compression requires input or text");
357
+ }
358
+ function validateAggressiveness(value, label) {
359
+ if (typeof value === "number") {
360
+ if (!isValidAggressiveness(value)) {
361
+ throw new Error(`${label} aggressiveness must be between 0.0 and 1.0`);
362
+ }
363
+ return;
364
+ }
365
+ for (const aggressiveness of Object.values(value)) {
366
+ if (aggressiveness !== void 0 && !isValidAggressiveness(aggressiveness)) {
367
+ throw new Error(`${label} aggressiveness must be between 0.0 and 1.0`);
368
+ }
369
+ }
370
+ }
371
+ function isValidAggressiveness(value) {
372
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 && value <= 1;
373
+ }
374
+ function cloneInputRecord(input) {
375
+ return input && typeof input === "object" && !Array.isArray(input) ? { ...input } : { input };
376
+ }
377
+ function normalizeCompatibleTokenCounts(data) {
378
+ const inputTokens = typeof data.input_tokens === "number" ? data.input_tokens : data.original_input_tokens;
379
+ const outputTokens = data.output_tokens;
380
+ if (typeof inputTokens !== "number" || typeof outputTokens !== "number") {
381
+ return void 0;
382
+ }
383
+ return {
384
+ originalTokens: inputTokens,
385
+ compressedTokens: outputTokens,
386
+ savedTokens: typeof data.tokens_saved === "number" ? data.tokens_saved : inputTokens - outputTokens
387
+ };
388
+ }
221
389
  function normalizeTheTokenCompanyCompressResponse(data) {
222
390
  if (typeof data.output !== "string" || typeof data.output_tokens !== "number") {
223
391
  return void 0;
@@ -295,6 +463,20 @@ function compressValue(value, techniques, options) {
295
463
  return value;
296
464
  }
297
465
  function compressText(value, techniques, options) {
466
+ const protectedSpans = [];
467
+ const text = value.replace(PROTECTED_TEXT_PATTERN, (match) => {
468
+ const placeholder = `__USAGETAP_PROTECTED_${protectedSpans.length}__`;
469
+ protectedSpans.push(match);
470
+ techniques.add("protected-text");
471
+ return placeholder;
472
+ });
473
+ const compressed = compressTextWithoutProtection(text, techniques, options);
474
+ return protectedSpans.reduce(
475
+ (output, span, index) => output.replace(`__USAGETAP_PROTECTED_${index}__`, span),
476
+ compressed
477
+ );
478
+ }
479
+ function compressTextWithoutProtection(value, techniques, options) {
298
480
  const fencePattern = /```([\w-]+)?\n([\s\S]*?)```/g;
299
481
  const parts = [];
300
482
  let cursor = 0;
@@ -588,7 +770,7 @@ var IDEMPOTENCY_HEADER = "idempotency-key";
588
770
  var SDK_HEADER = "x-usage-sdk";
589
771
  var USER_AGENT = "UsageTapClient";
590
772
  var CANONICAL_MEDIA_TYPE = "application/vnd.usagetap.v1+json";
591
- var SDK_VERSION = "1.0.0" ;
773
+ var SDK_VERSION = "1.2.0" ;
592
774
  var HAS_WINDOW = typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
593
775
  var UsageTapClient = class {
594
776
  apiKey;
@@ -605,6 +787,16 @@ var UsageTapClient = class {
605
787
  autoIdempotency;
606
788
  tokenCompanyApiKey;
607
789
  tokenCompanyEndpoint;
790
+ model;
791
+ tokenCompanyModel;
792
+ aggressiveness;
793
+ tokenCompanyAggressiveness;
794
+ tokenCompanyAppId;
795
+ usageTapCompressionApiKey;
796
+ usageTapCompressionEndpoint;
797
+ usageTapCompressionMessagesEndpoint;
798
+ usageTapCompressionModel;
799
+ usageTapCompressionAggressiveness;
608
800
  constructor(options) {
609
801
  if (!options) {
610
802
  throw new UsageTapError(
@@ -652,6 +844,16 @@ var UsageTapClient = class {
652
844
  this.autoIdempotency = options.autoIdempotency ?? true;
653
845
  this.tokenCompanyApiKey = options.tokenCompanyApiKey;
654
846
  this.tokenCompanyEndpoint = options.tokenCompanyEndpoint;
847
+ this.model = options.model;
848
+ this.tokenCompanyModel = options.tokenCompanyModel;
849
+ this.aggressiveness = options.aggressiveness;
850
+ this.tokenCompanyAggressiveness = options.tokenCompanyAggressiveness;
851
+ this.tokenCompanyAppId = options.tokenCompanyAppId;
852
+ this.usageTapCompressionApiKey = options.usageTapCompressionApiKey ?? options.apiKey;
853
+ this.usageTapCompressionEndpoint = options.usageTapCompressionEndpoint;
854
+ this.usageTapCompressionMessagesEndpoint = options.usageTapCompressionMessagesEndpoint;
855
+ this.usageTapCompressionModel = options.usageTapCompressionModel;
856
+ this.usageTapCompressionAggressiveness = options.usageTapCompressionAggressiveness;
655
857
  }
656
858
  async beginCall(request, options = {}) {
657
859
  const idempotencyKey = request.idempotencyKey ?? request.idempotency ?? (this.autoIdempotency ? this.idempotencyGenerator() : void 0);
@@ -681,17 +883,26 @@ var UsageTapClient = class {
681
883
  "promptCompress requires callId"
682
884
  );
683
885
  }
684
- const result = await compressPrompt({
685
- input: request.input,
886
+ const requestInput = request.input ?? request.text;
887
+ if (requestInput === void 0) {
888
+ throw new UsageTapError(
889
+ "USAGETAP_BAD_REQUEST",
890
+ "promptCompress requires input or text"
891
+ );
892
+ }
893
+ const result = await this.compressPromptInput(requestInput, {
686
894
  provider: request.provider,
687
- tokenCompanyApiKey: this.tokenCompanyApiKey,
688
- tokenCompanyEndpoint: this.tokenCompanyEndpoint,
689
- fetchImpl: this.fetchImpl,
895
+ model: request.model,
896
+ tokenCompanyModel: request.tokenCompanyModel,
897
+ aggressiveness: request.aggressiveness,
898
+ tokenCompanyAggressiveness: request.tokenCompanyAggressiveness,
899
+ tokenCompanyAppId: request.tokenCompanyAppId,
900
+ usageTapCompressionModel: request.usageTapCompressionModel,
901
+ usageTapCompressionAggressiveness: request.usageTapCompressionAggressiveness,
690
902
  signal: options.signal
691
903
  });
692
904
  try {
693
- await this.request(
694
- COMPRESS_PROMPT_PATH,
905
+ await this.recordPromptCompression(
695
906
  {
696
907
  callId: request.callId,
697
908
  promptCompression: this.toPromptCompressionTelemetry(result)
@@ -702,7 +913,7 @@ var UsageTapClient = class {
702
913
  } catch (error) {
703
914
  return {
704
915
  ...createPromptCompressionFallback(
705
- request.input,
916
+ requestInput,
706
917
  request.provider ?? result.provider,
707
918
  error
708
919
  ),
@@ -710,6 +921,55 @@ var UsageTapClient = class {
710
921
  };
711
922
  }
712
923
  }
924
+ async compressPromptInput(input, options = {}) {
925
+ return compressPrompt({
926
+ input,
927
+ provider: options.provider,
928
+ tokenCompanyApiKey: this.tokenCompanyApiKey,
929
+ tokenCompanyEndpoint: this.tokenCompanyEndpoint,
930
+ model: options.model ?? this.model,
931
+ tokenCompanyModel: options.tokenCompanyModel ?? this.tokenCompanyModel,
932
+ aggressiveness: options.aggressiveness ?? this.aggressiveness,
933
+ tokenCompanyAggressiveness: options.tokenCompanyAggressiveness ?? this.tokenCompanyAggressiveness,
934
+ tokenCompanyAppId: options.tokenCompanyAppId ?? this.tokenCompanyAppId,
935
+ usageTapCompressionApiKey: this.usageTapCompressionApiKey,
936
+ usageTapCompressionEndpoint: this.usageTapCompressionEndpoint,
937
+ usageTapCompressionModel: options.usageTapCompressionModel ?? this.usageTapCompressionModel,
938
+ usageTapCompressionAggressiveness: options.usageTapCompressionAggressiveness ?? this.usageTapCompressionAggressiveness,
939
+ fetchImpl: this.fetchImpl,
940
+ signal: options.signal,
941
+ failOpen: options.failOpen
942
+ });
943
+ }
944
+ async compressPromptMessages(input, options = {}) {
945
+ return compressPromptMessages({
946
+ input,
947
+ provider: options.provider ?? "usagetap",
948
+ usageTapCompressionApiKey: this.usageTapCompressionApiKey,
949
+ usageTapCompressionMessagesEndpoint: this.usageTapCompressionMessagesEndpoint,
950
+ aggressiveness: options.aggressiveness ?? this.aggressiveness,
951
+ usageTapCompressionAggressiveness: options.usageTapCompressionAggressiveness ?? this.usageTapCompressionAggressiveness,
952
+ fetchImpl: this.fetchImpl,
953
+ signal: options.signal,
954
+ failOpen: options.failOpen
955
+ });
956
+ }
957
+ async recordPromptCompression(request, options = {}) {
958
+ if (!request?.callId) {
959
+ throw new UsageTapError(
960
+ "USAGETAP_BAD_REQUEST",
961
+ "recordPromptCompression requires callId"
962
+ );
963
+ }
964
+ return this.request(
965
+ COMPRESS_PROMPT_PATH,
966
+ {
967
+ callId: request.callId,
968
+ promptCompression: request.promptCompression
969
+ },
970
+ options
971
+ );
972
+ }
713
973
  async endCall(request, options = {}) {
714
974
  if (!request?.callId) {
715
975
  throw new UsageTapError(
@@ -1539,10 +1799,13 @@ exports.UsageTapClient = UsageTapClient;
1539
1799
  exports.UsageTapError = UsageTapError;
1540
1800
  exports.compressPrompt = compressPrompt;
1541
1801
  exports.compressPromptHeuristic = compressPromptHeuristic;
1802
+ exports.compressPromptMessages = compressPromptMessages;
1542
1803
  exports.compressPromptToon = compressPromptToon;
1543
1804
  exports.createIdempotencyKey = createIdempotencyKey;
1544
1805
  exports.estimatePromptTokens = estimatePromptTokens;
1545
1806
  exports.isUsageTapError = isUsageTapError;
1807
+ exports.protect = protect;
1808
+ exports.protectPromptText = protectPromptText;
1546
1809
  exports.wrapFetch = wrapFetch;
1547
1810
  //# sourceMappingURL=index.cjs.map
1548
1811
  //# sourceMappingURL=index.cjs.map