@smythos/sre 1.7.41 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG +136 -64
- package/dist/index.js +65 -50
- package/dist/index.js.map +1 -1
- package/dist/types/Components/Async.class.d.ts +11 -5
- package/dist/types/index.d.ts +2 -0
- package/dist/types/subsystems/AgentManager/AgentData.service/connectors/SQLiteAgentDataConnector.class.d.ts +45 -0
- package/dist/types/subsystems/LLMManager/LLM.helper.d.ts +32 -1
- package/dist/types/subsystems/LLMManager/LLM.inference.d.ts +25 -2
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/Anthropic.class.d.ts +22 -2
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/Bedrock.class.d.ts +2 -2
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.d.ts +27 -2
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/Groq.class.d.ts +22 -2
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/Ollama.class.d.ts +22 -2
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.d.ts +3 -3
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.d.ts +23 -3
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ChatCompletionsApiInterface.d.ts +2 -2
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/OpenAIApiInterface.d.ts +2 -2
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.d.ts +2 -2
- package/dist/types/subsystems/LLMManager/LLM.service/connectors/xAI.class.d.ts +3 -3
- package/dist/types/subsystems/MemoryManager/LLMContext.d.ts +10 -3
- package/dist/types/subsystems/ObservabilityManager/Telemetry.service/connectors/OTel/OTel.class.d.ts +24 -0
- package/dist/types/subsystems/ObservabilityManager/Telemetry.service/connectors/OTel/OTel.redaction.helper.d.ts +49 -0
- package/dist/types/types/LLM.types.d.ts +30 -1
- package/package.json +4 -3
- package/src/Components/APICall/OAuth.helper.ts +16 -1
- package/src/Components/APIEndpoint.class.ts +11 -4
- package/src/Components/Async.class.ts +38 -5
- package/src/Components/GenAILLM.class.ts +13 -7
- package/src/Components/LLMAssistant.class.ts +3 -1
- package/src/Components/LogicAND.class.ts +13 -0
- package/src/Components/LogicAtLeast.class.ts +18 -0
- package/src/Components/LogicAtMost.class.ts +19 -0
- package/src/Components/LogicOR.class.ts +12 -2
- package/src/Components/LogicXOR.class.ts +11 -0
- package/src/constants.ts +1 -1
- package/src/helpers/Conversation.helper.ts +10 -8
- package/src/index.ts +2 -0
- package/src/index.ts.bak +2 -0
- package/src/subsystems/AgentManager/AgentData.service/connectors/SQLiteAgentDataConnector.class.ts +190 -0
- package/src/subsystems/AgentManager/AgentData.service/index.ts +2 -0
- package/src/subsystems/LLMManager/LLM.helper.ts +117 -1
- package/src/subsystems/LLMManager/LLM.inference.ts +136 -67
- package/src/subsystems/LLMManager/LLM.service/LLMConnector.ts +13 -6
- package/src/subsystems/LLMManager/LLM.service/connectors/Anthropic.class.ts +157 -33
- package/src/subsystems/LLMManager/LLM.service/connectors/Bedrock.class.ts +9 -8
- package/src/subsystems/LLMManager/LLM.service/connectors/GoogleAI.class.ts +121 -83
- package/src/subsystems/LLMManager/LLM.service/connectors/Groq.class.ts +125 -62
- package/src/subsystems/LLMManager/LLM.service/connectors/Ollama.class.ts +168 -76
- package/src/subsystems/LLMManager/LLM.service/connectors/Perplexity.class.ts +18 -8
- package/src/subsystems/LLMManager/LLM.service/connectors/VertexAI.class.ts +8 -4
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/OpenAIConnector.class.ts +50 -8
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ChatCompletionsApiInterface.ts +30 -16
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/OpenAIApiInterface.ts +2 -2
- package/src/subsystems/LLMManager/LLM.service/connectors/openai/apiInterfaces/ResponsesApiInterface.ts +29 -15
- package/src/subsystems/LLMManager/LLM.service/connectors/xAI.class.ts +10 -8
- package/src/subsystems/MemoryManager/LLMContext.ts +27 -8
- package/src/subsystems/ObservabilityManager/Telemetry.service/connectors/OTel/OTel.class.ts +467 -120
- package/src/subsystems/ObservabilityManager/Telemetry.service/connectors/OTel/OTel.redaction.helper.ts +203 -0
- package/src/types/LLM.types.ts +31 -1
- package/src/types/node-sqlite.d.ts +45 -0
|
@@ -4,6 +4,7 @@ import { ACL } from '@sre/Security/AccessControl/ACL.class';
|
|
|
4
4
|
import { IAccessCandidate } from '@sre/types/ACL.types';
|
|
5
5
|
import { TelemetryConnector } from '../../TelemetryConnector';
|
|
6
6
|
import { AgentCallLog } from '@sre/types/AgentLogger.types';
|
|
7
|
+
import { redactSensitiveString, redactData, redactHeaders } from './OTel.redaction.helper';
|
|
7
8
|
|
|
8
9
|
import { trace, context, SpanStatusCode, Tracer, propagation } from '@opentelemetry/api';
|
|
9
10
|
import { Logger as OTelLogger, logs, SeverityNumber } from '@opentelemetry/api-logs';
|
|
@@ -50,6 +51,14 @@ export type OTelLogConfig = {
|
|
|
50
51
|
* These will be replaced with '[REDACTED]' in logs
|
|
51
52
|
*/
|
|
52
53
|
redactFields?: string[];
|
|
54
|
+
/**
|
|
55
|
+
* Enable automatic redaction of sensitive data in logs and traces.
|
|
56
|
+
* When true (or omitted), sensitive data such as passwords, tokens,
|
|
57
|
+
* API keys, and JWT tokens are automatically replaced with '[REDACTED]'.
|
|
58
|
+
* Set to false to disable all automatic redaction.
|
|
59
|
+
* Default: true
|
|
60
|
+
*/
|
|
61
|
+
enableRedaction?: boolean;
|
|
53
62
|
};
|
|
54
63
|
const OTEL_DEBUG_LOGS = true;
|
|
55
64
|
export class OTel extends TelemetryConnector {
|
|
@@ -62,6 +71,9 @@ export class OTel extends TelemetryConnector {
|
|
|
62
71
|
|
|
63
72
|
constructor(protected _settings: OTelLogConfig) {
|
|
64
73
|
super();
|
|
74
|
+
// Default enableRedaction to true when not explicitly provided
|
|
75
|
+
_settings.enableRedaction = _settings.enableRedaction ?? true;
|
|
76
|
+
|
|
65
77
|
if (!_settings.endpoint) {
|
|
66
78
|
outputLogger.warn('OTel initialization skipped, endpoint is not set');
|
|
67
79
|
return;
|
|
@@ -141,6 +153,7 @@ export class OTel extends TelemetryConnector {
|
|
|
141
153
|
* Redact sensitive fields from an object
|
|
142
154
|
*/
|
|
143
155
|
private redactSensitiveData(data: any, redactFields?: string[]): any {
|
|
156
|
+
if (!this._settings.enableRedaction) return data;
|
|
144
157
|
if (!redactFields || redactFields.length === 0) return data;
|
|
145
158
|
if (typeof data !== 'object' || data === null) return data;
|
|
146
159
|
|
|
@@ -157,6 +170,35 @@ export class OTel extends TelemetryConnector {
|
|
|
157
170
|
return redacted;
|
|
158
171
|
}
|
|
159
172
|
|
|
173
|
+
/**
|
|
174
|
+
* Redact sensitive patterns from a string value.
|
|
175
|
+
* Skips redaction when enableRedaction is explicitly set to false.
|
|
176
|
+
*/
|
|
177
|
+
private redactString(value: string): string {
|
|
178
|
+
if (!this._settings.enableRedaction) return value;
|
|
179
|
+
return redactSensitiveString(value);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Redact sensitive data from any data type (objects, arrays, strings).
|
|
184
|
+
* Skips redaction when enableRedaction is explicitly set to false.
|
|
185
|
+
*/
|
|
186
|
+
private redactObject<T>(data: T): T {
|
|
187
|
+
if (!this._settings.enableRedaction) return data;
|
|
188
|
+
return redactData(data);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Redact sensitive HTTP headers.
|
|
193
|
+
* Skips redaction when enableRedaction is explicitly set to false.
|
|
194
|
+
*/
|
|
195
|
+
private redactRequestHeaders(
|
|
196
|
+
headers: Record<string, unknown> | string | undefined | null,
|
|
197
|
+
): Record<string, unknown> | string | undefined | null {
|
|
198
|
+
if (!this._settings.enableRedaction) return headers;
|
|
199
|
+
return redactHeaders(headers);
|
|
200
|
+
}
|
|
201
|
+
|
|
160
202
|
/**
|
|
161
203
|
* Safely format output for logging with size limits and redaction
|
|
162
204
|
*/
|
|
@@ -170,11 +212,17 @@ export class OTel extends TelemetryConnector {
|
|
|
170
212
|
return undefined;
|
|
171
213
|
}
|
|
172
214
|
|
|
173
|
-
// Redact sensitive fields
|
|
174
|
-
|
|
215
|
+
// Redact sensitive fields (config-based)
|
|
216
|
+
let redacted = this.redactSensitiveData(output, config.redactFields);
|
|
217
|
+
|
|
218
|
+
// Apply SENSITIVE_WORDS-based redaction on the object (automatic key-based redaction)
|
|
219
|
+
redacted = this.redactObject(redacted);
|
|
175
220
|
|
|
176
221
|
// Stringify
|
|
177
|
-
|
|
222
|
+
let outputStr = JSON.stringify(redacted);
|
|
223
|
+
|
|
224
|
+
// Apply string-based redaction on the stringified output to catch embedded JSON
|
|
225
|
+
outputStr = this.redactString(outputStr);
|
|
178
226
|
|
|
179
227
|
// Check size limit
|
|
180
228
|
if (outputStr && outputStr.length > maxSize) {
|
|
@@ -200,12 +248,38 @@ export class OTel extends TelemetryConnector {
|
|
|
200
248
|
for (let key in data) {
|
|
201
249
|
result[prefix ? `${prefix}.${key}` : key] = (typeof data[key] === 'object' ? JSON.stringify(data[key]) : data[key].toString()).substring(
|
|
202
250
|
0,
|
|
203
|
-
maxEntryLength
|
|
251
|
+
maxEntryLength,
|
|
204
252
|
);
|
|
205
253
|
}
|
|
206
254
|
|
|
207
255
|
return result;
|
|
208
256
|
}
|
|
257
|
+
|
|
258
|
+
private prepareContext(contextWindow: Array<{ role: string; content: string; [key: string]: unknown }>): string {
|
|
259
|
+
if (!contextWindow || !Array.isArray(contextWindow)) return '[]';
|
|
260
|
+
|
|
261
|
+
const filtered = contextWindow.filter((msg) => {
|
|
262
|
+
if (typeof msg !== 'object' || msg === null) return false;
|
|
263
|
+
const keys = Object.keys(msg);
|
|
264
|
+
return !keys.some((k) => k.includes('___smyth_metadata___'));
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
const lastAssistant = [...filtered].reverse().find((msg) => msg.role === 'assistant' && msg.content);
|
|
268
|
+
const lastUser = [...filtered].reverse().find((msg) => msg.role === 'user' && msg.content);
|
|
269
|
+
|
|
270
|
+
const messages: Array<{ role: string; content: string }> = [];
|
|
271
|
+
if (lastAssistant) {
|
|
272
|
+
const raw = typeof lastAssistant.content === 'string' ? lastAssistant.content : JSON.stringify(lastAssistant.content);
|
|
273
|
+
messages.push({ role: 'assistant', content: raw.substring(0, 2000) });
|
|
274
|
+
}
|
|
275
|
+
if (lastUser) {
|
|
276
|
+
const raw = typeof lastUser.content === 'string' ? lastUser.content : JSON.stringify(lastUser.content);
|
|
277
|
+
messages.push({ role: 'user', content: raw.substring(0, 2000) });
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
return JSON.stringify(messages);
|
|
281
|
+
}
|
|
282
|
+
|
|
209
283
|
protected setupHooks(): Promise<void> {
|
|
210
284
|
const tracer = this.tracer;
|
|
211
285
|
const logger = this.logger;
|
|
@@ -219,15 +293,18 @@ export class OTel extends TelemetryConnector {
|
|
|
219
293
|
|
|
220
294
|
const modelId = toolInfo.model;
|
|
221
295
|
const contextWindow = toolInfo.contextWindow;
|
|
222
|
-
const lastContext = contextWindow.filter((context) => context.role === 'user').slice(-2);
|
|
223
296
|
|
|
224
|
-
const toolNames = toolInfo.map((tool) =>
|
|
297
|
+
const toolNames = toolInfo.map((tool) => {
|
|
298
|
+
const args = typeof tool.arguments === 'string' ? tool.arguments : JSON.stringify(tool.arguments);
|
|
299
|
+
return `${tool.name}(${args})`;
|
|
300
|
+
});
|
|
225
301
|
hookContext.curLLMGenSpan.addEvent('llm.gen.tool.calls', {
|
|
226
|
-
'tool.calls': toolNames.join(', '),
|
|
227
|
-
'llm.model': modelId || '
|
|
228
|
-
'context.preview':
|
|
302
|
+
'tool.calls': oTelInstance.redactString(toolNames.join(', ')),
|
|
303
|
+
'llm.model': modelId || '',
|
|
304
|
+
'context.preview': oTelInstance.redactString(oTelInstance.prepareContext(contextWindow).substring(0, 200)),
|
|
229
305
|
});
|
|
230
306
|
|
|
307
|
+
const llmSpanCtx = hookContext.curLLMGenSpan.spanContext();
|
|
231
308
|
const spanContext = trace.setSpan(context.active(), hookContext.curLLMGenSpan);
|
|
232
309
|
context.with(spanContext, () => {
|
|
233
310
|
logger.emit({
|
|
@@ -235,10 +312,15 @@ export class OTel extends TelemetryConnector {
|
|
|
235
312
|
severityText: 'INFO',
|
|
236
313
|
body: `LLM tool calls: ${toolNames.join(', ')}`,
|
|
237
314
|
attributes: {
|
|
315
|
+
// Explicit trace correlation (some backends need these)
|
|
316
|
+
trace_id: llmSpanCtx.traceId,
|
|
317
|
+
span_id: llmSpanCtx.spanId,
|
|
318
|
+
trace_flags: llmSpanCtx.traceFlags,
|
|
319
|
+
|
|
238
320
|
'agent.id': hookContext.agentId,
|
|
239
321
|
'conv.id': hookContext.processId,
|
|
240
|
-
'llm.model': modelId || '
|
|
241
|
-
'context.preview':
|
|
322
|
+
'llm.model': modelId || '',
|
|
323
|
+
'context.preview': oTelInstance.redactString(oTelInstance.prepareContext(contextWindow)),
|
|
242
324
|
},
|
|
243
325
|
});
|
|
244
326
|
});
|
|
@@ -259,7 +341,6 @@ export class OTel extends TelemetryConnector {
|
|
|
259
341
|
const modelId = reqInfo.model;
|
|
260
342
|
const contextWindow = reqInfo.contextWindow;
|
|
261
343
|
|
|
262
|
-
const lastContext = contextWindow.filter((context) => context.role === 'user').slice(-2);
|
|
263
344
|
// End TTFB span when first data arrives
|
|
264
345
|
if (hookContext?.latencySpans?.[reqInfo.requestId]) {
|
|
265
346
|
const ttfbSpan = hookContext.latencySpans[reqInfo.requestId];
|
|
@@ -268,7 +349,7 @@ export class OTel extends TelemetryConnector {
|
|
|
268
349
|
ttfbSpan.addEvent('llm.first.byte.received', {
|
|
269
350
|
'request.id': reqInfo.requestId,
|
|
270
351
|
'data.size': JSON.stringify(data || {}).length,
|
|
271
|
-
'llm.model': modelId || '
|
|
352
|
+
'llm.model': modelId || '',
|
|
272
353
|
});
|
|
273
354
|
|
|
274
355
|
ttfbSpan.setStatus({ code: SpanStatusCode.OK });
|
|
@@ -284,22 +365,98 @@ export class OTel extends TelemetryConnector {
|
|
|
284
365
|
'agent.id': hookContext.agentId,
|
|
285
366
|
'conv.id': hookContext.processId,
|
|
286
367
|
'team.id': hookContext.teamId,
|
|
287
|
-
'llm.model': modelId || '
|
|
368
|
+
'llm.model': modelId || '',
|
|
288
369
|
},
|
|
289
370
|
},
|
|
290
|
-
trace.setSpan(context.active(), hookContext.convSpan)
|
|
371
|
+
trace.setSpan(context.active(), hookContext.convSpan),
|
|
291
372
|
);
|
|
292
373
|
llmGenSpan.addEvent('llm.gen.started', {
|
|
293
374
|
'request.id': reqInfo.requestId,
|
|
294
375
|
timestamp: Date.now(),
|
|
295
|
-
'llm.model': modelId || '
|
|
296
|
-
'context.preview':
|
|
376
|
+
'llm.model': modelId || '',
|
|
377
|
+
'context.preview': oTelInstance.redactString(oTelInstance.prepareContext(contextWindow).substring(0, 200)),
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
const llmGenSpanCtx = llmGenSpan.spanContext();
|
|
381
|
+
const llmGenSpanContext = trace.setSpan(context.active(), llmGenSpan);
|
|
382
|
+
context.with(llmGenSpanContext, () => {
|
|
383
|
+
logger.emit({
|
|
384
|
+
severityNumber: SeverityNumber.INFO,
|
|
385
|
+
severityText: 'INFO',
|
|
386
|
+
body: `LLM generation started: ${hookContext.processId}`,
|
|
387
|
+
attributes: {
|
|
388
|
+
// Explicit trace correlation (some backends need these)
|
|
389
|
+
trace_id: llmGenSpanCtx.traceId,
|
|
390
|
+
span_id: llmGenSpanCtx.spanId,
|
|
391
|
+
trace_flags: llmGenSpanCtx.traceFlags,
|
|
392
|
+
|
|
393
|
+
'agent.id': hookContext.agentId,
|
|
394
|
+
'conv.id': hookContext.processId,
|
|
395
|
+
'team.id': hookContext.teamId,
|
|
396
|
+
'llm.model': modelId || '',
|
|
397
|
+
'request.id': reqInfo.requestId,
|
|
398
|
+
'context.preview': oTelInstance.redactString(oTelInstance.prepareContext(contextWindow)),
|
|
399
|
+
},
|
|
400
|
+
});
|
|
297
401
|
});
|
|
402
|
+
|
|
298
403
|
hookContext.curLLMGenSpan = llmGenSpan;
|
|
299
404
|
if (OTEL_DEBUG_LOGS) outputLogger.debug('createDataHandler completed', reqInfo?.requestId, accessCandidate);
|
|
300
405
|
};
|
|
301
406
|
};
|
|
302
407
|
|
|
408
|
+
const createErrorHandler = function (hookContext: any) {
|
|
409
|
+
return function (error: Error, metadata?: { requestId?: string }) {
|
|
410
|
+
if (!hookContext.convSpan) return;
|
|
411
|
+
const accessCandidate = AccessCandidate.agent(hookContext?.agentId);
|
|
412
|
+
if (OTEL_DEBUG_LOGS)
|
|
413
|
+
outputLogger.debug('Error event received', { error: error?.message, requestId: metadata?.requestId }, accessCandidate);
|
|
414
|
+
|
|
415
|
+
// Mark that an error occurred so after hook knows not to log success
|
|
416
|
+
hookContext.hasError = true;
|
|
417
|
+
hookContext.errorDetails = error;
|
|
418
|
+
|
|
419
|
+
const convSpan = hookContext.convSpan;
|
|
420
|
+
const spanCtx = convSpan.spanContext();
|
|
421
|
+
const spanContext = trace.setSpan(context.active(), convSpan);
|
|
422
|
+
|
|
423
|
+
// Record exception on span
|
|
424
|
+
convSpan.recordException(error);
|
|
425
|
+
convSpan.setStatus({ code: SpanStatusCode.ERROR, message: error?.message || 'Unknown error' });
|
|
426
|
+
convSpan.addEvent('conv.error', {
|
|
427
|
+
'error.message': error?.message || 'Unknown error',
|
|
428
|
+
'request.id': metadata?.requestId || 'unknown',
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
context.with(spanContext, () => {
|
|
432
|
+
logger.emit({
|
|
433
|
+
severityNumber: SeverityNumber.ERROR,
|
|
434
|
+
severityText: 'ERROR',
|
|
435
|
+
body: `Conversation error: ${hookContext.processId}`,
|
|
436
|
+
attributes: {
|
|
437
|
+
// Explicit trace correlation (some backends need these)
|
|
438
|
+
trace_id: spanCtx.traceId,
|
|
439
|
+
span_id: spanCtx.spanId,
|
|
440
|
+
trace_flags: spanCtx.traceFlags,
|
|
441
|
+
|
|
442
|
+
'agent.id': hookContext.agentId,
|
|
443
|
+
'agent.name': hookContext.agentName,
|
|
444
|
+
'conv.id': hookContext.processId,
|
|
445
|
+
'error.message': error?.message || 'Unknown error',
|
|
446
|
+
'error.stack': error?.stack,
|
|
447
|
+
'team.id': hookContext.teamId,
|
|
448
|
+
'org.slot': hookContext.orgSlot,
|
|
449
|
+
'agent.debug': hookContext.isDebugSession,
|
|
450
|
+
'agent.isTest': hookContext.isTestDomain,
|
|
451
|
+
'request.id': metadata?.requestId || 'unknown',
|
|
452
|
+
},
|
|
453
|
+
});
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
if (OTEL_DEBUG_LOGS) outputLogger.debug('Error event handled', { error: error?.message }, accessCandidate);
|
|
457
|
+
};
|
|
458
|
+
};
|
|
459
|
+
|
|
303
460
|
const createRequestedHandler = function (hookContext) {
|
|
304
461
|
return function (reqInfo: any) {
|
|
305
462
|
if (!hookContext.convSpan) return;
|
|
@@ -308,8 +465,6 @@ export class OTel extends TelemetryConnector {
|
|
|
308
465
|
if (!hookContext.latencySpans) hookContext.latencySpans = {};
|
|
309
466
|
const contextWindow = reqInfo.contextWindow;
|
|
310
467
|
|
|
311
|
-
const lastContext = contextWindow.filter((context) => context.role === 'user').slice(-2);
|
|
312
|
-
|
|
313
468
|
const modelId = reqInfo.model;
|
|
314
469
|
const llmGenLatencySpan = tracer.startSpan(
|
|
315
470
|
'Conv.GenAI.TTFB',
|
|
@@ -319,16 +474,16 @@ export class OTel extends TelemetryConnector {
|
|
|
319
474
|
'conv.id': hookContext.processId,
|
|
320
475
|
'team.id': hookContext.teamId,
|
|
321
476
|
'request.id': reqInfo.requestId,
|
|
322
|
-
'llm.model': modelId || '
|
|
477
|
+
'llm.model': modelId || '',
|
|
323
478
|
'metric.type': 'ttfb',
|
|
324
479
|
},
|
|
325
480
|
},
|
|
326
|
-
trace.setSpan(context.active(), hookContext.convSpan)
|
|
481
|
+
trace.setSpan(context.active(), hookContext.convSpan),
|
|
327
482
|
);
|
|
328
483
|
llmGenLatencySpan.addEvent('llm.requested', {
|
|
329
484
|
'request.id': reqInfo.requestId,
|
|
330
485
|
timestamp: Date.now(),
|
|
331
|
-
'context.preview':
|
|
486
|
+
'context.preview': oTelInstance.redactString(oTelInstance.prepareContext(contextWindow).substring(0, 200)),
|
|
332
487
|
});
|
|
333
488
|
hookContext.latencySpans[reqInfo.requestId] = llmGenLatencySpan;
|
|
334
489
|
if (OTEL_DEBUG_LOGS) outputLogger.debug('createRequestedHandler completed', reqInfo?.requestId, accessCandidate);
|
|
@@ -351,6 +506,7 @@ export class OTel extends TelemetryConnector {
|
|
|
351
506
|
const sessionId = processId;
|
|
352
507
|
const workflowId = agentData?.workflowReqId || agentData?.workflowID || agentData?.workflowId || undefined;
|
|
353
508
|
const logTags = agentData?.sessionTag || (isDebugSession ? 'DEBUG' : undefined);
|
|
509
|
+
const agentName = agentData?.name || undefined;
|
|
354
510
|
|
|
355
511
|
if (message == null) {
|
|
356
512
|
//this is a conversation step, will be handled by createRequestedHandler
|
|
@@ -366,16 +522,17 @@ export class OTel extends TelemetryConnector {
|
|
|
366
522
|
attributes: {
|
|
367
523
|
// OTel standard attributes
|
|
368
524
|
'gen_ai.operation.name': 'chat',
|
|
369
|
-
'gen_ai.provider.name': conversation?.llmInference?.llmProviderName || '
|
|
525
|
+
'gen_ai.provider.name': conversation?.llmInference?.llmProviderName || '',
|
|
370
526
|
'gen_ai.conversation.id': processId,
|
|
371
|
-
'gen_ai.request.model': modelId || '
|
|
527
|
+
'gen_ai.request.model': modelId || '',
|
|
372
528
|
////////////////////////////////
|
|
373
529
|
'team.id': teamId,
|
|
374
530
|
'org.tier': orgTier,
|
|
375
531
|
'org.slot': orgSlot,
|
|
376
532
|
'agent.id': agentId,
|
|
533
|
+
'agent.name': agentName,
|
|
377
534
|
'conv.id': processId,
|
|
378
|
-
'llm.model': modelId || '
|
|
535
|
+
'llm.model': modelId || '',
|
|
379
536
|
'agent.debug': isDebugSession,
|
|
380
537
|
'agent.isTest': isTestDomain,
|
|
381
538
|
'session.id': sessionId,
|
|
@@ -384,6 +541,7 @@ export class OTel extends TelemetryConnector {
|
|
|
384
541
|
});
|
|
385
542
|
hookContext.convSpan = convSpan;
|
|
386
543
|
hookContext.agentId = agentId;
|
|
544
|
+
hookContext.agentName = agentData?.name || undefined;
|
|
387
545
|
hookContext.processId = processId;
|
|
388
546
|
hookContext.teamId = teamId;
|
|
389
547
|
hookContext.orgSlot = orgSlot;
|
|
@@ -410,12 +568,15 @@ export class OTel extends TelemetryConnector {
|
|
|
410
568
|
hookContext.toolInfoHandler = createToolInfoHandler(hookContext);
|
|
411
569
|
conversation.on(TLLMEvent.ToolInfo, hookContext.toolInfoHandler);
|
|
412
570
|
|
|
571
|
+
hookContext.errorHandler = createErrorHandler(hookContext);
|
|
572
|
+
conversation.on(TLLMEvent.Error, hookContext.errorHandler);
|
|
573
|
+
|
|
413
574
|
// Add start event
|
|
414
575
|
|
|
415
576
|
convSpan.addEvent('skill.process.started', {
|
|
416
577
|
'input.size': JSON.stringify(message || {}).length,
|
|
417
|
-
'input.preview': message.substring(0, 200),
|
|
418
|
-
'llm.model': modelId || '
|
|
578
|
+
'input.preview': oTelInstance.redactString(message.substring(0, 200)),
|
|
579
|
+
'llm.model': modelId || '',
|
|
419
580
|
});
|
|
420
581
|
|
|
421
582
|
OTelContextRegistry.startProcess(agentId, processId, convSpan);
|
|
@@ -438,9 +599,10 @@ export class OTel extends TelemetryConnector {
|
|
|
438
599
|
'org.slot': orgSlot,
|
|
439
600
|
|
|
440
601
|
'agent.id': agentId,
|
|
602
|
+
'agent.name': agentName,
|
|
441
603
|
'conv.id': processId,
|
|
442
604
|
'input.size': JSON.stringify(message || {}).length,
|
|
443
|
-
'input.preview': message.substring(0,
|
|
605
|
+
'input.preview': oTelInstance.redactString(message.substring(0, 4000)),
|
|
444
606
|
'agent.debug': isDebugSession,
|
|
445
607
|
'agent.isTest': isTestDomain,
|
|
446
608
|
'session.id': sessionId,
|
|
@@ -450,7 +612,7 @@ export class OTel extends TelemetryConnector {
|
|
|
450
612
|
});
|
|
451
613
|
});
|
|
452
614
|
},
|
|
453
|
-
THook.NonBlocking
|
|
615
|
+
THook.NonBlocking,
|
|
454
616
|
);
|
|
455
617
|
|
|
456
618
|
HookService.registerAfter(
|
|
@@ -471,6 +633,7 @@ export class OTel extends TelemetryConnector {
|
|
|
471
633
|
const sessionId = processId;
|
|
472
634
|
const workflowId = agentData?.workflowReqId || agentData?.workflowID || agentData?.workflowId || undefined;
|
|
473
635
|
const logTags = agentData?.sessionTag || (isDebugSession ? 'DEBUG' : undefined);
|
|
636
|
+
const agentName = agentData?.name || undefined;
|
|
474
637
|
|
|
475
638
|
if (message == null) {
|
|
476
639
|
return;
|
|
@@ -482,10 +645,16 @@ export class OTel extends TelemetryConnector {
|
|
|
482
645
|
const accessCandidate = AccessCandidate.agent(agentId);
|
|
483
646
|
if (OTEL_DEBUG_LOGS) outputLogger.debug('Conversation.streamPrompt completed', { processId }, accessCandidate);
|
|
484
647
|
|
|
648
|
+
// Handle curLLMGenSpan with error awareness
|
|
485
649
|
if (hookContext.curLLMGenSpan) {
|
|
650
|
+
if (error) {
|
|
651
|
+
hookContext.curLLMGenSpan.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
|
|
652
|
+
}
|
|
486
653
|
hookContext.curLLMGenSpan.addEvent('llm.gen.content', {
|
|
487
654
|
'content.size': JSON.stringify(result || {}).length,
|
|
488
|
-
'content.preview':
|
|
655
|
+
'content.preview': oTelInstance.redactString(
|
|
656
|
+
typeof result === 'string' ? result.substring(0, 200) : JSON.stringify(result || {}).substring(0, 200),
|
|
657
|
+
),
|
|
489
658
|
});
|
|
490
659
|
hookContext.curLLMGenSpan.end();
|
|
491
660
|
|
|
@@ -494,37 +663,92 @@ export class OTel extends TelemetryConnector {
|
|
|
494
663
|
if (hookContext.requestedHandler) conversation.off(TLLMEvent.Requested, hookContext.requestedHandler);
|
|
495
664
|
}
|
|
496
665
|
|
|
666
|
+
if (hookContext.errorHandler) conversation.off(TLLMEvent.Error, hookContext.errorHandler);
|
|
667
|
+
|
|
497
668
|
const { rootSpan: convSpan } = ctx;
|
|
498
669
|
|
|
499
670
|
const spanCtx = convSpan.spanContext();
|
|
500
671
|
const spanContext = trace.setSpan(context.active(), convSpan);
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
'
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
672
|
+
|
|
673
|
+
// Check for errors - either thrown (error param) or emitted via event (hookContext.hasError)
|
|
674
|
+
const hasError = error || hookContext.hasError;
|
|
675
|
+
|
|
676
|
+
if (hasError) {
|
|
677
|
+
if (error && !hookContext.hasError) {
|
|
678
|
+
convSpan.recordException(error);
|
|
679
|
+
convSpan.setStatus({ code: SpanStatusCode.ERROR, message: error.message || 'Unknown error' });
|
|
680
|
+
convSpan.addEvent('conv.error', {
|
|
681
|
+
'error.message': error.message || 'Unknown error',
|
|
682
|
+
});
|
|
683
|
+
|
|
684
|
+
context.with(spanContext, () => {
|
|
685
|
+
logger.emit({
|
|
686
|
+
severityNumber: SeverityNumber.ERROR,
|
|
687
|
+
severityText: 'ERROR',
|
|
688
|
+
body: `Conversation.streamPrompt failed: ${processId}`,
|
|
689
|
+
attributes: {
|
|
690
|
+
// Explicit trace correlation (some backends need these)
|
|
691
|
+
trace_id: spanCtx.traceId,
|
|
692
|
+
span_id: spanCtx.spanId,
|
|
693
|
+
trace_flags: spanCtx.traceFlags,
|
|
694
|
+
|
|
695
|
+
'agent.id': agentId,
|
|
696
|
+
'agent.name': agentName,
|
|
697
|
+
'conv.id': processId,
|
|
698
|
+
'error.message': error.message || 'Unknown error',
|
|
699
|
+
'error.stack': error.stack,
|
|
700
|
+
'team.id': teamId,
|
|
701
|
+
'org.tier': orgTier,
|
|
702
|
+
'org.slot': orgSlot,
|
|
703
|
+
'agent.debug': isDebugSession,
|
|
704
|
+
'agent.isTest': isTestDomain,
|
|
705
|
+
'session.id': sessionId,
|
|
706
|
+
'workflow.id': workflowId,
|
|
707
|
+
'log.tags': logTags,
|
|
708
|
+
},
|
|
709
|
+
});
|
|
710
|
+
});
|
|
711
|
+
}
|
|
712
|
+
} else {
|
|
713
|
+
// Success handling
|
|
714
|
+
convSpan.setStatus({ code: SpanStatusCode.OK });
|
|
715
|
+
|
|
716
|
+
context.with(spanContext, () => {
|
|
717
|
+
logger.emit({
|
|
718
|
+
severityNumber: SeverityNumber.INFO,
|
|
719
|
+
severityText: 'INFO',
|
|
720
|
+
body: `Conversation.streamPrompt completed: ${processId}`,
|
|
721
|
+
attributes: {
|
|
722
|
+
// Explicit trace correlation (some backends need these)
|
|
723
|
+
trace_id: spanCtx.traceId,
|
|
724
|
+
span_id: spanCtx.spanId,
|
|
725
|
+
trace_flags: spanCtx.traceFlags,
|
|
726
|
+
|
|
727
|
+
'agent.id': agentId,
|
|
728
|
+
'agent.name': agentName,
|
|
729
|
+
'conv.id': processId,
|
|
730
|
+
'output.size': JSON.stringify(result || {}).length,
|
|
731
|
+
'output.preview': oTelInstance.redactString(
|
|
732
|
+
(typeof result === 'string' ? result : JSON.stringify(result || {})).substring(0, 4000),
|
|
733
|
+
),
|
|
734
|
+
'team.id': teamId,
|
|
735
|
+
'org.tier': orgTier,
|
|
736
|
+
'org.slot': orgSlot,
|
|
737
|
+
'agent.debug': isDebugSession,
|
|
738
|
+
'agent.isTest': isTestDomain,
|
|
739
|
+
'session.id': sessionId,
|
|
740
|
+
'workflow.id': workflowId,
|
|
741
|
+
'log.tags': logTags,
|
|
742
|
+
},
|
|
743
|
+
});
|
|
520
744
|
});
|
|
521
|
-
}
|
|
745
|
+
}
|
|
522
746
|
|
|
523
747
|
convSpan.end();
|
|
524
748
|
|
|
525
749
|
OTelContextRegistry.endProcess(agentId, processId);
|
|
526
750
|
},
|
|
527
|
-
THook.NonBlocking
|
|
751
|
+
THook.NonBlocking,
|
|
528
752
|
);
|
|
529
753
|
|
|
530
754
|
HookService.register(
|
|
@@ -551,6 +775,7 @@ export class OTel extends TelemetryConnector {
|
|
|
551
775
|
const logTags = agent.sessionTag || (isDebugSession ? 'DEBUG' : undefined);
|
|
552
776
|
const isTestDomain = agent.usingTestDomain || false;
|
|
553
777
|
const domain = agent.domain || undefined;
|
|
778
|
+
const agentName = agent.name || undefined;
|
|
554
779
|
|
|
555
780
|
const accessCandidate = AccessCandidate.agent(agentId);
|
|
556
781
|
if (OTEL_DEBUG_LOGS) outputLogger.debug('SREAgent.process started', { processId, agentProcessId, endpointPath }, accessCandidate);
|
|
@@ -562,6 +787,11 @@ export class OTel extends TelemetryConnector {
|
|
|
562
787
|
|
|
563
788
|
const input = { body, query, headers, processInput: agentInput };
|
|
564
789
|
|
|
790
|
+
const logBody = oTelInstance.prepareComponentData(agentRequest.body || {}, undefined, 4000);
|
|
791
|
+
const logQuery = oTelInstance.prepareComponentData(agentRequest.query || {}, undefined, 4000);
|
|
792
|
+
const logHeaders = oTelInstance.prepareComponentData(agentRequest.headers || {}, undefined, 4000);
|
|
793
|
+
const logAgentInput = oTelInstance.prepareComponentData(inputData || {}, undefined, 4000);
|
|
794
|
+
|
|
565
795
|
let convSpan;
|
|
566
796
|
let parentContext = context.active();
|
|
567
797
|
|
|
@@ -594,6 +824,7 @@ export class OTel extends TelemetryConnector {
|
|
|
594
824
|
{
|
|
595
825
|
attributes: {
|
|
596
826
|
'agent.id': agentId,
|
|
827
|
+
'agent.name': agentName,
|
|
597
828
|
'team.id': teamId,
|
|
598
829
|
'conv.id': conversationId,
|
|
599
830
|
'process.id': agentProcessId,
|
|
@@ -606,11 +837,11 @@ export class OTel extends TelemetryConnector {
|
|
|
606
837
|
'agent.domain': domain,
|
|
607
838
|
},
|
|
608
839
|
},
|
|
609
|
-
parentContext
|
|
840
|
+
parentContext,
|
|
610
841
|
);
|
|
611
842
|
|
|
612
843
|
// Add start event
|
|
613
|
-
const inputPreview = JSON.stringify(input || {}).substring(0, 200);
|
|
844
|
+
const inputPreview = oTelInstance.redactString(JSON.stringify(input || {}).substring(0, 200));
|
|
614
845
|
agentSpan.addEvent('skill.process.started', {
|
|
615
846
|
endpoint: endpointPath,
|
|
616
847
|
'input.size': JSON.stringify(input || {}).length,
|
|
@@ -632,12 +863,14 @@ export class OTel extends TelemetryConnector {
|
|
|
632
863
|
trace_id: spanCtx.traceId,
|
|
633
864
|
span_id: spanCtx.spanId,
|
|
634
865
|
trace_flags: spanCtx.traceFlags,
|
|
866
|
+
|
|
635
867
|
'agent.id': agentId,
|
|
868
|
+
'agent.name': agentName,
|
|
636
869
|
'process.id': agentProcessId,
|
|
637
|
-
input:
|
|
638
|
-
body,
|
|
639
|
-
query,
|
|
640
|
-
headers,
|
|
870
|
+
input: oTelInstance.redactObject(logAgentInput),
|
|
871
|
+
body: oTelInstance.redactObject(logBody),
|
|
872
|
+
query: oTelInstance.redactObject(logQuery),
|
|
873
|
+
headers: oTelInstance.redactRequestHeaders(logHeaders),
|
|
641
874
|
'team.id': teamId,
|
|
642
875
|
'org.slot': orgSlot,
|
|
643
876
|
'org.tier': orgTier,
|
|
@@ -652,7 +885,7 @@ export class OTel extends TelemetryConnector {
|
|
|
652
885
|
} as any);
|
|
653
886
|
});
|
|
654
887
|
},
|
|
655
|
-
THook.NonBlocking
|
|
888
|
+
THook.NonBlocking,
|
|
656
889
|
);
|
|
657
890
|
|
|
658
891
|
HookService.registerAfter(
|
|
@@ -674,6 +907,7 @@ export class OTel extends TelemetryConnector {
|
|
|
674
907
|
const logTags = agent.sessionTag || (isDebugSession ? 'DEBUG' : undefined);
|
|
675
908
|
const isTestDomain = agent.usingTestDomain || false;
|
|
676
909
|
const domain = agent.domain || undefined;
|
|
910
|
+
const agentName = agent.name || undefined;
|
|
677
911
|
|
|
678
912
|
const ctx = OTelContextRegistry.get(agentId, agentProcessId);
|
|
679
913
|
if (!ctx) return;
|
|
@@ -684,12 +918,32 @@ export class OTel extends TelemetryConnector {
|
|
|
684
918
|
const accessCandidate = AccessCandidate.agent(agentId);
|
|
685
919
|
if (OTEL_DEBUG_LOGS) outputLogger.debug('SREAgent.process completed', { agentProcessId }, accessCandidate);
|
|
686
920
|
|
|
921
|
+
// Check for error indicators in result (process returned error without throwing)
|
|
922
|
+
const hasResultError = !error && (!!result?._error || !!result?.error);
|
|
923
|
+
const resultError = hasResultError ? result._error || result.error : null;
|
|
924
|
+
const resultErrorMessage = resultError?.message || (typeof resultError === 'string' ? resultError : null);
|
|
925
|
+
|
|
926
|
+
// Determine if this is an error case (either thrown error or result error)
|
|
927
|
+
const isError = !!error || hasResultError;
|
|
928
|
+
const errorMessage = error?.message || resultErrorMessage || 'Process returned error';
|
|
929
|
+
|
|
687
930
|
if (error) {
|
|
688
931
|
agentSpan.recordException(error);
|
|
689
932
|
agentSpan.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
|
|
690
933
|
agentSpan.addEvent('skill.process.error', {
|
|
691
934
|
'error.message': error.message,
|
|
692
935
|
});
|
|
936
|
+
} else if (hasResultError) {
|
|
937
|
+
// Handle error in result (no exception thrown)
|
|
938
|
+
agentSpan.setStatus({ code: SpanStatusCode.ERROR, message: errorMessage });
|
|
939
|
+
agentSpan.addEvent('skill.process.error', {
|
|
940
|
+
'error.message': errorMessage,
|
|
941
|
+
'error.type': 'result_error',
|
|
942
|
+
});
|
|
943
|
+
agentSpan.setAttributes({
|
|
944
|
+
'output.size': JSON.stringify(result || {}).length,
|
|
945
|
+
'output.has_error': true,
|
|
946
|
+
});
|
|
693
947
|
} else {
|
|
694
948
|
agentSpan.setStatus({ code: SpanStatusCode.OK });
|
|
695
949
|
agentSpan.addEvent('skill.process.completed', {
|
|
@@ -701,18 +955,21 @@ export class OTel extends TelemetryConnector {
|
|
|
701
955
|
}
|
|
702
956
|
|
|
703
957
|
// Emit log BEFORE ending span to ensure context is active
|
|
704
|
-
const outputForLog = oTelInstance.formatOutputForLog(result,
|
|
958
|
+
const outputForLog = oTelInstance.formatOutputForLog(result, isError);
|
|
705
959
|
const spanCtx = agentSpan.spanContext();
|
|
706
960
|
const logAttributes: Record<string, any> = {
|
|
707
961
|
// Explicit trace correlation (some backends need these)
|
|
708
962
|
trace_id: spanCtx.traceId,
|
|
709
963
|
span_id: spanCtx.spanId,
|
|
710
964
|
trace_flags: spanCtx.traceFlags,
|
|
965
|
+
|
|
711
966
|
'agent.id': agentId,
|
|
967
|
+
'agent.name': agentName,
|
|
712
968
|
'process.id': agentProcessId,
|
|
713
|
-
hasError:
|
|
714
|
-
'error.message':
|
|
969
|
+
hasError: isError,
|
|
970
|
+
'error.message': isError ? errorMessage : undefined,
|
|
715
971
|
'error.stack': error?.stack,
|
|
972
|
+
'error.type': hasResultError ? 'result_error' : undefined,
|
|
716
973
|
'team.id': teamId,
|
|
717
974
|
'org.slot': orgSlot,
|
|
718
975
|
'org.tier': orgTier,
|
|
@@ -734,9 +991,9 @@ export class OTel extends TelemetryConnector {
|
|
|
734
991
|
const spanContext = trace.setSpan(context.active(), agentSpan);
|
|
735
992
|
context.with(spanContext, () => {
|
|
736
993
|
logger.emit({
|
|
737
|
-
severityNumber:
|
|
738
|
-
severityText:
|
|
739
|
-
body: `Agent process ${
|
|
994
|
+
severityNumber: isError ? SeverityNumber.ERROR : SeverityNumber.INFO,
|
|
995
|
+
severityText: isError ? 'ERROR' : 'INFO',
|
|
996
|
+
body: `Agent process ${isError ? 'failed' : 'completed'}: ${agentProcessId}`,
|
|
740
997
|
attributes: logAttributes,
|
|
741
998
|
} as any);
|
|
742
999
|
});
|
|
@@ -746,7 +1003,7 @@ export class OTel extends TelemetryConnector {
|
|
|
746
1003
|
|
|
747
1004
|
OTelContextRegistry.endProcess(agentId, agentProcessId);
|
|
748
1005
|
},
|
|
749
|
-
THook.NonBlocking
|
|
1006
|
+
THook.NonBlocking,
|
|
750
1007
|
);
|
|
751
1008
|
|
|
752
1009
|
// In setupHooks() - Enhanced Component.process hook
|
|
@@ -777,6 +1034,7 @@ export class OTel extends TelemetryConnector {
|
|
|
777
1034
|
const isDebugSession = agent.debugSessionEnabled || agent.agentRuntime?.debug || false;
|
|
778
1035
|
const logTags = agent.sessionTag || (isDebugSession ? 'DEBUG' : undefined);
|
|
779
1036
|
const isTestDomain = agent.usingTestDomain || false;
|
|
1037
|
+
const agentName = agent.name || undefined;
|
|
780
1038
|
|
|
781
1039
|
const inputAction = input?.__action || undefined;
|
|
782
1040
|
const inputStatus = input?.__status || undefined;
|
|
@@ -793,6 +1051,7 @@ export class OTel extends TelemetryConnector {
|
|
|
793
1051
|
{
|
|
794
1052
|
attributes: {
|
|
795
1053
|
'agent.id': agentId,
|
|
1054
|
+
'agent.name': agentName,
|
|
796
1055
|
'process.id': processId,
|
|
797
1056
|
'event.id': eventId,
|
|
798
1057
|
'cmp.id': componentId,
|
|
@@ -811,22 +1070,30 @@ export class OTel extends TelemetryConnector {
|
|
|
811
1070
|
...compSettingsData,
|
|
812
1071
|
},
|
|
813
1072
|
},
|
|
814
|
-
parentSpan ? trace.setSpan(context.active(), parentSpan) : undefined
|
|
1073
|
+
parentSpan ? trace.setSpan(context.active(), parentSpan) : undefined,
|
|
815
1074
|
);
|
|
816
1075
|
|
|
817
1076
|
// Add event: Component started - includes input.action and input.status for workflow tracking
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
const
|
|
1077
|
+
// Use component-specific input (from predecessor nodes), not the merged object with agent variables
|
|
1078
|
+
// For APIEndpoint, use HTTP request body/query as the actual user input
|
|
1079
|
+
const componentInput =
|
|
1080
|
+
componentType === 'APIEndpoint'
|
|
1081
|
+
? agent.agentRequest?.method === 'GET'
|
|
1082
|
+
? agent.agentRequest?.query
|
|
1083
|
+
: agent.agentRequest?.body
|
|
1084
|
+
: componentData?.runtimeData?.input || {};
|
|
1085
|
+
|
|
1086
|
+
const compInputData = oTelInstance.prepareComponentData(componentInput || {});
|
|
821
1087
|
span.addEvent('cmp.call', {
|
|
822
1088
|
'event.id': eventId,
|
|
823
|
-
'cmp.input.size': JSON.stringify(
|
|
824
|
-
'cmp.input': JSON.stringify(compInputData),
|
|
1089
|
+
'cmp.input.size': JSON.stringify(componentInput || {}).length,
|
|
1090
|
+
'cmp.input': oTelInstance.redactString(JSON.stringify(compInputData)),
|
|
825
1091
|
'input.action': inputAction,
|
|
826
1092
|
'input.status': inputStatus,
|
|
827
1093
|
});
|
|
828
1094
|
|
|
829
1095
|
// Emit structured log with full details
|
|
1096
|
+
const cmpSpanCtx = span.spanContext();
|
|
830
1097
|
const spanContext = trace.setSpan(context.active(), span);
|
|
831
1098
|
context.with(spanContext, () => {
|
|
832
1099
|
logger.emit({
|
|
@@ -834,13 +1101,19 @@ export class OTel extends TelemetryConnector {
|
|
|
834
1101
|
severityText: 'INFO',
|
|
835
1102
|
body: `Component ${componentType} started`,
|
|
836
1103
|
attributes: {
|
|
1104
|
+
// Explicit trace correlation (some backends need these)
|
|
1105
|
+
trace_id: cmpSpanCtx.traceId,
|
|
1106
|
+
span_id: cmpSpanCtx.spanId,
|
|
1107
|
+
trace_flags: cmpSpanCtx.traceFlags,
|
|
1108
|
+
|
|
837
1109
|
'agent.id': agentId,
|
|
1110
|
+
'agent.name': agentName,
|
|
838
1111
|
'process.id': processId,
|
|
839
1112
|
'event.id': eventId,
|
|
840
1113
|
'cmp.id': componentId,
|
|
841
1114
|
'cmp.type': componentType,
|
|
842
1115
|
'cmp.name': componentName,
|
|
843
|
-
'cmp.input':
|
|
1116
|
+
'cmp.input': oTelInstance.redactObject(componentInput),
|
|
844
1117
|
'team.id': teamId,
|
|
845
1118
|
'org.slot': orgSlot,
|
|
846
1119
|
'org.tier': orgTier,
|
|
@@ -859,7 +1132,7 @@ export class OTel extends TelemetryConnector {
|
|
|
859
1132
|
// Store span in hook context (isolated per component execution, concurrency-safe)
|
|
860
1133
|
this.context.otelSpan = span;
|
|
861
1134
|
},
|
|
862
|
-
THook.NonBlocking
|
|
1135
|
+
THook.NonBlocking,
|
|
863
1136
|
);
|
|
864
1137
|
|
|
865
1138
|
HookService.registerAfter(
|
|
@@ -894,6 +1167,7 @@ export class OTel extends TelemetryConnector {
|
|
|
894
1167
|
const isDebugSession = agent.debugSessionEnabled || agent.agentRuntime?.debug || false;
|
|
895
1168
|
const logTags = agent.sessionTag || (isDebugSession ? 'DEBUG' : undefined);
|
|
896
1169
|
const isTestDomain = agent.usingTestDomain || false;
|
|
1170
|
+
const agentName = agent.name || undefined;
|
|
897
1171
|
|
|
898
1172
|
const accessCandidate = AccessCandidate.agent(agentId);
|
|
899
1173
|
if (OTEL_DEBUG_LOGS) outputLogger.debug('Component.process completed', { componentId }, accessCandidate);
|
|
@@ -915,6 +1189,7 @@ export class OTel extends TelemetryConnector {
|
|
|
915
1189
|
});
|
|
916
1190
|
|
|
917
1191
|
// Emit error log
|
|
1192
|
+
const cmpErrorSpanCtx = span.spanContext();
|
|
918
1193
|
const spanContext = trace.setSpan(context.active(), span);
|
|
919
1194
|
context.with(spanContext, () => {
|
|
920
1195
|
logger.emit({
|
|
@@ -922,7 +1197,13 @@ export class OTel extends TelemetryConnector {
|
|
|
922
1197
|
severityText: 'ERROR',
|
|
923
1198
|
body: `Component ${componentType} (${componentId}) failed: ${error.message}`,
|
|
924
1199
|
attributes: {
|
|
1200
|
+
// Explicit trace correlation (some backends need these)
|
|
1201
|
+
trace_id: cmpErrorSpanCtx.traceId,
|
|
1202
|
+
span_id: cmpErrorSpanCtx.spanId,
|
|
1203
|
+
trace_flags: cmpErrorSpanCtx.traceFlags,
|
|
1204
|
+
|
|
925
1205
|
'agent.id': agentId,
|
|
1206
|
+
'agent.name': agentName,
|
|
926
1207
|
'process.id': processId,
|
|
927
1208
|
'event.id': eventId,
|
|
928
1209
|
'cmp.id': componentId,
|
|
@@ -946,63 +1227,129 @@ export class OTel extends TelemetryConnector {
|
|
|
946
1227
|
});
|
|
947
1228
|
});
|
|
948
1229
|
} else {
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
// Add success event with output summary
|
|
1230
|
+
// Check if result contains an error indicator (component returned error without throwing)
|
|
1231
|
+
const hasResultError = !!result?._error || !!result?.error;
|
|
952
1232
|
const resultStr = JSON.stringify(result || {});
|
|
953
|
-
span.addEvent('cmp.call.result', {
|
|
954
|
-
'output.size': resultStr.length,
|
|
955
|
-
'output.preview': resultStr.substring(0, 200),
|
|
956
|
-
});
|
|
957
1233
|
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
'
|
|
962
|
-
});
|
|
1234
|
+
if (hasResultError) {
|
|
1235
|
+
// Treat as error even though no exception was thrown
|
|
1236
|
+
const resultError = result._error || result.error;
|
|
1237
|
+
const errorMessage = resultError?.message || (typeof resultError === 'string' ? resultError : 'Component returned error');
|
|
963
1238
|
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
'cmp.output': result,
|
|
974
|
-
'team.id': teamId,
|
|
975
|
-
'org.slot': orgSlot,
|
|
976
|
-
'org.tier': orgTier,
|
|
977
|
-
'source.id': sourceId,
|
|
978
|
-
'source.name': sourceName,
|
|
979
|
-
'session.id': sessionId,
|
|
980
|
-
'workflow.id': workflowId,
|
|
981
|
-
'workflow.step': workflowStep,
|
|
982
|
-
'log.tags': logTags,
|
|
983
|
-
'agent.debug': isDebugSession,
|
|
984
|
-
'agent.isTest': isTestDomain,
|
|
985
|
-
};
|
|
1239
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: errorMessage });
|
|
1240
|
+
span.addEvent('cmp.call.error', {
|
|
1241
|
+
'event.id': eventId,
|
|
1242
|
+
'cmp.id': componentId,
|
|
1243
|
+
'cmp.type': componentType,
|
|
1244
|
+
'cmp.name': componentName,
|
|
1245
|
+
'error.type': 'result_error',
|
|
1246
|
+
'error.message': errorMessage,
|
|
1247
|
+
});
|
|
986
1248
|
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1249
|
+
// Add output attributes to span
|
|
1250
|
+
span.setAttributes({
|
|
1251
|
+
'output.size': resultStr.length,
|
|
1252
|
+
'output.has_error': true,
|
|
1253
|
+
});
|
|
991
1254
|
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
1255
|
+
// Emit ERROR log for result error
|
|
1256
|
+
const cmpResultErrorSpanCtx = span.spanContext();
|
|
1257
|
+
const spanContext = trace.setSpan(context.active(), span);
|
|
1258
|
+
context.with(spanContext, () => {
|
|
1259
|
+
logger.emit({
|
|
1260
|
+
severityNumber: SeverityNumber.ERROR,
|
|
1261
|
+
severityText: 'ERROR',
|
|
1262
|
+
body: `Component ${componentType} (${componentId}) failed: ${errorMessage}`,
|
|
1263
|
+
attributes: {
|
|
1264
|
+
// Explicit trace correlation (some backends need these)
|
|
1265
|
+
trace_id: cmpResultErrorSpanCtx.traceId,
|
|
1266
|
+
span_id: cmpResultErrorSpanCtx.spanId,
|
|
1267
|
+
trace_flags: cmpResultErrorSpanCtx.traceFlags,
|
|
1268
|
+
|
|
1269
|
+
'agent.id': agentId,
|
|
1270
|
+
'agent.name': agentName,
|
|
1271
|
+
'process.id': processId,
|
|
1272
|
+
'event.id': eventId,
|
|
1273
|
+
'cmp.id': componentId,
|
|
1274
|
+
'cmp.name': componentName,
|
|
1275
|
+
'cmp.type': componentType,
|
|
1276
|
+
'error.type': 'result_error',
|
|
1277
|
+
'error.message': errorMessage,
|
|
1278
|
+
'cmp.output': oTelInstance.redactObject(result),
|
|
1279
|
+
'team.id': teamId,
|
|
1280
|
+
'org.slot': orgSlot,
|
|
1281
|
+
'org.tier': orgTier,
|
|
1282
|
+
'source.id': sourceId,
|
|
1283
|
+
'source.name': sourceName,
|
|
1284
|
+
'session.id': sessionId,
|
|
1285
|
+
'workflow.id': workflowId,
|
|
1286
|
+
'workflow.step': workflowStep,
|
|
1287
|
+
'log.tags': logTags,
|
|
1288
|
+
'agent.debug': isDebugSession,
|
|
1289
|
+
'agent.isTest': isTestDomain,
|
|
1290
|
+
},
|
|
1291
|
+
});
|
|
999
1292
|
});
|
|
1000
|
-
}
|
|
1293
|
+
} else {
|
|
1294
|
+
// True success case
|
|
1295
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
1296
|
+
|
|
1297
|
+
// Add success event with output summary
|
|
1298
|
+
span.addEvent('cmp.call.result', {
|
|
1299
|
+
'output.size': resultStr.length,
|
|
1300
|
+
'output.preview': oTelInstance.redactString(resultStr.substring(0, 200)),
|
|
1301
|
+
});
|
|
1302
|
+
|
|
1303
|
+
// Add output attributes to span
|
|
1304
|
+
span.setAttributes({
|
|
1305
|
+
'output.size': resultStr.length,
|
|
1306
|
+
'output.has_error': false,
|
|
1307
|
+
});
|
|
1308
|
+
|
|
1309
|
+
// Emit success log with output (formatted safely)
|
|
1310
|
+
const cmpSuccessSpanCtx = span.spanContext();
|
|
1311
|
+
const logAttributes: Record<string, any> = {
|
|
1312
|
+
// Explicit trace correlation (some backends need these)
|
|
1313
|
+
trace_id: cmpSuccessSpanCtx.traceId,
|
|
1314
|
+
span_id: cmpSuccessSpanCtx.spanId,
|
|
1315
|
+
trace_flags: cmpSuccessSpanCtx.traceFlags,
|
|
1316
|
+
|
|
1317
|
+
'agent.id': agentId,
|
|
1318
|
+
'agent.name': agentName,
|
|
1319
|
+
'cmp.id': componentId,
|
|
1320
|
+
'cmp.type': componentType,
|
|
1321
|
+
'cmp.name': componentName,
|
|
1322
|
+
'process.id': processId,
|
|
1323
|
+
'event.id': eventId,
|
|
1324
|
+
'cmp.output': oTelInstance.redactObject(result),
|
|
1325
|
+
'team.id': teamId,
|
|
1326
|
+
'org.slot': orgSlot,
|
|
1327
|
+
'org.tier': orgTier,
|
|
1328
|
+
'source.id': sourceId,
|
|
1329
|
+
'source.name': sourceName,
|
|
1330
|
+
'session.id': sessionId,
|
|
1331
|
+
'workflow.id': workflowId,
|
|
1332
|
+
'workflow.step': workflowStep,
|
|
1333
|
+
'log.tags': logTags,
|
|
1334
|
+
'agent.debug': isDebugSession,
|
|
1335
|
+
'agent.isTest': isTestDomain,
|
|
1336
|
+
};
|
|
1337
|
+
|
|
1338
|
+
const spanContext = trace.setSpan(context.active(), span);
|
|
1339
|
+
context.with(spanContext, () => {
|
|
1340
|
+
logger.emit({
|
|
1341
|
+
severityNumber: SeverityNumber.INFO,
|
|
1342
|
+
severityText: 'INFO',
|
|
1343
|
+
body: `Component ${componentType} (${componentId}) completed successfully`,
|
|
1344
|
+
attributes: logAttributes,
|
|
1345
|
+
});
|
|
1346
|
+
});
|
|
1347
|
+
}
|
|
1001
1348
|
}
|
|
1002
1349
|
|
|
1003
1350
|
span.end();
|
|
1004
1351
|
},
|
|
1005
|
-
THook.NonBlocking
|
|
1352
|
+
THook.NonBlocking,
|
|
1006
1353
|
);
|
|
1007
1354
|
return Promise.resolve();
|
|
1008
1355
|
}
|