Repository navigation
Conversation
A chat completion with stream=True that fails after the first chunk was exported with status OK. __next__/__anext__ called _ensure_cleanup(), which sets OK and ends the span, and the set_status(ERROR) that followed could never run because an ended span no longer records. The caller still received the exception; the trace said the call succeeded. Cleanup is what ends the span, so let it decide the final status. On error it now sets error.type, records the exception and sets ERROR, the same way the non-streaming paths in this file do. error=None keeps the previous OK for __del__ and __exit__. Adds tests for the sync and the async iterator.
|
Hi — picking this back up. Head is Recap: a chat completion with The fix lets cleanup, which is what ends the span, decide the final status: It has drifted behind |
Problem
A chat completion with
stream=Truethat fails after the first chunk is exported with status OK. InChatStream.__next__/__anext__the cleanup path runs first —_ensure_cleanup()setsStatusCode.OKand callsend()— and theset_status(StatusCode.ERROR, ...)on the next line can never take effect, because an ended span no longer records. The caller still gets the exception out of the iterator, so this is silent: the trace reports a successful call, with noerror.type, no exception event, and no failed span.That is a dropped connection, a 5xx from a gateway, a rate limit or a timeout between chunks — i.e. the error rate and success rate this instrumentation exists to report are wrong in exactly the situation people look at them.
Fix
Cleanup is what ends the span, so let it decide the final status.
_ensure_cleanup(error=None)keeps the previous OK behaviour for the__del__/__exit__callers, and when an error is passed it setserror.type, records the exception and sets ERROR — the same three calls the non-streaming paths in this file already make (chat_wrappers.py:117sync,:218async).Test
The new file drives
ChatStreamdirectly, the waytests/traces/test_semconv_compliance.py::TestPartialStreamCleanupOutputMessagesdoes: a stub stream yields one chunk and then raises, once throughforand once throughasync for, and the test asserts the exported span is ERROR witherror.type == "RuntimeError"and anexceptionevent.One note on the PR template: it asks for a screenshot from an observability platform when an existing instrumentation changes. I cannot attach one, so the evidence above is the exported span itself — the status code,
error.typeand the exception event, read back from the in-memory span exporter. That is the same data a trace view would show, and it is asserted rather than eyeballed. The other template boxes are checked.Related, different path: #4394 ends the span for an early
close()on the Completions stream, where the span was never finished at all. This is the Chat stream, where the span is finished with the wrong status.Summary by CodeRabbit