@usagetap/sdk 0.10.0 → 1.1.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/LICENSE +21 -0
- package/README.md +83 -22
- package/dist/adapters/anthropic.cjs +943 -0
- package/dist/adapters/anthropic.cjs.map +1 -0
- package/dist/adapters/anthropic.d.cts +81 -0
- package/dist/adapters/anthropic.d.ts +81 -0
- package/dist/adapters/anthropic.mjs +940 -0
- package/dist/adapters/anthropic.mjs.map +1 -0
- package/dist/adapters/openai.cjs +601 -17
- package/dist/adapters/openai.cjs.map +1 -1
- package/dist/adapters/openai.d.cts +57 -2
- package/dist/adapters/openai.d.ts +57 -2
- package/dist/adapters/openai.mjs +601 -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 +943 -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 +940 -0
- package/dist/anthropic/index.mjs.map +1 -0
- package/dist/{client-DEbk0Q2l.d.cts → client-BA-QlnRq.d.cts} +95 -1
- package/dist/{client-DEbk0Q2l.d.ts → client-BA-QlnRq.d.ts} +95 -1
- package/dist/express/index.cjs +597 -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 +597 -17
- package/dist/express/index.mjs.map +1 -1
- package/dist/index.cjs +586 -1
- 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 +581 -2
- package/dist/index.mjs.map +1 -1
- package/dist/openai/index.cjs +601 -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 +601 -18
- package/dist/openai/index.mjs.map +1 -1
- package/package.json +22 -2
package/dist/index.cjs
CHANGED
|
@@ -115,9 +115,500 @@ async function runWithRetry(operation, options, shouldRetry, onSchedule, signal)
|
|
|
115
115
|
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
// src/prompt-compression.ts
|
|
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 PROTECTED_TEXT_PATTERN = /<ttc_safe>[\s\S]*?<\/ttc_safe>|<usagetap_safe>[\s\S]*?<\/usagetap_safe>/g;
|
|
123
|
+
function protectPromptText(text) {
|
|
124
|
+
return `<ttc_safe>${text}</ttc_safe>`;
|
|
125
|
+
}
|
|
126
|
+
var protect = protectPromptText;
|
|
127
|
+
async function compressPrompt(options) {
|
|
128
|
+
try {
|
|
129
|
+
if (options.provider === "thetokencompany" || options.tokenCompanyApiKey) {
|
|
130
|
+
return await compressWithTheTokenCompany(options);
|
|
131
|
+
}
|
|
132
|
+
if (options.provider === "toon") {
|
|
133
|
+
return compressPromptToon(options.input);
|
|
134
|
+
}
|
|
135
|
+
return compressPromptHeuristic(options.input);
|
|
136
|
+
} catch (error) {
|
|
137
|
+
if (options.failOpen === false) {
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
140
|
+
return createPromptCompressionFallback(
|
|
141
|
+
options.input,
|
|
142
|
+
options.provider ?? (options.tokenCompanyApiKey ? "thetokencompany" : "heuristic"),
|
|
143
|
+
error
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function compressPromptHeuristic(input) {
|
|
148
|
+
const original = stableStringifyInput(input);
|
|
149
|
+
const techniques = /* @__PURE__ */ new Set();
|
|
150
|
+
const compressedInput = compressValue(input, techniques, { allowToonString: false });
|
|
151
|
+
const compressed = stableStringifyInput(compressedInput);
|
|
152
|
+
const chosenInput = compressed.length <= original.length ? compressedInput : input;
|
|
153
|
+
const chosen = compressed.length <= original.length ? compressed : original;
|
|
154
|
+
if (!techniques.size) {
|
|
155
|
+
techniques.add("no-op");
|
|
156
|
+
}
|
|
157
|
+
return buildResult(
|
|
158
|
+
input,
|
|
159
|
+
chosenInput,
|
|
160
|
+
"heuristic",
|
|
161
|
+
original,
|
|
162
|
+
chosen,
|
|
163
|
+
Array.from(techniques)
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
function compressPromptToon(input) {
|
|
167
|
+
const original = stableStringifyInput(input);
|
|
168
|
+
const compressedInput = typeof input === "string" ? compressText(input, /* @__PURE__ */ new Set(), { allowToonString: true }) : encodeToon(input);
|
|
169
|
+
const compressed = stableStringifyInput(compressedInput);
|
|
170
|
+
return buildResult(input, compressedInput, "toon", original, compressed, [
|
|
171
|
+
"toon",
|
|
172
|
+
"json-minify"
|
|
173
|
+
]);
|
|
174
|
+
}
|
|
175
|
+
async function compressWithTheTokenCompany(options) {
|
|
176
|
+
if (!options.tokenCompanyApiKey) {
|
|
177
|
+
throw new Error(
|
|
178
|
+
"tokenCompanyApiKey is required when provider is thetokencompany"
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
const fetchCandidate = options.fetchImpl ?? globalThis.fetch;
|
|
182
|
+
if (typeof fetchCandidate !== "function") {
|
|
183
|
+
throw new Error(
|
|
184
|
+
"A fetch implementation is required for The Token Company compression"
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
const original = stableStringifyInput(options.input);
|
|
188
|
+
const heuristic = compressPromptHeuristic(options.input);
|
|
189
|
+
const input = typeof heuristic.compressedInput === "string" ? heuristic.compressedInput : stableStringifyInput(heuristic.compressedInput);
|
|
190
|
+
const model = options.tokenCompanyModel ?? DEFAULT_TTC_MODEL;
|
|
191
|
+
const aggressiveness = options.tokenCompanyAggressiveness ?? DEFAULT_TTC_AGGRESSIVENESS;
|
|
192
|
+
if (typeof aggressiveness !== "number" || !Number.isFinite(aggressiveness) || aggressiveness < 0 || aggressiveness > 1) {
|
|
193
|
+
throw new Error("tokenCompanyAggressiveness must be between 0.0 and 1.0");
|
|
194
|
+
}
|
|
195
|
+
const response = await fetchCandidate(
|
|
196
|
+
options.tokenCompanyEndpoint ?? DEFAULT_TTC_ENDPOINT,
|
|
197
|
+
{
|
|
198
|
+
method: "POST",
|
|
199
|
+
headers: {
|
|
200
|
+
authorization: `Bearer ${options.tokenCompanyApiKey}`,
|
|
201
|
+
"content-type": "application/json"
|
|
202
|
+
},
|
|
203
|
+
body: JSON.stringify({
|
|
204
|
+
model,
|
|
205
|
+
input,
|
|
206
|
+
compression_settings: { aggressiveness },
|
|
207
|
+
...options.tokenCompanyAppId ? { app_id: options.tokenCompanyAppId } : {}
|
|
208
|
+
}),
|
|
209
|
+
signal: options.signal
|
|
210
|
+
}
|
|
211
|
+
);
|
|
212
|
+
if (!response.ok) {
|
|
213
|
+
throw new Error(
|
|
214
|
+
`The Token Company compression failed with HTTP ${response.status}`
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
const payload = await response.json();
|
|
218
|
+
const tokenCompanyResult = normalizeTheTokenCompanyCompressResponse(payload);
|
|
219
|
+
const compressedInput = payload.compressedInput ?? payload.compressed ?? tokenCompanyResult?.output ?? payload.output ?? payload.text;
|
|
220
|
+
if (compressedInput === void 0) {
|
|
221
|
+
throw new Error("The Token Company response did not include compressed content");
|
|
222
|
+
}
|
|
223
|
+
const compressed = stableStringifyInput(compressedInput);
|
|
224
|
+
const tokenCounts = tokenCompanyResult ? {
|
|
225
|
+
originalTokens: tokenCompanyResult.input_tokens,
|
|
226
|
+
compressedTokens: tokenCompanyResult.output_tokens,
|
|
227
|
+
savedTokens: tokenCompanyResult.tokens_saved
|
|
228
|
+
} : void 0;
|
|
229
|
+
return buildResult(
|
|
230
|
+
options.input,
|
|
231
|
+
compressedInput,
|
|
232
|
+
"thetokencompany",
|
|
233
|
+
original,
|
|
234
|
+
compressed,
|
|
235
|
+
[...heuristic.techniques, "thetokencompany"],
|
|
236
|
+
tokenCounts
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
function normalizeTheTokenCompanyCompressResponse(data) {
|
|
240
|
+
if (typeof data.output !== "string" || typeof data.output_tokens !== "number") {
|
|
241
|
+
return void 0;
|
|
242
|
+
}
|
|
243
|
+
const inputTokens = typeof data.input_tokens === "number" ? data.input_tokens : data.original_input_tokens;
|
|
244
|
+
if (typeof inputTokens !== "number") {
|
|
245
|
+
return void 0;
|
|
246
|
+
}
|
|
247
|
+
const tokensSaved = typeof data.tokens_saved === "number" ? data.tokens_saved : inputTokens - data.output_tokens;
|
|
248
|
+
const compressionRatio = typeof data.compression_ratio === "number" ? data.compression_ratio : data.output_tokens === 0 ? 0 : inputTokens / data.output_tokens;
|
|
249
|
+
return {
|
|
250
|
+
output: data.output,
|
|
251
|
+
output_tokens: data.output_tokens,
|
|
252
|
+
input_tokens: inputTokens,
|
|
253
|
+
tokens_saved: tokensSaved,
|
|
254
|
+
compression_ratio: compressionRatio
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
function createPromptCompressionFallback(input, provider = "heuristic", error) {
|
|
258
|
+
const original = stableStringifyInput(input);
|
|
259
|
+
const techniques = ["fallback-original"];
|
|
260
|
+
if (error) {
|
|
261
|
+
techniques.push("compression-error");
|
|
262
|
+
}
|
|
263
|
+
return buildResult(input, input, provider, original, original, techniques);
|
|
264
|
+
}
|
|
265
|
+
function buildResult(input, compressedInput, provider, original, compressed, techniques, tokenCounts) {
|
|
266
|
+
const originalCharacters = original.length;
|
|
267
|
+
const compressedCharacters = compressed.length;
|
|
268
|
+
const savedCharacters = Math.max(
|
|
269
|
+
0,
|
|
270
|
+
originalCharacters - compressedCharacters
|
|
271
|
+
);
|
|
272
|
+
const originalTokens = tokenCounts?.originalTokens ?? estimatePromptTokens(original);
|
|
273
|
+
const compressedTokens = tokenCounts?.compressedTokens ?? estimatePromptTokens(compressed);
|
|
274
|
+
const savedTokens = Math.max(
|
|
275
|
+
0,
|
|
276
|
+
tokenCounts?.savedTokens ?? originalTokens - compressedTokens
|
|
277
|
+
);
|
|
278
|
+
return {
|
|
279
|
+
input,
|
|
280
|
+
compressedInput,
|
|
281
|
+
provider,
|
|
282
|
+
originalCharacters,
|
|
283
|
+
compressedCharacters,
|
|
284
|
+
savedCharacters,
|
|
285
|
+
originalTokens,
|
|
286
|
+
compressedTokens,
|
|
287
|
+
savedTokens,
|
|
288
|
+
tokenSavingsRatio: originalTokens > 0 ? savedTokens / originalTokens : 0,
|
|
289
|
+
savingsRatio: originalCharacters > 0 ? savedCharacters / originalCharacters : 0,
|
|
290
|
+
techniques
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
function estimatePromptTokens(input) {
|
|
294
|
+
const text = typeof input === "string" ? input : stableStringifyInput(input);
|
|
295
|
+
return text.match(/[\p{L}\p{N}]+|[^\s]/gu)?.length ?? 0;
|
|
296
|
+
}
|
|
297
|
+
function compressValue(value, techniques, options) {
|
|
298
|
+
if (typeof value === "string") return compressText(value, techniques, options);
|
|
299
|
+
if (Array.isArray(value)) {
|
|
300
|
+
techniques.add("json-minify");
|
|
301
|
+
return value.map((item) => compressValue(item, techniques, options));
|
|
302
|
+
}
|
|
303
|
+
if (value && typeof value === "object") {
|
|
304
|
+
techniques.add("json-minify");
|
|
305
|
+
return Object.keys(value).reduce((acc, key) => {
|
|
306
|
+
const child = value[key];
|
|
307
|
+
if (child !== void 0) {
|
|
308
|
+
acc[key] = compressValue(child, techniques, options);
|
|
309
|
+
}
|
|
310
|
+
return acc;
|
|
311
|
+
}, {});
|
|
312
|
+
}
|
|
313
|
+
return value;
|
|
314
|
+
}
|
|
315
|
+
function compressText(value, techniques, options) {
|
|
316
|
+
const protectedSpans = [];
|
|
317
|
+
const text = value.replace(PROTECTED_TEXT_PATTERN, (match) => {
|
|
318
|
+
const placeholder = `__USAGETAP_PROTECTED_${protectedSpans.length}__`;
|
|
319
|
+
protectedSpans.push(match);
|
|
320
|
+
techniques.add("protected-text");
|
|
321
|
+
return placeholder;
|
|
322
|
+
});
|
|
323
|
+
const compressed = compressTextWithoutProtection(text, techniques, options);
|
|
324
|
+
return protectedSpans.reduce(
|
|
325
|
+
(output, span, index) => output.replace(`__USAGETAP_PROTECTED_${index}__`, span),
|
|
326
|
+
compressed
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
function compressTextWithoutProtection(value, techniques, options) {
|
|
330
|
+
const fencePattern = /```([\w-]+)?\n([\s\S]*?)```/g;
|
|
331
|
+
const parts = [];
|
|
332
|
+
let cursor = 0;
|
|
333
|
+
let match;
|
|
334
|
+
while ((match = fencePattern.exec(value)) !== null) {
|
|
335
|
+
const before = value.slice(cursor, match.index);
|
|
336
|
+
const compressedBefore = compressPlainTextAndEmbeddedJson(
|
|
337
|
+
before,
|
|
338
|
+
techniques,
|
|
339
|
+
options
|
|
340
|
+
);
|
|
341
|
+
if (compressedBefore) parts.push(compressedBefore);
|
|
342
|
+
const lang = match[1]?.toLowerCase();
|
|
343
|
+
const code = cleanCodeBlock(match[2] ?? "");
|
|
344
|
+
const compressedCode = lang === "json" ? compressJsonText(code, techniques, options) : void 0;
|
|
345
|
+
if (compressedCode?.format === "toon") {
|
|
346
|
+
parts.push(`\`\`\`toon
|
|
347
|
+
${compressedCode.text}
|
|
348
|
+
\`\`\``);
|
|
349
|
+
} else if (compressedCode?.format === "json") {
|
|
350
|
+
parts.push(`\`\`\`json
|
|
351
|
+
${compressedCode.text}
|
|
352
|
+
\`\`\``);
|
|
353
|
+
} else {
|
|
354
|
+
if (code !== match[2]) {
|
|
355
|
+
techniques.add("code-whitespace");
|
|
356
|
+
}
|
|
357
|
+
parts.push(lang ? `\`\`\`${lang}
|
|
358
|
+
${code}
|
|
359
|
+
\`\`\`` : `\`\`\`
|
|
360
|
+
${code}
|
|
361
|
+
\`\`\``);
|
|
362
|
+
}
|
|
363
|
+
cursor = match.index + match[0].length;
|
|
364
|
+
}
|
|
365
|
+
const after = compressPlainTextAndEmbeddedJson(value.slice(cursor), techniques, options);
|
|
366
|
+
if (after) parts.push(after);
|
|
367
|
+
return parts.join("\n").trim();
|
|
368
|
+
}
|
|
369
|
+
function compressPlainText(value, techniques) {
|
|
370
|
+
const compressed = value.split("\n").map((line) => line.trim()).filter((line) => line).join("\n").replace(/[ \t]{2,}/g, " ").trim();
|
|
371
|
+
if (compressed !== value.trim()) {
|
|
372
|
+
techniques.add("text-whitespace");
|
|
373
|
+
}
|
|
374
|
+
return compressed;
|
|
375
|
+
}
|
|
376
|
+
function compressPlainTextAndEmbeddedJson(value, techniques, options) {
|
|
377
|
+
const normalized = compressPlainText(value, techniques);
|
|
378
|
+
return compressEmbeddedJson(normalized, techniques, options);
|
|
379
|
+
}
|
|
380
|
+
function cleanCodeBlock(code) {
|
|
381
|
+
const lines = code.replace(/\r\n/g, "\n").split("\n");
|
|
382
|
+
while (lines.length && lines[0].trim() === "") lines.shift();
|
|
383
|
+
while (lines.length && lines[lines.length - 1].trim() === "") lines.pop();
|
|
384
|
+
const commonIndent = lines.filter((line) => line.trim()).reduce((min, line) => {
|
|
385
|
+
const indent = /^[ \t]*/.exec(line)?.[0].length ?? 0;
|
|
386
|
+
return min === void 0 ? indent : Math.min(min, indent);
|
|
387
|
+
}, void 0);
|
|
388
|
+
return lines.map((line) => commonIndent ? line.slice(commonIndent) : line).join("\n").replace(/[ \t]+$/gm, "");
|
|
389
|
+
}
|
|
390
|
+
function stableStringifyInput(input) {
|
|
391
|
+
if (typeof input === "string") return input;
|
|
392
|
+
return JSON.stringify(input) ?? String(input);
|
|
393
|
+
}
|
|
394
|
+
function compressJsonText(text, techniques, options) {
|
|
395
|
+
const parsed = safeParseJson(text);
|
|
396
|
+
if (parsed === void 0) {
|
|
397
|
+
return void 0;
|
|
398
|
+
}
|
|
399
|
+
const compactJson = JSON.stringify(parsed);
|
|
400
|
+
const candidates = [
|
|
401
|
+
{ format: "json", text: compactJson }
|
|
402
|
+
];
|
|
403
|
+
if (options.allowToonString || shouldUseToonForJson(parsed)) {
|
|
404
|
+
candidates.push({ format: "toon", text: encodeToon(parsed) });
|
|
405
|
+
}
|
|
406
|
+
const originalLength = text.trim().length;
|
|
407
|
+
const best = candidates.reduce(
|
|
408
|
+
(winner, candidate) => candidate.text.length < winner.text.length ? candidate : winner
|
|
409
|
+
);
|
|
410
|
+
if (best.text.length >= originalLength) {
|
|
411
|
+
return void 0;
|
|
412
|
+
}
|
|
413
|
+
techniques.add(best.format === "toon" ? "embedded-json-toon" : "embedded-json-minify");
|
|
414
|
+
return best;
|
|
415
|
+
}
|
|
416
|
+
function compressEmbeddedJson(text, techniques, options) {
|
|
417
|
+
let result = "";
|
|
418
|
+
let cursor = 0;
|
|
419
|
+
while (cursor < text.length) {
|
|
420
|
+
const start = findNextJsonStart(text, cursor);
|
|
421
|
+
if (start < 0) {
|
|
422
|
+
result += text.slice(cursor);
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
result += text.slice(cursor, start);
|
|
426
|
+
const span = findBalancedJsonSpan(text, start);
|
|
427
|
+
if (!span) {
|
|
428
|
+
result += text[start];
|
|
429
|
+
cursor = start + 1;
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
const candidate = compressJsonText(span.text, techniques, options);
|
|
433
|
+
if (candidate) {
|
|
434
|
+
result += candidate.text;
|
|
435
|
+
} else {
|
|
436
|
+
result += span.text;
|
|
437
|
+
}
|
|
438
|
+
cursor = span.end;
|
|
439
|
+
}
|
|
440
|
+
return result;
|
|
441
|
+
}
|
|
442
|
+
function findNextJsonStart(text, from) {
|
|
443
|
+
const objectStart = text.indexOf("{", from);
|
|
444
|
+
const arrayStart = text.indexOf("[", from);
|
|
445
|
+
if (objectStart < 0) return arrayStart;
|
|
446
|
+
if (arrayStart < 0) return objectStart;
|
|
447
|
+
return Math.min(objectStart, arrayStart);
|
|
448
|
+
}
|
|
449
|
+
function findBalancedJsonSpan(text, start) {
|
|
450
|
+
const opener = text[start];
|
|
451
|
+
const closer = opener === "{" ? "}" : opener === "[" ? "]" : void 0;
|
|
452
|
+
if (!closer) return void 0;
|
|
453
|
+
const stack = [closer];
|
|
454
|
+
let inString = false;
|
|
455
|
+
let escaped = false;
|
|
456
|
+
for (let index = start + 1; index < text.length; index += 1) {
|
|
457
|
+
const char = text[index];
|
|
458
|
+
if (inString) {
|
|
459
|
+
if (escaped) {
|
|
460
|
+
escaped = false;
|
|
461
|
+
} else if (char === "\\") {
|
|
462
|
+
escaped = true;
|
|
463
|
+
} else if (char === '"') {
|
|
464
|
+
inString = false;
|
|
465
|
+
}
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
if (char === '"') {
|
|
469
|
+
inString = true;
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
if (char === "{" || char === "[") {
|
|
473
|
+
stack.push(char === "{" ? "}" : "]");
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
if (char === stack[stack.length - 1]) {
|
|
477
|
+
stack.pop();
|
|
478
|
+
if (!stack.length) {
|
|
479
|
+
const end = index + 1;
|
|
480
|
+
return { text: text.slice(start, end), end };
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
return void 0;
|
|
485
|
+
}
|
|
486
|
+
function safeParseJson(text) {
|
|
487
|
+
try {
|
|
488
|
+
return JSON.parse(text);
|
|
489
|
+
} catch {
|
|
490
|
+
return void 0;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
function shouldUseToonForJson(value) {
|
|
494
|
+
if (Array.isArray(value)) {
|
|
495
|
+
return isUniformObjectArray(value) || value.some(shouldUseToonForJson);
|
|
496
|
+
}
|
|
497
|
+
if (isPlainObject(value)) {
|
|
498
|
+
return Object.values(value).some(shouldUseToonForJson);
|
|
499
|
+
}
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
function encodeToon(value, indent = 0) {
|
|
503
|
+
if (isPrimitive(value)) {
|
|
504
|
+
return scalarToToon(value);
|
|
505
|
+
}
|
|
506
|
+
if (Array.isArray(value)) {
|
|
507
|
+
return encodeArrayToon(value, indent);
|
|
508
|
+
}
|
|
509
|
+
if (isPlainObject(value)) {
|
|
510
|
+
const lines = [];
|
|
511
|
+
for (const [key, child] of Object.entries(value)) {
|
|
512
|
+
lines.push(...encodePropertyToon(key, child, indent));
|
|
513
|
+
}
|
|
514
|
+
return lines.join("\n");
|
|
515
|
+
}
|
|
516
|
+
return scalarToToon(String(value));
|
|
517
|
+
}
|
|
518
|
+
function encodePropertyToon(key, value, indent) {
|
|
519
|
+
const prefix = " ".repeat(indent);
|
|
520
|
+
const toonKey = keyToToon(key);
|
|
521
|
+
if (isPrimitive(value)) {
|
|
522
|
+
return [`${prefix}${toonKey}: ${scalarToToon(value)}`];
|
|
523
|
+
}
|
|
524
|
+
if (Array.isArray(value)) {
|
|
525
|
+
if (value.every(isPrimitive)) {
|
|
526
|
+
return [`${prefix}${toonKey}[${value.length}]: ${value.map(scalarToToon).join(",")}`];
|
|
527
|
+
}
|
|
528
|
+
if (isUniformObjectArray(value)) {
|
|
529
|
+
const fields = Object.keys(value[0]);
|
|
530
|
+
const header = `${prefix}${toonKey}[${value.length}]{${fields.map(keyToToon).join(",")}}:`;
|
|
531
|
+
const rows = value.map(
|
|
532
|
+
(item) => `${" ".repeat(indent + 2)}${fields.map(
|
|
533
|
+
(field) => scalarToToon(item[field])
|
|
534
|
+
).join(",")}`
|
|
535
|
+
);
|
|
536
|
+
return [header, ...rows];
|
|
537
|
+
}
|
|
538
|
+
return [
|
|
539
|
+
`${prefix}${toonKey}[${value.length}]:`,
|
|
540
|
+
...value.flatMap((item, index) => {
|
|
541
|
+
if (isPrimitive(item)) {
|
|
542
|
+
return [`${" ".repeat(indent + 2)}- ${scalarToToon(item)}`];
|
|
543
|
+
}
|
|
544
|
+
return [
|
|
545
|
+
`${" ".repeat(indent + 2)}- item${index}:`,
|
|
546
|
+
...encodeToon(item, indent + 4).split("\n")
|
|
547
|
+
];
|
|
548
|
+
})
|
|
549
|
+
];
|
|
550
|
+
}
|
|
551
|
+
return [`${prefix}${toonKey}:`, ...encodeToon(value, indent + 2).split("\n")];
|
|
552
|
+
}
|
|
553
|
+
function encodeArrayToon(value, indent) {
|
|
554
|
+
if (value.every(isPrimitive)) {
|
|
555
|
+
return `[${value.length}]: ${value.map(scalarToToon).join(",")}`;
|
|
556
|
+
}
|
|
557
|
+
if (isUniformObjectArray(value)) {
|
|
558
|
+
const fields = Object.keys(value[0]);
|
|
559
|
+
return [
|
|
560
|
+
`[${value.length}]{${fields.map(keyToToon).join(",")}}:`,
|
|
561
|
+
...value.map(
|
|
562
|
+
(item) => `${" ".repeat(indent + 2)}${fields.map(
|
|
563
|
+
(field) => scalarToToon(item[field])
|
|
564
|
+
).join(",")}`
|
|
565
|
+
)
|
|
566
|
+
].join("\n");
|
|
567
|
+
}
|
|
568
|
+
return value.flatMap((item, index) => [
|
|
569
|
+
`${" ".repeat(indent)}- item${index}:`,
|
|
570
|
+
...encodeToon(item, indent + 2).split("\n")
|
|
571
|
+
]).join("\n");
|
|
572
|
+
}
|
|
573
|
+
function isUniformObjectArray(value) {
|
|
574
|
+
if (!value.length || !value.every(isPlainObject)) {
|
|
575
|
+
return false;
|
|
576
|
+
}
|
|
577
|
+
const fields = Object.keys(value[0]);
|
|
578
|
+
if (!fields.length) {
|
|
579
|
+
return false;
|
|
580
|
+
}
|
|
581
|
+
return value.every((item) => {
|
|
582
|
+
const record = item;
|
|
583
|
+
const itemFields = Object.keys(record);
|
|
584
|
+
return itemFields.length === fields.length && fields.every((field) => itemFields.includes(field) && isPrimitive(record[field]));
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
function isPlainObject(value) {
|
|
588
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
589
|
+
}
|
|
590
|
+
function isPrimitive(value) {
|
|
591
|
+
return value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean";
|
|
592
|
+
}
|
|
593
|
+
function keyToToon(key) {
|
|
594
|
+
return /^[A-Za-z_][A-Za-z0-9_-]*$/.test(key) ? key : JSON.stringify(key);
|
|
595
|
+
}
|
|
596
|
+
function scalarToToon(value) {
|
|
597
|
+
if (value === null) return "null";
|
|
598
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
599
|
+
return String(value);
|
|
600
|
+
}
|
|
601
|
+
const text = String(value);
|
|
602
|
+
if (text && !/^(true|false|null|-?\d+(?:\.\d+)?)$/i.test(text) && /^[A-Za-z0-9_./@-]+(?: [A-Za-z0-9_./@-]+)*$/.test(text)) {
|
|
603
|
+
return text;
|
|
604
|
+
}
|
|
605
|
+
return JSON.stringify(text);
|
|
606
|
+
}
|
|
607
|
+
|
|
118
608
|
// src/client.ts
|
|
119
609
|
var CALL_BEGIN_PATH = "call_begin";
|
|
120
610
|
var CALL_END_PATH = "call_end";
|
|
611
|
+
var COMPRESS_PROMPT_PATH = "compress_prompt";
|
|
121
612
|
var CHECK_USAGE_PATH = "customers/{customerId}/usage";
|
|
122
613
|
var CREATE_CUSTOMER_PATH = "customers";
|
|
123
614
|
var CHANGE_PLAN_PATH = "customers/{customerId}/change_plan";
|
|
@@ -129,7 +620,7 @@ var IDEMPOTENCY_HEADER = "idempotency-key";
|
|
|
129
620
|
var SDK_HEADER = "x-usage-sdk";
|
|
130
621
|
var USER_AGENT = "UsageTapClient";
|
|
131
622
|
var CANONICAL_MEDIA_TYPE = "application/vnd.usagetap.v1+json";
|
|
132
|
-
var SDK_VERSION = "
|
|
623
|
+
var SDK_VERSION = "1.1.0" ;
|
|
133
624
|
var HAS_WINDOW = typeof globalThis !== "undefined" && typeof globalThis.window !== "undefined";
|
|
134
625
|
var UsageTapClient = class {
|
|
135
626
|
apiKey;
|
|
@@ -144,6 +635,11 @@ var UsageTapClient = class {
|
|
|
144
635
|
metricFn;
|
|
145
636
|
authHeader;
|
|
146
637
|
autoIdempotency;
|
|
638
|
+
tokenCompanyApiKey;
|
|
639
|
+
tokenCompanyEndpoint;
|
|
640
|
+
tokenCompanyModel;
|
|
641
|
+
tokenCompanyAggressiveness;
|
|
642
|
+
tokenCompanyAppId;
|
|
147
643
|
constructor(options) {
|
|
148
644
|
if (!options) {
|
|
149
645
|
throw new UsageTapError(
|
|
@@ -189,6 +685,11 @@ var UsageTapClient = class {
|
|
|
189
685
|
this.metricFn = options.onUsageMetric;
|
|
190
686
|
this.authHeader = options.useApiKeyHeader ? API_KEY_HEADER : AUTH_HEADER;
|
|
191
687
|
this.autoIdempotency = options.autoIdempotency ?? true;
|
|
688
|
+
this.tokenCompanyApiKey = options.tokenCompanyApiKey;
|
|
689
|
+
this.tokenCompanyEndpoint = options.tokenCompanyEndpoint;
|
|
690
|
+
this.tokenCompanyModel = options.tokenCompanyModel;
|
|
691
|
+
this.tokenCompanyAggressiveness = options.tokenCompanyAggressiveness;
|
|
692
|
+
this.tokenCompanyAppId = options.tokenCompanyAppId;
|
|
192
693
|
}
|
|
193
694
|
async beginCall(request, options = {}) {
|
|
194
695
|
const idempotencyKey = request.idempotencyKey ?? request.idempotency ?? (this.autoIdempotency ? this.idempotencyGenerator() : void 0);
|
|
@@ -211,6 +712,70 @@ var UsageTapClient = class {
|
|
|
211
712
|
);
|
|
212
713
|
return response;
|
|
213
714
|
}
|
|
715
|
+
async promptCompress(request, options = {}) {
|
|
716
|
+
if (!request?.callId) {
|
|
717
|
+
throw new UsageTapError(
|
|
718
|
+
"USAGETAP_BAD_REQUEST",
|
|
719
|
+
"promptCompress requires callId"
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
const result = await this.compressPromptInput(request.input, {
|
|
723
|
+
provider: request.provider,
|
|
724
|
+
tokenCompanyModel: request.tokenCompanyModel,
|
|
725
|
+
tokenCompanyAggressiveness: request.tokenCompanyAggressiveness,
|
|
726
|
+
tokenCompanyAppId: request.tokenCompanyAppId,
|
|
727
|
+
signal: options.signal
|
|
728
|
+
});
|
|
729
|
+
try {
|
|
730
|
+
await this.recordPromptCompression(
|
|
731
|
+
{
|
|
732
|
+
callId: request.callId,
|
|
733
|
+
promptCompression: this.toPromptCompressionTelemetry(result)
|
|
734
|
+
},
|
|
735
|
+
options
|
|
736
|
+
);
|
|
737
|
+
return { ...result, callId: request.callId };
|
|
738
|
+
} catch (error) {
|
|
739
|
+
return {
|
|
740
|
+
...createPromptCompressionFallback(
|
|
741
|
+
request.input,
|
|
742
|
+
request.provider ?? result.provider,
|
|
743
|
+
error
|
|
744
|
+
),
|
|
745
|
+
callId: request.callId
|
|
746
|
+
};
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
async compressPromptInput(input, options = {}) {
|
|
750
|
+
return compressPrompt({
|
|
751
|
+
input,
|
|
752
|
+
provider: options.provider,
|
|
753
|
+
tokenCompanyApiKey: this.tokenCompanyApiKey,
|
|
754
|
+
tokenCompanyEndpoint: this.tokenCompanyEndpoint,
|
|
755
|
+
tokenCompanyModel: options.tokenCompanyModel ?? this.tokenCompanyModel,
|
|
756
|
+
tokenCompanyAggressiveness: options.tokenCompanyAggressiveness ?? this.tokenCompanyAggressiveness,
|
|
757
|
+
tokenCompanyAppId: options.tokenCompanyAppId ?? this.tokenCompanyAppId,
|
|
758
|
+
fetchImpl: this.fetchImpl,
|
|
759
|
+
signal: options.signal,
|
|
760
|
+
failOpen: options.failOpen
|
|
761
|
+
});
|
|
762
|
+
}
|
|
763
|
+
async recordPromptCompression(request, options = {}) {
|
|
764
|
+
if (!request?.callId) {
|
|
765
|
+
throw new UsageTapError(
|
|
766
|
+
"USAGETAP_BAD_REQUEST",
|
|
767
|
+
"recordPromptCompression requires callId"
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
return this.request(
|
|
771
|
+
COMPRESS_PROMPT_PATH,
|
|
772
|
+
{
|
|
773
|
+
callId: request.callId,
|
|
774
|
+
promptCompression: request.promptCompression
|
|
775
|
+
},
|
|
776
|
+
options
|
|
777
|
+
);
|
|
778
|
+
}
|
|
214
779
|
async endCall(request, options = {}) {
|
|
215
780
|
if (!request?.callId) {
|
|
216
781
|
throw new UsageTapError(
|
|
@@ -435,6 +1000,20 @@ var UsageTapClient = class {
|
|
|
435
1000
|
}
|
|
436
1001
|
return handlerResult;
|
|
437
1002
|
}
|
|
1003
|
+
toPromptCompressionTelemetry(result) {
|
|
1004
|
+
return {
|
|
1005
|
+
provider: result.provider,
|
|
1006
|
+
originalCharacters: result.originalCharacters,
|
|
1007
|
+
compressedCharacters: result.compressedCharacters,
|
|
1008
|
+
savedCharacters: result.savedCharacters,
|
|
1009
|
+
originalTokens: result.originalTokens,
|
|
1010
|
+
compressedTokens: result.compressedTokens,
|
|
1011
|
+
savedTokens: result.savedTokens,
|
|
1012
|
+
tokenSavingsRatio: result.tokenSavingsRatio,
|
|
1013
|
+
savingsRatio: result.savingsRatio,
|
|
1014
|
+
techniques: result.techniques
|
|
1015
|
+
};
|
|
1016
|
+
}
|
|
438
1017
|
async request(path, payload, options) {
|
|
439
1018
|
const url = new URL(path, this.baseUrl).toString();
|
|
440
1019
|
const body = payload !== void 0 ? JSON.stringify(payload) : void 0;
|
|
@@ -1024,8 +1603,14 @@ async function finalizeCall(callState, usageTap, error, usage) {
|
|
|
1024
1603
|
|
|
1025
1604
|
exports.UsageTapClient = UsageTapClient;
|
|
1026
1605
|
exports.UsageTapError = UsageTapError;
|
|
1606
|
+
exports.compressPrompt = compressPrompt;
|
|
1607
|
+
exports.compressPromptHeuristic = compressPromptHeuristic;
|
|
1608
|
+
exports.compressPromptToon = compressPromptToon;
|
|
1027
1609
|
exports.createIdempotencyKey = createIdempotencyKey;
|
|
1610
|
+
exports.estimatePromptTokens = estimatePromptTokens;
|
|
1028
1611
|
exports.isUsageTapError = isUsageTapError;
|
|
1612
|
+
exports.protect = protect;
|
|
1613
|
+
exports.protectPromptText = protectPromptText;
|
|
1029
1614
|
exports.wrapFetch = wrapFetch;
|
|
1030
1615
|
//# sourceMappingURL=index.cjs.map
|
|
1031
1616
|
//# sourceMappingURL=index.cjs.map
|