From 2715fed8f83bea6add5b00894e0878a066a82c2d Mon Sep 17 00:00:00 2001 From: Alessandro <155005371+3clyp50@users.noreply.github.com> Date: Wed, 30 Sep 2026 16:47:56 +0200 Subject: [PATCH] test(models): cover Chat Completions reasoning fallback Cover streaming and complete messages, legacy-field precedence, empty or missing reasoning, and encrypted-reasoning filtering. Document the transport contract. Validation: transport, Responses architecture, encrypted-reasoning, and refusal suites passed (126 tests); git diff --check passed. --- helpers/litellm_transport.py.dox.md | 1 + tests/test_stream_tool_early_stop.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/helpers/litellm_transport.py.dox.md b/helpers/litellm_transport.py.dox.md index 551b41259..ce9af15e1 100644 --- a/helpers/litellm_transport.py.dox.md +++ b/helpers/litellm_transport.py.dox.md @@ -45,6 +45,7 @@ - Serialize synthesized Responses function-call JSON with literal Unicode so streamed raw-response logs preserve tool arguments. - Preserve provider-state metadata when Responses API calls succeed, and fall back to local replay when provider state is unsupported. - Keep prompt-cache markers only for providers that accept them. +- Chat Completions parsing prefers `reasoning_content` and falls back to `reasoning` for both streaming deltas and complete messages, preserving response content and applying the same encrypted-reasoning filter. Verify with `test_chat_reasoning_field_fallback` in `tests/test_stream_tool_early_stop.py`. - Automatic `cache_control` marker placement is provider-gated; the OpenAI/Codex Responses configuration bypasses it. Skipping a marker target never removes its message, tool call, or reasoning content. Responses `phase: commentary`, encrypted reasoning, native replay, and request flags remain owned by their Responses paths. - For supported Chat providers, mark the last leading system/developer message and the last two nonempty assistant messages. Retain the previous assistant boundary as the conversation grows; leave user/tool tails containing rebuilt extras unmarked. Before the first assistant message, cache only the leading context. Do not add markers to empty assistant content or thinking/redacted-thinking blocks. Automatic placement uses at most three message breakpoints, leaving one for tool definitions; this is not a limit on conversation messages or native tool calls. Do not mutate caller messages or stored history. diff --git a/tests/test_stream_tool_early_stop.py b/tests/test_stream_tool_early_stop.py index 65d9310c6..988962009 100644 --- a/tests/test_stream_tool_early_stop.py +++ b/tests/test_stream_tool_early_stop.py @@ -1444,6 +1444,26 @@ def test_responses_fallback_on_untyped_input_item_rejection(): assert policy.mode is litellm_transport.TransportMode.CHAT_COMPLETIONS +@pytest.mark.parametrize("source", ["delta", "message"]) +@pytest.mark.parametrize( + ("fields", "expected"), + [ + ({"reasoning_content": "legacy"}, "legacy"), + ({"reasoning": "fallback"}, "fallback"), + ({"reasoning_content": "legacy", "reasoning": "fallback"}, "legacy"), + ({"reasoning_content": "", "reasoning": "fallback"}, "fallback"), + ({"reasoning": "summary__ENCRYPTED_REASONING__opaque"}, "summary"), + ({}, ""), + ], +) +def test_chat_reasoning_field_fallback(source, fields, expected): + parsed = litellm_transport.ChatCompletionsTransport.parse( + {"choices": [{source: {"content": "answer", **fields}}]} + ) + + assert parsed == {"reasoning_delta": expected, "response_delta": "answer"} + + def test_responses_response_parser_extracts_text_reasoning_and_function_calls(): text_response = { "output": [