mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-10 22:41:41 +00:00
test(e2e): stop the fallback tests flaking on gpt-5.5's reasoning budget
max_tokens=64 caps reasoning plus visible output on gpt-5.5, so the fallback target can legitimately return finish_reason="length" with empty content. litellm-e2e build 90 hit exactly that: the response cost of $0.002005 backs out to 64 completion tokens at gpt-5.5's $3e-05/token, i.e. the whole budget spent reasoning about "say hi" with none left to answer. The fallback itself worked -- 200, served by gpt-5.5-2026-04-23, x-litellm-attempted-fallbacks present -- so the only thing that failed was an assertion about OpenAI's token budgeting rather than about routing. Raise the reliability helper's budget to 512 and accept empty content only when finish_reason is "length". Empty content under any other finish_reason still fails, so the tests keep catching a fallback that returns nothing for a reason we do control. The relaxed assertion lives in the helper both reliability fallback tests share, so test_timeout_routes_to_fallback is covered too; it has the same shape and had not tripped yet.
This commit is contained in:
parent
3e2999f29f
commit
6e341b79d8
3 changed files with 26 additions and 6 deletions
|
|
@ -333,6 +333,7 @@ class OutMessage(BaseModel):
|
|||
|
||||
class ChatChoice(BaseModel):
|
||||
message: OutMessage | None = None
|
||||
finish_reason: str | None = None
|
||||
|
||||
|
||||
class PromptTokensDetails(BaseModel):
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ def chat_override(
|
|||
json=ReliabilityChatBody(
|
||||
model=model,
|
||||
messages=[ChatMessage(role="user", content=content)],
|
||||
max_tokens=64,
|
||||
max_tokens=512,
|
||||
stream=stream,
|
||||
router_settings_override=override,
|
||||
cache=cache,
|
||||
|
|
@ -77,3 +77,13 @@ def content_of(resp: StreamingResponse) -> str | None:
|
|||
return None
|
||||
message = parsed.choices[0].message
|
||||
return message.content if message is not None else None
|
||||
|
||||
|
||||
def finish_reason_of(resp: StreamingResponse) -> str | None:
|
||||
try:
|
||||
parsed = ChatResponse.model_validate_json(resp.body)
|
||||
except ValidationError:
|
||||
return None
|
||||
if not parsed.choices:
|
||||
return None
|
||||
return parsed.choices[0].finish_reason
|
||||
|
|
|
|||
|
|
@ -3,9 +3,11 @@ healthy one.
|
|||
|
||||
Each test registers a primary deployment that fails (an unreachable base URL, or
|
||||
a 1ms deadline) and calls it with a `router_settings_override` mapping it to the
|
||||
real `gpt-5.5`. The proof the fallback fired is twofold: the response is a real
|
||||
completion from `gpt-5.5` (a non-empty content string), and the proxy reports at
|
||||
least one attempted fallback in the x-litellm-attempted-fallbacks header.
|
||||
real `gpt-5.5`. The proof the fallback fired is twofold: the response is a
|
||||
completion from `gpt-5.5`, and the proxy reports at least one attempted fallback
|
||||
in the x-litellm-attempted-fallbacks header. Empty content is accepted only for
|
||||
`finish_reason == "length"`, since gpt-5.5 counts reasoning against max_tokens
|
||||
and can consume the whole budget before emitting any text.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
|
@ -22,6 +24,7 @@ from reliability_support import (
|
|||
content_of,
|
||||
create_bad_base_deployment,
|
||||
create_timeout_deployment,
|
||||
finish_reason_of,
|
||||
)
|
||||
|
||||
pytestmark = pytest.mark.e2e
|
||||
|
|
@ -30,10 +33,16 @@ pytestmark = pytest.mark.e2e
|
|||
def _assert_served_by_fallback(resp: StreamingResponse) -> None:
|
||||
assert resp.status_code == 200, f"expected 200 after fallback, got {resp.status_code}: {resp.body[:300]}"
|
||||
content = content_of(resp)
|
||||
assert isinstance(content, str) and content, (
|
||||
f"the gpt-5.5 fallback should have returned a real completion, got content {content!r} "
|
||||
finish_reason = finish_reason_of(resp)
|
||||
assert isinstance(content, str), (
|
||||
f"the gpt-5.5 fallback should have returned a completion body, got content {content!r} "
|
||||
f"(body={resp.body[:300]})"
|
||||
)
|
||||
assert content or finish_reason == "length", (
|
||||
f"the gpt-5.5 fallback returned empty content with finish_reason={finish_reason!r}; only "
|
||||
f"finish_reason='length' may be empty, since gpt-5.5 can spend the whole max_tokens "
|
||||
f"budget on reasoning (body={resp.body[:300]})"
|
||||
)
|
||||
attempted = resp.headers.get("x-litellm-attempted-fallbacks")
|
||||
assert attempted is not None, "response is missing the x-litellm-attempted-fallbacks header"
|
||||
assert int(attempted) >= 1, f"x-litellm-attempted-fallbacks should be >= 1, got {attempted!r}"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue