Skip to content

Commit e261754

Browse files
committed
fix(agents): Added statusCode propagation for workflow spans.
1 parent 0749f37 commit e261754

File tree

3 files changed

+151
-2
lines changed

3 files changed

+151
-2
lines changed

src/handlers/openai-agents/index.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -470,7 +470,8 @@ export class GalileoTracingProcessor implements TracingProcessor {
470470
durationNs,
471471
metadata,
472472
tags,
473-
createdAt: startedAt
473+
createdAt: startedAt,
474+
statusCode
474475
});
475476
}
476477

@@ -487,7 +488,7 @@ export class GalileoTracingProcessor implements TracingProcessor {
487488
!firstNode &&
488489
(node.nodeType === 'workflow' || node.nodeType === 'agent')
489490
) {
490-
this._galileoLogger.conclude({ output, durationNs });
491+
this._galileoLogger.conclude({ output, durationNs, statusCode });
491492
}
492493
}
493494

src/utils/galileo-logger.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,6 +1115,7 @@ class GalileoLogger implements IGalileoLogger {
11151115
* @param options.createdAt - (Optional) The timestamp when the span was created.
11161116
* @param options.metadata - (Optional) Additional metadata as key-value pairs.
11171117
* @param options.tags - (Optional) Array of tags to categorize the span.
1118+
* @param options.statusCode - (Optional) HTTP status code or execution status (e.g., 200 for success, 500 for error).
11181119
* @param options.stepNumber - (Optional) The step number in a multi-step process.
11191120
* @returns The created workflow span.
11201121
*/
@@ -1128,6 +1129,7 @@ class GalileoLogger implements IGalileoLogger {
11281129
createdAt?: Date;
11291130
metadata?: Record<string, string>;
11301131
tags?: string[];
1132+
statusCode?: number;
11311133
stepNumber?: number;
11321134
}): WorkflowSpan {
11331135
const span = new WorkflowSpan({
@@ -1139,6 +1141,7 @@ class GalileoLogger implements IGalileoLogger {
11391141
createdAt: options.createdAt || GalileoApiClient.getTimestampRecord(),
11401142
metadata: options.metadata,
11411143
tags: options.tags,
1144+
statusCode: options.statusCode,
11421145
metrics: new Metrics({ durationNs: options.durationNs }),
11431146
stepNumber: options.stepNumber
11441147
});

tests/handlers/openai-agents/integration.test.ts

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -562,3 +562,148 @@ describe('Output tracking integration', () => {
562562
).toBe(true);
563563
});
564564
});
565+
566+
describe('Workflow span statusCode propagation', () => {
567+
test('test workflow span statusCode passed to addWorkflowSpan', async () => {
568+
const mockLogger = createMockLogger();
569+
const processor = new GalileoTracingProcessor(mockLogger as never, false);
570+
const trace = makeTrace();
571+
572+
await processor.onTraceStart(trace);
573+
574+
// Create a workflow span (handoff type maps to workflow nodeType)
575+
const workflow = makeSpan({
576+
spanId: 'workflow-001',
577+
parentId: 'trace-001',
578+
spanData: { type: 'handoff', from_agent: 'Agent1', to_agent: 'Agent2' }
579+
});
580+
581+
// Create a successful child LLM span
582+
const llm = makeSpan({
583+
spanId: 'llm-001',
584+
parentId: 'workflow-001',
585+
spanData: {
586+
type: 'generation',
587+
model: 'gpt-4',
588+
input: [],
589+
output: 'successful response'
590+
},
591+
error: null
592+
});
593+
594+
await processor.onSpanStart(workflow);
595+
await processor.onSpanStart(llm);
596+
await processor.onSpanEnd(llm);
597+
await processor.onSpanEnd(workflow);
598+
await processor.onTraceEnd(trace);
599+
600+
// Verify addWorkflowSpan was called (note: statusCode may be 200 by default)
601+
expect(mockLogger.addWorkflowSpan).toHaveBeenCalledTimes(1);
602+
const workflowSpanCall = mockLogger.addWorkflowSpan.mock.calls[0][0];
603+
// Verify statusCode parameter is being passed through (defaults to 200 for success)
604+
expect(workflowSpanCall.statusCode).toBe(200);
605+
});
606+
607+
test('test workflow span with direct error has statusCode 500', async () => {
608+
const mockLogger = createMockLogger();
609+
const processor = new GalileoTracingProcessor(mockLogger as never, false);
610+
const trace = makeTrace();
611+
612+
await processor.onTraceStart(trace);
613+
614+
// Create a workflow span that itself has an error
615+
const workflowWithError = makeSpan({
616+
spanId: 'workflow-001',
617+
parentId: 'trace-001',
618+
spanData: { type: 'handoff', from_agent: 'Agent1', to_agent: 'Agent2' },
619+
error: {
620+
message: 'Workflow execution failed',
621+
data: { reason: 'timeout' }
622+
}
623+
});
624+
625+
await processor.onSpanStart(workflowWithError);
626+
await processor.onSpanEnd(workflowWithError);
627+
await processor.onTraceEnd(trace);
628+
629+
// Verify addWorkflowSpan was called with statusCode 500
630+
expect(mockLogger.addWorkflowSpan).toHaveBeenCalledTimes(1);
631+
const workflowSpanCall = mockLogger.addWorkflowSpan.mock.calls[0][0];
632+
expect(workflowSpanCall.statusCode).toBe(500);
633+
});
634+
635+
test('test agent span statusCode passed to addAgentSpan', async () => {
636+
const mockLogger = createMockLogger();
637+
const processor = new GalileoTracingProcessor(mockLogger as never, false);
638+
const trace = makeTrace();
639+
640+
await processor.onTraceStart(trace);
641+
642+
// Create an agent span
643+
const agent = makeSpan({
644+
spanId: 'agent-001',
645+
parentId: 'trace-001',
646+
spanData: { type: 'agent', name: 'TestAgent' }
647+
});
648+
649+
// Create a child LLM span
650+
const llm = makeSpan({
651+
spanId: 'llm-001',
652+
parentId: 'agent-001',
653+
spanData: {
654+
type: 'generation',
655+
model: 'gpt-4',
656+
input: [],
657+
output: 'test output'
658+
},
659+
error: null
660+
});
661+
662+
await processor.onSpanStart(agent);
663+
await processor.onSpanStart(llm);
664+
await processor.onSpanEnd(llm);
665+
await processor.onSpanEnd(agent);
666+
await processor.onTraceEnd(trace);
667+
668+
// Verify addAgentSpan was called with statusCode parameter
669+
expect(mockLogger.addAgentSpan).toHaveBeenCalledTimes(1);
670+
const agentSpanCall = mockLogger.addAgentSpan.mock.calls[0][0];
671+
expect(agentSpanCall.statusCode).toBe(200);
672+
});
673+
674+
test('test conclude called with statusCode for workflow spans', async () => {
675+
const mockLogger = createMockLogger();
676+
const processor = new GalileoTracingProcessor(mockLogger as never, false);
677+
const trace = makeTrace();
678+
679+
await processor.onTraceStart(trace);
680+
681+
// Create nested workflow spans to test conclude calls
682+
const outerWorkflow = makeSpan({
683+
spanId: 'workflow-001',
684+
parentId: 'trace-001',
685+
spanData: { type: 'handoff', from_agent: 'Agent1', to_agent: 'Agent2' }
686+
});
687+
688+
const innerWorkflow = makeSpan({
689+
spanId: 'workflow-002',
690+
parentId: 'workflow-001',
691+
spanData: { type: 'custom', name: 'InnerWorkflow' }
692+
});
693+
694+
await processor.onSpanStart(outerWorkflow);
695+
await processor.onSpanStart(innerWorkflow);
696+
await processor.onSpanEnd(innerWorkflow);
697+
await processor.onSpanEnd(outerWorkflow);
698+
await processor.onTraceEnd(trace);
699+
700+
// Verify conclude was called for the workflow spans
701+
expect(mockLogger.conclude).toHaveBeenCalled();
702+
// Find calls that pass statusCode
703+
const concludeCalls = mockLogger.conclude.mock.calls;
704+
const callsWithStatusCode = concludeCalls.filter(
705+
(call) => call[0]?.statusCode !== undefined
706+
);
707+
expect(callsWithStatusCode.length).toBeGreaterThan(0);
708+
});
709+
});

0 commit comments

Comments
 (0)