From 6760379b4a736a720fd2d8b0928bfc3382c57b5d Mon Sep 17 00:00:00 2001 From: Mateo Wang Date: Fri, 21 Aug 2026 20:12:54 -0700 Subject: [PATCH] test: pin the capped turn that carries only the refused call --- .../test_websearch_agentic_loop_cap.py | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/test_litellm/integrations/websearch_interception/test_websearch_agentic_loop_cap.py b/tests/test_litellm/integrations/websearch_interception/test_websearch_agentic_loop_cap.py index 1d36ca76832..de3fba51eec 100644 --- a/tests/test_litellm/integrations/websearch_interception/test_websearch_agentic_loop_cap.py +++ b/tests/test_litellm/integrations/websearch_interception/test_websearch_agentic_loop_cap.py @@ -213,6 +213,40 @@ class TestCappedLoopReturnsTerminalResponse: assert _block_types(result) == ["server_tool_use", "web_search_tool_result", "text"] + @pytest.mark.asyncio + async def test_turn_carrying_only_the_refused_call_still_ends_cleanly(self): + """ + The refused call can be every block the model produced, which leaves the + turn with no content once it is dropped. That still has to come back as a + finished turn rather than as the leaked call, so the client stops instead + of waiting on a tool it cannot run, and the rest of the message survives + so the request is still billed and traceable. + + An empty turn renders as nothing, which is the ceiling being set too low + for the question rather than a malformed response. + """ + nothing_but_the_refused_call = { + "id": "msg_123", + "type": "message", + "role": "assistant", + "model": "claude-sonnet-4-5", + "content": [_internal_tool_use_block()], + "stop_reason": "tool_use", + "usage": {"input_tokens": 10, "output_tokens": 5}, + } + + result = await _run_hooks( + self.handler, + self.callback, + kwargs={"_agentic_loop_depth": 3, "max_agentic_loops": 3}, + response=nothing_but_the_refused_call, + ) + + assert result["content"] == [] + assert result["stop_reason"] == "end_turn" + assert result["usage"] == {"input_tokens": 10, "output_tokens": 5} + assert result["id"] == "msg_123" + @pytest.mark.asyncio async def test_no_follow_up_model_call_is_planned(self): """