fix(transport): fall back on untyped Responses items
Treat opaque 400 type-discrimination errors from OpenAI-compatible Responses endpoints as shape-specific request rejections and retry through Chat Completions. Add focused regression coverage.
gdeyoung committed
Aug 12, 2026 at 05:57 UTC
cb9c8775385b203107c62467e26d17efb807c219
3 files changed
+16
helpers/litellm_transport.py
+1
@@ -1817,6 +1817,7 @@ def _looks_like_responses_request_rejected(text: str) -> bool:
1817
"failed to deserialize input",
1818
"failed to deserialize response",
1819
"failed to deserialize responses",
1820
+ "cannot determine type",
1821
)
1822
)
1823
helpers/litellm_transport.py.dox.md
+1
@@ -29,6 +29,7 @@
29
- Normalize function tool parameter schemas with an explicit object `properties` field before Responses requests so OpenAI-compatible chat backends reached through LiteLLM can validate them.
30
- Prefer Responses API when configured, but fallback to Chat Completions when the provider does not support Responses.
31
- Fall back to Chat Completions when a Responses request is rejected before any output by an endpoint-specific or shape-specific Bad Request indicating the provider cannot parse Responses payloads.
32
+- Treat opaque type-discrimination errors such as `cannot determine type` from OpenAI-compatible Responses endpoints as shape-specific rejections.
33
- Fall back to Chat Completions when a Responses endpoint fails before output with an endpoint-specific server error, proxy path-unavailable error, or LiteLLM proxy-extra import error.
34
- Fall back to Chat Completions when LiteLLM's Responses mock streaming path tries to JSON-decode a real SSE stream before any output.
35
- Preserve Chat Completions tool calls from both non-streaming responses and streaming deltas as canonical `LLMResult` function-call items.
tests/test_stream_tool_early_stop.py
+14
@@ -1366,6 +1366,20 @@ def test_responses_fallback_does_not_mask_rate_limits():
1366
)
1367
1368
1369
+def test_responses_fallback_on_untyped_input_item_rejection():
1370
+ class BadRequestError(RuntimeError):
1371
+ status_code = 400
1372
+
1373
+ policy = litellm_transport.TransportPolicy(
1374
+ mode=litellm_transport.TransportMode.RESPONSES
1375
+ )
1376
+
1377
+ assert policy.recover(
1378
+ BadRequestError("Cannot determine type of item"), got_any_chunk=False
1379
+ ) is litellm_transport.TransportRecovery.FALLBACK_TO_CHAT
1380
+ assert policy.mode is litellm_transport.TransportMode.CHAT_COMPLETIONS
1381
+
1382
+
1383
def test_responses_response_parser_extracts_text_reasoning_and_function_calls():
1384
text_response = {
1385
"output": [