@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.
@@ -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 (!isString(cfg?.reveniumBaseUrl)) {
144
- errors.push('reveniumBaseUrl is required and must be a string');
145
- }
146
- else if (!cfg?.reveniumBaseUrl?.trim()) {
147
- errors.push('reveniumBaseUrl cannot be empty');
148
- }
149
- else {
150
- try {
151
- const url = new URL(cfg?.reveniumBaseUrl);
152
- if (!url.protocol.startsWith('http')) {
153
- errors.push('reveniumBaseUrl must use HTTP or HTTPS protocol');
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
- // Check for localhost/development hostnames (IPv4, IPv6, and named)
156
- const localhostHostnames = ['localhost', '127.0.0.1', '::1', '[::1]'];
157
- if (localhostHostnames.includes(url.hostname)) {
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
- // Build validated config
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,
@@ -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
- const apiKey = process.env.ANTHROPIC_API_KEY;
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('Unable to access Anthropic Messages prototype: No API key available and direct prototype access failed');
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('Anthropic SDK already patched, skipping duplicate initialization');
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('Unable to access Anthropic Messages prototype');
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('Unable to find original create method');
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('Anthropic SDK patched successfully');
94
+ logger.info("Anthropic SDK patched successfully");
92
95
  }
93
96
  catch (error) {
94
97
  const errorContext = (0, error_handling_1.createErrorContext)()
95
- .with('patchingAttempt', true)
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('Anthropic SDK unpatched successfully');
123
+ logger.info("Anthropic SDK unpatched successfully");
121
124
  }
122
125
  catch (error) {
123
126
  const errorContext = (0, error_handling_1.createErrorContext)()
124
- .with('unpatchingAttempt', true)
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 === 'content_block_delta') {
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
- logger.debug('Stream completed, extracting usage', {
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('Anthropic streaming request completed successfully', {
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('Error processing streaming response', {
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('Intercepted Anthropic messages.create call', {
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('Invalid Anthropic parameters detected', {
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('Original create method not available');
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('Anthropic request completed successfully', {
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('Intercepted Anthropic messages.stream call', {
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('Invalid Anthropic streaming parameters detected', {
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('Original stream method not available');
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 === 'content_block_delta') {
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 ? firstTokenTime - startTime : undefined;
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('Anthropic streaming request completed successfully', {
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('isStreaming', true)
370
- .with('chunkCount', chunks.length)
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;
@@ -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
- validateConfig(config);
118
- globalConfig = config;
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: config.reveniumBaseUrl,
121
- hasApiKey: !!config.reveniumApiKey,
122
- hasAnthropicKey: !!config.anthropicApiKey
168
+ baseUrl: globalConfig.reveniumBaseUrl,
169
+ hasApiKey: !!globalConfig.reveniumApiKey,
170
+ hasAnthropicKey: !!globalConfig.anthropicApiKey
123
171
  });
124
172
  }
125
173
  /**
@@ -7,7 +7,7 @@
7
7
  */
8
8
  export const DEFAULT_CONFIG = {
9
9
  /** Default Revenium API base URL */
10
- REVENIUM_BASE_URL: 'https://api.revenium.io',
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: 'hak_',
63
+ REVENIUM_API_KEY_PREFIX: "hak_",
64
64
  /** Required API key prefix for Anthropic */
65
- ANTHROPIC_API_KEY_PREFIX: 'sk-ant-',
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: 'Revenium',
86
+ MIDDLEWARE_NAME: "Revenium",
87
87
  /** User agent string for API requests */
88
- USER_AGENT: 'revenium-middleware-anthropic-node/1.0.0',
88
+ USER_AGENT: "revenium-middleware-anthropic-node/1.0.0",
89
89
  /** Debug environment variable name */
90
- DEBUG_ENV_VAR: 'REVENIUM_DEBUG',
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: 'REVENIUM_METERING_API_KEY',
97
+ REVENIUM_API_KEY: "REVENIUM_METERING_API_KEY",
98
98
  /** Revenium base URL */
99
- REVENIUM_BASE_URL: 'REVENIUM_METERING_BASE_URL',
99
+ REVENIUM_BASE_URL: "REVENIUM_METERING_BASE_URL",
100
100
  /** Anthropic API key */
101
- ANTHROPIC_API_KEY: 'ANTHROPIC_API_KEY',
101
+ ANTHROPIC_API_KEY: "ANTHROPIC_API_KEY",
102
102
  /** Debug mode */
103
- DEBUG: 'REVENIUM_DEBUG',
103
+ DEBUG: "REVENIUM_DEBUG",
104
104
  /** Log level */
105
- LOG_LEVEL: 'REVENIUM_LOG_LEVEL',
105
+ LOG_LEVEL: "REVENIUM_LOG_LEVEL",
106
106
  /** API timeout */
107
- API_TIMEOUT: 'REVENIUM_API_TIMEOUT',
107
+ API_TIMEOUT: "REVENIUM_API_TIMEOUT",
108
108
  /** Fail silent mode */
109
- FAIL_SILENT: 'REVENIUM_FAIL_SILENT',
109
+ FAIL_SILENT: "REVENIUM_FAIL_SILENT",
110
110
  /** Maximum retries */
111
- MAX_RETRIES: 'REVENIUM_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: '/meter/v2/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: 'end_turn',
129
- MAX_TOKENS: 'max_tokens',
130
- STOP_SEQUENCE: 'stop_sequence',
131
- TOOL_USE: '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
- 'end_turn': 'END',
136
- 'max_tokens': 'TOKEN_LIMIT',
137
- 'stop_sequence': 'END_SEQUENCE',
138
- 'tool_use': 'END',
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