@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.
@@ -15,8 +15,21 @@ const config_1 = require("./config");
15
15
  const circuit_breaker_1 = require("./utils/circuit-breaker");
16
16
  const error_handling_1 = require("./utils/error-handling");
17
17
  const constants_1 = require("./constants");
18
+ const trace_fields_1 = require("./utils/trace-fields");
19
+ const summary_printer_1 = require("./utils/summary-printer");
18
20
  // Global logger
19
21
  const logger = (0, config_1.getLogger)();
22
+ if (typeof process !== "undefined") {
23
+ process.on("uncaughtException", (error) => {
24
+ if (error.name === "AbortError") {
25
+ return;
26
+ }
27
+ if (error.stack && error.stack.includes("tracking.ts")) {
28
+ return;
29
+ }
30
+ throw error;
31
+ });
32
+ }
20
33
  /**
21
34
  * Send tracking data to Revenium API with resilience patterns
22
35
  */
@@ -34,7 +47,8 @@ async function sendReveniumMetrics(data) {
34
47
  isStreamed: data.isStreamed,
35
48
  });
36
49
  // Build payload using exact structure from working implementations
37
- const payload = buildReveniumPayload(data);
50
+ // Built early so we can print summary even if Revenium tracking fails
51
+ const payload = await buildReveniumPayload(data);
38
52
  // Create request options
39
53
  const requestOptions = {
40
54
  method: "POST",
@@ -50,10 +64,6 @@ async function sendReveniumMetrics(data) {
50
64
  const controller = new AbortController();
51
65
  const timeoutId = setTimeout(() => controller.abort(), requestOptions.timeout);
52
66
  requestOptions.signal = controller.signal;
53
- // Handle abort signal errors to prevent unhandled error events
54
- controller.signal.addEventListener("abort", () => {
55
- // Silently handle abort - this is expected behavior for timeouts
56
- });
57
67
  try {
58
68
  // Execute with circuit breaker and retry logic
59
69
  await (0, circuit_breaker_1.executeWithCircuitBreaker)(async () => {
@@ -66,9 +76,12 @@ async function sendReveniumMetrics(data) {
66
76
  let response;
67
77
  try {
68
78
  response = await (0, node_fetch_1.default)(`${config.reveniumBaseUrl}${constants_1.API_ENDPOINTS.AI_COMPLETIONS}`, requestOptions);
79
+ if (response.body) {
80
+ response.body.once("error", () => { });
81
+ response.body.resume();
82
+ }
69
83
  }
70
84
  catch (fetchError) {
71
- // Handle AbortError and other fetch errors
72
85
  if (fetchError instanceof Error && fetchError.name === "AbortError") {
73
86
  throw new Error(`Request timeout after ${requestOptions.timeout}ms`);
74
87
  }
@@ -91,8 +104,19 @@ async function sendReveniumMetrics(data) {
91
104
  status: response.status,
92
105
  duration: data.duration,
93
106
  });
107
+ try {
108
+ await response.text();
109
+ }
110
+ catch (bodyError) {
111
+ logger.debug("Error reading response body (non-critical)", {
112
+ requestId,
113
+ error: bodyError instanceof Error
114
+ ? bodyError.message
115
+ : String(bodyError),
116
+ });
117
+ }
94
118
  return response;
95
- }, config.maxRetries || constants_1.DEFAULT_CONFIG.MAX_RETRIES);
119
+ }, config.maxRetries ?? constants_1.DEFAULT_CONFIG.MAX_RETRIES);
96
120
  });
97
121
  }
98
122
  catch (error) {
@@ -102,37 +126,67 @@ async function sendReveniumMetrics(data) {
102
126
  .withDuration(data.duration)
103
127
  .build();
104
128
  (0, error_handling_1.handleError)(error, logger, errorContext);
105
- // Always fail silently for tracking errors to prevent breaking user's application
106
- // Tracking errors should never break the user's main application flow
129
+ // Respect failSilent configuration
130
+ // By default (failSilent=true), tracking errors don't break the user's application
131
+ // If failSilent=false, re-throw the error for user handling
132
+ if (config.failSilent === false) {
133
+ throw error;
134
+ }
107
135
  }
108
136
  finally {
109
137
  clearTimeout(timeoutId);
138
+ // Print usage summary regardless of whether Revenium tracking succeeded or failed
139
+ // This ensures users see the summary "after each Anthropic API request" as documented
140
+ (0, summary_printer_1.printUsageSummary)(payload);
110
141
  }
111
142
  }
