@revenium/anthropic 1.0.7 → 1.0.9
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 +53 -1
- package/README.md +174 -21
- package/dist/cjs/config.js +55 -7
- package/dist/cjs/constants.js +39 -24
- package/dist/cjs/tracking.js +107 -20
- package/dist/cjs/types/anthropic-augmentation.js +0 -62
- package/dist/cjs/utils/summary-printer.js +189 -0
- package/dist/cjs/utils/trace-fields.js +117 -0
- package/dist/cjs/utils/validation.js +58 -26
- package/dist/cjs/wrapper.js +54 -40
- package/dist/esm/config.js +55 -7
- package/dist/esm/constants.js +38 -23
- package/dist/esm/tracking.js +107 -20
- package/dist/esm/types/anthropic-augmentation.js +0 -62
- package/dist/esm/utils/summary-printer.js +186 -0
- package/dist/esm/utils/trace-fields.js +106 -0
- package/dist/esm/utils/validation.js +59 -27
- package/dist/esm/wrapper.js +60 -46
- package/dist/types/config.d.ts +1 -0
- package/dist/types/constants.d.ts +16 -1
- package/dist/types/types/anthropic-augmentation.d.ts +0 -92
- package/dist/types/types.d.ts +28 -196
- package/dist/types/utils/summary-printer.d.ts +3 -0
- package/dist/types/utils/trace-fields.d.ts +10 -0
- package/examples/README.md +3 -3
- package/examples/advanced.ts +128 -0
- package/examples/basic.ts +132 -0
- package/examples/getting_started.ts +6 -6
- package/examples/metadata.ts +58 -0
- package/package.json +7 -6
- package/examples/.claude/settings.local.json +0 -24
- package/examples/advanced-features.ts +0 -469
- package/examples/basic-usage.ts +0 -314
|
@@ -139,38 +139,40 @@ function validateReveniumConfig(config) {
|
|
|
139
139
|
else if (cfg?.reveniumApiKey?.length < constants_1.VALIDATION_CONFIG.MIN_API_KEY_LENGTH) {
|
|
140
140
|
warnings.push('reveniumApiKey appears to be too short - verify it is correct');
|
|
141
141
|
}
|
|
142
|
-
// Validate Revenium base URL
|
|
143
|
-
if (
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
142
|
+
// Validate Revenium base URL (optional - defaults to https://api.revenium.ai)
|
|
143
|
+
if (cfg?.reveniumBaseUrl !== undefined) {
|
|
144
|
+
if (!isString(cfg?.reveniumBaseUrl)) {
|
|
145
|
+
errors.push('reveniumBaseUrl must be a string if provided');
|
|
146
|
+
}
|
|
147
|
+
else if (!cfg?.reveniumBaseUrl?.trim()) {
|
|
148
|
+
errors.push('reveniumBaseUrl cannot be empty if provided');
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
try {
|
|
152
|
+
const url = new URL(cfg?.reveniumBaseUrl);
|
|
153
|
+
if (!url.protocol.startsWith('http')) {
|
|
154
|
+
errors.push('reveniumBaseUrl must use HTTP or HTTPS protocol');
|
|
155
|
+
}
|
|
156
|
+
// Check for localhost/development hostnames (IPv4, IPv6, and named)
|
|
157
|
+
const localhostHostnames = ['localhost', '127.0.0.1', '::1', '[::1]'];
|
|
158
|
+
if (localhostHostnames.includes(url.hostname)) {
|
|
159
|
+
warnings.push('Using localhost for Revenium API - ensure this is intended for development');
|
|
160
|
+
}
|
|
154
161
|
}
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
warnings.push('Using localhost for Revenium API - ensure this is intended for development');
|
|
162
|
+
catch {
|
|
163
|
+
errors.push('reveniumBaseUrl must be a valid URL');
|
|
164
|
+
suggestions.push('Use format: https://api.revenium.ai');
|
|
159
165
|
}
|
|
160
166
|
}
|
|
161
|
-
catch {
|
|
162
|
-
errors.push('reveniumBaseUrl must be a valid URL');
|
|
163
|
-
suggestions.push('Use format: https://api.revenium.io');
|
|
164
|
-
}
|
|
165
167
|
}
|
|
166
168
|
// Validate optional Anthropic API key
|
|
167
|
-
if (!isString(cfg?.anthropicApiKey)) {
|
|
169
|
+
if (cfg?.anthropicApiKey !== undefined && !isString(cfg?.anthropicApiKey)) {
|
|
168
170
|
errors.push('anthropicApiKey must be a string if provided');
|
|
169
171
|
}
|
|
170
|
-
else if (cfg?.anthropicApiKey?.trim()?.length === 0) {
|
|
172
|
+
else if (cfg?.anthropicApiKey !== undefined && cfg?.anthropicApiKey?.trim()?.length === 0) {
|
|
171
173
|
warnings.push('anthropicApiKey is empty - API calls may fail');
|
|
172
174
|
}
|
|
173
|
-
else if (!cfg?.anthropicApiKey?.startsWith(constants_1.VALIDATION_CONFIG.ANTHROPIC_API_KEY_PREFIX)) {
|
|
175
|
+
else if (cfg?.anthropicApiKey && !cfg?.anthropicApiKey?.startsWith(constants_1.VALIDATION_CONFIG.ANTHROPIC_API_KEY_PREFIX)) {
|
|
174
176
|
warnings.push(`anthropicApiKey does not start with "${constants_1.VALIDATION_CONFIG.ANTHROPIC_API_KEY_PREFIX}" - verify it is correct`);
|
|
175
177
|
}
|
|
176
178
|
// Validate optional timeout using constants
|
|
@@ -203,6 +205,23 @@ function validateReveniumConfig(config) {
|
|
|
203
205
|
else if (cfg?.maxRetries !== undefined && cfg?.maxRetries === 0) {
|
|
204
206
|
warnings.push('maxRetries is 0 - no retry attempts will be made');
|
|
205
207
|
}
|
|
208
|
+
// Validate optional printSummary
|
|
209
|
+
if (cfg?.printSummary !== undefined) {
|
|
210
|
+
const validPrintSummaryValues = [true, false, 'human', 'json'];
|
|
211
|
+
if (!validPrintSummaryValues.includes(cfg?.printSummary)) {
|
|
212
|
+
errors.push("printSummary must be a boolean or one of: 'human', 'json'");
|
|
213
|
+
suggestions.push("Use printSummary: true, 'human', or 'json' to enable summary output");
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// Validate optional teamId
|
|
217
|
+
if (cfg?.teamId !== undefined) {
|
|
218
|
+
if (!isString(cfg?.teamId)) {
|
|
219
|
+
errors.push('teamId must be a string if provided');
|
|
220
|
+
}
|
|
221
|
+
else if (cfg?.teamId?.trim()?.length === 0) {
|
|
222
|
+
errors.push('teamId cannot be empty if provided');
|
|
223
|
+
}
|
|
224
|
+
}
|
|
206
225
|
if (errors.length > 0) {
|
|
207
226
|
return {
|
|
208
227
|
isValid: false,
|
|
@@ -211,14 +230,27 @@ function validateReveniumConfig(config) {
|
|
|
211
230
|
suggestions
|
|
212
231
|
};
|
|
213
232
|
}
|
|
214
|
-
//
|
|
233
|
+
// Determine validated printSummary value
|
|
234
|
+
let validatedPrintSummary;
|
|
235
|
+
if (cfg?.printSummary === true || cfg?.printSummary === 'human') {
|
|
236
|
+
validatedPrintSummary = 'human';
|
|
237
|
+
}
|
|
238
|
+
else if (cfg?.printSummary === 'json') {
|
|
239
|
+
validatedPrintSummary = 'json';
|
|
240
|
+
}
|
|
241
|
+
else if (cfg?.printSummary === false) {
|
|
242
|
+
validatedPrintSummary = false;
|
|
243
|
+
}
|
|
244
|
+
// Build validated config (apply default for reveniumBaseUrl if not provided)
|
|
215
245
|
const validatedConfig = {
|
|
216
246
|
reveniumApiKey: cfg?.reveniumApiKey,
|
|
217
|
-
reveniumBaseUrl: cfg?.reveniumBaseUrl,
|
|
247
|
+
reveniumBaseUrl: isString(cfg?.reveniumBaseUrl) ? cfg?.reveniumBaseUrl : constants_1.DEFAULT_CONFIG.REVENIUM_BASE_URL,
|
|
218
248
|
anthropicApiKey: isString(cfg?.anthropicApiKey) ? cfg?.anthropicApiKey : undefined,
|
|
219
249
|
apiTimeout: isNumber(cfg?.apiTimeout) ? cfg?.apiTimeout : undefined,
|
|
220
250
|
failSilent: isBoolean(cfg?.failSilent) ? cfg?.failSilent : undefined,
|
|
221
|
-
maxRetries: isNumber(cfg?.maxRetries) ? cfg?.maxRetries : undefined
|
|
251
|
+
maxRetries: isNumber(cfg?.maxRetries) ? cfg?.maxRetries : undefined,
|
|
252
|
+
printSummary: validatedPrintSummary,
|
|
253
|
+
teamId: isString(cfg?.teamId) ? cfg?.teamId?.trim() : undefined
|
|
222
254
|
};
|
|
223
255
|
return {
|
|
224
256
|
isValid: true,
|
package/dist/cjs/wrapper.js
CHANGED
|
@@ -23,7 +23,7 @@ const logger = (0, config_1.getLogger)();
|
|
|
23
23
|
const patchingContext = {
|
|
24
24
|
originalMethods: {},
|
|
25
25
|
isPatched: false,
|
|
26
|
-
patchedInstances: new WeakSet()
|
|
26
|
+
patchedInstances: new WeakSet(),
|
|
27
27
|
};
|
|
28
28
|
/**
|
|
29
29
|
* Get the Messages prototype using sophisticated prototype access
|
|
@@ -42,9 +42,12 @@ function getMessagesPrototype() {
|
|
|
42
42
|
return anthropicConstructor?._Messages?.prototype;
|
|
43
43
|
// Method 3: Create a minimal instance with the real API key if available
|
|
44
44
|
// Fallback approach when direct prototype access methods fail
|
|
45
|
-
|
|
45
|
+
// Check config first, then environment variable
|
|
46
|
+
const config = (0, config_1.getConfig)();
|
|
47
|
+
const apiKey = config?.anthropicApiKey ?? process.env.ANTHROPIC_API_KEY;
|
|
46
48
|
if (!apiKey) {
|
|
47
|
-
throw new error_handling_1.AnthropicPatchingError(
|
|
49
|
+
throw new error_handling_1.AnthropicPatchingError("Unable to access Anthropic Messages prototype: No API key available and direct prototype access failed. " +
|
|
50
|
+
"Provide ANTHROPIC_API_KEY environment variable or pass anthropicApiKey in config.");
|
|
48
51
|
}
|
|
49
52
|
const minimalInstance = new sdk_1.default({ apiKey });
|
|
50
53
|
const messagesPrototype = Object.getPrototypeOf(minimalInstance.messages);
|
|
@@ -62,19 +65,19 @@ function getMessagesPrototype() {
|
|
|
62
65
|
*/
|
|
63
66
|
function patchAnthropic() {
|
|
64
67
|
if (patchingContext.isPatched) {
|
|
65
|
-
logger.debug(
|
|
68
|
+
logger.debug("Anthropic SDK already patched, skipping duplicate initialization");
|
|
66
69
|
return;
|
|
67
70
|
}
|
|
68
71
|
try {
|
|
69
72
|
// Access the Messages class prototype using sophisticated prototype access
|
|
70
73
|
const messagesPrototype = getMessagesPrototype();
|
|
71
74
|
if (!messagesPrototype)
|
|
72
|
-
throw new error_handling_1.AnthropicPatchingError(
|
|
75
|
+
throw new error_handling_1.AnthropicPatchingError("Unable to access Anthropic Messages prototype");
|
|
73
76
|
// Store original methods
|
|
74
77
|
patchingContext.originalMethods.create = messagesPrototype?.create;
|
|
75
78
|
patchingContext.originalMethods.stream = messagesPrototype?.stream;
|
|
76
79
|
if (!patchingContext.originalMethods?.create) {
|
|
77
|
-
throw new error_handling_1.AnthropicPatchingError(
|
|
80
|
+
throw new error_handling_1.AnthropicPatchingError("Unable to find original create method");
|
|
78
81
|
}
|
|
79
82
|
// Patch the create method
|
|
80
83
|
const patchedCreateFunction = function (params, options) {
|
|
@@ -88,11 +91,11 @@ function patchAnthropic() {
|
|
|
88
91
|
};
|
|
89
92
|
}
|
|
90
93
|
patchingContext.isPatched = true;
|
|
91
|
-
logger.info(
|
|
94
|
+
logger.info("Anthropic SDK patched successfully");
|
|
92
95
|
}
|
|
93
96
|
catch (error) {
|
|
94
97
|
const errorContext = (0, error_handling_1.createErrorContext)()
|
|
95
|
-
.with(
|
|
98
|
+
.with("patchingAttempt", true)
|
|
96
99
|
.build();
|
|
97
100
|
(0, error_handling_1.handleError)(error, logger, errorContext);
|
|
98
101
|
if (error instanceof error_handling_1.AnthropicPatchingError)
|
|
@@ -117,11 +120,11 @@ function unpatchAnthropic() {
|
|
|
117
120
|
}
|
|
118
121
|
patchingContext.isPatched = false;
|
|
119
122
|
patchingContext.originalMethods = {};
|
|
120
|
-
logger.info(
|
|
123
|
+
logger.info("Anthropic SDK unpatched successfully");
|
|
121
124
|
}
|
|
122
125
|
catch (error) {
|
|
123
126
|
const errorContext = (0, error_handling_1.createErrorContext)()
|
|
124
|
-
.with(
|
|
127
|
+
.with("unpatchingAttempt", true)
|
|
125
128
|
.build();
|
|
126
129
|
(0, error_handling_1.handleError)(error, logger, errorContext);
|
|
127
130
|
throw new error_handling_1.AnthropicPatchingError(`Failed to unpatch Anthropic SDK: ${error instanceof Error ? error.message : String(error)}`, errorContext);
|
|
@@ -137,7 +140,7 @@ function isAnthropicPatched() {
|
|
|
137
140
|
* Handle streaming response by collecting chunks and extracting usage data
|
|
138
141
|
*/
|
|
139
142
|
async function handleStreamingResponse(stream, context) {
|
|
140
|
-
const { requestId, model, metadata, requestTime, startTime } = context;
|
|
143
|
+
const { requestId, model, metadata, requestTime, startTime, requestBody } = context;
|
|
141
144
|
// Create a new async generator that collects chunks and tracks usage
|
|
142
145
|
async function* trackingStream() {
|
|
143
146
|
const chunks = [];
|
|
@@ -145,7 +148,7 @@ async function handleStreamingResponse(stream, context) {
|
|
|
145
148
|
try {
|
|
146
149
|
for await (const chunk of stream) {
|
|
147
150
|
// Track first token time
|
|
148
|
-
if (!firstTokenTime && chunk.type ===
|
|
151
|
+
if (!firstTokenTime && chunk.type === "content_block_delta") {
|
|
149
152
|
firstTokenTime = Date.now();
|
|
150
153
|
}
|
|
151
154
|
chunks.push(chunk);
|
|
@@ -155,10 +158,14 @@ async function handleStreamingResponse(stream, context) {
|
|
|
155
158
|
const endTime = Date.now();
|
|
156
159
|
const responseTime = new Date();
|
|
157
160
|
const duration = endTime - startTime;
|
|
158
|
-
|
|
161
|
+
const timeToFirstToken = firstTokenTime
|
|
162
|
+
? firstTokenTime - startTime
|
|
163
|
+
: undefined;
|
|
164
|
+
logger.debug("Stream completed, extracting usage", {
|
|
159
165
|
requestId,
|
|
160
166
|
chunkCount: chunks.length,
|
|
161
|
-
duration
|
|
167
|
+
duration,
|
|
168
|
+
timeToFirstToken,
|
|
162
169
|
});
|
|
163
170
|
const usage = (0, tracking_1.extractUsageFromStream)(chunks);
|
|
164
171
|
// Create tracking data
|
|
@@ -174,22 +181,24 @@ async function handleStreamingResponse(stream, context) {
|
|
|
174
181
|
stopReason: usage.stopReason,
|
|
175
182
|
metadata,
|
|
176
183
|
requestTime,
|
|
177
|
-
responseTime
|
|
184
|
+
responseTime,
|
|
185
|
+
timeToFirstToken,
|
|
186
|
+
requestBody: requestBody,
|
|
178
187
|
};
|
|
179
188
|
// Track usage asynchronously
|
|
180
189
|
(0, tracking_1.trackUsageAsync)(trackingData);
|
|
181
|
-
logger.debug(
|
|
190
|
+
logger.debug("Anthropic streaming request completed successfully", {
|
|
182
191
|
requestId,
|
|
183
192
|
model,
|
|
184
193
|
inputTokens: usage.inputTokens,
|
|
185
194
|
outputTokens: usage.outputTokens,
|
|
186
|
-
duration
|
|
195
|
+
duration,
|
|
187
196
|
});
|
|
188
197
|
}
|
|
189
198
|
catch (error) {
|
|
190
|
-
logger.error(
|
|
199
|
+
logger.error("Error processing streaming response", {
|
|
191
200
|
requestId,
|
|
192
|
-
error: error instanceof Error ? error.message : String(error)
|
|
201
|
+
error: error instanceof Error ? error.message : String(error),
|
|
193
202
|
});
|
|
194
203
|
throw error;
|
|
195
204
|
}
|
|
@@ -203,19 +212,19 @@ async function patchedCreateMethod(params, options) {
|
|
|
203
212
|
const requestId = (0, crypto_1.randomUUID)();
|
|
204
213
|
const startTime = Date.now();
|
|
205
214
|
const requestTime = new Date();
|
|
206
|
-
logger.debug(
|
|
215
|
+
logger.debug("Intercepted Anthropic messages.create call", {
|
|
207
216
|
requestId,
|
|
208
217
|
model: params.model,
|
|
209
218
|
hasMetadata: !!params.usageMetadata,
|
|
210
|
-
isStreaming: !!params.stream
|
|
219
|
+
isStreaming: !!params.stream,
|
|
211
220
|
});
|
|
212
221
|
// Validate parameters
|
|
213
222
|
const validation = (0, validation_1.validateAnthropicMessageParams)(params);
|
|
214
223
|
if (!validation.isValid) {
|
|
215
|
-
logger.warn(
|
|
224
|
+
logger.warn("Invalid Anthropic parameters detected", {
|
|
216
225
|
requestId,
|
|
217
226
|
errors: validation.errors,
|
|
218
|
-
warnings: validation.warnings
|
|
227
|
+
warnings: validation.warnings,
|
|
219
228
|
});
|
|
220
229
|
}
|
|
221
230
|
// Extract and validate metadata
|
|
@@ -226,7 +235,7 @@ async function patchedCreateMethod(params, options) {
|
|
|
226
235
|
// Call original method
|
|
227
236
|
const originalCreate = patchingContext.originalMethods.create;
|
|
228
237
|
if (!originalCreate)
|
|
229
|
-
throw new error_handling_1.RequestProcessingError(
|
|
238
|
+
throw new error_handling_1.RequestProcessingError("Original create method not available");
|
|
230
239
|
const response = await originalCreate.call(this, cleanParams, options);
|
|
231
240
|
// Check if this is a streaming response
|
|
232
241
|
const isStreaming = !!params.stream;
|
|
@@ -249,16 +258,17 @@ async function patchedCreateMethod(params, options) {
|
|
|
249
258
|
stopReason: usage.stopReason,
|
|
250
259
|
metadata,
|
|
251
260
|
requestTime,
|
|
252
|
-
responseTime
|
|
261
|
+
responseTime,
|
|
262
|
+
requestBody: params,
|
|
253
263
|
};
|
|
254
264
|
// Track usage asynchronously
|
|
255
265
|
(0, tracking_1.trackUsageAsync)(trackingData);
|
|
256
|
-
logger.debug(
|
|
266
|
+
logger.debug("Anthropic request completed successfully", {
|
|
257
267
|
requestId,
|
|
258
268
|
model: params.model,
|
|
259
269
|
inputTokens: usage.inputTokens,
|
|
260
270
|
outputTokens: usage.outputTokens,
|
|
261
|
-
duration
|
|
271
|
+
duration,
|
|
262
272
|
});
|
|
263
273
|
return response;
|
|
264
274
|
}
|
|
@@ -268,7 +278,8 @@ async function patchedCreateMethod(params, options) {
|
|
|
268
278
|
model: params.model,
|
|
269
279
|
metadata,
|
|
270
280
|
requestTime,
|
|
271
|
-
startTime
|
|
281
|
+
startTime,
|
|
282
|
+
requestBody: params,
|
|
272
283
|
});
|
|
273
284
|
}
|
|
274
285
|
catch (error) {
|
|
@@ -293,18 +304,18 @@ async function* patchedStreamMethod(params, options) {
|
|
|
293
304
|
const responseTime = new Date();
|
|
294
305
|
const chunks = [];
|
|
295
306
|
let firstTokenTime;
|
|
296
|
-
logger.debug(
|
|
307
|
+
logger.debug("Intercepted Anthropic messages.stream call", {
|
|
297
308
|
requestId,
|
|
298
309
|
model: params.model,
|
|
299
|
-
hasMetadata: !!params.usageMetadata
|
|
310
|
+
hasMetadata: !!params.usageMetadata,
|
|
300
311
|
});
|
|
301
312
|
// Validate parameters
|
|
302
313
|
const validation = (0, validation_1.validateAnthropicMessageParams)(params);
|
|
303
314
|
if (!validation.isValid) {
|
|
304
|
-
logger.warn(
|
|
315
|
+
logger.warn("Invalid Anthropic streaming parameters detected", {
|
|
305
316
|
requestId,
|
|
306
317
|
errors: validation.errors,
|
|
307
|
-
warnings: validation.warnings
|
|
318
|
+
warnings: validation.warnings,
|
|
308
319
|
});
|
|
309
320
|
}
|
|
310
321
|
// Extract and validate metadata
|
|
@@ -315,12 +326,12 @@ async function* patchedStreamMethod(params, options) {
|
|
|
315
326
|
// Call original stream method
|
|
316
327
|
const originalStream = patchingContext.originalMethods?.stream;
|
|
317
328
|
if (!originalStream) {
|
|
318
|
-
throw new error_handling_1.StreamProcessingError(
|
|
329
|
+
throw new error_handling_1.StreamProcessingError("Original stream method not available");
|
|
319
330
|
}
|
|
320
331
|
const stream = originalStream.call(this, cleanParams, options);
|
|
321
332
|
for await (const chunk of stream) {
|
|
322
333
|
// Track first token time
|
|
323
|
-
if (!firstTokenTime && chunk.type ===
|
|
334
|
+
if (!firstTokenTime && chunk.type === "content_block_delta") {
|
|
324
335
|
firstTokenTime = Date.now();
|
|
325
336
|
}
|
|
326
337
|
chunks.push(chunk);
|
|
@@ -328,7 +339,9 @@ async function* patchedStreamMethod(params, options) {
|
|
|
328
339
|
}
|
|
329
340
|
const endTime = Date.now();
|
|
330
341
|
const duration = endTime - startTime;
|
|
331
|
-
const timeToFirstToken = firstTokenTime
|
|
342
|
+
const timeToFirstToken = firstTokenTime
|
|
343
|
+
? firstTokenTime - startTime
|
|
344
|
+
: undefined;
|
|
332
345
|
// Extract usage information from all chunks
|
|
333
346
|
const usage = (0, tracking_1.extractUsageFromStream)(chunks);
|
|
334
347
|
// Create tracking data
|
|
@@ -345,18 +358,19 @@ async function* patchedStreamMethod(params, options) {
|
|
|
345
358
|
metadata,
|
|
346
359
|
requestTime,
|
|
347
360
|
responseTime,
|
|
348
|
-
timeToFirstToken
|
|
361
|
+
timeToFirstToken,
|
|
362
|
+
requestBody: params,
|
|
349
363
|
};
|
|
350
364
|
// Track usage asynchronously
|
|
351
365
|
(0, tracking_1.trackUsageAsync)(trackingData);
|
|
352
|
-
logger.debug(
|
|
366
|
+
logger.debug("Anthropic streaming request completed successfully", {
|
|
353
367
|
requestId,
|
|
354
368
|
model: params.model,
|
|
355
369
|
inputTokens: usage.inputTokens,
|
|
356
370
|
outputTokens: usage.outputTokens,
|
|
357
371
|
duration,
|
|
358
372
|
timeToFirstToken,
|
|
359
|
-
chunkCount: chunks.length
|
|
373
|
+
chunkCount: chunks.length,
|
|
360
374
|
});
|
|
361
375
|
}
|
|
362
376
|
catch (error) {
|
|
@@ -366,8 +380,8 @@ async function* patchedStreamMethod(params, options) {
|
|
|
366
380
|
.withRequestId(requestId)
|
|
367
381
|
.withModel(params.model)
|
|
368
382
|
.withDuration(duration)
|
|
369
|
-
.with(
|
|
370
|
-
.with(
|
|
383
|
+
.with("isStreaming", true)
|
|
384
|
+
.with("chunkCount", chunks.length)
|
|
371
385
|
.build();
|
|
372
386
|
(0, error_handling_1.handleError)(error, logger, errorContext);
|
|
373
387
|
throw error;
|
package/dist/esm/config.js
CHANGED
|
@@ -44,10 +44,27 @@ function loadConfigFromEnvironment() {
|
|
|
44
44
|
logLevel: process.env[ENV_VARS.LOG_LEVEL],
|
|
45
45
|
apiTimeout: process.env[ENV_VARS.API_TIMEOUT],
|
|
46
46
|
failSilent: process.env[ENV_VARS.FAIL_SILENT],
|
|
47
|
-
maxRetries: process.env[ENV_VARS.MAX_RETRIES]
|
|
47
|
+
maxRetries: process.env[ENV_VARS.MAX_RETRIES],
|
|
48
|
+
printSummary: process.env[ENV_VARS.PRINT_SUMMARY],
|
|
49
|
+
teamId: process.env[ENV_VARS.TEAM_ID]
|
|
48
50
|
};
|
|
49
51
|
return env;
|
|
50
52
|
}
|
|
53
|
+
/**
|
|
54
|
+
* Parse printSummary environment variable value
|
|
55
|
+
*/
|
|
56
|
+
function parsePrintSummary(value) {
|
|
57
|
+
if (!value)
|
|
58
|
+
return undefined;
|
|
59
|
+
const lowerValue = value.toLowerCase();
|
|
60
|
+
if (lowerValue === 'true' || lowerValue === 'human')
|
|
61
|
+
return 'human';
|
|
62
|
+
if (lowerValue === 'json')
|
|
63
|
+
return 'json';
|
|
64
|
+
if (lowerValue === 'false')
|
|
65
|
+
return false;
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
51
68
|
/**
|
|
52
69
|
* Convert environment config to Revenium config
|
|
53
70
|
*/
|
|
@@ -58,13 +75,16 @@ function createConfigFromEnvironment(env) {
|
|
|
58
75
|
const apiTimeout = env.apiTimeout ? parseInt(env.apiTimeout, 10) : undefined;
|
|
59
76
|
const failSilent = env.failSilent !== 'false'; // Default to true
|
|
60
77
|
const maxRetries = env.maxRetries ? parseInt(env.maxRetries, 10) : undefined;
|
|
78
|
+
const printSummary = parsePrintSummary(env.printSummary);
|
|
61
79
|
return {
|
|
62
80
|
reveniumApiKey: env.reveniumApiKey,
|
|
63
81
|
reveniumBaseUrl: env.reveniumBaseUrl || DEFAULT_CONFIG.REVENIUM_BASE_URL,
|
|
64
82
|
anthropicApiKey: env.anthropicApiKey,
|
|
65
83
|
apiTimeout,
|
|
66
84
|
failSilent,
|
|
67
|
-
maxRetries
|
|
85
|
+
maxRetries,
|
|
86
|
+
printSummary,
|
|
87
|
+
teamId: env.teamId?.trim()
|
|
68
88
|
};
|
|
69
89
|
}
|
|
70
90
|
/**
|
|
@@ -112,14 +132,42 @@ export function getConfig() {
|
|
|
112
132
|
}
|
|
113
133
|
/**
|
|
114
134
|
* Set the global configuration
|
|
135
|
+
* Uses the normalized config from validation (with defaults applied and fields trimmed)
|
|
115
136
|
*/
|
|
116
137
|
export function setConfig(config) {
|
|
117
|
-
|
|
118
|
-
|
|
138
|
+
const validation = validateReveniumConfig(config);
|
|
139
|
+
if (!validation.isValid) {
|
|
140
|
+
// Log detailed validation errors
|
|
141
|
+
getLogger().error('Configuration validation failed', {
|
|
142
|
+
errors: validation.errors,
|
|
143
|
+
warnings: validation.warnings,
|
|
144
|
+
suggestions: validation.suggestions
|
|
145
|
+
});
|
|
146
|
+
// Create detailed error message
|
|
147
|
+
let errorMessage = 'Configuration validation failed:\n';
|
|
148
|
+
validation.errors.forEach((error, index) => {
|
|
149
|
+
errorMessage += ` ${index + 1}. ${error}\n`;
|
|
150
|
+
});
|
|
151
|
+
if (validation.suggestions && validation.suggestions.length > 0) {
|
|
152
|
+
errorMessage += '\nSuggestions:\n';
|
|
153
|
+
validation.suggestions.forEach((suggestion) => {
|
|
154
|
+
errorMessage += ` • ${suggestion}\n`;
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
throw new Error(errorMessage.trim());
|
|
158
|
+
}
|
|
159
|
+
// Log warnings if any
|
|
160
|
+
if (validation.warnings && validation.warnings.length > 0) {
|
|
161
|
+
getLogger().warn('Configuration warnings', {
|
|
162
|
+
warnings: validation.warnings
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
// Use the normalized config from validation (with defaults applied and fields trimmed)
|
|
166
|
+
globalConfig = validation.config;
|
|
119
167
|
globalLogger.debug('Revenium configuration updated', {
|
|
120
|
-
baseUrl:
|
|
121
|
-
hasApiKey: !!
|
|
122
|
-
hasAnthropicKey: !!
|
|
168
|
+
baseUrl: globalConfig.reveniumBaseUrl,
|
|
169
|
+
hasApiKey: !!globalConfig.reveniumApiKey,
|
|
170
|
+
hasAnthropicKey: !!globalConfig.anthropicApiKey
|
|
123
171
|
});
|
|
124
172
|
}
|
|
125
173
|
/**
|
package/dist/esm/constants.js
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
export const DEFAULT_CONFIG = {
|
|
9
9
|
/** Default Revenium API base URL */
|
|
10
|
-
REVENIUM_BASE_URL:
|
|
10
|
+
REVENIUM_BASE_URL: "https://api.revenium.ai",
|
|
11
11
|
/** Default API timeout in milliseconds */
|
|
12
12
|
API_TIMEOUT: 5000,
|
|
13
13
|
/** Default maximum retries for failed API calls */
|
|
@@ -60,9 +60,9 @@ export const VALIDATION_CONFIG = {
|
|
|
60
60
|
/** Minimum API key length */
|
|
61
61
|
MIN_API_KEY_LENGTH: 20,
|
|
62
62
|
/** Required API key prefix for Revenium */
|
|
63
|
-
REVENIUM_API_KEY_PREFIX:
|
|
63
|
+
REVENIUM_API_KEY_PREFIX: "hak_",
|
|
64
64
|
/** Required API key prefix for Anthropic */
|
|
65
|
-
ANTHROPIC_API_KEY_PREFIX:
|
|
65
|
+
ANTHROPIC_API_KEY_PREFIX: "sk-ant-",
|
|
66
66
|
/** Maximum tokens warning threshold */
|
|
67
67
|
HIGH_MAX_TOKENS_THRESHOLD: 4096,
|
|
68
68
|
/** Temperature range */
|
|
@@ -83,39 +83,54 @@ export const VALIDATION_CONFIG = {
|
|
|
83
83
|
*/
|
|
84
84
|
export const LOGGING_CONFIG = {
|
|
85
85
|
/** Middleware name for log prefixes */
|
|
86
|
-
MIDDLEWARE_NAME:
|
|
86
|
+
MIDDLEWARE_NAME: "Revenium",
|
|
87
87
|
/** User agent string for API requests */
|
|
88
|
-
USER_AGENT:
|
|
88
|
+
USER_AGENT: "revenium-middleware-anthropic-node/1.0.0",
|
|
89
89
|
/** Debug environment variable name */
|
|
90
|
-
DEBUG_ENV_VAR:
|
|
90
|
+
DEBUG_ENV_VAR: "REVENIUM_DEBUG",
|
|
91
91
|
};
|
|
92
92
|
/**
|
|
93
93
|
* Environment variable names
|
|
94
94
|
*/
|
|
95
95
|
export const ENV_VARS = {
|
|
96
96
|
/** Revenium API key */
|
|
97
|
-
REVENIUM_API_KEY:
|
|
97
|
+
REVENIUM_API_KEY: "REVENIUM_METERING_API_KEY",
|
|
98
98
|
/** Revenium base URL */
|
|
99
|
-
REVENIUM_BASE_URL:
|
|
99
|
+
REVENIUM_BASE_URL: "REVENIUM_METERING_BASE_URL",
|
|
100
100
|
/** Anthropic API key */
|
|
101
|
-
ANTHROPIC_API_KEY:
|
|
101
|
+
ANTHROPIC_API_KEY: "ANTHROPIC_API_KEY",
|
|
102
102
|
/** Debug mode */
|
|
103
|
-
DEBUG:
|
|
103
|
+
DEBUG: "REVENIUM_DEBUG",
|
|
104
104
|
/** Log level */
|
|
105
|
-
LOG_LEVEL:
|
|
105
|
+
LOG_LEVEL: "REVENIUM_LOG_LEVEL",
|
|
106
106
|
/** API timeout */
|
|
107
|
-
API_TIMEOUT:
|
|
107
|
+
API_TIMEOUT: "REVENIUM_API_TIMEOUT",
|
|
108
108
|
/** Fail silent mode */
|
|
109
|
-
FAIL_SILENT:
|
|
109
|
+
FAIL_SILENT: "REVENIUM_FAIL_SILENT",
|
|
110
110
|
/** Maximum retries */
|
|
111
|
-
MAX_RETRIES:
|
|
111
|
+
MAX_RETRIES: "REVENIUM_MAX_RETRIES",
|
|
112
|
+
/** Print summary mode (true/false/human/json) */
|
|
113
|
+
PRINT_SUMMARY: "REVENIUM_PRINT_SUMMARY",
|
|
114
|
+
/** Team ID for cost metrics retrieval */
|
|
115
|
+
TEAM_ID: "REVENIUM_TEAM_ID",
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Summary printer configuration
|
|
119
|
+
*/
|
|
120
|
+
export const SUMMARY_PRINTER_CONFIG = {
|
|
121
|
+
/** Maximum number of retries when fetching cost metrics */
|
|
122
|
+
MAX_RETRIES: 3,
|
|
123
|
+
/** Delay between retries in milliseconds */
|
|
124
|
+
RETRY_DELAY: 2000,
|
|
125
|
+
/** Fetch timeout in milliseconds (prevents hung requests from keeping Node process alive) */
|
|
126
|
+
FETCH_TIMEOUT: 10000,
|
|
112
127
|
};
|
|
113
128
|
/**
|
|
114
129
|
* API endpoints
|
|
115
130
|
*/
|
|
116
131
|
export const API_ENDPOINTS = {
|
|
117
132
|
/** Revenium AI completions endpoint */
|
|
118
|
-
AI_COMPLETIONS:
|
|
133
|
+
AI_COMPLETIONS: "/meter/v2/ai/completions",
|
|
119
134
|
};
|
|
120
135
|
/**
|
|
121
136
|
* Anthropic model patterns
|
|
@@ -125,17 +140,17 @@ export const ANTHROPIC_PATTERNS = {
|
|
|
125
140
|
CLAUDE_MODEL_PATTERN: /claude/i,
|
|
126
141
|
/** Known Anthropic stop reasons */
|
|
127
142
|
STOP_REASONS: {
|
|
128
|
-
END_TURN:
|
|
129
|
-
MAX_TOKENS:
|
|
130
|
-
STOP_SEQUENCE:
|
|
131
|
-
TOOL_USE:
|
|
143
|
+
END_TURN: "end_turn",
|
|
144
|
+
MAX_TOKENS: "max_tokens",
|
|
145
|
+
STOP_SEQUENCE: "stop_sequence",
|
|
146
|
+
TOOL_USE: "tool_use",
|
|
132
147
|
},
|
|
133
148
|
/** Revenium stop reason mappings */
|
|
134
149
|
REVENIUM_STOP_REASON_MAP: {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
150
|
+
end_turn: "END",
|
|
151
|
+
max_tokens: "TOKEN_LIMIT",
|
|
152
|
+
stop_sequence: "END_SEQUENCE",
|
|
153
|
+
tool_use: "END",
|
|
139
154
|
},
|
|
140
155
|
};
|
|
141
156
|
//# sourceMappingURL=constants.js.map
|