@usagetap/sdk 1.0.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/README.md +53 -16
- 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-BHNMYvlO.d.cts → client-BA-QlnRq.d.cts} +32 -1
- package/dist/{client-BHNMYvlO.d.ts → client-BA-QlnRq.d.ts} +32 -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 +77 -9
- 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 +76 -10
- 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 +21 -1
|
@@ -0,0 +1,943 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// src/errors.ts
|
|
4
|
+
var UsageTapError = class extends Error {
|
|
5
|
+
code;
|
|
6
|
+
status;
|
|
7
|
+
retryable;
|
|
8
|
+
correlationId;
|
|
9
|
+
details;
|
|
10
|
+
constructor(code, message, init = {}) {
|
|
11
|
+
super(message, init.cause ? { cause: init.cause } : void 0);
|
|
12
|
+
this.name = "UsageTapError";
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.status = init.status;
|
|
15
|
+
this.retryable = init.retryable ?? false;
|
|
16
|
+
this.correlationId = init.correlationId;
|
|
17
|
+
this.details = init.details;
|
|
18
|
+
}
|
|
19
|
+
toJSON() {
|
|
20
|
+
return {
|
|
21
|
+
name: this.name,
|
|
22
|
+
message: this.message,
|
|
23
|
+
code: this.code,
|
|
24
|
+
status: this.status,
|
|
25
|
+
retryable: this.retryable,
|
|
26
|
+
correlationId: this.correlationId,
|
|
27
|
+
details: this.details
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
};
|
|
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
|
+
|
|
42
|
+
// src/adapters/anthropic.ts
|
|
43
|
+
var AnthropicPromptCompressionStats = 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
|
+
};
|
|
88
|
+
var USAGETAP_CORRELATION_HEADER = "x-usage-correlation-id";
|
|
89
|
+
function wrapAnthropic(client, usageTap, options = {}) {
|
|
90
|
+
if (!client) {
|
|
91
|
+
throw new UsageTapError("USAGETAP_BAD_REQUEST", "wrapAnthropic requires an Anthropic client instance");
|
|
92
|
+
}
|
|
93
|
+
if (!client.messages || !isObjectRecord(client.messages)) {
|
|
94
|
+
throw new UsageTapError("USAGETAP_BAD_REQUEST", "wrapAnthropic requires client.messages");
|
|
95
|
+
}
|
|
96
|
+
const defaultContext = options.defaultContext;
|
|
97
|
+
const applyVendorHints = options.applyVendorHints !== false;
|
|
98
|
+
const defaultPromptCompression = normalizePromptCompressionOptions(options.promptCompression);
|
|
99
|
+
const promptCompressionStats = new AnthropicPromptCompressionStats();
|
|
100
|
+
const proxiedMessages = createMessagesProxy(
|
|
101
|
+
client.messages,
|
|
102
|
+
usageTap,
|
|
103
|
+
defaultContext,
|
|
104
|
+
applyVendorHints,
|
|
105
|
+
defaultPromptCompression,
|
|
106
|
+
promptCompressionStats
|
|
107
|
+
);
|
|
108
|
+
const handler = {
|
|
109
|
+
get(target, prop, receiver) {
|
|
110
|
+
if (prop === "messages") {
|
|
111
|
+
return proxiedMessages;
|
|
112
|
+
}
|
|
113
|
+
if (prop === "promptCompression") {
|
|
114
|
+
return promptCompressionStats;
|
|
115
|
+
}
|
|
116
|
+
if (prop === "unwrap") {
|
|
117
|
+
return () => target;
|
|
118
|
+
}
|
|
119
|
+
return Reflect.get(target, prop, receiver);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
return new Proxy(client, handler);
|
|
123
|
+
}
|
|
124
|
+
function createMessagesProxy(resource, usageTap, defaultContext, applyVendorHints, defaultPromptCompression, promptCompressionStats) {
|
|
125
|
+
if (typeof resource.create !== "function") {
|
|
126
|
+
throw new UsageTapError("USAGETAP_BAD_REQUEST", "wrapAnthropic requires client.messages.create");
|
|
127
|
+
}
|
|
128
|
+
const originalCreate = resource.create.bind(resource);
|
|
129
|
+
const wrappedCreate = ((params, options) => {
|
|
130
|
+
const {
|
|
131
|
+
requestOptions,
|
|
132
|
+
usageContext,
|
|
133
|
+
withUsage,
|
|
134
|
+
promptCompression
|
|
135
|
+
} = splitUsageOptions(options);
|
|
136
|
+
return invokeMessagesCreate({
|
|
137
|
+
params,
|
|
138
|
+
requestOptions,
|
|
139
|
+
usageContext,
|
|
140
|
+
withUsage,
|
|
141
|
+
promptCompression,
|
|
142
|
+
originalCreate,
|
|
143
|
+
usageTap,
|
|
144
|
+
defaultContext,
|
|
145
|
+
applyVendorHints,
|
|
146
|
+
defaultPromptCompression,
|
|
147
|
+
promptCompressionStats
|
|
148
|
+
});
|
|
149
|
+
});
|
|
150
|
+
const handler = {
|
|
151
|
+
get(target, prop, receiver) {
|
|
152
|
+
if (prop === "create") {
|
|
153
|
+
return wrappedCreate;
|
|
154
|
+
}
|
|
155
|
+
return Reflect.get(target, prop, receiver);
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
return new Proxy(resource, handler);
|
|
159
|
+
}
|
|
160
|
+
async function invokeMessagesCreate(args) {
|
|
161
|
+
const beginRequest = resolveBeginRequest(args.defaultContext, args.usageContext);
|
|
162
|
+
const begin = await args.usageTap.beginCall(
|
|
163
|
+
beginRequest,
|
|
164
|
+
beginCallOptions(args.withUsage)
|
|
165
|
+
);
|
|
166
|
+
const state = createCallState(beginRequest, begin);
|
|
167
|
+
const ctx = createUsageContext(state);
|
|
168
|
+
try {
|
|
169
|
+
const hintedParams = args.applyVendorHints ? applyAnthropicVendorHints(args.params, ctx.begin.data.vendorHints) : args.params;
|
|
170
|
+
const finalParams = await compressAnthropicParamsForCall({
|
|
171
|
+
params: hintedParams,
|
|
172
|
+
usageTap: args.usageTap,
|
|
173
|
+
ctx,
|
|
174
|
+
defaultPromptCompression: args.defaultPromptCompression,
|
|
175
|
+
callPromptCompression: args.promptCompression,
|
|
176
|
+
stats: args.promptCompressionStats,
|
|
177
|
+
withUsage: args.withUsage,
|
|
178
|
+
operation: "messages.create"
|
|
179
|
+
});
|
|
180
|
+
const request = attachCorrelationHeader(args.requestOptions, ctx.begin.correlationId);
|
|
181
|
+
const response = await args.originalCreate(finalParams, request);
|
|
182
|
+
if (isStreamingRequest(finalParams)) {
|
|
183
|
+
ensureAsyncIterable(response, "messages.create");
|
|
184
|
+
return wrapAnthropicStreamForUsageTap(
|
|
185
|
+
response,
|
|
186
|
+
state,
|
|
187
|
+
args.usageTap,
|
|
188
|
+
args.withUsage,
|
|
189
|
+
readString(finalParams.model)
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
inferAnthropicUsage(response, readString(finalParams.model), ctx);
|
|
193
|
+
await finalizeCall(state, args.usageTap, args.withUsage);
|
|
194
|
+
return response;
|
|
195
|
+
} catch (error) {
|
|
196
|
+
if (!state.error) {
|
|
197
|
+
state.error = {
|
|
198
|
+
code: args.withUsage?.defaultErrorCode ?? "VENDOR_ERROR",
|
|
199
|
+
message: error instanceof Error ? error.message : String(error)
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
await finalizeCall(state, args.usageTap, args.withUsage);
|
|
203
|
+
throw error;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async function compressAnthropicParamsForCall(args) {
|
|
207
|
+
const compression = resolveEffectivePromptCompressionOptions(
|
|
208
|
+
args.defaultPromptCompression,
|
|
209
|
+
args.callPromptCompression
|
|
210
|
+
);
|
|
211
|
+
if (!compression) {
|
|
212
|
+
return args.params;
|
|
213
|
+
}
|
|
214
|
+
const outcome = await compressAnthropicParams(
|
|
215
|
+
args.params,
|
|
216
|
+
args.usageTap,
|
|
217
|
+
compression,
|
|
218
|
+
args.withUsage?.signal
|
|
219
|
+
);
|
|
220
|
+
await recordCompressionOutcome({
|
|
221
|
+
outcome,
|
|
222
|
+
compression,
|
|
223
|
+
usageTap: args.usageTap,
|
|
224
|
+
ctx: args.ctx,
|
|
225
|
+
stats: args.stats,
|
|
226
|
+
withUsage: args.withUsage,
|
|
227
|
+
operation: args.operation
|
|
228
|
+
});
|
|
229
|
+
return outcome.params;
|
|
230
|
+
}
|
|
231
|
+
async function recordCompressionOutcome(args) {
|
|
232
|
+
const telemetry = buildPromptCompressionTelemetry(args.outcome.segments);
|
|
233
|
+
if (!telemetry) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const turn = {
|
|
237
|
+
...telemetry,
|
|
238
|
+
callId: args.ctx.begin.data.callId,
|
|
239
|
+
operation: args.operation,
|
|
240
|
+
messagesCompressed: args.outcome.segments.length,
|
|
241
|
+
timestamp: Date.now()
|
|
242
|
+
};
|
|
243
|
+
args.stats._record(turn);
|
|
244
|
+
try {
|
|
245
|
+
await args.usageTap.recordPromptCompression(
|
|
246
|
+
{
|
|
247
|
+
callId: args.ctx.begin.data.callId,
|
|
248
|
+
promptCompression: telemetry
|
|
249
|
+
},
|
|
250
|
+
promptCompressionRequestOptions(args.withUsage, args.ctx.begin.correlationId)
|
|
251
|
+
);
|
|
252
|
+
} catch (error) {
|
|
253
|
+
args.stats._recordFailure({
|
|
254
|
+
callId: args.ctx.begin.data.callId,
|
|
255
|
+
operation: args.operation,
|
|
256
|
+
stage: "telemetry",
|
|
257
|
+
message: error instanceof Error ? error.message : String(error),
|
|
258
|
+
timestamp: Date.now()
|
|
259
|
+
});
|
|
260
|
+
if (args.compression.failOpen === false) {
|
|
261
|
+
throw error;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
async function compressAnthropicParams(params, usageTap, compression, signal) {
|
|
266
|
+
if (!params || typeof params !== "object") {
|
|
267
|
+
return { params, segments: [] };
|
|
268
|
+
}
|
|
269
|
+
const source = cloneRecord(params);
|
|
270
|
+
const segments = [];
|
|
271
|
+
if (typeof source.system === "string") {
|
|
272
|
+
const compressed = await compressTextForRole(
|
|
273
|
+
source.system,
|
|
274
|
+
"system",
|
|
275
|
+
usageTap,
|
|
276
|
+
compression,
|
|
277
|
+
signal
|
|
278
|
+
);
|
|
279
|
+
if (compressed) {
|
|
280
|
+
source.system = compressed.text;
|
|
281
|
+
segments.push(compressed.segment);
|
|
282
|
+
}
|
|
283
|
+
} else if (Array.isArray(source.system)) {
|
|
284
|
+
const systemResults = await Promise.all(
|
|
285
|
+
source.system.map(
|
|
286
|
+
(block) => compressAnthropicTextBlock(block, "system", usageTap, compression, signal)
|
|
287
|
+
)
|
|
288
|
+
);
|
|
289
|
+
const systemSegments = systemResults.flatMap(
|
|
290
|
+
(result) => result.segment ? [result.segment] : []
|
|
291
|
+
);
|
|
292
|
+
if (systemSegments.length) {
|
|
293
|
+
source.system = systemResults.map((result) => result.value);
|
|
294
|
+
segments.push(...systemSegments);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
if (Array.isArray(source.messages)) {
|
|
298
|
+
const messageResults = await Promise.all(
|
|
299
|
+
source.messages.map(
|
|
300
|
+
(message) => compressAnthropicMessage(message, usageTap, compression, signal)
|
|
301
|
+
)
|
|
302
|
+
);
|
|
303
|
+
source.messages = messageResults.map((result) => result.value);
|
|
304
|
+
segments.push(...messageResults.flatMap((result) => result.segments));
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
params: source,
|
|
308
|
+
segments
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
async function compressAnthropicMessage(message, usageTap, compression, signal) {
|
|
312
|
+
if (!isObjectRecord(message)) {
|
|
313
|
+
return { value: message, segments: [] };
|
|
314
|
+
}
|
|
315
|
+
const role = mapAnthropicMessageRole(message.role);
|
|
316
|
+
if (!role) {
|
|
317
|
+
return { value: message, segments: [] };
|
|
318
|
+
}
|
|
319
|
+
const content = message.content;
|
|
320
|
+
if (typeof content === "string") {
|
|
321
|
+
const compressed = await compressTextForRole(
|
|
322
|
+
content,
|
|
323
|
+
role,
|
|
324
|
+
usageTap,
|
|
325
|
+
compression,
|
|
326
|
+
signal
|
|
327
|
+
);
|
|
328
|
+
if (!compressed) {
|
|
329
|
+
return { value: message, segments: [] };
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
value: { ...message, content: compressed.text },
|
|
333
|
+
segments: [compressed.segment]
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
if (Array.isArray(content)) {
|
|
337
|
+
const blockResults = await Promise.all(
|
|
338
|
+
content.map(
|
|
339
|
+
(block) => compressAnthropicContentBlock(block, role, usageTap, compression, signal)
|
|
340
|
+
)
|
|
341
|
+
);
|
|
342
|
+
const segments = blockResults.flatMap((result) => result.segments);
|
|
343
|
+
return {
|
|
344
|
+
value: segments.length ? { ...message, content: blockResults.map((result) => result.value) } : message,
|
|
345
|
+
segments
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
return { value: message, segments: [] };
|
|
349
|
+
}
|
|
350
|
+
async function compressAnthropicContentBlock(block, messageRole, usageTap, compression, signal) {
|
|
351
|
+
if (!isObjectRecord(block)) {
|
|
352
|
+
return { value: block, segments: [] };
|
|
353
|
+
}
|
|
354
|
+
if (block.type === "tool_result") {
|
|
355
|
+
return compressAnthropicToolResultBlock(block, usageTap, compression, signal);
|
|
356
|
+
}
|
|
357
|
+
const textResult = await compressAnthropicTextBlock(
|
|
358
|
+
block,
|
|
359
|
+
messageRole,
|
|
360
|
+
usageTap,
|
|
361
|
+
compression,
|
|
362
|
+
signal
|
|
363
|
+
);
|
|
364
|
+
return {
|
|
365
|
+
value: textResult.value,
|
|
366
|
+
segments: textResult.segment ? [textResult.segment] : []
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
async function compressAnthropicToolResultBlock(block, usageTap, compression, signal) {
|
|
370
|
+
const content = block.content;
|
|
371
|
+
if (typeof content === "string") {
|
|
372
|
+
const compressed = await compressTextForRole(
|
|
373
|
+
content,
|
|
374
|
+
"tool",
|
|
375
|
+
usageTap,
|
|
376
|
+
compression,
|
|
377
|
+
signal
|
|
378
|
+
);
|
|
379
|
+
if (!compressed) {
|
|
380
|
+
return { value: block, segments: [] };
|
|
381
|
+
}
|
|
382
|
+
return {
|
|
383
|
+
value: { ...block, content: compressed.text },
|
|
384
|
+
segments: [compressed.segment]
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
if (Array.isArray(content)) {
|
|
388
|
+
const contentResults = await Promise.all(
|
|
389
|
+
content.map(
|
|
390
|
+
(child) => compressAnthropicTextBlock(child, "tool", usageTap, compression, signal)
|
|
391
|
+
)
|
|
392
|
+
);
|
|
393
|
+
const segments = contentResults.flatMap(
|
|
394
|
+
(result) => result.segment ? [result.segment] : []
|
|
395
|
+
);
|
|
396
|
+
if (!segments.length) {
|
|
397
|
+
return { value: block, segments: [] };
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
value: {
|
|
401
|
+
...block,
|
|
402
|
+
content: contentResults.map((result) => result.value)
|
|
403
|
+
},
|
|
404
|
+
segments
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
return { value: block, segments: [] };
|
|
408
|
+
}
|
|
409
|
+
async function compressAnthropicTextBlock(block, role, usageTap, compression, signal) {
|
|
410
|
+
if (!isObjectRecord(block) || block.type !== "text" || typeof block.text !== "string") {
|
|
411
|
+
return { value: block };
|
|
412
|
+
}
|
|
413
|
+
const compressed = await compressTextForRole(
|
|
414
|
+
block.text,
|
|
415
|
+
role,
|
|
416
|
+
usageTap,
|
|
417
|
+
compression,
|
|
418
|
+
signal
|
|
419
|
+
);
|
|
420
|
+
if (!compressed) {
|
|
421
|
+
return { value: block };
|
|
422
|
+
}
|
|
423
|
+
return {
|
|
424
|
+
value: { ...block, text: compressed.text },
|
|
425
|
+
segment: compressed.segment
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
async function compressTextForRole(text, role, usageTap, compression, signal) {
|
|
429
|
+
if (!text.trim()) {
|
|
430
|
+
return void 0;
|
|
431
|
+
}
|
|
432
|
+
const roleOptions = resolveRoleCompressionOptions(compression, role);
|
|
433
|
+
if (!roleOptions) {
|
|
434
|
+
return void 0;
|
|
435
|
+
}
|
|
436
|
+
const estimatedTokens = estimatePromptTokens(text);
|
|
437
|
+
if (typeof roleOptions.minTokens === "number" && estimatedTokens < roleOptions.minTokens) {
|
|
438
|
+
return void 0;
|
|
439
|
+
}
|
|
440
|
+
const result = await usageTap.compressPromptInput(text, {
|
|
441
|
+
provider: roleOptions.provider,
|
|
442
|
+
failOpen: roleOptions.failOpen,
|
|
443
|
+
tokenCompanyModel: roleOptions.tokenCompanyModel,
|
|
444
|
+
tokenCompanyAggressiveness: roleOptions.tokenCompanyAggressiveness,
|
|
445
|
+
tokenCompanyAppId: roleOptions.tokenCompanyAppId,
|
|
446
|
+
signal
|
|
447
|
+
});
|
|
448
|
+
const compressedText = typeof result.compressedInput === "string" ? result.compressedInput : String(result.compressedInput);
|
|
449
|
+
return {
|
|
450
|
+
text: compressedText,
|
|
451
|
+
segment: { role, result: { ...result, compressedInput: compressedText } }
|
|
452
|
+
};
|
|
453
|
+
}
|
|
454
|
+
function normalizePromptCompressionOptions(options) {
|
|
455
|
+
if (!options) {
|
|
456
|
+
return void 0;
|
|
457
|
+
}
|
|
458
|
+
if (options === true) {
|
|
459
|
+
return {};
|
|
460
|
+
}
|
|
461
|
+
if (options.enabled === false) {
|
|
462
|
+
return void 0;
|
|
463
|
+
}
|
|
464
|
+
return options;
|
|
465
|
+
}
|
|
466
|
+
function resolveEffectivePromptCompressionOptions(defaults, override) {
|
|
467
|
+
if (override === false) {
|
|
468
|
+
return void 0;
|
|
469
|
+
}
|
|
470
|
+
if (override === void 0) {
|
|
471
|
+
return defaults;
|
|
472
|
+
}
|
|
473
|
+
if (override === true) {
|
|
474
|
+
return defaults ?? {};
|
|
475
|
+
}
|
|
476
|
+
const merged = {
|
|
477
|
+
...defaults ?? {},
|
|
478
|
+
...override,
|
|
479
|
+
roles: override.roles ?? defaults?.roles
|
|
480
|
+
};
|
|
481
|
+
return normalizePromptCompressionOptions(merged);
|
|
482
|
+
}
|
|
483
|
+
function resolveRoleCompressionOptions(compression, role) {
|
|
484
|
+
const hasExplicitRoles = compression.roles !== void 0;
|
|
485
|
+
const setting = compression.roles?.[role];
|
|
486
|
+
if (hasExplicitRoles && setting === void 0) {
|
|
487
|
+
return void 0;
|
|
488
|
+
}
|
|
489
|
+
if (!hasExplicitRoles && role === "assistant") {
|
|
490
|
+
return void 0;
|
|
491
|
+
}
|
|
492
|
+
if (setting === false) {
|
|
493
|
+
return void 0;
|
|
494
|
+
}
|
|
495
|
+
const roleOptions = typeof setting === "object" ? setting : void 0;
|
|
496
|
+
if (roleOptions?.enabled === false) {
|
|
497
|
+
return void 0;
|
|
498
|
+
}
|
|
499
|
+
return {
|
|
500
|
+
provider: roleOptions?.provider ?? compression.provider,
|
|
501
|
+
minTokens: roleOptions?.minTokens ?? compression.minTokens,
|
|
502
|
+
failOpen: compression.failOpen,
|
|
503
|
+
tokenCompanyModel: compression.tokenCompanyModel,
|
|
504
|
+
tokenCompanyAggressiveness: roleOptions?.tokenCompanyAggressiveness ?? resolveTokenCompanyAggressiveness(compression, role),
|
|
505
|
+
tokenCompanyAppId: compression.tokenCompanyAppId
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
function resolveTokenCompanyAggressiveness(compression, role) {
|
|
509
|
+
if (typeof compression.tokenCompanyAggressiveness === "number") {
|
|
510
|
+
return compression.tokenCompanyAggressiveness;
|
|
511
|
+
}
|
|
512
|
+
return compression.tokenCompanyAggressiveness?.[role];
|
|
513
|
+
}
|
|
514
|
+
function buildPromptCompressionTelemetry(segments) {
|
|
515
|
+
if (!segments.length) {
|
|
516
|
+
return void 0;
|
|
517
|
+
}
|
|
518
|
+
const originalCharacters = segments.reduce(
|
|
519
|
+
(sum, segment) => sum + segment.result.originalCharacters,
|
|
520
|
+
0
|
|
521
|
+
);
|
|
522
|
+
const compressedCharacters = segments.reduce(
|
|
523
|
+
(sum, segment) => sum + segment.result.compressedCharacters,
|
|
524
|
+
0
|
|
525
|
+
);
|
|
526
|
+
const originalTokens = segments.reduce(
|
|
527
|
+
(sum, segment) => sum + segment.result.originalTokens,
|
|
528
|
+
0
|
|
529
|
+
);
|
|
530
|
+
const compressedTokens = segments.reduce(
|
|
531
|
+
(sum, segment) => sum + segment.result.compressedTokens,
|
|
532
|
+
0
|
|
533
|
+
);
|
|
534
|
+
const savedCharacters = Math.max(0, originalCharacters - compressedCharacters);
|
|
535
|
+
const savedTokens = Math.max(0, originalTokens - compressedTokens);
|
|
536
|
+
const providers = dedupeStrings(segments.map((segment) => segment.result.provider));
|
|
537
|
+
const roles = dedupeStrings(segments.map((segment) => `role:${segment.role}`));
|
|
538
|
+
const techniques = dedupeStrings([
|
|
539
|
+
"anthropic-wrapper",
|
|
540
|
+
...roles,
|
|
541
|
+
...segments.flatMap((segment) => segment.result.techniques),
|
|
542
|
+
...providers.length > 1 ? ["mixed-providers"] : []
|
|
543
|
+
]);
|
|
544
|
+
return {
|
|
545
|
+
provider: segments[0]?.result.provider ?? "heuristic",
|
|
546
|
+
originalCharacters,
|
|
547
|
+
compressedCharacters,
|
|
548
|
+
savedCharacters,
|
|
549
|
+
originalTokens,
|
|
550
|
+
compressedTokens,
|
|
551
|
+
savedTokens,
|
|
552
|
+
tokenSavingsRatio: originalTokens > 0 ? savedTokens / originalTokens : 0,
|
|
553
|
+
savingsRatio: originalCharacters > 0 ? savedCharacters / originalCharacters : 0,
|
|
554
|
+
techniques
|
|
555
|
+
};
|
|
556
|
+
}
|
|
557
|
+
function promptCompressionRequestOptions(withUsage, correlationId) {
|
|
558
|
+
return {
|
|
559
|
+
signal: withUsage?.signal,
|
|
560
|
+
headers: withUsage?.headers,
|
|
561
|
+
retries: withUsage?.retries,
|
|
562
|
+
correlationId
|
|
563
|
+
};
|
|
564
|
+
}
|
|
565
|
+
function splitUsageOptions(options) {
|
|
566
|
+
if (!options || typeof options !== "object") {
|
|
567
|
+
return {};
|
|
568
|
+
}
|
|
569
|
+
const { usageTap, withUsage, promptCompression, ...rest } = options;
|
|
570
|
+
const requestOptions = Object.keys(rest).length ? cloneRequestOptions(rest) : void 0;
|
|
571
|
+
return {
|
|
572
|
+
requestOptions,
|
|
573
|
+
usageContext: usageTap,
|
|
574
|
+
withUsage,
|
|
575
|
+
promptCompression
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
function resolveBeginRequest(defaults, override) {
|
|
579
|
+
const base = defaults ?? {};
|
|
580
|
+
const current = override ?? {};
|
|
581
|
+
const customerId = current.customerId ?? base.customerId;
|
|
582
|
+
if (!customerId) {
|
|
583
|
+
throw new UsageTapError(
|
|
584
|
+
"USAGETAP_BAD_REQUEST",
|
|
585
|
+
"wrapAnthropic requires usageTap.customerId (provide defaultContext or options.usageTap)"
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
const tags = mergeTags(base.tags, current.tags);
|
|
589
|
+
const begin = { customerId };
|
|
590
|
+
const requested = current.requested ?? base.requested;
|
|
591
|
+
if (requested) begin.requested = requested;
|
|
592
|
+
const feature = current.feature ?? base.feature;
|
|
593
|
+
if (feature) begin.feature = feature;
|
|
594
|
+
const idempotency = current.idempotency ?? base.idempotency;
|
|
595
|
+
if (idempotency) begin.idempotency = idempotency;
|
|
596
|
+
const idempotencyKey = current.idempotencyKey ?? base.idempotencyKey;
|
|
597
|
+
if (idempotencyKey) begin.idempotencyKey = idempotencyKey;
|
|
598
|
+
const customerName = current.customerName ?? base.customerName;
|
|
599
|
+
if (customerName) begin.customerName = customerName;
|
|
600
|
+
const customerEmail = current.customerEmail ?? base.customerEmail;
|
|
601
|
+
if (customerEmail) begin.customerEmail = customerEmail;
|
|
602
|
+
const stripeCustomerId = current.stripeCustomerId ?? base.stripeCustomerId;
|
|
603
|
+
if (stripeCustomerId) begin.stripeCustomerId = stripeCustomerId;
|
|
604
|
+
const holdUsd = current.holdUsd ?? base.holdUsd;
|
|
605
|
+
if (typeof holdUsd === "number") begin.holdUsd = holdUsd;
|
|
606
|
+
const batch = current.batch ?? base.batch;
|
|
607
|
+
if (typeof batch === "boolean") begin.batch = batch;
|
|
608
|
+
const pricingMode = current.pricingMode ?? base.pricingMode;
|
|
609
|
+
if (pricingMode) begin.pricingMode = pricingMode;
|
|
610
|
+
if (tags?.length) {
|
|
611
|
+
begin.tags = tags;
|
|
612
|
+
}
|
|
613
|
+
return begin;
|
|
614
|
+
}
|
|
615
|
+
function createCallState(beginRequest, begin) {
|
|
616
|
+
const usage = {};
|
|
617
|
+
const stripeCustomerId = typeof begin.data.stripeCustomerId === "string" ? begin.data.stripeCustomerId : typeof beginRequest.stripeCustomerId === "string" ? beginRequest.stripeCustomerId : void 0;
|
|
618
|
+
if (stripeCustomerId) {
|
|
619
|
+
usage.stripeCustomerId = stripeCustomerId;
|
|
620
|
+
}
|
|
621
|
+
return {
|
|
622
|
+
beginRequest,
|
|
623
|
+
begin,
|
|
624
|
+
usage,
|
|
625
|
+
finalized: false
|
|
626
|
+
};
|
|
627
|
+
}
|
|
628
|
+
function createUsageContext(state) {
|
|
629
|
+
return {
|
|
630
|
+
begin: state.begin,
|
|
631
|
+
setUsage: (usage) => {
|
|
632
|
+
state.usage = { ...state.usage, ...usage };
|
|
633
|
+
},
|
|
634
|
+
setError: (error) => {
|
|
635
|
+
state.error = error;
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
async function finalizeCall(state, usageTap, options) {
|
|
640
|
+
if (state.finalized) {
|
|
641
|
+
return;
|
|
642
|
+
}
|
|
643
|
+
state.finalized = true;
|
|
644
|
+
await usageTap.endCall(
|
|
645
|
+
{
|
|
646
|
+
callId: state.begin.data.callId,
|
|
647
|
+
customerId: state.beginRequest.customerId,
|
|
648
|
+
feature: state.beginRequest.feature,
|
|
649
|
+
tags: state.beginRequest.tags,
|
|
650
|
+
...state.usage,
|
|
651
|
+
error: state.error
|
|
652
|
+
},
|
|
653
|
+
endCallOptions(options, state.begin.correlationId)
|
|
654
|
+
);
|
|
655
|
+
}
|
|
656
|
+
function beginCallOptions(options) {
|
|
657
|
+
return {
|
|
658
|
+
signal: options?.signal,
|
|
659
|
+
headers: options?.headers,
|
|
660
|
+
retries: options?.retries,
|
|
661
|
+
correlationId: options?.correlationId
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
function endCallOptions(options, correlationId) {
|
|
665
|
+
return {
|
|
666
|
+
signal: options?.signal,
|
|
667
|
+
headers: options?.headers,
|
|
668
|
+
retries: options?.retries,
|
|
669
|
+
correlationId
|
|
670
|
+
};
|
|
671
|
+
}
|
|
672
|
+
function inferAnthropicUsage(response, fallbackModel, ctx) {
|
|
673
|
+
const usage = extractAnthropicUsage(response, fallbackModel);
|
|
674
|
+
if (usage) {
|
|
675
|
+
ctx.setUsage(usage);
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
function extractAnthropicUsage(payload, fallbackModel) {
|
|
679
|
+
if (!isObjectRecord(payload)) {
|
|
680
|
+
return fallbackModel ? { modelUsed: fallbackModel } : void 0;
|
|
681
|
+
}
|
|
682
|
+
const usage = findAnthropicUsageRecord(payload);
|
|
683
|
+
const result = {};
|
|
684
|
+
const model = readString(payload.model) ?? readString(payload.message?.model) ?? fallbackModel;
|
|
685
|
+
if (model) {
|
|
686
|
+
result.modelUsed = model;
|
|
687
|
+
}
|
|
688
|
+
if (usage) {
|
|
689
|
+
applyAnthropicUsageRecord(result, usage);
|
|
690
|
+
}
|
|
691
|
+
return Object.keys(result).length ? result : void 0;
|
|
692
|
+
}
|
|
693
|
+
function findAnthropicUsageRecord(payload) {
|
|
694
|
+
if (isObjectRecord(payload.usage)) {
|
|
695
|
+
return payload.usage;
|
|
696
|
+
}
|
|
697
|
+
if (isObjectRecord(payload.message) && isObjectRecord(payload.message.usage)) {
|
|
698
|
+
return payload.message.usage;
|
|
699
|
+
}
|
|
700
|
+
return void 0;
|
|
701
|
+
}
|
|
702
|
+
function applyAnthropicUsageRecord(usage, usageRecord) {
|
|
703
|
+
const inputTokens = readNumber(usageRecord.input_tokens ?? usageRecord.prompt_tokens);
|
|
704
|
+
if (inputTokens !== void 0) {
|
|
705
|
+
usage.inputTokens = inputTokens;
|
|
706
|
+
}
|
|
707
|
+
const outputTokens = readNumber(usageRecord.output_tokens ?? usageRecord.completion_tokens);
|
|
708
|
+
if (outputTokens !== void 0) {
|
|
709
|
+
usage.responseTokens = outputTokens;
|
|
710
|
+
}
|
|
711
|
+
const cachedInputTokens = readNumber(
|
|
712
|
+
usageRecord.cache_read_input_tokens ?? usageRecord.prompt_cache_hit_tokens ?? usageRecord.cached_tokens
|
|
713
|
+
);
|
|
714
|
+
if (cachedInputTokens !== void 0) {
|
|
715
|
+
usage.cachedInputTokens = cachedInputTokens;
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
function accumulateAnthropicStreamUsage(chunk, fallbackModel, state) {
|
|
719
|
+
const usage = extractAnthropicUsage(chunk, fallbackModel);
|
|
720
|
+
if (usage) {
|
|
721
|
+
state.usage = { ...state.usage, ...usage };
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
function wrapAnthropicStreamForUsageTap(source, state, usageTap, options, fallbackModel) {
|
|
725
|
+
const getIterator = source[Symbol.asyncIterator];
|
|
726
|
+
if (typeof getIterator !== "function") {
|
|
727
|
+
throw new TypeError("Stream is not async iterable");
|
|
728
|
+
}
|
|
729
|
+
const iterator = getIterator.call(source);
|
|
730
|
+
const invokeFinalize = async (error) => {
|
|
731
|
+
if (error && !state.error) {
|
|
732
|
+
state.error = {
|
|
733
|
+
code: options?.defaultErrorCode ?? "VENDOR_ERROR",
|
|
734
|
+
message: error instanceof Error ? error.message : String(error)
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
await finalizeCall(state, usageTap, options);
|
|
738
|
+
};
|
|
739
|
+
const prototype = Object.getPrototypeOf(source) ?? Object.prototype;
|
|
740
|
+
const wrapped = Object.create(prototype);
|
|
741
|
+
for (const key of Reflect.ownKeys(source)) {
|
|
742
|
+
try {
|
|
743
|
+
const descriptor = Object.getOwnPropertyDescriptor(source, key);
|
|
744
|
+
if (descriptor) {
|
|
745
|
+
Object.defineProperty(wrapped, key, descriptor);
|
|
746
|
+
}
|
|
747
|
+
} catch {
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
Object.defineProperty(wrapped, Symbol.asyncIterator, {
|
|
751
|
+
value() {
|
|
752
|
+
return this;
|
|
753
|
+
},
|
|
754
|
+
configurable: true
|
|
755
|
+
});
|
|
756
|
+
Object.defineProperty(wrapped, "next", {
|
|
757
|
+
value: async (...args) => {
|
|
758
|
+
try {
|
|
759
|
+
const result = await iterator.next(...args);
|
|
760
|
+
if (result.value !== void 0) {
|
|
761
|
+
accumulateAnthropicStreamUsage(result.value, fallbackModel, state);
|
|
762
|
+
}
|
|
763
|
+
if (result.done) {
|
|
764
|
+
await invokeFinalize();
|
|
765
|
+
}
|
|
766
|
+
return result;
|
|
767
|
+
} catch (error) {
|
|
768
|
+
await invokeFinalize(error).catch(() => void 0);
|
|
769
|
+
throw error;
|
|
770
|
+
}
|
|
771
|
+
},
|
|
772
|
+
configurable: true,
|
|
773
|
+
writable: true
|
|
774
|
+
});
|
|
775
|
+
Object.defineProperty(wrapped, "return", {
|
|
776
|
+
value: async (value) => {
|
|
777
|
+
if (typeof iterator.return === "function") {
|
|
778
|
+
const rawResult = await iterator.return(value);
|
|
779
|
+
if (!isIteratorResult(rawResult)) {
|
|
780
|
+
throw new TypeError("Iterator.return() returned an invalid result");
|
|
781
|
+
}
|
|
782
|
+
await invokeFinalize();
|
|
783
|
+
return rawResult;
|
|
784
|
+
}
|
|
785
|
+
await invokeFinalize();
|
|
786
|
+
return { done: true, value };
|
|
787
|
+
},
|
|
788
|
+
configurable: true,
|
|
789
|
+
writable: true
|
|
790
|
+
});
|
|
791
|
+
Object.defineProperty(wrapped, "throw", {
|
|
792
|
+
value: async (error) => {
|
|
793
|
+
if (typeof iterator.throw === "function") {
|
|
794
|
+
const rawResult = await iterator.throw(error);
|
|
795
|
+
if (!isIteratorResult(rawResult)) {
|
|
796
|
+
throw new TypeError("Iterator.throw() returned an invalid result");
|
|
797
|
+
}
|
|
798
|
+
await invokeFinalize(error);
|
|
799
|
+
return rawResult;
|
|
800
|
+
}
|
|
801
|
+
await invokeFinalize(error);
|
|
802
|
+
throw error;
|
|
803
|
+
},
|
|
804
|
+
configurable: true,
|
|
805
|
+
writable: true
|
|
806
|
+
});
|
|
807
|
+
Object.defineProperty(wrapped, "__usageTapFinalize", {
|
|
808
|
+
value: async () => {
|
|
809
|
+
await invokeFinalize();
|
|
810
|
+
},
|
|
811
|
+
configurable: true
|
|
812
|
+
});
|
|
813
|
+
return wrapped;
|
|
814
|
+
}
|
|
815
|
+
function applyAnthropicVendorHints(params, hints) {
|
|
816
|
+
if (!hints) {
|
|
817
|
+
return params;
|
|
818
|
+
}
|
|
819
|
+
const next = cloneRecord(params);
|
|
820
|
+
if (hints.preferredModel && (next.model === void 0 || next.model === null)) {
|
|
821
|
+
next.model = hints.preferredModel;
|
|
822
|
+
}
|
|
823
|
+
if (typeof hints.maxResponseTokens === "number" && next.max_tokens == null) {
|
|
824
|
+
next.max_tokens = hints.maxResponseTokens;
|
|
825
|
+
}
|
|
826
|
+
return next;
|
|
827
|
+
}
|
|
828
|
+
function attachCorrelationHeader(options, correlationId) {
|
|
829
|
+
const normalized = normalizeHeaders(options?.headers);
|
|
830
|
+
if (correlationId && !normalized[USAGETAP_CORRELATION_HEADER]) {
|
|
831
|
+
normalized[USAGETAP_CORRELATION_HEADER] = correlationId;
|
|
832
|
+
}
|
|
833
|
+
if (!options) {
|
|
834
|
+
return Object.keys(normalized).length ? { headers: normalized } : void 0;
|
|
835
|
+
}
|
|
836
|
+
const next = { ...options };
|
|
837
|
+
if (Object.keys(normalized).length) {
|
|
838
|
+
next.headers = normalized;
|
|
839
|
+
}
|
|
840
|
+
return next;
|
|
841
|
+
}
|
|
842
|
+
function cloneRequestOptions(source) {
|
|
843
|
+
const clone = { ...source };
|
|
844
|
+
if ("headers" in clone) {
|
|
845
|
+
clone.headers = normalizeHeaders(clone.headers);
|
|
846
|
+
}
|
|
847
|
+
return clone;
|
|
848
|
+
}
|
|
849
|
+
function normalizeHeaders(headers) {
|
|
850
|
+
if (!headers) {
|
|
851
|
+
return {};
|
|
852
|
+
}
|
|
853
|
+
if (headers instanceof Headers) {
|
|
854
|
+
const result = {};
|
|
855
|
+
headers.forEach((value, key) => {
|
|
856
|
+
result[key.toLowerCase()] = value;
|
|
857
|
+
});
|
|
858
|
+
return result;
|
|
859
|
+
}
|
|
860
|
+
if (Array.isArray(headers)) {
|
|
861
|
+
const result = {};
|
|
862
|
+
for (const entry of headers) {
|
|
863
|
+
if (!isStringTuple(entry)) {
|
|
864
|
+
continue;
|
|
865
|
+
}
|
|
866
|
+
const [key, value] = entry;
|
|
867
|
+
result[key.toLowerCase()] = value;
|
|
868
|
+
}
|
|
869
|
+
return result;
|
|
870
|
+
}
|
|
871
|
+
if (isObjectRecord(headers)) {
|
|
872
|
+
const result = {};
|
|
873
|
+
const record = headers;
|
|
874
|
+
for (const key of Object.keys(record)) {
|
|
875
|
+
const value = record[key];
|
|
876
|
+
if (value !== void 0 && value !== null) {
|
|
877
|
+
result[key.toLowerCase()] = String(value);
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
return result;
|
|
881
|
+
}
|
|
882
|
+
return {};
|
|
883
|
+
}
|
|
884
|
+
function mapAnthropicMessageRole(role) {
|
|
885
|
+
if (role === "user") {
|
|
886
|
+
return "user";
|
|
887
|
+
}
|
|
888
|
+
if (role === "assistant") {
|
|
889
|
+
return "assistant";
|
|
890
|
+
}
|
|
891
|
+
return void 0;
|
|
892
|
+
}
|
|
893
|
+
function isObjectRecord(value) {
|
|
894
|
+
return typeof value === "object" && value !== null;
|
|
895
|
+
}
|
|
896
|
+
function cloneRecord(value) {
|
|
897
|
+
return isObjectRecord(value) ? { ...value } : {};
|
|
898
|
+
}
|
|
899
|
+
function isStringTuple(value) {
|
|
900
|
+
return Array.isArray(value) && value.length >= 2 && typeof value[0] === "string" && typeof value[1] === "string";
|
|
901
|
+
}
|
|
902
|
+
function readString(value) {
|
|
903
|
+
return typeof value === "string" ? value : void 0;
|
|
904
|
+
}
|
|
905
|
+
function readNumber(value) {
|
|
906
|
+
return typeof value === "number" ? value : void 0;
|
|
907
|
+
}
|
|
908
|
+
function mergeTags(a, b) {
|
|
909
|
+
const values = [...a ?? [], ...b ?? []].map((value) => typeof value === "string" ? value.trim() : "").filter(Boolean);
|
|
910
|
+
if (!values.length) {
|
|
911
|
+
return void 0;
|
|
912
|
+
}
|
|
913
|
+
return dedupeStrings(values);
|
|
914
|
+
}
|
|
915
|
+
function dedupeStrings(values) {
|
|
916
|
+
return Array.from(new Set(values));
|
|
917
|
+
}
|
|
918
|
+
function isStreamingRequest(params) {
|
|
919
|
+
if (!params || typeof params !== "object") {
|
|
920
|
+
return false;
|
|
921
|
+
}
|
|
922
|
+
const stream = params.stream;
|
|
923
|
+
if (typeof stream === "boolean") {
|
|
924
|
+
return stream;
|
|
925
|
+
}
|
|
926
|
+
return stream != null;
|
|
927
|
+
}
|
|
928
|
+
function ensureAsyncIterable(value, label) {
|
|
929
|
+
if (!value || typeof value !== "object" || typeof value[Symbol.asyncIterator] !== "function") {
|
|
930
|
+
throw new UsageTapError(
|
|
931
|
+
"USAGETAP_BAD_REQUEST",
|
|
932
|
+
`${label} expected an async iterable stream but received ${typeof value}`
|
|
933
|
+
);
|
|
934
|
+
}
|
|
935
|
+
}
|
|
936
|
+
function isIteratorResult(value) {
|
|
937
|
+
return isObjectRecord(value) && "done" in value;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
exports.AnthropicPromptCompressionStats = AnthropicPromptCompressionStats;
|
|
941
|
+
exports.wrapAnthropic = wrapAnthropic;
|
|
942
|
+
//# sourceMappingURL=anthropic.cjs.map
|
|
943
|
+
//# sourceMappingURL=anthropic.cjs.map
|