112
143
  /**
113
144
  * Build Revenium payload from tracking data
114
145
  */
115
- function buildReveniumPayload(data) {
146
+ async function buildReveniumPayload(data) {
116
147
  const now = new Date().toISOString();
117
148
  const requestTime = data.requestTime.toISOString();
118
149
  const completionStartTime = data.responseTime.toISOString();
150
+ // Standard known fields
151
+ const knownFields = new Set([
152
+ "taskType",
153
+ "agent",
154
+ "organizationId",
155
+ "productId",
156
+ "subscriber",
157
+ "subscriptionId",
158
+ "traceId",
159
+ "responseQualityScore",
160
+ ]);
161
+ // Extract custom fields (anything NOT in the standard 8 fields)
162
+ const customFields = {};
163
+ if (data.metadata) {
164
+ for (const [key, value] of Object.entries(data.metadata)) {
165
+ if (!knownFields.has(key)) {
166
+ customFields[key] = value;
167
+ }
168
+ }
169
+ }
170
+ const environment = (0, trace_fields_1.getEnvironment)();
171
+ const region = await (0, trace_fields_1.getRegion)();
172
+ const credentialAlias = (0, trace_fields_1.getCredentialAlias)();
173
+ const traceType = (0, trace_fields_1.getTraceType)();
174
+ const traceName = (0, trace_fields_1.getTraceName)();
175
+ const parentTransactionId = (0, trace_fields_1.getParentTransactionId)();
176
+ const transactionName = (0, trace_fields_1.getTransactionName)();
177
+ const retryNumber = (0, trace_fields_1.getRetryNumber)();
178
+ const operationSubtype = (0, trace_fields_1.detectOperationSubtype)(data.requestBody);
119
179
  return {
120
180
  stopReason: getStopReason(data.stopReason),
121
181
  costType: "AI",
122
182
  isStreamed: data.isStreamed,
123
- taskType: data.metadata?.taskType,
124
- agent: data.metadata?.agent,
125
183
  operationType: "CHAT",
126
184
  inputTokenCount: data.inputTokens,
127
185
  outputTokenCount: data.outputTokens,
128
- reasoningTokenCount: 0, // Anthropic doesn't currently have reasoning tokens
186
+ reasoningTokenCount: 0,
129
187
  cacheCreationTokenCount: data.cacheCreationTokens || 0,
130
188
  cacheReadTokenCount: data.cacheReadTokens || 0,
131
189
  totalTokenCount: data.inputTokens + data.outputTokens,
132
- organizationId: data.metadata?.organizationId,
133
- productId: data.metadata?.productId,
134
- subscriber: data.metadata?.subscriber, // Pass through nested subscriber object directly
135
- subscriptionId: data.metadata?.subscriptionId,
136
190
  model: data.model,
137
191
  transactionId: data.requestId,
138
192
  responseTime: now,
@@ -141,9 +195,31 @@ function buildReveniumPayload(data) {
141
195
  requestTime: requestTime,
142
196
  completionStartTime: completionStartTime,
143
197
  timeToFirstToken: data.timeToFirstToken || 0,
144
- traceId: data.metadata?.traceId,
145
- responseQualityScore: data.metadata?.responseQualityScore, // Fixed: Now sending to Revenium
146
198
  middlewareSource: "nodejs",
199
+ ...(data.metadata?.taskType && { taskType: data.metadata.taskType }),
200
+ ...(data.metadata?.agent && { agent: data.metadata.agent }),
201
+ ...(data.metadata?.organizationId && {
202
+ organizationId: data.metadata.organizationId,
203
+ }),
204
+ ...(data.metadata?.productId && { productId: data.metadata.productId }),
205
+ ...(data.metadata?.subscriber && { subscriber: data.metadata.subscriber }),
206
+ ...(data.metadata?.subscriptionId && {
207
+ subscriptionId: data.metadata.subscriptionId,
208
+ }),
209
+ ...(data.metadata?.traceId && { traceId: data.metadata.traceId }),
210
+ ...(data.metadata?.responseQualityScore !== undefined && {
211
+ responseQualityScore: data.metadata.responseQualityScore,
212
+ }),
213
+ ...(environment && { environment }),
214
+ ...(region && { region }),
215
+ ...(credentialAlias && { credentialAlias }),
216
+ ...(traceType && { traceType }),
217
+ ...(traceName && { traceName }),
218
+ ...(parentTransactionId && { parentTransactionId }),
219
+ ...(transactionName && { transactionName }),
220
+ ...(retryNumber !== undefined && { retryNumber }),
221
+ ...(operationSubtype && { operationSubtype }),
222
+ ...customFields,
147
223
  };
148
224
  }
149
225
  /**
@@ -165,7 +241,6 @@ function trackUsageAsync(trackingData) {
165
241
  return logger.warn("Revenium configuration not available - skipping tracking", {
166
242
  requestId: trackingData.requestId,
167
243
  });
168
- // Run tracking in background without awaiting
169
244
  sendReveniumMetrics(trackingData)
170
245
  .then(() => {
171
246
  logger.debug("Revenium tracking completed successfully", {
@@ -175,6 +250,12 @@ function trackUsageAsync(trackingData) {
175
250
  });
176
251
  })
177
252
  .catch((error) => {
253
+ if (error instanceof Error && error.name === "AbortError") {
254
+ logger.debug("Metrics request aborted (process exiting)", {
255
+ requestId: trackingData.requestId,
256
+ });
257
+ return;
258
+ }
178
259
  const errorContext = (0, error_handling_1.createErrorContext)()
179
260
  .withRequestId(trackingData.requestId)
180
261
  .withModel(trackingData.model)
@@ -184,6 +265,12 @@ function trackUsageAsync(trackingData) {
184
265
  requestId: trackingData.requestId,
185
266
  context: errorContext,
186
267
  });
268
+ // If failSilent=false, propagate the error
269
+ // Note: Since this is fire-and-forget, the error won't be caught by caller
270
+ // but it will appear as an unhandled promise rejection
271
+ if (config.failSilent === false) {
272
+ throw error;
273
+ }
187
274
  });
188
275
  }
189
276
  /**
@@ -19,68 +19,6 @@
19
19
  * - **Automatic Validation**: TypeScript validates the structure at compile time
20
20
  * - **Better Developer Experience**: Auto-completion and error detection
21
21
  *
22
- * ## Usage Examples:
23
- *
24
- * ### Basic Usage:
25
- * ```typescript
26
- * import '@revenium/anthropic';
27
- * import Anthropic from '@anthropic-ai/sdk';
28
- *
29
- * const anthropic = new Anthropic();
30
- *
31
- * const response = await anthropic.messages.create({
32
- * model: 'claude-3-5-sonnet-latest',
33
- * max_tokens: 1024,
34
- * messages: [{ role: 'user', content: 'Hello!' }],
35
- * usageMetadata: { // TypeScript recognizes this natively
36
- * subscriber: { id: 'user-123', email: 'user@example.com' },
37
- * organizationId: 'my-company',
38
- * taskType: 'customer-support',
39
- * traceId: 'session-abc-123'
40
- * }
41
- * });
42
- * ```
43
- *
44
- * ### Streaming Usage:
45
- * ```typescript
46
- * const stream = await anthropic.messages.stream({
47
- * model: 'claude-3-5-sonnet-latest',
48
- * max_tokens: 1024,
49
- * messages: [{ role: 'user', content: 'Generate a report' }],
50
- * usageMetadata: {
51
- * taskType: 'content-generation',
52
- * productId: 'report-generator',
53
- * responseQualityScore: 0.95
54
- * }
55
- * });
56
- * ```
57
- *
58
- * ### Advanced Usage with All Fields:
59
- * ```typescript
60
- * const response = await anthropic.messages.create({
61
- * model: 'claude-3-5-sonnet-latest',
62
- * max_tokens: 2048,
63
- * messages: [{ role: 'user', content: 'Complex analysis task' }],
64
- * usageMetadata: {
65
- * subscriber: {
66
- * id: 'user-456',
67
- * email: 'analyst@company.com',
68
- * credential: { name: 'api-key', value: 'sk-...' }
69
- * },
70
- * traceId: 'analysis-session-789',
71
- * taskType: 'data-analysis',
72
- * organizationId: 'enterprise-client',
73
- * subscriptionId: 'premium-plan',
74
- * productId: 'analytics-suite',
75
- * agent: 'data-analyst-bot',
76
- * responseQualityScore: 0.98,
77
- * customField: 'custom-value' // Extensible with custom fields
78
- * }
79
- * });
80
- * ```
81
- *
82
- * @public
83
- * @since 1.1.0
84
22
  */
85
23
  Object.defineProperty(exports, "__esModule", { value: true });
86
24
  //# sourceMappingURL=anthropic-augmentation.js.map
@@ -0,0 +1,189 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.printUsageSummary = printUsageSummary;
4
+ const config_1 = require("../config");
5
+ const constants_1 = require("../constants");
6
+ const logger = (0, config_1.getLogger)();
7
+ function delayWithUnref(ms) {
8
+ return new Promise((resolve) => {
9
+ const timer = setTimeout(resolve, ms);
10
+ if (typeof timer.unref === "function") {
11
+ timer.unref();
12
+ }
13
+ });
14
+ }
15
+ async function fetchCompletionMetrics(transactionId, maxRetries = constants_1.SUMMARY_PRINTER_CONFIG.MAX_RETRIES, retryDelay = constants_1.SUMMARY_PRINTER_CONFIG.RETRY_DELAY) {
16
+ const config = (0, config_1.getConfig)();
17
+ if (!config) {
18
+ logger.debug("No config available for summary printing");
19
+ return null;
20
+ }
21
+ if (!config.teamId) {
22
+ logger.debug("Team ID not configured, skipping cost retrieval for summary");
23
+ return null;
24
+ }
25
+ const baseUrl = (config.reveniumBaseUrl || constants_1.DEFAULT_CONFIG.REVENIUM_BASE_URL).replace(/\/+$/, "");
26
+ const url = `${baseUrl}/profitstream/v2/api/sources/metrics/ai/completions`;
27
+ const teamId = config.teamId.trim();
28
+ const urlWithParams = `${url}?teamId=${encodeURIComponent(teamId)}&transactionId=${encodeURIComponent(transactionId)}`;
29
+ logger.debug("Fetching completion metrics", { url: urlWithParams });
30
+ for (let attempt = 0; attempt < maxRetries; attempt++) {
31
+ // Create an AbortController with timeout to prevent hung requests from keeping Node process alive
32
+ const controller = new AbortController();
33
+ const timeoutId = setTimeout(() => controller.abort(), constants_1.SUMMARY_PRINTER_CONFIG.FETCH_TIMEOUT);
34
+ // Unref the timer so it doesn't keep the process alive
35
+ if (typeof timeoutId.unref === "function") {
36
+ timeoutId.unref();
37
+ }
38
+ try {
39
+ const response = await fetch(urlWithParams, {
40
+ method: "GET",
41
+ headers: {
42
+ Accept: "application/json",
43
+ "x-api-key": config.reveniumApiKey,
44
+ },
45
+ signal: controller.signal,
46
+ });
47
+ if (!response.ok) {
48
+ try {
49
+ await response.text();
50
+ }
51
+ catch { }
52
+ logger.debug(`Completions metrics API returned ${response.status}`, {
53
+ attempt: attempt + 1,
54
+ });
55
+ if (attempt < maxRetries - 1) {
56
+ await delayWithUnref(retryDelay);
57
+ continue;
58
+ }
59
+ return null;
60
+ }
61
+ const data = (await response.json());
62
+ const completions = data._embedded?.aICompletionMetricResourceList;
63
+ if (completions && completions.length > 0) {
64
+ return completions[0];
65
+ }
66
+ if (attempt < maxRetries - 1) {
67
+ logger.debug(`Waiting for metrics to aggregate (attempt ${attempt + 1}/${maxRetries})...`);
68
+ await delayWithUnref(retryDelay);
69
+ }
70
+ }
71
+ catch (error) {
72
+ logger.debug("Failed to fetch completion metrics", {
73
+ error: error instanceof Error ? error.message : String(error),
74
+ attempt: attempt + 1,
75
+ });
76
+ if (attempt < maxRetries - 1) {
77
+ await delayWithUnref(retryDelay);
78
+ }
79
+ }
80
+ finally {
81
+ clearTimeout(timeoutId);
82
+ }
83
+ }
84
+ return null;
85
+ }
86
+ function isSummaryFormat(value) {
87
+ return value === "human" || value === "json";
88
+ }
89
+ function formatAndPrintJsonSummary(payload, metrics) {
90
+ const config = (0, config_1.getConfig)();
91
+ const summary = {
92
+ model: payload.model,
93
+ provider: payload.provider,
94
+ durationSeconds: payload.requestDuration / 1000,
95
+ inputTokenCount: payload.inputTokenCount,
96
+ outputTokenCount: payload.outputTokenCount,
97
+ totalTokenCount: payload.totalTokenCount,
98
+ cost: typeof metrics?.totalCost === "number" ? metrics.totalCost : null,
99
+ };
100
+ if (summary.cost === null) {
101
+ summary.costStatus = config?.teamId ? "pending" : "unavailable";
102
+ }
103
+ if (payload.traceId) {
104
+ summary.traceId = payload.traceId;
105
+ }
106
+ console.log(JSON.stringify(summary));
107
+ }
108
+ function formatAndPrintHumanSummary(payload, metrics) {
109
+ console.log("\n" + "=".repeat(60));
110
+ console.log("📊 REVENIUM USAGE SUMMARY");
111
+ console.log("=".repeat(60));
112
+ console.log(`🤖 Model: ${payload.model}`);
113
+ console.log(`🏢 Provider: ${payload.provider}`);
114
+ console.log(`⏱️ Duration: ${(payload.requestDuration / 1000).toFixed(2)}s`);
115
+ console.log("\n💬 Token Usage:");
116
+ console.log(` 📥 Input Tokens: ${(payload.inputTokenCount ?? 0).toLocaleString()}`);
117
+ console.log(` 📤 Output Tokens: ${(payload.outputTokenCount ?? 0).toLocaleString()}`);
118
+ console.log(` 📊 Total Tokens: ${(payload.totalTokenCount ?? 0).toLocaleString()}`);
119
+ if (typeof metrics?.totalCost === "number") {
120
+ console.log(`\n💰 Cost: $${metrics.totalCost.toFixed(6)}`);
121
+ }
122
+ else {
123
+ const config = (0, config_1.getConfig)();
124
+ if (!config?.teamId) {
125
+ console.log(`\n💰 Cost: Set REVENIUM_TEAM_ID environment variable to see pricing`);
126
+ }
127
+ else {
128
+ console.log(`\n💰 Cost: (pending aggregation)`);
129
+ }
130
+ }
131
+ if (payload.traceId) {
132
+ console.log(`\n🔖 Trace ID: ${payload.traceId}`);
133
+ }
134
+ console.log("=".repeat(60) + "\n");
135
+ }
136
+ function formatAndPrintSummary(payload, metrics, format) {
137
+ if (format === "json") {
138
+ formatAndPrintJsonSummary(payload, metrics);
139
+ }
140
+ else {
141
+ formatAndPrintHumanSummary(payload, metrics);
142
+ }
143
+ }
144
+ function safeFormatAndPrintSummary(payload, metrics, format) {
145
+ try {
146
+ formatAndPrintSummary(payload, metrics, format);
147
+ }
148
+ catch (error) {
149
+ logger.debug("Failed to format and print summary", {
150
+ error: error instanceof Error ? error.message : String(error),
151
+ });
152
+ }
153
+ }
154
+ function getSummaryFormat(value) {
155
+ if (!value)
156
+ return null;
157
+ if (value === true)
158
+ return "human";
159
+ if (isSummaryFormat(value)) {
160
+ return value;
161
+ }
162
+ return null;
163
+ }
164
+ function printUsageSummary(payload) {
165
+ const config = (0, config_1.getConfig)();
166
+ const format = getSummaryFormat(config?.printSummary);
167
+ if (!format) {
168
+ return;
169
+ }
170
+ if (config?.teamId && payload.transactionId) {
171
+ fetchCompletionMetrics(payload.transactionId)
172
+ .then((metrics) => {
173
+ safeFormatAndPrintSummary(payload, metrics, format);
174
+ })
175
+ .catch((error) => {
176
+ logger.debug("Failed to print usage summary with metrics", {
177
+ error: error instanceof Error ? error.message : String(error),
178
+ });
179
+ safeFormatAndPrintSummary(payload, null, format);
180
+ })
181
+ .catch(() => {
182
+ // Final safety catch to prevent unhandled rejections
183
+ });
184
+ }
185
+ else {
186
+ safeFormatAndPrintSummary(payload, null, format);
187
+ }
188
+ }
189
+ //# sourceMappingURL=summary-printer.js.map
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getEnvironment = getEnvironment;
4
+ exports.getRegion = getRegion;
5
+ exports.getCredentialAlias = getCredentialAlias;
6
+ exports.getTraceType = getTraceType;
7
+ exports.getTraceName = getTraceName;
8
+ exports.detectOperationSubtype = detectOperationSubtype;
9
+ exports.getParentTransactionId = getParentTransactionId;
10
+ exports.getTransactionName = getTransactionName;
11
+ exports.getRetryNumber = getRetryNumber;
12
+ const config_1 = require("../config");
13
+ const logger = (0, config_1.getLogger)();
14
+ let cachedRegion = null;
15
+ let regionCached = false;
16
+ function getEnvironment() {
17
+ const env = process.env.REVENIUM_ENVIRONMENT ||
18
+ process.env.NODE_ENV ||
19
+ process.env.DEPLOYMENT_ENV ||
20
+ null;
21
+ if (env && env.length > 255) {
22
+ logger.warn(`environment exceeds max length of 255 characters. Truncating.`);
23
+ return env.substring(0, 255).trim();
24
+ }
25
+ return env ? env.trim() : null;
26
+ }
27
+ async function getRegion() {
28
+ if (regionCached) {
29
+ return cachedRegion;
30
+ }
31
+ const envRegion = process.env.AWS_REGION ||
32
+ process.env.AZURE_REGION ||
33
+ process.env.GCP_REGION ||
34
+ process.env.REVENIUM_REGION;
35
+ if (envRegion) {
36
+ cachedRegion = envRegion.trim();
37
+ regionCached = true;
38
+ return cachedRegion;
39
+ }
40
+ try {
41
+ const controller = new AbortController();
42
+ const timeoutId = setTimeout(() => controller.abort(), 1000);
43
+ const response = await fetch("http://169.254.169.254/latest/meta-data/placement/region", {
44
+ signal: controller.signal,
45
+ });
46
+ clearTimeout(timeoutId);
47
+ if (!response.ok) {
48
+ cachedRegion = null;
49
+ regionCached = true;
50
+ return null;
51
+ }
52
+ const text = await response.text();
53
+ cachedRegion = text.trim();
54
+ regionCached = true;
55
+ return cachedRegion;
56
+ }
57
+ catch (error) {
58
+ cachedRegion = null;
59
+ regionCached = true;
60
+ return null;
61
+ }
62
+ }
63
+ function getCredentialAlias() {
64
+ const alias = process.env.REVENIUM_CREDENTIAL_ALIAS || null;
65
+ if (alias && alias.length > 255) {
66
+ logger.warn(`credentialAlias exceeds max length of 255 characters. Truncating.`);
67
+ return alias.substring(0, 255).trim();
68
+ }
69
+ return alias ? alias.trim() : null;
70
+ }
71
+ function getTraceType() {
72
+ const traceType = process.env.REVENIUM_TRACE_TYPE;
73
+ if (!traceType) {
74
+ return null;
75
+ }
76
+ if (!/^[a-zA-Z0-9_-]+$/.test(traceType)) {
77
+ logger.warn(`Invalid trace_type format: ${traceType}. Must be alphanumeric with hyphens/underscores only.`);
78
+ return null;
79
+ }
80
+ if (traceType.length > 128) {
81
+ logger.warn(`trace_type exceeds max length of 128 characters: ${traceType}. Truncating.`);
82
+ return traceType.substring(0, 128);
83
+ }
84
+ return traceType;
85
+ }
86
+ function getTraceName() {
87
+ const traceName = process.env.REVENIUM_TRACE_NAME;
88
+ if (!traceName) {
89
+ return null;
90
+ }
91
+ if (traceName.length > 256) {
92
+ logger.warn(`trace_name exceeds max length of 256 characters. Truncating.`);
93
+ return traceName.substring(0, 256);
94
+ }
95
+ return traceName;
96
+ }
97
+ function detectOperationSubtype(requestBody) {
98
+ if (requestBody && requestBody.tools && requestBody.tools.length > 0) {
99
+ return "function_call";
100
+ }
101
+ return null;
102
+ }
103
+ function getParentTransactionId() {
104
+ return process.env.REVENIUM_PARENT_TRANSACTION_ID || null;
105
+ }
106
+ function getTransactionName() {
107
+ return process.env.REVENIUM_TRANSACTION_NAME || null;
108
+ }
109
+ function getRetryNumber() {
110
+ const retryNum = process.env.REVENIUM_RETRY_NUMBER;
111
+ if (retryNum) {
112
+ const parsed = parseInt(retryNum, 10);
113
+ return isNaN(parsed) ? 0 : parsed;
114
+ }
115
+ return 0;
116
+ }
117
+ //# sourceMappingURL=trace-fields.js.map