From b1c6c9eacde22637e3873318c506ae95dde6ba06 Mon Sep 17 00:00:00 2001 From: Vineeth Sai Date: Wed, 21 Oct 2026 09:31:24 -0700 Subject: [PATCH 1/2] test(agentic loop): pin the converted-stream replay when no tool is called The converted-stream fix landed upstream and the tool-call follow-up is covered. The path where the model answers without a tool call and no callback gates ends in _converted_stream_replay, and replacing that with return None passes every existing test. Add a case per interception prefix that expects a CustomStreamWrapper iterating back to the original reply. --- .../test_chat_completion_agentic_loop.py | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/unit/litellm_core_utils/test_chat_completion_agentic_loop.py b/tests/unit/litellm_core_utils/test_chat_completion_agentic_loop.py index d26da679246..7f09f93c977 100644 --- a/tests/unit/litellm_core_utils/test_chat_completion_agentic_loop.py +++ b/tests/unit/litellm_core_utils/test_chat_completion_agentic_loop.py @@ -1040,6 +1040,43 @@ async def test_converted_stream_replays_the_final_answer_with_the_requested_usag ) +@pytest.mark.asyncio +@pytest.mark.parametrize( + "prefix", (WEBSEARCH_INTERCEPTION_PREFIX, CODE_INTERPRETER_INTERCEPTION_PREFIX, HEADROOM_INTERCEPTION_PREFIX) +) +async def test_converted_stream_replays_the_original_reply_when_no_tool_is_called( + monkeypatch: pytest.MonkeyPatch, prefix: str +): + """A streamed request that was converted to a non-streaming call still gets a + stream back when the model answers without a tool call and no callback gates.""" + monkeypatch.setattr(litellm, "callbacks", []) + logging_obj: Final = Logging( + model="gpt-4o", + messages=[{"role": "user", "content": "hello"}], + stream=False, + call_type="acompletion", + start_time=datetime.datetime(2026, 1, 1, tzinfo=datetime.timezone.utc), + litellm_call_id="call-no-tool", + function_id="fn-no-tool", + ) + + result: Final = await maybe_run_chat_completion_agentic_loop( + response=_plain_model_response("no tool needed"), + model="gpt-4o", + messages=[{"role": "user", "content": "hello"}], + optional_params={}, + kwargs={f"{prefix}_converted_stream": True}, + logging_obj=logging_obj, + custom_llm_provider="azure", + stream=False, + ) + + assert isinstance(result, CustomStreamWrapper) + chunks: Final = [chunk async for chunk in result] + content: Final = "".join(chunk.choices[0].delta.content or "" for chunk in chunks if chunk.choices) + assert content == "no tool needed" + + class TestAgenticFollowUpKeepsTheProviderPrefix: @staticmethod async def _run_followup( From d5a9ff2aac6f483b68acd17eb24af901b8be4f06 Mon Sep 17 00:00:00 2001 From: Vineeth Sai Date: Thu, 5 Nov 2026 16:32:02 -0800 Subject: [PATCH 2/2] test(agentic loop): drop the redundant callbacks reset The unit conftest already clears litellm.callbacks before every test, so the monkeypatch did nothing. Also drop the docstring, which only restated the test name. --- .../test_chat_completion_agentic_loop.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/unit/litellm_core_utils/test_chat_completion_agentic_loop.py b/tests/unit/litellm_core_utils/test_chat_completion_agentic_loop.py index 7f09f93c977..d86128eae68 100644 --- a/tests/unit/litellm_core_utils/test_chat_completion_agentic_loop.py +++ b/tests/unit/litellm_core_utils/test_chat_completion_agentic_loop.py @@ -1044,12 +1044,7 @@ async def test_converted_stream_replays_the_final_answer_with_the_requested_usag @pytest.mark.parametrize( "prefix", (WEBSEARCH_INTERCEPTION_PREFIX, CODE_INTERPRETER_INTERCEPTION_PREFIX, HEADROOM_INTERCEPTION_PREFIX) ) -async def test_converted_stream_replays_the_original_reply_when_no_tool_is_called( - monkeypatch: pytest.MonkeyPatch, prefix: str -): - """A streamed request that was converted to a non-streaming call still gets a - stream back when the model answers without a tool call and no callback gates.""" - monkeypatch.setattr(litellm, "callbacks", []) +async def test_converted_stream_replays_the_original_reply_when_no_tool_is_called(prefix: str): logging_obj: Final = Logging( model="gpt-4o", messages=[{"role": "user", "content": "hello"}],