diff --git a/src/observe/reporter.test.ts b/src/observe/reporter.test.ts index dfc52286..f8dda214 100644 --- a/src/observe/reporter.test.ts +++ b/src/observe/reporter.test.ts @@ -105,6 +105,24 @@ describe('observeExporter', () => { expect(allEvents(posted).at(-1)?.type).toBe('agent.deregister'); }); + it('maps an ended span with unset status to ok', async () => { + const { r, posted } = reporter(); + const span = makeSpan({ id: 'ended', endTime: 150 }); + r.onSpanStart(span); + r.onSpanEnd(span); + await r.close(); + expect(observations(posted).find((o) => o.data.obs_id === 'ended')?.data.status).toBe('ok'); + }); + + it('keeps an open span status unset', async () => { + const { r, posted } = reporter(); + const span = makeSpan({ id: 'open', endTime: undefined }); + r.onSpanStart(span); + r.onSpanEnd(span); + await r.close(); + expect(observations(posted).find((o) => o.data.obs_id === 'open')?.data.status).toBeUndefined(); + }); + it('carries usage/cost/flags verbatim from gen_ai + lousho attrs', async () => { const { r, posted } = reporter(); const span = makeSpan({ diff --git a/src/observe/reporter.ts b/src/observe/reporter.ts index 4f665447..3be43b2f 100644 --- a/src/observe/reporter.ts +++ b/src/observe/reporter.ts @@ -154,7 +154,9 @@ const GENERIC_OUTPUT_KEYS = [GenAiAttr.OUTPUT_MESSAGES, LegacyAttr.RESULT] as co function observationOf(span: Span, traceId: string, agentId: string, fallbackSessionId: string, now: () => number): ObservationData { const attrs = span.attributes; const op = str(attrs[GenAiAttr.OPERATION_NAME]) ?? ''; - const status = span.status?.code; + const code = span.status?.code; + // An ended span nobody marked failed succeeded; only a still-open span stays unset. + const status = code === 'error' ? 'error' : span.endTime !== undefined ? 'ok' : undefined; return { obs_id: span.id, trace_id: traceId, @@ -165,7 +167,7 @@ function observationOf(span: Span, traceId: string, agentId: string, fallbackSes name: span.name, start: span.startTime, end: span.endTime ?? now(), - ...(status === 'ok' || status === 'error' ? { status } : {}), + ...(status !== undefined ? { status } : {}), ...(pickAttr(attrs, INPUT_KEYS[op] ?? GENERIC_INPUT_KEYS) !== undefined ? { input: pickAttr(attrs, INPUT_KEYS[op] ?? GENERIC_INPUT_KEYS) } : {}),