bug(openai): a stream that fails mid-iteration ends the generation without level ERROR
Maintainer antworten meist innerhalb von 1 Tag
Bewertung
Dieses Issue wurde noch nicht bewertet.
Beschreibung
Describe the bug
When an OpenAI chat stream fails after the first chunk, the generation ends with no level and no status_message. The caller gets openai.APIError, while Langfuse shows a normal generation holding the partial output. The same failure before the stream starts is recorded as ERROR.
The cause is in langfuse/openai.py. _wrap catches only what create() raises. Exceptions raised while iterating go through traced_iterator's finally (line 1112) into _finalize_stream_response, which calls generation.end() with no error. The async path (_instrument_openai_async_stream) and LangfuseResponseGeneratorSync/Async do the same.
To reproduce
import httpx
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import SimpleSpanProcessor
from opentelemetry.sdk.trace.export.in_memory_span_exporter import InMemorySpanExporter
from langfuse import Langfuse
from langfuse.openai import OpenAI
exporter = InMemorySpanExporter()
provider = TracerProvider()
provider.add_span_processor(SimpleSpanProcessor(exporter))
Langfuse(public_key="pk", secret_key="sk", base_url="http://127.0.0.1:9", tracer_provider=provider)
SSE = (
b'data: {"id":"c1","object":"chat.completion.chunk","created":1,"model":"gpt-4o-mini",'
b'"choices":[{"index":0,"delta":{"role":"assistant","content":"Hel"},"finish_reason":null}]}\n\n'
b'data: {"error":{"message":"The server had an error while processing your request.","type":"server_error"}}\n\n'
)
client = OpenAI(api_key="x", http_client=httpx.Client(transport=httpx.MockTransport(
lambda r: httpx.Response(200, headers={"content-type": "text/event-stream"}, content=SSE))))
stream = client.chat.completions.create(name="failed-stream", model="gpt-4o-mini",
messages=[{"role": "user", "content": "hi"}], stream=True)
try:
for chunk in stream:
pass
except Exception as e:
print("caller saw :", type(e).__name__, e)
a = next(s for s in exporter.get_finished_spans() if s.name == "failed-stream").attributes
print("level :", a.get("langfuse.observation.level"))
print("status_msg :", a.get("langfuse.observation.status_message"))
print("output :", a.get("langfuse.observation.output"))
$ python lf_stream_error.py 2>/dev/null
caller saw : APIError The server had an error while processing your request.
level : None
status_msg : None
output : Hel
With the transport answering 500 to create(stream=True) instead, the generation records ERROR | Error code: 500 - {'error': {'message': 'server_error'}}. The AsyncOpenAI version of the failing stream also prints level: None.
Expected
level="ERROR" and status_message=str(error) on the generation, with the partial output and any usage kept. The OpenAI integration docs say Langfuse "automatically tracks and monitors OpenAI API errors" in level and statusMessage, and test_chat_completion_error_marks_generation_error pins that for the non-streaming case.
SDK versions
langfuse 5.0.2 at 81c457c, openai 3.35.1, opentelemetry-sdk 1.45.1, Python 3.12. Not checked: the Responses API, where a response.failed event raises nothing.
@hassiebp I'd catch the exception in each iterator wrapper, set the error on the generation, and re-raise. A test using the SSE body above can live next to the existing break test in tests/unit/test_openai.py. Shall I open that PR?
- Vorherrschende Sprache
- Python
- Sterne
- 502
- Forks
- 381
- Ø Merge
- 1 T. 1 Std.
- Gemergte PRs (30 T.)
- 64
Entwicklungsumgebung
- Kein Dockerfile und keine Docker-Compose-Datei
- Hat eine Pull-Request-Vorlage
- Beitragsleitfaden lesen
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus langfuse/langfuse-python
-
bug(scores): create_score sends numpy.bool_ as JSON `true` and Decimal as "<Decimal>" since v5.0.2Evtl. vergeben @lalith0407 hat das vor 3 Tagen übernommen. Offenbug feat-scores sdk-python
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 74/100
langfuse/langfuse-python#2057 ·
Maintainer antworten meist innerhalb von 1 Tag
-
tests/unit/test_prompt.py fails on a fresh clone without LANGFUSE_* env varsEvtl. vergeben @Mathoholic hat das vor 17 Tagen übernommen. Offenbug feat-prompt-management sdk-python
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 80/100
langfuse/langfuse-python#1976 · 3 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
-
fix(serializer): handle single-string __slots__ and detect cycles in slotted objectsEvtl. vergeben @hassiebp hat das vor 12 Tagen übernommen. Offenbug sdk-python
langfuse/langfuse-python#2014 · 1 zugewiesene Person ·
Maintainer antworten meist innerhalb von 1 Tag
-
bug: ChatPromptClient.compile appends str(whole list) once per non-dict placeholder itemEvtl. vergeben @nimarb hat das vor 7 Tagen übernommen. Offenbug feat-prompt-management sdk-python
langfuse/langfuse-python#1971 · 1 zugewiesene Person ·
Maintainer antworten meist innerhalb von 1 Tag
-
bug: GCS upload detection in MediaManager uses substring match on the full URLEvtl. vergeben @hassiebp hat das vor 27 Tagen übernommen. Offenbug feat-multimodal-media sdk-python
langfuse/langfuse-python#1913 · 1 Kommentar · 1 zugewiesene Person ·
Maintainer antworten meist innerhalb von 1 Tag
Alle Issues in langfuse/langfuse-python
Ähnliche Issues
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 74/100
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 66/100
suitenumerique/accounts#160 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Restoring an exhausted flatmap checkpoint can replay consumed recordsEvtl. vergeben @salma2vec hat das heute übernommen. Offentype:bug
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 82/100
Maintainer antworten meist innerhalb von 1 Tag
-
`ingest` fails with pynndescent 0.7Evtl. vergeben @eroell hat das heute übernommen. OffenTriage 🩺
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 68/100
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
Maintainer antworten meist innerhalb von 1 Tag