Skip to content

test(e2e): cover Bedrock provider response headers on streaming /v1/messages - #35861

Draft
mubashir1osmani wants to merge 1 commit into
BerriAI:litellm_internal_stagingfrom
mubashir1osmani:litellm_e2e_regression_coverage
Draft

test(e2e): cover Bedrock provider response headers on streaming /v1/messages#35861
mubashir1osmani wants to merge 1 commit into
BerriAI:litellm_internal_stagingfrom
mubashir1osmani:litellm_e2e_regression_coverage

Conversation

@mubashir1osmani

Copy link
Copy Markdown
Collaborator

TLDR

Problem this solves:

  • Streaming /v1/messages over Bedrock dropped provider response headers
  • A customer could not read x-amzn-requestid to open an AWS support case
  • No e2e guard existed, so the streaming path could silently regress again

How it solves it:

  • Adds a live e2e that streams /v1/messages to a bedrock/invoke Claude model
  • Asserts the response carries a non-empty llm_provider-x-amzn-requestid header
  • Registers the matching coverage_registry cell so the gap is tracked

Relevant issues

Adds e2e coverage for the fix in #32160 (streaming header forwarding). Internal context: LIT-3724

Linear ticket

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 (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Screenshots / Proof of Fix

This PR adds an e2e regression test only, no product code changes, so there is no before/after product behavior to curl. The behavior it guards was fixed in #32160 and is already on staging

Honest disclosure on verification. I could not run this test against a live Bedrock-backed gateway in my environment, so the live assertion will run in the e2e stage pipeline rather than here. What I verified instead is that the assertion targets a header that actually reaches the client on the streaming path, by tracing the source rather than trusting a docstring:

  • litellm/llms/anthropic/experimental_pass_through/messages/streaming_iterator.py wraps the SSE stream in AnthropicMessagesStreamingResponse, carrying _hidden_params["additional_headers"] with the provider headers prefixed llm_provider-* (this is exactly what fix(anthropic_messages): forward provider response headers on streaming /v1/messages responses #32160 added; a bare async generator could not carry them, which is why streaming dropped them before)
  • litellm/proxy/common_request_processing.py:1799-1838 reads those hidden params on the streaming branch and merges **additional_headers into the streaming response custom_headers, which become the SSE HTTP response headers
  • The e2e harness captures the initial streaming response headers into StreamingResponse.headers (lowercased), so result.headers["llm_provider-x-amzn-requestid"] is the right seam

The registry cell is marked fail_before_fix: unproven because I have not run the red-before/green-after inversion against a live gateway. A reviewer running the QA runbook below on a live proxy can confirm both the pass and, by reverting #32160, the regression

Type

✅ Test

Changes

  • tests/e2e/llm_translation/test_messages_e2e.py: new TestBedrockMessagesProviderHeaders streaming /v1/messages test over a bedrock/invoke Claude deployment, asserting a non-empty llm_provider-x-amzn-requestid response header
  • tests/e2e/coverage_registry/llm_conversational.yaml: new cell llm.messages.bedrock_invoke.basic.stream.provider_headers

QA runbook

  • tests/e2e/llm_translation/test_messages_e2e.py::TestBedrockMessagesProviderHeaders::test_streaming_messages_forwards_bedrock_request_id_header - streaming /v1/messages over native Bedrock invoke exposes the provider request id to the client
    • Register a bedrock invoke deployment (needs STORE_MODEL_IN_DB=True and AWS credentials): curl -X POST http://localhost:4000/model/new -H "Authorization: Bearer sk-1234" -H "Content-Type: application/json" -d '{"model_name":"e2e-bedrock-msg","litellm_params":{"model":"bedrock/invoke/us.anthropic.claude-haiku-4-5-20251001-v1:0","aws_access_key_id":"os.environ/AWS_ACCESS_KEY_ID","aws_secret_access_key":"os.environ/AWS_SECRET_ACCESS_KEY","aws_region_name":"os.environ/AWS_REGION"}}'
    • Stream a message and dump response headers: curl -sS -D - -o /dev/null -X POST http://localhost:4000/v1/messages -H "Authorization: Bearer sk-1234" -H "Content-Type: application/json" -d '{"model":"e2e-bedrock-msg","max_tokens":64,"stream":true,"messages":[{"role":"user","content":"Count from one to three."}]}'
    • Expect a non-empty llm_provider-x-amzn-requestid header in the response
    • Regression check: revert fix(anthropic_messages): forward provider response headers on streaming /v1/messages responses #32160 and repeat; the header should be absent on the streamed response, present again with the fix
    • Sanity check: this test makes sense to add and is not hand-wavey (asserts the actual provider request id header, not a generic 200) or potentially flaky

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

…essages

Streaming /v1/messages over native Bedrock invoke must forward the upstream
provider response headers so a customer can pull x-amzn-requestid off the
response to open an AWS support case. The streaming path previously dropped
them because a bare async generator could not carry the header context; only
the non-streaming path forwarded them (LIT-3724, fix BerriAI#32160).

The test registers a bedrock/invoke Claude deployment, streams a /v1/messages
call, and asserts the response carries a non-empty llm_provider-x-amzn-requestid
header. Adds the matching coverage_registry cell
llm.messages.bedrock_invoke.basic.stream.provider_headers.
@greptile-apps

greptile-apps Bot commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR adds live end-to-end coverage for forwarding the Bedrock request ID header on streaming /v1/messages responses

  • Registers a Bedrock Invoke streaming provider-header coverage cell
  • Creates a temporary Bedrock deployment and asserts that the streamed response carries a non-empty llm_provider-x-amzn-requestid header

Confidence Score: 4/5

The PR appears safe to merge after aligning the new coverage ID with the registry's documented assertion vocabulary

The live test follows the existing e2e lifecycle and streaming-response contracts, while the only accepted concern is a non-blocking inconsistency in coverage taxonomy

Files Needing Attention: tests/e2e/coverage_registry/llm_conversational.yaml

Important Files Changed

Filename Overview
tests/e2e/coverage_registry/llm_conversational.yaml Adds the intended coverage cell, but its provider_headers assertion falls outside the documented LLM registry vocabulary
tests/e2e/llm_translation/test_messages_e2e.py Adds a focused live Bedrock streaming regression test using the shared model lifecycle and streaming transport helpers

Reviews (1): Last reviewed commit: "test(e2e): cover Bedrock provider respon..." | Re-trigger Greptile

- {id: llm.messages.anthropic.vision.nonstream.works, module: llm, tier: P0, subject_endpoint: messages, route: anthropic, capability: vision, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Vision via Messages API"}
- {id: llm.messages.anthropic.prompt_cache_5m.nonstream.works, module: llm, tier: P0, subject_endpoint: messages, route: anthropic, capability: prompt_cache_5m, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Prompt caching via Messages API"}
- {id: llm.messages.anthropic.thinking.nonstream.works, module: llm, tier: P1, subject_endpoint: messages, route: anthropic, capability: thinking, streaming: nonstream, assertions: [works], source: "model_prices json", rationale: "Extended thinking via Messages API"}
- {id: llm.messages.bedrock_invoke.basic.stream.provider_headers, module: llm, tier: P1, subject_endpoint: messages, route: bedrock_invoke, capability: basic, streaming: stream, assertions: [works, provider_headers], source: "llms/anthropic/experimental_pass_through/messages/streaming_iterator.py", rationale: "Streaming /v1/messages over native Bedrock invoke must forward provider response headers as llm_provider-* so a customer can pull x-amzn-requestid for an AWS support case; the streaming path previously dropped them (LIT-3724 / #32160)", fail_before_fix: unproven}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Nonstandard coverage assertion taxonomy

The new coverage ID uses provider_headers, but the documented LLM registry grammar limits assertions to works, cost_logged, or cache_hit. This creates a nonstandard category that downstream reporting and future registry maintenance cannot interpret consistently; update both this row and its matching covers marker to use the documented taxonomy.

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

@codecov

codecov Bot commented Aug 5, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@mubashir1osmani
mubashir1osmani marked this pull request as draft August 5, 2026 01:44
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.

1 participant