fix(completion): surface a non-streaming upstream abort as a server error

This commit is contained in:
Ishan Katoch 2026-10-28 14:52:03 +05:30
parent c7c895db45
commit cea101402d
2 changed files with 39 additions and 0 deletions

View file

@ -422,6 +422,7 @@ class _PromptCacheTokenCounts(TypedDict, total=False):
_CACHED_MESSAGE_PROVIDER_FIELDS: Final = TypeAdapter(_CachedMessageProviderFields)
_CACHED_USAGE_DETAILS: Final = TypeAdapter(_CachedUsageDetails)
_FIELD_MAP: Final = TypeAdapter(dict[str, object])
_CHOICE_FIELD_MAPS: Final = TypeAdapter(list[dict[str, object]])
_COMPLETION_TOKENS_DETAILS: Final = TypeAdapter(CompletionTokensDetailsWrapper)
_PROMPT_TOKENS_DETAILS: Final = TypeAdapter(PromptTokensDetailsWrapper)
_PROMPT_CACHE_TOKEN_COUNTS: Final = TypeAdapter(_PromptCacheTokenCounts)
@ -757,6 +758,20 @@ def convert_to_model_response_object(
model="",
)
response_fields: Final = _FIELD_MAP.validate_python(response_object)
if any(
choice.get("finish_reason") == "abort"
for choice in _CHOICE_FIELD_MAPS.validate_python(response_fields["choices"])
):
from litellm.exceptions import APIError
raise APIError(
status_code=500,
message="Upstream generation aborted (finish_reason=abort)",
llm_provider="",
model=str(response_fields.get("model") or ""),
)
for idx, choice in enumerate(response_object["choices"]):
## HANDLE JSON MODE - anthropic returns single function call]
tool_calls = choice["message"].get("tool_calls", None)

View file

@ -228,6 +228,30 @@ def test_convert_missing_choices_raises_api_error() -> None:
assert "no 'choices'" in str(exc_info.value)
@pytest.mark.parametrize("finish_reasons", [("abort",), ("stop", "abort"), ("abort", "stop")])
def test_convert_aborted_choice_raises_api_error(finish_reasons: tuple[str, ...]) -> None:
from litellm.exceptions import APIError
resp: Final = {
"id": "x",
"created": 1,
"model": "Qwen/Qwen3-8B",
"object": "chat.completion",
"choices": [
{"index": index, "message": {"role": "assistant", "content": "Oslo is 4C and"}, "finish_reason": reason}
for index, reason in enumerate(finish_reasons)
],
}
with pytest.raises(APIError, match="finish_reason=abort") as exc_info:
convert_to_model_response_object(
response_object=resp,
model_response_object=ModelResponse(),
response_type="completion",
)
assert exc_info.value.status_code == 500
assert exc_info.value.model == "Qwen/Qwen3-8B"
@pytest.mark.parametrize(("choices", "type_name"), [({}, "dict"), ("", "str"), (None, "NoneType"), (0, "int")])
@pytest.mark.asyncio
async def test_convert_non_list_choices_raises_api_error(choices: object, type_name: str) -> None: