import { TelemetryConfig } from './telemetry.js';
import { appTracer } from './appTracer.js';
import { extractImageUrls } from './probeChat.js';
import { readFileSync, unlinkSync, existsSync } from 'fs';
import { createHash } from 'crypto';
console.log('๐ Testing Trace Hierarchy\n');
const testTraceFile = './hierarchy-traces.jsonl';
if (existsSync(testTraceFile)) {
unlinkSync(testTraceFile);
}
const telemetryConfig = new TelemetryConfig({
serviceName: 'probe-chat',
serviceVersion: '1.0.0',
enableFile: true,
enableConsole: false,
enableRemote: false,
filePath: testTraceFile,
});
telemetryConfig.initialize();
console.log('โ
Telemetry initialized');
async function testTraceHierarchy() {
const sessionId = 'test-session-123';
const messageId = 'test-msg-456';
const message = 'Here is an image: https://github.com/user-attachments/assets/example.png';
const expectedTraceId = createHash('sha256').update(sessionId).digest('hex').substring(0, 32);
console.log(`๐ Session ID: ${sessionId}`);
console.log(`๐ Expected Trace ID: ${expectedTraceId}`);
console.log('๐ Creating hierarchical test traces...');
const sessionSpan = appTracer.startChatSession(sessionId, message, 'anthropic', 'claude-3-7-sonnet-20250219');
await appTracer.withSessionContext(sessionId, async () => {
const userProcessingSpan = appTracer.startUserMessageProcessing(sessionId, messageId, message, 1);
const result = appTracer.withUserProcessingContext(sessionId, () => {
return extractImageUrls(message, true);
});
console.log('โ
extractImageUrls result:', result);
appTracer.withUserProcessingContext(sessionId, () => {
appTracer.startAgentLoop(sessionId, 5);
appTracer.withAgentLoopContext(sessionId, () => {
appTracer.startAgentIteration(sessionId, 1, 3, 1000);
appTracer.withIterationContext(sessionId, 1, () => {
const aiRequestSpan = appTracer.startAiGenerationRequest(sessionId, 1, 'claude-3-7-sonnet-20250219', 'anthropic', {
temperature: 0.3,
maxTokens: 1000,
maxRetries: 2
});
appTracer.endAiRequest(sessionId, 1, true);
});
appTracer.endIteration(sessionId, 1, true, 'completion');
});
appTracer.endAgentLoop(sessionId, 1, true, 'completion');
});
appTracer.endUserMessageProcessing(sessionId, true);
});
appTracer.endChatSession(sessionId, true, 1500);
console.log('โ
All spans created and ended');
}
try {
await testTraceHierarchy();
await telemetryConfig.sdk.getTracerProvider().forceFlush();
await new Promise(resolve => setTimeout(resolve, 1000));
console.log('\n๐ Verifying trace hierarchy...');
if (existsSync(testTraceFile)) {
const traceData = readFileSync(testTraceFile, 'utf8');
const traceLines = traceData.split('\n').filter(Boolean);
console.log(`โ
Found ${traceLines.length} trace entries`);
if (traceLines.length > 0) {
const traces = traceLines.map(line => JSON.parse(line));
const traceGroups = {};
traces.forEach(trace => {
if (!traceGroups[trace.traceId]) {
traceGroups[trace.traceId] = [];
}
traceGroups[trace.traceId].push(trace);
});
console.log(`\n๐ Trace Analysis:`);
console.log(` โ Total spans: ${traces.length}`);
console.log(` โ Unique trace IDs: ${Object.keys(traceGroups).length}`);
if (Object.keys(traceGroups).length === 1) {
console.log(` โ
SUCCESS: All spans belong to the same trace ID!`);
const traceId = Object.keys(traceGroups)[0];
const spans = traceGroups[traceId];
console.log(`\n๐ Trace ID: ${traceId}`);
console.log(`๐ Spans in hierarchy:`);
spans.sort((a, b) => a.startTimeUnixNano - b.startTimeUnixNano);
spans.forEach((span, index) => {
const hasParent = span.parentSpanId ? 'โโ' : 'โโ';
console.log(` ${hasParent} [${index + 1}] ${span.name}`);
console.log(` Span ID: ${span.spanId}`);
if (span.parentSpanId) {
console.log(` Parent: ${span.parentSpanId}`);
}
console.log(` Status: ${span.status?.code === 1 ? 'OK' : span.status?.code === 2 ? 'ERROR' : 'UNKNOWN'}`);
});
let parentChildRelationships = 0;
spans.forEach(span => {
if (span.parentSpanId) {
const parentExists = spans.some(s => s.spanId === span.parentSpanId);
if (parentExists) {
parentChildRelationships++;
} else {
console.log(` โ ๏ธ Span ${span.name} has invalid parent reference`);
}
}
});
console.log(`\nโ
Parent-child relationships verified: ${parentChildRelationships}/${spans.length - 1} spans have valid parents`);
} else {
console.log(` โ FAILURE: Spans are spread across ${Object.keys(traceGroups).length} different trace IDs`);
Object.entries(traceGroups).forEach(([traceId, spans]) => {
console.log(` Trace ${traceId}: ${spans.length} spans`);
spans.forEach(span => {
console.log(` - ${span.name}`);
});
});
}
}
console.log('\n๐ Hierarchy test completed!');
} else {
console.log('โ Trace file was not created');
}
} catch (error) {
console.error('โ Test failed:', error);
} finally {
await telemetryConfig.shutdown();
console.log('\n๐งน Telemetry shutdown complete');
if (existsSync(testTraceFile)) {
console.log(`๐ Trace file preserved: ${testTraceFile}`);
}
}
console.log('\nโ
Trace hierarchy test completed!');