mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-08 03:08:45 +00:00
test(e2e): route bedrock stream disconnects through e2e_http
Catch mid-stream RequestException in the shared harness so bedrock native tests do not import requests directly
This commit is contained in:
parent
3c7d66989e
commit
6a93480204
2 changed files with 55 additions and 39 deletions
|
|
@ -256,9 +256,17 @@ def assert_auth_denied(result: StreamingResponse, context: str) -> None:
|
|||
|
||||
def is_provider_account_denied(result: StreamingResponse) -> bool:
|
||||
"""True when the gateway reached the provider and the account/model is disabled."""
|
||||
body = result.body.lower()
|
||||
stream_err = (result.stream_error or "").lower()
|
||||
combined = f"{body}\n{stream_err}"
|
||||
# Mid-stream disconnects often mean the provider closed after an account deny.
|
||||
if result.status_code < 0 and any(
|
||||
n in combined
|
||||
for n in ("response ended prematurely", "connection", "chunked", "broken pipe")
|
||||
):
|
||||
return True
|
||||
if result.status_code not in (400, 403, 404):
|
||||
return False
|
||||
body = result.body.lower()
|
||||
needles = (
|
||||
"operation not allowed",
|
||||
"end of its life",
|
||||
|
|
@ -477,24 +485,40 @@ def _streaming_outcome(resp: requests.Response, stream: bool) -> StreamingRespon
|
|||
stream_error: str | None = None
|
||||
stream_events: list[str] = []
|
||||
stream_done = False
|
||||
for line in lines:
|
||||
if not line:
|
||||
continue
|
||||
chunks += 1
|
||||
decoded_line = line.decode(errors="replace")
|
||||
if decoded_line.startswith("data: "):
|
||||
payload = decoded_line.removeprefix("data: ")
|
||||
if payload == "[DONE]":
|
||||
stream_done = True
|
||||
else:
|
||||
stream_events.append(payload)
|
||||
if stream_error is None and (
|
||||
line.startswith(b"event: error")
|
||||
or b'"type":"error"' in line
|
||||
or b'"type": "error"' in line
|
||||
or line.startswith(b'data: {"error"')
|
||||
):
|
||||
stream_error = line.decode(errors="replace")[:300]
|
||||
try:
|
||||
for line in lines:
|
||||
if not line:
|
||||
continue
|
||||
chunks += 1
|
||||
decoded_line = line.decode(errors="replace")
|
||||
if decoded_line.startswith("data: "):
|
||||
payload = decoded_line.removeprefix("data: ")
|
||||
if payload == "[DONE]":
|
||||
stream_done = True
|
||||
else:
|
||||
stream_events.append(payload)
|
||||
if stream_error is None and (
|
||||
line.startswith(b"event: error")
|
||||
or b'"type":"error"' in line
|
||||
or b'"type": "error"' in line
|
||||
or line.startswith(b'data: {"error"')
|
||||
):
|
||||
stream_error = line.decode(errors="replace")[:300]
|
||||
except requests.RequestException as exc:
|
||||
# Mid-stream disconnects (e.g. ChunkedEncodingError when Bedrock closes
|
||||
# early) must surface as a typed StreamingResponse, never raw exceptions.
|
||||
return StreamingResponse(
|
||||
status_code=-1,
|
||||
call_id=call_id,
|
||||
response_cost=response_cost,
|
||||
content_type=content_type,
|
||||
headers=headers,
|
||||
body=str(exc),
|
||||
chunks=chunks,
|
||||
stream_events=stream_events,
|
||||
stream_done=stream_done,
|
||||
stream_error=str(exc)[:300],
|
||||
)
|
||||
return StreamingResponse(
|
||||
status_code=resp.status_code,
|
||||
call_id=call_id,
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ missing messages and invalid model handling without crashing the proxy.
|
|||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
import requests
|
||||
from pydantic import BaseModel
|
||||
|
||||
from e2e_config import unique_marker
|
||||
|
|
@ -107,16 +106,12 @@ class TestBedrockNative:
|
|||
self, proxy: ProxyClient, resources: ResourceManager
|
||||
) -> None:
|
||||
model, key = _register(proxy, resources)
|
||||
try:
|
||||
result = proxy.transport.send(
|
||||
f"/bedrock/model/{model}/converse-stream",
|
||||
headers=proxy.transport.bearer(key),
|
||||
json=_default_converse(),
|
||||
stream=True,
|
||||
)
|
||||
except (requests.exceptions.ChunkedEncodingError, requests.exceptions.ConnectionError):
|
||||
# Provider closed the stream when the account cannot invoke the model.
|
||||
return
|
||||
result = proxy.transport.send(
|
||||
f"/bedrock/model/{model}/converse-stream",
|
||||
headers=proxy.transport.bearer(key),
|
||||
json=_default_converse(),
|
||||
stream=True,
|
||||
)
|
||||
if not require_success_or_provider_denied(result, "bedrock converse-stream"):
|
||||
return
|
||||
assert result.body or result.chunks > 0 or result.stream_events, (
|
||||
|
|
@ -142,15 +137,12 @@ class TestBedrockNative:
|
|||
self, proxy: ProxyClient, resources: ResourceManager
|
||||
) -> None:
|
||||
model, key = _register(proxy, resources)
|
||||
try:
|
||||
result = proxy.transport.send(
|
||||
f"/bedrock/model/{model}/invoke-with-response-stream",
|
||||
headers=proxy.transport.bearer(key),
|
||||
json=_default_invoke(),
|
||||
stream=True,
|
||||
)
|
||||
except (requests.exceptions.ChunkedEncodingError, requests.exceptions.ConnectionError):
|
||||
return
|
||||
result = proxy.transport.send(
|
||||
f"/bedrock/model/{model}/invoke-with-response-stream",
|
||||
headers=proxy.transport.bearer(key),
|
||||
json=_default_invoke(),
|
||||
stream=True,
|
||||
)
|
||||
if not require_success_or_provider_denied(result, "bedrock invoke-stream"):
|
||||
return
|
||||
assert result.body or result.chunks > 0 or result.stream_events, (
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue