@@ -261,9 +261,10 @@ async def text_chat(self, **kwargs) -> LLMResponse:
261261
262262
263263class SequentialToolProvider (MockProvider ):
264- def __init__ (self , tool_sequence : list [str ]):
264+ def __init__ (self , tool_sequence : list [str ], * , same_args : bool = False ):
265265 super ().__init__ ()
266266 self .tool_sequence = tool_sequence
267+ self .same_args = same_args
267268
268269 async def text_chat (self , ** kwargs ) -> LLMResponse :
269270 self .call_count += 1
@@ -276,11 +277,16 @@ async def text_chat(self, **kwargs) -> LLMResponse:
276277 )
277278
278279 tool_name = self .tool_sequence [self .call_count - 1 ]
280+ tool_args = (
281+ {"query" : "same" }
282+ if self .same_args
283+ else {"query" : f"step-{ self .call_count } " }
284+ )
279285 return LLMResponse (
280286 role = "assistant" ,
281287 completion_text = "" ,
282288 tools_call_name = [tool_name ],
283- tools_call_args = [{ "query" : f"step- { self . call_count } " } ],
289+ tools_call_args = [tool_args ],
284290 tools_call_ids = [f"call_{ self .call_count } " ],
285291 usage = TokenUsage (input_other = 10 , output = 5 ),
286292 )
@@ -734,7 +740,7 @@ async def test_same_tool_consecutive_results_include_escalating_guidance(
734740):
735741 runner_cls = type (runner )
736742 total_calls = runner_cls .REPEATED_TOOL_NOTICE_L3_THRESHOLD
737- provider = SequentialToolProvider (["test_tool" ] * total_calls )
743+ provider = SequentialToolProvider (["test_tool" ] * total_calls , same_args = True )
738744 tool = FunctionTool (
739745 name = "test_tool" ,
740746 description = "测试工具" ,
@@ -797,14 +803,69 @@ async def test_same_tool_consecutive_results_include_escalating_guidance(
797803 assert level_3_notice in content
798804
799805
806+ @pytest .mark .asyncio
807+ async def test_same_tool_streak_resets_when_arguments_change (
808+ runner , mock_tool_executor , mock_hooks
809+ ):
810+ runner_cls = type (runner )
811+ total_calls = runner_cls .REPEATED_TOOL_NOTICE_L3_THRESHOLD
812+ provider = SequentialToolProvider (["test_tool" ] * total_calls )
813+ tool = FunctionTool (
814+ name = "test_tool" ,
815+ description = "测试工具" ,
816+ parameters = {"type" : "object" , "properties" : {"query" : {"type" : "string" }}},
817+ handler = AsyncMock (),
818+ )
819+ request = ProviderRequest (
820+ prompt = "请连续执行不同参数的工具" ,
821+ func_tool = ToolSet (tools = [tool ]),
822+ contexts = [],
823+ )
824+
825+ await runner .reset (
826+ provider = provider ,
827+ request = request ,
828+ run_context = ContextWrapper (context = None ),
829+ tool_executor = mock_tool_executor ,
830+ agent_hooks = mock_hooks ,
831+ streaming = False ,
832+ )
833+
834+ async for _ in runner .step_until_done (total_calls + 1 ):
835+ pass
836+
837+ tool_messages = [
838+ m for m in runner .run_context .messages if getattr (m , "role" , None ) == "tool"
839+ ]
840+ assert len (tool_messages ) == total_calls
841+
842+ tool_contents = [str (message .content ) for message in tool_messages ]
843+ notices = [
844+ runner_cls .REPEATED_TOOL_NOTICE_L1_TEMPLATE .format (
845+ tool_name = "test_tool" ,
846+ streak = runner_cls .REPEATED_TOOL_NOTICE_L1_THRESHOLD ,
847+ ),
848+ runner_cls .REPEATED_TOOL_NOTICE_L2_TEMPLATE .format (
849+ tool_name = "test_tool" ,
850+ streak = runner_cls .REPEATED_TOOL_NOTICE_L2_THRESHOLD ,
851+ ),
852+ runner_cls .REPEATED_TOOL_NOTICE_L3_TEMPLATE .format (
853+ tool_name = "test_tool" ,
854+ streak = runner_cls .REPEATED_TOOL_NOTICE_L3_THRESHOLD ,
855+ ),
856+ ]
857+ assert all (notice not in content for content in tool_contents for notice in notices )
858+
859+
800860@pytest .mark .asyncio
801861async def test_same_tool_streak_resets_after_switching_tools (
802862 runner , mock_tool_executor , mock_hooks
803863):
804864 runner_cls = type (runner )
805865 repeated_after_reset = runner_cls .REPEATED_TOOL_NOTICE_L1_THRESHOLD
806866 provider = SequentialToolProvider (
807- ["test_tool" , "other_tool" , * (["test_tool" ] * repeated_after_reset )]
867+ ["test_tool" , "other_tool" , * (["test_tool" ] * repeated_after_reset )],
868+ same_args = True ,
808869 )
809870 tool_a = FunctionTool (
810871 name = "test_tool" ,
0 commit comments