test(e2e): otel trace completeness on streaming /v1/responses (LIT-3787) - #33262
Conversation
Greptile SummaryThis PR adds the third surface of the streaming OTEL trace completeness contract: a new e2e test for streamed
Confidence Score: 5/5Test-only change; no production code is touched and all existing test assertions are preserved with their original defaults. Both modified helpers default to their original behavior, so no existing test is weakened. The new test deliberately relaxes two assertions with a tracked ticket (LIT-4428) and documents the rationale clearly. The No files require special attention.
|
| Filename | Overview |
|---|---|
| tests/e2e/logging/logging_client.py | Adds optional stream parameter to responses_raw and a matching `stream: bool |
| tests/e2e/logging/test_otel_trace_e2e.py | Adds test_responses_stream_exports_complete_trace; _assert_complete_trace / _settled_names gain a backward-compatible require_cost_span=True flag so the new test can skip the cost-span check (LIT-4428) while all existing callers are unaffected. |
Reviews (3): Last reviewed commit: "test(e2e): parenthesize the settle-names..." | Re-trigger Greptile
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
9488bae to
e53fc53
Compare
Covers the responses surface of logging.otel.stream.exports_metric: same tree contract plus the stream-side assertions (event-stream content type, chunks consumed, exactly one gen-AI span). Two assertions are knowingly relaxed on this surface, both verified against live traces and tracked in LIT-4428: the responses route does not stamp litellm.request.streaming on the gen-AI span, and the spend write for a streamed responses call records spend correctly but emits no batch_write_to_db cost span. Adds a stream mode to responses_raw
…cion explicit per review
e53fc53 to
7d016fd
Compare
ac9c99c to
183bfb1
Compare
a712e86
into
litellm_e2e_otel_stream_messages_trace
…7) (#33247) * test(e2e): OTEL trace completeness on streaming /v1/messages Covers the messages surface of logging.otel.stream.exports_metric: same tree contract as the non-streaming tests plus the stream-specific assertions (the response actually streamed, exactly one gen-AI span, and the span records litellm.request.streaming=true). Adds a stream field to the shared AnthropicMessagesBody and a stream mode to messages_raw * test(e2e): make the stream field coercion explicit per review * test(e2e): otel trace completeness on streaming /v1/responses (LIT-3787) (#33262) * test(e2e): OTEL trace completeness on streaming /v1/responses Covers the responses surface of logging.otel.stream.exports_metric: same tree contract plus the stream-side assertions (event-stream content type, chunks consumed, exactly one gen-AI span). Two assertions are knowingly relaxed on this surface, both verified against live traces and tracked in LIT-4428: the responses route does not stamp litellm.request.streaming on the gen-AI span, and the spend write for a streamed responses call records spend correctly but emits no batch_write_to_db cost span. Adds a stream mode to responses_raw * test(e2e): parenthesize the settle-names ternary and make stream coercion explicit per review
…7) (#33247) * test(e2e): OTEL trace completeness on streaming /v1/messages Covers the messages surface of logging.otel.stream.exports_metric: same tree contract as the non-streaming tests plus the stream-specific assertions (the response actually streamed, exactly one gen-AI span, and the span records litellm.request.streaming=true). Adds a stream field to the shared AnthropicMessagesBody and a stream mode to messages_raw * test(e2e): make the stream field coercion explicit per review * test(e2e): otel trace completeness on streaming /v1/responses (LIT-3787) (#33262) * test(e2e): OTEL trace completeness on streaming /v1/responses Covers the responses surface of logging.otel.stream.exports_metric: same tree contract plus the stream-side assertions (event-stream content type, chunks consumed, exactly one gen-AI span). Two assertions are knowingly relaxed on this surface, both verified against live traces and tracked in LIT-4428: the responses route does not stamp litellm.request.streaming on the gen-AI span, and the spend write for a streamed responses call records spend correctly but emits no batch_write_to_db cost span. Adds a stream mode to responses_raw * test(e2e): parenthesize the settle-names ternary and make stream coercion explicit per review
Relevant issues
Related: LIT-4428 (product gap found while writing this test)
Linear ticket
https://linear.app/litellm-ai/issue/LIT-3787 (streaming follow-up; top of the streaming stack)
https://linear.app/litellm-ai/issue/LIT-4428 (tracks the two assertions this test knowingly relaxes)
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
Screenshots / Proof of Fix
Stacked on the streaming /v1/messages PR; merge that first. Same infra, no new services. Run on 2026-07-14, proxies from source at exact refs. Outputs verbatim.
Provider disclosure: the OpenAI org behind the .env key is currently out of quota (insufficient_quota arrives inside the stream after an HTTP 200, since streaming commits the status before upstream completes), so verification ran with
E2E_CHEAP_OPENAI_MODEL=gemini-2.5-flash(the constant is env-overridable by design). The route under test is unchanged; the expected span name derives from the constant.Evidence-first, and it found a product gap. A successful streamed /v1/responses call records spend correctly, but its trace never receives the cost-write span, and the gen-AI span carries no litellm.request.streaming tag (streamed chat/messages and non-streamed responses all have both):
Filed as LIT-4428. This test therefore relaxes exactly those two assertions on this surface (documented in the docstring with the ticket id); everything else in the completeness contract is asserted, and the sibling streamed chat/messages tests keep the strict versions. Tighten when LIT-4428 lands.
Pass on current code (compose stack):
Fail-before-fix at 1bd603d (parent of fix(otel): one v2 logger owns the global provider; scope tenant OTLP creds per exporter #30590): on that ref a streamed responses call exports nothing tagged to the destination at all, so the test fails at its first trace assertion (an even earlier break than the orphan the non-streaming tests catch):
Static gates:
make lint-e2e-basedpyright-> 0 errors;coverage_registry.collector --strictpasses.Type
✅ Test
Changes
Third surface of the
logging.otel.stream.exports_metricrow: streamed/v1/responses.logging/test_otel_trace_e2e.py: addstest_responses_stream_exports_complete_trace(shared completeness contract + streamed-response checks + exactly one gen-AI span)._assert_complete_trace/_settled_namesgain arequire_cost_spanflag so this surface can skip the cost-span requirement while the siblings keep it strict; the docstring names LIT-4428 as the tracked reason.logging/logging_client.py:responses_rawgains astreammode;ResponsesRequestBodygains an optionalstreamfield.Behavior changes
None; test-only changes under tests/e2e.
QA runbook
OpenTelemetryV2appears insuccess_callbacks{"models":["gemini-2.5-flash"],"key_alias":"otel-stream-responses-<uniq>"}and save the returned key (gemini-2.5-flash viaE2E_CHEAP_OPENAI_MODELwhile the OpenAI org quota is exhausted; default is gpt-5.5)curl -NPOST /v1/responses with"stream": true,max_output_tokens: 64, and a unique phrase; retry the first call on 401 for a few seconds; confirmcontent-type: text/event-stream, that events ending inresponse.completedarrive (an in-stream"type":"error"event means the upstream failed despite the 200), and note thex-litellm-call-idheaderlitellm), search by taglitellm.call_id=<that id>and wait for the trace (spans flush in batches)POST /v1/responses(kind server) and no span referencing a parent missing from the traceauth /v1/responsesand apostgres ...span (the cost-write span is knowingly absent on this surface; see LIT-4428)chat <model>span (kind client) and that its parent chain reaches the rootFinal attestation: "I agree this test is testing what the writer wanted to test."