@revenium/anthropic 1.0.9 → 1.1.1
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/CHANGELOG.md +12 -0
- package/README.md +62 -10
- package/dist/cjs/config.js +39 -36
- package/dist/cjs/constants.js +6 -0
- package/dist/cjs/tracking.js +21 -3
- package/dist/cjs/utils/prompt-extraction.js +158 -0
- package/dist/cjs/utils/trace-fields.js +36 -0
- package/dist/cjs/utils/validation.js +105 -69
- package/dist/cjs/wrapper.js +79 -0
- package/dist/esm/config.js +41 -38
- package/dist/esm/constants.js +6 -0
- package/dist/esm/tracking.js +21 -3
- package/dist/esm/utils/prompt-extraction.js +154 -0
- package/dist/esm/utils/trace-fields.js +35 -0
- package/dist/esm/utils/validation.js +106 -70
- package/dist/esm/wrapper.js +79 -0
- package/dist/types/config.d.ts +1 -1
- package/dist/types/constants.d.ts +6 -0
- package/dist/types/types.d.ts +38 -8
- package/dist/types/utils/prompt-extraction.d.ts +10 -0
- package/dist/types/utils/trace-fields.d.ts +1 -0
- package/dist/types/utils/validation.d.ts +1 -1
- package/examples/advanced.ts +4 -4
- package/examples/getting_started.ts +2 -2
- package/examples/metadata.ts +3 -3
- package/examples/prompt-capture-example.ts +105 -0
- package/package.json +1 -1
|
@@ -18,30 +18,30 @@ const constants_1 = require("../constants");
|
|
|
18
18
|
* Type guard for checking if a value is a non-null object
|
|
19
19
|
*/
|
|
20
20
|
function isObject(value) {
|
|
21
|
-
return typeof value ===
|
|
21
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* Type guard for checking if a value is a string
|
|
25
25
|
*/
|
|
26
26
|
function isString(value) {
|
|
27
|
-
return typeof value ===
|
|
27
|
+
return typeof value === "string";
|
|
28
28
|
}
|
|
29
29
|
/**
|
|
30
30
|
* Type guard for checking if a value is a number
|
|
31
31
|
*/
|
|
32
32
|
function isNumber(value) {
|
|
33
|
-
return typeof value ===
|
|
33
|
+
return typeof value === "number" && !isNaN(value);
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
36
36
|
* Type guard for checking if a value is a boolean
|
|
37
37
|
*/
|
|
38
38
|
function isBoolean(value) {
|
|
39
|
-
return typeof value ===
|
|
39
|
+
return typeof value === "boolean";
|
|
40
40
|
}
|
|
41
41
|
/**
|
|
42
42
|
* Validate and extract string from unknown value
|
|
43
43
|
*/
|
|
44
|
-
function validateString(value, defaultValue =
|
|
44
|
+
function validateString(value, defaultValue = "") {
|
|
45
45
|
return isString(value) ? value : defaultValue;
|
|
46
46
|
}
|
|
47
47
|
/**
|
|
@@ -60,8 +60,14 @@ function validateUsageMetadata(metadata) {
|
|
|
60
60
|
const validated = {};
|
|
61
61
|
// Validate string fields
|
|
62
62
|
const stringFields = [
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
"traceId",
|
|
64
|
+
"taskType",
|
|
65
|
+
"organizationId",
|
|
66
|
+
"organizationName",
|
|
67
|
+
"subscriptionId",
|
|
68
|
+
"productId",
|
|
69
|
+
"productName",
|
|
70
|
+
"agent",
|
|
65
71
|
];
|
|
66
72
|
for (const field of stringFields) {
|
|
67
73
|
const value = metadata[field];
|
|
@@ -74,21 +80,25 @@ function validateUsageMetadata(metadata) {
|
|
|
74
80
|
if (isObject(subscriberData)) {
|
|
75
81
|
const subscriber = {};
|
|
76
82
|
// Validate subscriber.id
|
|
77
|
-
if (isString(subscriberData?.id) &&
|
|
83
|
+
if (isString(subscriberData?.id) &&
|
|
84
|
+
subscriberData?.id?.trim()?.length > 0) {
|
|
78
85
|
subscriber.id = subscriberData?.id?.trim();
|
|
79
86
|
}
|
|
80
87
|
// Validate subscriber.email
|
|
81
|
-
if (isString(subscriberData?.email) &&
|
|
88
|
+
if (isString(subscriberData?.email) &&
|
|
89
|
+
subscriberData?.email?.trim()?.length > 0) {
|
|
82
90
|
subscriber.email = subscriberData?.email?.trim();
|
|
83
91
|
}
|
|
84
92
|
// Validate subscriber.credential object
|
|
85
93
|
const credentialData = subscriberData?.credential;
|
|
86
94
|
if (isObject(credentialData)) {
|
|
87
95
|
const credential = {};
|
|
88
|
-
if (isString(credentialData?.name) &&
|
|
96
|
+
if (isString(credentialData?.name) &&
|
|
97
|
+
credentialData?.name?.trim()?.length > 0) {
|
|
89
98
|
credential.name = credentialData?.name?.trim();
|
|
90
99
|
}
|
|
91
|
-
if (isString(credentialData?.value) &&
|
|
100
|
+
if (isString(credentialData?.value) &&
|
|
101
|
+
credentialData?.value?.trim()?.length > 0) {
|
|
92
102
|
credential.value = credentialData?.value?.trim();
|
|
93
103
|
}
|
|
94
104
|
// Only include credential if it has at least name or value
|
|
@@ -102,10 +112,18 @@ function validateUsageMetadata(metadata) {
|
|
|
102
112
|
}
|
|
103
113
|
}
|
|
104
114
|
// Validate number fields
|
|
105
|
-
const responseQualityScore = metadata
|
|
106
|
-
|
|
115
|
+
const responseQualityScore = metadata
|
|
116
|
+
.responseQualityScore;
|
|
117
|
+
if (isNumber(responseQualityScore) &&
|
|
118
|
+
responseQualityScore >= 0 &&
|
|
119
|
+
responseQualityScore <= 1) {
|
|
107
120
|
validated.responseQualityScore = responseQualityScore;
|
|
108
121
|
}
|
|
122
|
+
// Validate boolean fields
|
|
123
|
+
const capturePrompts = metadata.capturePrompts;
|
|
124
|
+
if (typeof capturePrompts === "boolean") {
|
|
125
|
+
validated.capturePrompts = capturePrompts;
|
|
126
|
+
}
|
|
109
127
|
return validated;
|
|
110
128
|
}
|
|
111
129
|
/**
|
|
@@ -118,96 +136,104 @@ function validateReveniumConfig(config) {
|
|
|
118
136
|
if (!isObject(config)) {
|
|
119
137
|
return {
|
|
120
138
|
isValid: false,
|
|
121
|
-
errors: [
|
|
139
|
+
errors: ["Configuration must be an object"],
|
|
122
140
|
warnings: [],
|
|
123
|
-
suggestions: [
|
|
141
|
+
suggestions: [
|
|
142
|
+
"Ensure you are passing a valid configuration object with required fields",
|
|
143
|
+
],
|
|
124
144
|
};
|
|
125
145
|
}
|
|
126
146
|
const cfg = config;
|
|
127
147
|
// Validate required Revenium API key
|
|
128
148
|
if (!isString(cfg?.reveniumApiKey)) {
|
|
129
|
-
errors.push(
|
|
130
|
-
suggestions.push(
|
|
149
|
+
errors.push("reveniumApiKey is required and must be a string");
|
|
150
|
+
suggestions.push("Set REVENIUM_METERING_API_KEY environment variable or provide reveniumApiKey in config");
|
|
131
151
|
}
|
|
132
152
|
else if (!cfg?.reveniumApiKey?.trim()) {
|
|
133
|
-
errors.push(
|
|
153
|
+
errors.push("reveniumApiKey cannot be empty");
|
|
134
154
|
}
|
|
135
155
|
else if (!cfg?.reveniumApiKey?.startsWith(constants_1.VALIDATION_CONFIG.REVENIUM_API_KEY_PREFIX)) {
|
|
136
156
|
errors.push(`reveniumApiKey must start with "${constants_1.VALIDATION_CONFIG.REVENIUM_API_KEY_PREFIX}"`);
|
|
137
|
-
suggestions.push(
|
|
157
|
+
suggestions.push("Obtain a valid Revenium API key from your Revenium dashboard");
|
|
138
158
|
}
|
|
139
159
|
else if (cfg?.reveniumApiKey?.length < constants_1.VALIDATION_CONFIG.MIN_API_KEY_LENGTH) {
|
|
140
|
-
warnings.push(
|
|
160
|
+
warnings.push("reveniumApiKey appears to be too short - verify it is correct");
|
|
141
161
|
}
|
|
142
162
|
// Validate Revenium base URL (optional - defaults to https://api.revenium.ai)
|
|
143
163
|
if (cfg?.reveniumBaseUrl !== undefined) {
|
|
144
164
|
if (!isString(cfg?.reveniumBaseUrl)) {
|
|
145
|
-
errors.push(
|
|
165
|
+
errors.push("reveniumBaseUrl must be a string if provided");
|
|
146
166
|
}
|
|
147
167
|
else if (!cfg?.reveniumBaseUrl?.trim()) {
|
|
148
|
-
errors.push(
|
|
168
|
+
errors.push("reveniumBaseUrl cannot be empty if provided");
|
|
149
169
|
}
|
|
150
170
|
else {
|
|
151
171
|
try {
|
|
152
172
|
const url = new URL(cfg?.reveniumBaseUrl);
|
|
153
|
-
if (!url.protocol.startsWith(
|
|
154
|
-
errors.push(
|
|
173
|
+
if (!url.protocol.startsWith("http")) {
|
|
174
|
+
errors.push("reveniumBaseUrl must use HTTP or HTTPS protocol");
|
|
155
175
|
}
|
|
156
176
|
// Check for localhost/development hostnames (IPv4, IPv6, and named)
|
|
157
|
-
const localhostHostnames = [
|
|
177
|
+
const localhostHostnames = ["localhost", "127.0.0.1", "::1", "[::1]"];
|
|
158
178
|
if (localhostHostnames.includes(url.hostname)) {
|
|
159
|
-
warnings.push(
|
|
179
|
+
warnings.push("Using localhost for Revenium API - ensure this is intended for development");
|
|
160
180
|
}
|
|
161
181
|
}
|
|
162
182
|
catch {
|
|
163
|
-
errors.push(
|
|
164
|
-
suggestions.push(
|
|
183
|
+
errors.push("reveniumBaseUrl must be a valid URL");
|
|
184
|
+
suggestions.push("Use format: https://api.revenium.ai");
|
|
165
185
|
}
|
|
166
186
|
}
|
|
167
187
|
}
|
|
168
188
|
// Validate optional Anthropic API key
|
|
169
189
|
if (cfg?.anthropicApiKey !== undefined && !isString(cfg?.anthropicApiKey)) {
|
|
170
|
-
errors.push(
|
|
190
|
+
errors.push("anthropicApiKey must be a string if provided");
|
|
171
191
|
}
|
|
172
|
-
else if (cfg?.anthropicApiKey !== undefined &&
|
|
173
|
-
|
|
192
|
+
else if (cfg?.anthropicApiKey !== undefined &&
|
|
193
|
+
cfg?.anthropicApiKey?.trim()?.length === 0) {
|
|
194
|
+
warnings.push("anthropicApiKey is empty - API calls may fail");
|
|
174
195
|
}
|
|
175
|
-
else if (cfg?.anthropicApiKey &&
|
|
196
|
+
else if (cfg?.anthropicApiKey &&
|
|
197
|
+
!cfg?.anthropicApiKey?.startsWith(constants_1.VALIDATION_CONFIG.ANTHROPIC_API_KEY_PREFIX)) {
|
|
176
198
|
warnings.push(`anthropicApiKey does not start with "${constants_1.VALIDATION_CONFIG.ANTHROPIC_API_KEY_PREFIX}" - verify it is correct`);
|
|
177
199
|
}
|
|
178
200
|
// Validate optional timeout using constants
|
|
179
201
|
if (cfg?.apiTimeout !== undefined && !isNumber(cfg?.apiTimeout)) {
|
|
180
|
-
errors.push(
|
|
202
|
+
errors.push("apiTimeout must be a number if provided");
|
|
181
203
|
}
|
|
182
|
-
else if (cfg?.apiTimeout !== undefined &&
|
|
204
|
+
else if (cfg?.apiTimeout !== undefined &&
|
|
205
|
+
cfg?.apiTimeout < constants_1.VALIDATION_CONFIG.MIN_API_TIMEOUT) {
|
|
183
206
|
errors.push(`apiTimeout must be at least ${constants_1.VALIDATION_CONFIG.MIN_API_TIMEOUT}ms`);
|
|
184
207
|
}
|
|
185
|
-
else if (cfg?.apiTimeout !== undefined &&
|
|
208
|
+
else if (cfg?.apiTimeout !== undefined &&
|
|
209
|
+
cfg?.apiTimeout > constants_1.VALIDATION_CONFIG.MAX_API_TIMEOUT) {
|
|
186
210
|
errors.push(`apiTimeout must not exceed ${constants_1.VALIDATION_CONFIG.MAX_API_TIMEOUT}ms`);
|
|
187
211
|
}
|
|
188
|
-
else if (cfg?.apiTimeout !== undefined &&
|
|
189
|
-
|
|
212
|
+
else if (cfg?.apiTimeout !== undefined &&
|
|
213
|
+
cfg?.apiTimeout < constants_1.VALIDATION_CONFIG.LOW_TIMEOUT_WARNING_THRESHOLD) {
|
|
214
|
+
warnings.push("apiTimeout is very low - may cause timeouts for slow networks");
|
|
190
215
|
}
|
|
191
216
|
// Validate optional failSilent
|
|
192
217
|
if (cfg?.failSilent && !isBoolean(cfg?.failSilent)) {
|
|
193
|
-
errors.push(
|
|
218
|
+
errors.push("failSilent must be a boolean if provided");
|
|
194
219
|
}
|
|
195
220
|
// Validate optional maxRetries using constants
|
|
196
221
|
if (cfg?.maxRetries !== undefined && !isNumber(cfg?.maxRetries)) {
|
|
197
|
-
errors.push(
|
|
222
|
+
errors.push("maxRetries must be a number if provided");
|
|
198
223
|
}
|
|
199
224
|
else if (cfg?.maxRetries !== undefined && cfg?.maxRetries < 0) {
|
|
200
|
-
errors.push(
|
|
225
|
+
errors.push("maxRetries cannot be negative");
|
|
201
226
|
}
|
|
202
|
-
else if (cfg?.maxRetries !== undefined &&
|
|
227
|
+
else if (cfg?.maxRetries !== undefined &&
|
|
228
|
+
cfg?.maxRetries > constants_1.VALIDATION_CONFIG.MAX_RETRY_ATTEMPTS) {
|
|
203
229
|
errors.push(`maxRetries should not exceed ${constants_1.VALIDATION_CONFIG.MAX_RETRY_ATTEMPTS}`);
|
|
204
230
|
}
|
|
205
231
|
else if (cfg?.maxRetries !== undefined && cfg?.maxRetries === 0) {
|
|
206
|
-
warnings.push(
|
|
232
|
+
warnings.push("maxRetries is 0 - no retry attempts will be made");
|
|
207
233
|
}
|
|
208
234
|
// Validate optional printSummary
|
|
209
235
|
if (cfg?.printSummary !== undefined) {
|
|
210
|
-
const validPrintSummaryValues = [true, false,
|
|
236
|
+
const validPrintSummaryValues = [true, false, "human", "json"];
|
|
211
237
|
if (!validPrintSummaryValues.includes(cfg?.printSummary)) {
|
|
212
238
|
errors.push("printSummary must be a boolean or one of: 'human', 'json'");
|
|
213
239
|
suggestions.push("Use printSummary: true, 'human', or 'json' to enable summary output");
|
|
@@ -216,10 +242,10 @@ function validateReveniumConfig(config) {
|
|
|
216
242
|
// Validate optional teamId
|
|
217
243
|
if (cfg?.teamId !== undefined) {
|
|
218
244
|
if (!isString(cfg?.teamId)) {
|
|
219
|
-
errors.push(
|
|
245
|
+
errors.push("teamId must be a string if provided");
|
|
220
246
|
}
|
|
221
247
|
else if (cfg?.teamId?.trim()?.length === 0) {
|
|
222
|
-
errors.push(
|
|
248
|
+
errors.push("teamId cannot be empty if provided");
|
|
223
249
|
}
|
|
224
250
|
}
|
|
225
251
|
if (errors.length > 0) {
|
|
@@ -227,16 +253,16 @@ function validateReveniumConfig(config) {
|
|
|
227
253
|
isValid: false,
|
|
228
254
|
errors,
|
|
229
255
|
warnings,
|
|
230
|
-
suggestions
|
|
256
|
+
suggestions,
|
|
231
257
|
};
|
|
232
258
|
}
|
|
233
259
|
// Determine validated printSummary value
|
|
234
260
|
let validatedPrintSummary;
|
|
235
|
-
if (cfg?.printSummary === true || cfg?.printSummary ===
|
|
236
|
-
validatedPrintSummary =
|
|
261
|
+
if (cfg?.printSummary === true || cfg?.printSummary === "human") {
|
|
262
|
+
validatedPrintSummary = "human";
|
|
237
263
|
}
|
|
238
|
-
else if (cfg?.printSummary ===
|
|
239
|
-
validatedPrintSummary =
|
|
264
|
+
else if (cfg?.printSummary === "json") {
|
|
265
|
+
validatedPrintSummary = "json";
|
|
240
266
|
}
|
|
241
267
|
else if (cfg?.printSummary === false) {
|
|
242
268
|
validatedPrintSummary = false;
|
|
@@ -244,20 +270,27 @@ function validateReveniumConfig(config) {
|
|
|
244
270
|
// Build validated config (apply default for reveniumBaseUrl if not provided)
|
|
245
271
|
const validatedConfig = {
|
|
246
272
|
reveniumApiKey: cfg?.reveniumApiKey,
|
|
247
|
-
reveniumBaseUrl: isString(cfg?.reveniumBaseUrl)
|
|
248
|
-
|
|
273
|
+
reveniumBaseUrl: isString(cfg?.reveniumBaseUrl)
|
|
274
|
+
? cfg?.reveniumBaseUrl
|
|
275
|
+
: constants_1.DEFAULT_CONFIG.REVENIUM_BASE_URL,
|
|
276
|
+
anthropicApiKey: isString(cfg?.anthropicApiKey)
|
|
277
|
+
? cfg?.anthropicApiKey
|
|
278
|
+
: undefined,
|
|
249
279
|
apiTimeout: isNumber(cfg?.apiTimeout) ? cfg?.apiTimeout : undefined,
|
|
250
280
|
failSilent: isBoolean(cfg?.failSilent) ? cfg?.failSilent : undefined,
|
|
251
281
|
maxRetries: isNumber(cfg?.maxRetries) ? cfg?.maxRetries : undefined,
|
|
252
282
|
printSummary: validatedPrintSummary,
|
|
253
|
-
teamId: isString(cfg?.teamId) ? cfg?.teamId?.trim() : undefined
|
|
283
|
+
teamId: isString(cfg?.teamId) ? cfg?.teamId?.trim() : undefined,
|
|
284
|
+
capturePrompts: isBoolean(cfg?.capturePrompts)
|
|
285
|
+
? cfg?.capturePrompts
|
|
286
|
+
: undefined,
|
|
254
287
|
};
|
|
255
288
|
return {
|
|
256
289
|
isValid: true,
|
|
257
290
|
errors: [],
|
|
258
291
|
warnings,
|
|
259
292
|
config: validatedConfig,
|
|
260
|
-
suggestions: suggestions.length > 0 ? suggestions : undefined
|
|
293
|
+
suggestions: suggestions.length > 0 ? suggestions : undefined,
|
|
261
294
|
};
|
|
262
295
|
}
|
|
263
296
|
/**
|
|
@@ -269,27 +302,27 @@ function validateAnthropicMessageParams(params) {
|
|
|
269
302
|
if (!isObject(params)) {
|
|
270
303
|
return {
|
|
271
304
|
isValid: false,
|
|
272
|
-
errors: [
|
|
273
|
-
warnings: []
|
|
305
|
+
errors: ["Message parameters must be an object"],
|
|
306
|
+
warnings: [],
|
|
274
307
|
};
|
|
275
308
|
}
|
|
276
309
|
const data = params;
|
|
277
310
|
// Validate required model field
|
|
278
311
|
if (!isString(data?.model)) {
|
|
279
|
-
errors.push(
|
|
312
|
+
errors.push("model field is required and must be a string");
|
|
280
313
|
}
|
|
281
314
|
else if (data?.model?.trim()?.length === 0) {
|
|
282
|
-
errors.push(
|
|
315
|
+
errors.push("model field cannot be empty");
|
|
283
316
|
}
|
|
284
317
|
else if (!constants_1.ANTHROPIC_PATTERNS.CLAUDE_MODEL_PATTERN.test(data?.model)) {
|
|
285
318
|
warnings.push('Model name does not contain "claude" - verify it is a valid Anthropic model');
|
|
286
319
|
}
|
|
287
320
|
// Validate required messages array
|
|
288
321
|
if (!Array.isArray(data?.messages)) {
|
|
289
|
-
errors.push(
|
|
322
|
+
errors.push("messages field is required and must be an array");
|
|
290
323
|
}
|
|
291
324
|
else if (data?.messages?.length === 0) {
|
|
292
|
-
errors.push(
|
|
325
|
+
errors.push("messages array cannot be empty");
|
|
293
326
|
}
|
|
294
327
|
else {
|
|
295
328
|
// Validate message structure
|
|
@@ -302,37 +335,40 @@ function validateAnthropicMessageParams(params) {
|
|
|
302
335
|
if (!isString(msg.role)) {
|
|
303
336
|
errors.push(`Message at index ${index} must have a role field`);
|
|
304
337
|
}
|
|
305
|
-
else if (![
|
|
338
|
+
else if (!["user", "assistant", "system"].includes(msg.role)) {
|
|
306
339
|
warnings.push(`Message at index ${index} has unusual role: ${msg.role}`);
|
|
307
340
|
}
|
|
308
|
-
if (msg?.content &&
|
|
341
|
+
if (msg?.content &&
|
|
342
|
+
!isString(msg?.content) &&
|
|
343
|
+
!Array.isArray(msg?.content)) {
|
|
309
344
|
warnings.push(`Message at index ${index} content should be a string or array`);
|
|
310
345
|
}
|
|
311
346
|
});
|
|
312
347
|
}
|
|
313
348
|
// Validate optional parameters
|
|
314
349
|
if (!isNumber(data?.max_tokens)) {
|
|
315
|
-
warnings.push(
|
|
350
|
+
warnings.push("max_tokens should be a number");
|
|
316
351
|
}
|
|
317
352
|
else if (data?.max_tokens <= 0) {
|
|
318
|
-
warnings.push(
|
|
353
|
+
warnings.push("max_tokens should be positive");
|
|
319
354
|
}
|
|
320
355
|
else if (data?.max_tokens > constants_1.VALIDATION_CONFIG.HIGH_MAX_TOKENS_THRESHOLD) {
|
|
321
|
-
warnings.push(
|
|
356
|
+
warnings.push("max_tokens is very high - verify this is intended");
|
|
322
357
|
}
|
|
323
358
|
if (!isNumber(data?.temperature)) {
|
|
324
|
-
warnings.push(
|
|
359
|
+
warnings.push("temperature should be a number");
|
|
325
360
|
}
|
|
326
|
-
else if (data?.temperature < constants_1.VALIDATION_CONFIG.MIN_TEMPERATURE ||
|
|
361
|
+
else if (data?.temperature < constants_1.VALIDATION_CONFIG.MIN_TEMPERATURE ||
|
|
362
|
+
data?.temperature > constants_1.VALIDATION_CONFIG.MAX_TEMPERATURE) {
|
|
327
363
|
warnings.push(`temperature should be between ${constants_1.VALIDATION_CONFIG.MIN_TEMPERATURE} and ${constants_1.VALIDATION_CONFIG.MAX_TEMPERATURE} for Anthropic models`);
|
|
328
364
|
}
|
|
329
365
|
if (data?.stream && !isBoolean(data?.stream)) {
|
|
330
|
-
warnings.push(
|
|
366
|
+
warnings.push("stream should be a boolean");
|
|
331
367
|
}
|
|
332
368
|
return {
|
|
333
369
|
isValid: errors.length === 0,
|
|
334
370
|
errors,
|
|
335
|
-
warnings
|
|
371
|
+
warnings,
|
|
336
372
|
};
|
|
337
373
|
}
|
|
338
374
|
//# sourceMappingURL=validation.js.map
|
package/dist/cjs/wrapper.js
CHANGED
|
@@ -14,6 +14,8 @@ const config_1 = require("./config");
|
|
|
14
14
|
const tracking_1 = require("./tracking");
|
|
15
15
|
const validation_1 = require("./utils/validation");
|
|
16
16
|
const error_handling_1 = require("./utils/error-handling");
|
|
17
|
+
const prompt_extraction_1 = require("./utils/prompt-extraction");
|
|
18
|
+
const trace_fields_1 = require("./utils/trace-fields");
|
|
17
19
|
const crypto_1 = require("crypto");
|
|
18
20
|
// Global logger
|
|
19
21
|
const logger = (0, config_1.getLogger)();
|
|
@@ -136,6 +138,65 @@ function unpatchAnthropic() {
|
|
|
136
138
|
function isAnthropicPatched() {
|
|
137
139
|
return patchingContext.isPatched;
|
|
138
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Reconstruct a response object from streaming chunks for prompt capture
|
|
143
|
+
*/
|
|
144
|
+
function reconstructResponseFromChunks(chunks, model) {
|
|
145
|
+
const contentBlocks = [];
|
|
146
|
+
let stopReason;
|
|
147
|
+
let stopSequence;
|
|
148
|
+
const usage = {};
|
|
149
|
+
for (const chunk of chunks) {
|
|
150
|
+
if (chunk.type === "content_block_start" && chunk.content_block) {
|
|
151
|
+
contentBlocks.push({ ...chunk.content_block });
|
|
152
|
+
}
|
|
153
|
+
else if (chunk.type === "content_block_delta" && chunk.delta) {
|
|
154
|
+
const lastBlock = contentBlocks[contentBlocks.length - 1];
|
|
155
|
+
if (lastBlock && chunk.delta.type === "text_delta") {
|
|
156
|
+
if (lastBlock.type === "text") {
|
|
157
|
+
lastBlock.text = (lastBlock.text || "") + (chunk.delta.text || "");
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
else if (lastBlock && chunk.delta.type === "input_json_delta") {
|
|
161
|
+
if (lastBlock.type === "tool_use") {
|
|
162
|
+
lastBlock.input = lastBlock.input || "";
|
|
163
|
+
lastBlock.input +=
|
|
164
|
+
chunk.delta.partial_json || "";
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else if (chunk.type === "message_delta" && chunk.delta) {
|
|
169
|
+
const delta = chunk.delta;
|
|
170
|
+
if (delta.stop_reason) {
|
|
171
|
+
stopReason = delta.stop_reason;
|
|
172
|
+
}
|
|
173
|
+
if (delta.stop_sequence) {
|
|
174
|
+
stopSequence = delta.stop_sequence;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
else if (chunk.type === "message_start" && chunk.message?.usage) {
|
|
178
|
+
Object.assign(usage, chunk.message.usage);
|
|
179
|
+
}
|
|
180
|
+
else if (chunk.usage) {
|
|
181
|
+
Object.assign(usage, chunk.usage);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
id: `reconstructed-${Date.now()}`,
|
|
186
|
+
type: "message",
|
|
187
|
+
role: "assistant",
|
|
188
|
+
content: contentBlocks,
|
|
189
|
+
model,
|
|
190
|
+
stop_reason: stopReason || "end_turn",
|
|
191
|
+
stop_sequence: stopSequence,
|
|
192
|
+
usage: {
|
|
193
|
+
input_tokens: usage.input_tokens || 0,
|
|
194
|
+
output_tokens: usage.output_tokens || 0,
|
|
195
|
+
cache_creation_input_tokens: usage.cache_creation_input_tokens,
|
|
196
|
+
cache_read_input_tokens: usage.cache_read_input_tokens,
|
|
197
|
+
},
|
|
198
|
+
};
|
|
199
|
+
}
|
|
139
200
|
/**
|
|
140
201
|
* Handle streaming response by collecting chunks and extracting usage data
|
|
141
202
|
*/
|
|
@@ -168,6 +229,10 @@ async function handleStreamingResponse(stream, context) {
|
|
|
168
229
|
timeToFirstToken,
|
|
169
230
|
});
|
|
170
231
|
const usage = (0, tracking_1.extractUsageFromStream)(chunks);
|
|
232
|
+
let reconstructedResponse = undefined;
|
|
233
|
+
if ((0, prompt_extraction_1.shouldCapturePrompts)(metadata)) {
|
|
234
|
+
reconstructedResponse = reconstructResponseFromChunks(chunks, model);
|
|
235
|
+
}
|
|
171
236
|
// Create tracking data
|
|
172
237
|
const trackingData = {
|
|
173
238
|
requestId,
|
|
@@ -184,6 +249,8 @@ async function handleStreamingResponse(stream, context) {
|
|
|
184
249
|
responseTime,
|
|
185
250
|
timeToFirstToken,
|
|
186
251
|
requestBody: requestBody,
|
|
252
|
+
response: reconstructedResponse,
|
|
253
|
+
hasVisionContent: (0, trace_fields_1.detectVisionContent)(requestBody),
|
|
187
254
|
};
|
|
188
255
|
// Track usage asynchronously
|
|
189
256
|
(0, tracking_1.trackUsageAsync)(trackingData);
|
|
@@ -245,6 +312,8 @@ async function patchedCreateMethod(params, options) {
|
|
|
245
312
|
const responseTime = new Date();
|
|
246
313
|
// Extract usage information
|
|
247
314
|
const usage = (0, tracking_1.extractUsageFromResponse)(response);
|
|
315
|
+
// Detect vision content
|
|
316
|
+
const hasVisionContent = (0, trace_fields_1.detectVisionContent)(params);
|
|
248
317
|
// Create tracking data
|
|
249
318
|
const trackingData = {
|
|
250
319
|
requestId,
|
|
@@ -259,7 +328,9 @@ async function patchedCreateMethod(params, options) {
|
|
|
259
328
|
metadata,
|
|
260
329
|
requestTime,
|
|
261
330
|
responseTime,
|
|
331
|
+
hasVisionContent,
|
|
262
332
|
requestBody: params,
|
|
333
|
+
response,
|
|
263
334
|
};
|
|
264
335
|
// Track usage asynchronously
|
|
265
336
|
(0, tracking_1.trackUsageAsync)(trackingData);
|
|
@@ -344,6 +415,12 @@ async function* patchedStreamMethod(params, options) {
|
|
|
344
415
|
: undefined;
|
|
345
416
|
// Extract usage information from all chunks
|
|
346
417
|
const usage = (0, tracking_1.extractUsageFromStream)(chunks);
|
|
418
|
+
// Detect vision content
|
|
419
|
+
const hasVisionContent = (0, trace_fields_1.detectVisionContent)(params);
|
|
420
|
+
let reconstructedResponse = undefined;
|
|
421
|
+
if ((0, prompt_extraction_1.shouldCapturePrompts)(metadata)) {
|
|
422
|
+
reconstructedResponse = reconstructResponseFromChunks(chunks, params.model);
|
|
423
|
+
}
|
|
347
424
|
// Create tracking data
|
|
348
425
|
const trackingData = {
|
|
349
426
|
requestId,
|
|
@@ -359,7 +436,9 @@ async function* patchedStreamMethod(params, options) {
|
|
|
359
436
|
requestTime,
|
|
360
437
|
responseTime,
|
|
361
438
|
timeToFirstToken,
|
|
439
|
+
hasVisionContent,
|
|
362
440
|
requestBody: params,
|
|
441
|
+
response: reconstructedResponse,
|
|
363
442
|
};
|
|
364
443
|
// Track usage asynchronously
|
|
365
444
|
(0, tracking_1.trackUsageAsync)(trackingData);
|