Skip to content

test(e2e): otel trace completeness on /v1/responses - #33134

Merged
mubashir1osmani merged 3 commits into
litellm_e2e_otel_messages_tracefrom
litellm_e2e_otel_responses_trace
Jul 14, 2026
Merged

test(e2e): otel trace completeness on /v1/responses#33134
mubashir1osmani merged 3 commits into
litellm_e2e_otel_messages_tracefrom
litellm_e2e_otel_responses_trace

Conversation

@yucheng-berri

@yucheng-berri yucheng-berri commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Relevant issues

Resolves LIT-3787

Linear ticket

https://linear.app/litellm-ai/issue/LIT-3787

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • My PR passes all CI/CD checks (e.g., lint, format, unit tests)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review

Screenshots / Proof of Fix

Top of the 3-PR LIT-3787 stack (chat_completions -> messages -> responses); merge the two below first. Verification was run on 2026-07-13 at this branch's tip f354f98 (all three tests); see the bottom PR for the full setup. Proxies ran from source at exact refs since this machine has no docker buildx. Outputs verbatim.

  1. Pass on current code, the whole suite:

    $ LITELLM_PROXY_URL=http://localhost:4000 uv run pytest logging/test_otel_trace_e2e.py -v
    logging/test_otel_trace_e2e.py::TestOtelTraceCompleteness::test_chat_completions_exports_complete_trace PASSED
    logging/test_otel_trace_e2e.py::TestOtelTraceCompleteness::test_messages_exports_complete_trace PASSED
    logging/test_otel_trace_e2e.py::TestOtelTraceCompleteness::test_responses_exports_complete_trace PASSED
    ============================== 3 passed in 19.43s ==============================
    

    The /v1/responses trace at the destination:

    - [server] POST /v1/responses
      - [internal] auth /v1/responses
      - [client] chat gpt-5.5
      - [client] batch_write_to_db _PROXY_track_cost_callback
      - [client] postgres get_key_object
    
  2. Fail-before-fix at 1bd603d (parent of fix(otel): one v2 logger owns the global provider; scope tenant OTLP creds per exporter #30590): all three fail, each with its own orphaned gen-AI span; note the responses route orphans chat gpt-5.5:

    $ LITELLM_PROXY_URL=http://localhost:4611 E2E_OTEL_QUERY_URL=http://localhost:16690 \
        uv run pytest logging/test_otel_trace_e2e.py
    E  AssertionError: span(s) ['chat claude-haiku-4-5'] reference a parent that never
       reached the destination (orphaned trace); spans present: ['chat claude-haiku-4-5']
    E  AssertionError: span(s) ['chat claude-haiku-4-5'] reference a parent that never
       reached the destination (orphaned trace); spans present: ['chat claude-haiku-4-5']
    E  AssertionError: span(s) ['chat gpt-5.5'] reference a parent that never
       reached the destination (orphaned trace); spans present: ['chat gpt-5.5']
    ======================== 3 failed in 364.91s (0:06:04) =========================
    
  3. Static gates: make lint-e2e-basedpyright -> 0 errors; coverage_registry.collector --strict passes with the registry edit (responses added to the otel cell's exercised_on).
    Before:

Screenshot 2026-07-13 at 7 20 11 PM

After:
Screenshot 2026-07-13 at 7 19 31 PM

Type

✅ Test

Changes

Third scenario of LIT-3787: /v1/responses is the newest of the three handlers with its own translation layer, which is exactly where span-anchor plumbing regressions land first, and it is the route agent frameworks increasingly default to; an orphaned gen-AI span there means agent steps with no originating request, auth identity, or cost linkage.

  • logging/test_otel_trace_e2e.py: adds test_responses_exports_complete_trace (same two-sided contract as the chat/messages tests). The gen-AI span keeps the semconv operation name chat on this surface, so the expected span is chat gpt-5.5; the product maps the responses call type to the chat operation by design.
  • logging/logging_client.py: adds responses_raw, a typed non-streaming /v1/responses sender with max_output_tokens (default 64) so a reasoning model cannot burn unbounded output tokens per run; a capped response is still a 200 and still exports the trace.
  • e2e_config.py: CHEAP_OPENAI_MODEL (env E2E_CHEAP_OPENAI_MODEL, default gpt-5.5). gpt-5.5 is kept deliberately for stack-wide consistency: it is the model the compose config prewires and the rest of the e2e suite uses; the constant is env-overridable when the stack migrates.
  • coverage_registry/logging.yaml: responses added to logging.otel.success.exports_metric's exercised_on (the schema already lists it as a valid endpoint). The row's embeddings surface remains a tracked follow-up under LIT-3787.

Known deferral: /v1/responses raw senders now exist in two suite clients (endpoints_client.py and here); promoting a single sender to e2e_gateway.Gateway is a follow-up, kept out of this PR to keep its scope one scenario.

Behavior changes

None; test-only changes under tests/e2e.

QA runbook

  • tests/e2e/logging/test_otel_trace_e2e.py::TestOtelTraceCompleteness::test_responses_exports_complete_trace - one successful Responses API call shows up at the OTEL destination as one connected trace tree
    • GET /health/readiness/details with the master key and confirm OpenTelemetryV2 appears in success_callbacks
    • POST /key/generate with {"models":["gpt-5.5"],"key_alias":"otel-trace-responses-<uniq>"} and save the returned key
    • POST /v1/responses (non-streaming: model, input with a unique phrase, max_output_tokens: 64) with the key; retry the first call on 401 for a few seconds; confirm 200 and note x-litellm-call-id
    • In Jaeger (http://localhost:16686, service litellm), search by tag litellm.call_id=<that id> and wait for the trace (spans flush in batches)
    • Confirm exactly ONE trace holds the call-id
    • Confirm a single root span POST /v1/responses (kind server) and no span referencing a parent missing from the trace
    • Confirm the children: auth /v1/responses, a postgres ... span, and batch_write_to_db _PROXY_track_cost_callback
    • Confirm chat gpt-5.5 (kind client) is in the SAME trace and its parent chain reaches the root
    • POST /key/delete to clean up

Final attestation: "I agree this test is testing what the writer wanted to test."

@greptile-apps

greptile-apps Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This test-only PR completes the LIT-3787 stack by adding an e2e OTEL trace-completeness scenario for /v1/responses, verifying that one non-streaming Responses API call produces a single connected trace tree at the OTEL destination.

  • test_responses_exports_complete_trace follows the same two-sided contract as the existing chat_completions and messages tests: it checks the recorded state (OTEL v2 logger active), fires a capped request via a freshly generated key, and then reads the trace back through Jaeger to assert no dangling spans, a single root POST /v1/responses server span, and chat gpt-5.5 reachable from that root.
  • responses_raw() and ResponsesRequestBody are added to logging_client.py in the same style as messages_raw(); CHEAP_OPENAI_MODEL is added to e2e_config.py as an env-overridable constant; and responses is appended to the exercised_on list in the coverage registry.

Confidence Score: 5/5

Test-only changes confined entirely to tests/e2e; no production code is touched.

All four changed files live under tests/e2e and have no effect on production behavior. The new test mirrors the established pattern of the other two trace-completeness tests exactly, uses proper key isolation with unique markers and deferred cleanup, and the author has provided passing test output and a fail-before-fix baseline. No logic paths, schemas, or existing assertions are weakened.

No files require special attention.

Important Files Changed

Filename Overview
tests/e2e/logging/test_otel_trace_e2e.py Adds test_responses_exports_complete_trace following the exact same two-sided contract (recorded state + enforced behavior) as the existing chat_completions and messages tests; also adds explicit exercised_on annotation to the chat_completions test for coverage-registry consistency.
tests/e2e/logging/logging_client.py Adds ResponsesRequestBody (model/input/max_output_tokens) and responses_raw(), both consistent in style and typing with the existing messages_raw() method.
tests/e2e/e2e_config.py Adds CHEAP_OPENAI_MODEL constant (env-overridable, defaults to gpt-5.5) following the same pattern as CHEAP_ANTHROPIC_MODEL.
tests/e2e/coverage_registry/logging.yaml Adds responses to exercised_on for logging.otel.success.exports_metric; no structural changes to the registry schema.

Reviews (3): Last reviewed commit: "test(e2e): declare the responses and cha..." | Re-trigger Greptile

@yucheng-berri
yucheng-berri force-pushed the litellm_e2e_otel_responses_trace branch from 328c04c to a309013 Compare July 13, 2026 23:33
@yucheng-berri
yucheng-berri force-pushed the litellm_e2e_otel_messages_trace branch from e9ac83c to 01a47dd Compare July 13, 2026 23:33
@codecov

codecov Bot commented Jul 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@yucheng-berri
yucheng-berri force-pushed the litellm_e2e_otel_responses_trace branch from a309013 to f354f98 Compare July 14, 2026 00:44
@yucheng-berri
yucheng-berri force-pushed the litellm_e2e_otel_messages_trace branch from 01a47dd to 4c1cbd3 Compare July 14, 2026 00:45
@yucheng-berri
yucheng-berri force-pushed the litellm_e2e_otel_responses_trace branch from f354f98 to e74ff98 Compare July 14, 2026 00:57
@yucheng-berri
yucheng-berri force-pushed the litellm_e2e_otel_messages_trace branch from 4c1cbd3 to bf11211 Compare July 14, 2026 00:58
@yucheng-berri yucheng-berri changed the title [test] e2e: OTEL trace completeness on /v1/responses (LIT-3787) test(e2e): OTEL trace completeness on /v1/responses Jul 14, 2026
@yucheng-berri yucheng-berri changed the title test(e2e): OTEL trace completeness on /v1/responses test(e2e): otel trace completeness on /v1/responses Jul 14, 2026
@yucheng-berri
yucheng-berri force-pushed the litellm_e2e_otel_responses_trace branch from e74ff98 to 4ceaa98 Compare July 14, 2026 01:51
@yucheng-berri
yucheng-berri force-pushed the litellm_e2e_otel_messages_trace branch from bf11211 to 0658e85 Compare July 14, 2026 01:51
@yucheng-berri
yucheng-berri force-pushed the litellm_e2e_otel_responses_trace branch from 4ceaa98 to c493eb5 Compare July 14, 2026 02:09
@yucheng-berri
yucheng-berri force-pushed the litellm_e2e_otel_messages_trace branch from 0658e85 to 9aaa28f Compare July 14, 2026 02:09
@yucheng-berri
yucheng-berri force-pushed the litellm_e2e_otel_responses_trace branch from c493eb5 to c84907f Compare July 14, 2026 02:17
@yucheng-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

Extends the LIT-3787 trace-completeness suite to the OpenAI Responses API
route: one successful non-streaming /v1/responses call must land at the
destination as ONE connected trace. Adds the raw /v1/responses sender, a
CHEAP_OPENAI_MODEL config constant, and registers responses in the otel
registry cell's exercised_on.
@yucheng-berri
yucheng-berri force-pushed the litellm_e2e_otel_responses_trace branch from 0f45000 to aaed3ac Compare July 14, 2026 02:30
@yucheng-berri

Copy link
Copy Markdown
Contributor Author

@greptileai

@mubashir1osmani
mubashir1osmani merged commit 97f102a into litellm_e2e_otel_messages_trace Jul 14, 2026
59 of 83 checks passed
@mubashir1osmani
mubashir1osmani deleted the litellm_e2e_otel_responses_trace branch July 14, 2026 02:33
yucheng-berri added a commit that referenced this pull request Jul 14, 2026
* test(e2e): OTEL trace completeness on /v1/messages

Extends the LIT-3787 trace-completeness suite to the Anthropic-native route:
one successful non-streaming /v1/messages call must land at the destination as
ONE connected trace (root SERVER span + auth/db/cost children + gen-AI CLIENT
span, no dangling parents). Adds the raw /v1/messages sender to the logging
suite client.

* test(e2e): reuse the shared AnthropicMessagesBody per review

Drops the duplicate /v1/messages request model in favor of the one models.py
already provides (budget_client uses the same one), passes max_tokens at the
call site to match the sibling chat test, notes in the docstring why the
gen-AI span is named chat on this surface, and adopts the hardened read-back
signature

* test(e2e): author the messages trace test docstring

* test(e2e): declare the messages surface on the covers marker

* test(e2e): otel trace completeness on /v1/responses (#33134)

* test(e2e): OTEL trace completeness on /v1/responses

Extends the LIT-3787 trace-completeness suite to the OpenAI Responses API
route: one successful non-streaming /v1/responses call must land at the
destination as ONE connected trace. Adds the raw /v1/responses sender, a
CHEAP_OPENAI_MODEL config constant, and registers responses in the otel
registry cell's exercised_on.

* test(e2e): author the responses trace test docstring

* test(e2e): declare the responses and chat surfaces on the covers markers
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants