test(router): call both mid-stream fallback attempt functions directly

The router coverage gate wants every router.py function reached by name from
a router test. The two per-endpoint attempt functions were only reached through
their callers, so each now has a direct test proving the per-request controls
carrier never reaches the provider call and every hop's stream comes back wrapped.
This commit is contained in:
mateo-berri 2026-09-21 14:48:42 -07:00
parent a2ae80ec9b
commit b0305d0a31
2 changed files with 82 additions and 0 deletions

View file

@ -423,6 +423,44 @@ async def test_aresponses_per_request_fallbacks_survive_into_hop_streams():
assert collected == [completed_event]
@pytest.mark.asyncio
async def test_aresponses_attempt_strips_the_controls_carrier_and_wraps_every_hop_stream():
"""Each attempt of the chain, not only the primary's, comes back wrapped for mid-stream
failover, and the per-request controls carrier rides into the wrapper's re-entry kwargs
without ever reaching the provider call."""
from types import MappingProxyType
from litellm.router_utils.fallback_event_handlers import (
MID_STREAM_FALLBACK_CONTROLS_KEY,
MidStreamFallbackControls,
)
router = _make_three_tier_router()
completed_event = _make_completed_event(1, 1, 2)
hop_stream = _scripted_responses_stream([completed_event])
seen: dict = {}
async def fake_original(**kwargs):
seen.update(kwargs)
return hop_stream
controls = MidStreamFallbackControls(MappingProxyType({"fallbacks": [{"primary": ["fb1", "fb2"]}]}))
stream = await router._ageneric_api_call_with_fallbacks_responses_attempt(
model="fb1",
original_generic_function=fake_original,
stream=True,
input="hi",
**{MID_STREAM_FALLBACK_CONTROLS_KEY: controls},
)
collected = [event async for event in stream]
assert seen["model"] == "openai/fb1-model"
assert MID_STREAM_FALLBACK_CONTROLS_KEY not in seen
assert "fallbacks" not in seen
assert stream is not hop_stream
assert collected == [completed_event]
@pytest.mark.asyncio
async def test_aresponses_fallback_on_in_stream_error_event():
"""A retriable in-stream error event (429) must trigger the router's mid-stream

View file

@ -14222,6 +14222,50 @@ async def test_anthropic_messages_hop_stream_failure_reaches_second_fallback_ent
assert b"overloaded_error" not in body
@pytest.mark.asyncio
async def test_anthropic_messages_attempt_strips_the_controls_carrier_and_wraps_every_hop_stream():
"""Each attempt of the chain, not only the primary's, comes back wrapped for mid-stream
failover, and the per-request controls carrier never reaches the provider call."""
from types import MappingProxyType
from litellm.router_utils.fallback_event_handlers import (
MID_STREAM_FALLBACK_CONTROLS_KEY,
MidStreamFallbackControls,
)
router = Router(
model_list=[
{"model_name": "fb1", "litellm_params": {"model": "anthropic/fb1-model", "api_key": "sk-test"}},
],
num_retries=0,
)
hop_stream = _AnthropicMessagesFakeByteStream(
[_anthropic_messages_message_start_chunk(), _anthropic_messages_content_chunk("from fb1")]
)
seen: dict = {}
async def fake_original(**kwargs):
seen.update(kwargs)
return hop_stream
controls = MidStreamFallbackControls(MappingProxyType({"fallbacks": [{"primary": ["fb1", "fb2"]}]}))
stream = await router._ageneric_api_call_with_fallbacks_anthropic_messages_attempt(
model="fb1",
original_generic_function=fake_original,
stream=True,
messages=[{"role": "user", "content": "hi"}],
max_tokens=10,
**{MID_STREAM_FALLBACK_CONTROLS_KEY: controls},
)
body = b"".join([chunk async for chunk in stream])
assert seen["model"] == "anthropic/fb1-model"
assert MID_STREAM_FALLBACK_CONTROLS_KEY not in seen
assert "fallbacks" not in seen
assert stream is not hop_stream
assert b"from fb1" in body
@pytest.mark.asyncio
async def test_anthropic_messages_fallback_triggers_after_lifecycle_only_frame():
"""Regression: Anthropic routinely sends a message_start lifecycle frame