test_agent2_react_prompt.py
1 """Tests for restai.agent2.react_prompt — ReAct parser and prompt builder.""" 2 from restai.agent2.react_prompt import ( 3 build_react_system_prompt, 4 format_tool_for_react, 5 parse_react_response, 6 ) 7 from restai.agent2.tool_adapter import AdaptedTool 8 9 10 def _make_tool(name: str, description: str = "", schema: dict | None = None) -> AdaptedTool: 11 return AdaptedTool( 12 name=name, 13 description=description or f"Tool {name}", 14 input_schema=schema or {"type": "object", "properties": {"q": {"type": "string"}}, "required": ["q"]}, 15 fn=lambda **kw: "ok", 16 ) 17 18 19 # ---------- parse_react_response ---------- 20 21 22 def test_parse_action_basic(): 23 text = 'Thought: think\nAction: search\nAction Input: {"q": "hello"}' 24 result = parse_react_response(text) 25 assert result.kind == "action" 26 assert result.thought == "think" 27 assert result.action_name == "search" 28 assert result.action_input == {"q": "hello"} 29 30 31 def test_parse_action_fenced_json(): 32 text = ( 33 "Thought: ok\n" 34 "Action: foo\n" 35 "Action Input:\n" 36 "```json\n" 37 '{"x": 1}\n' 38 "```" 39 ) 40 result = parse_react_response(text) 41 assert result.kind == "action" 42 assert result.action_name == "foo" 43 assert result.action_input == {"x": 1} 44 45 46 def test_parse_final_answer(): 47 text = "Thought: done\nFinal Answer: 42" 48 result = parse_react_response(text) 49 assert result.kind == "final" 50 assert result.thought == "done" 51 assert result.final_text == "42" 52 53 54 def test_parse_plain_text(): 55 text = "Just some text without structure" 56 result = parse_react_response(text) 57 assert result.kind == "text" 58 assert result.final_text == text.strip() 59 60 61 def test_parse_action_wins_over_final(): 62 text = "Thought: x\nAction: tool\nAction Input: {}\nFinal Answer: done" 63 result = parse_react_response(text) 64 assert result.kind == "action" 65 assert result.action_name == "tool" 66 67 68 def test_parse_action_no_action_input(): 69 text = "Action: bar" 70 result = parse_react_response(text) 71 assert result.kind == "action" 72 assert result.action_name == "bar" 73 assert result.action_input == {} 74 75 76 # ---------- build_react_system_prompt ---------- 77 78 79 def test_build_react_system_prompt_with_tools(): 80 tools = [_make_tool("search", "Search the web"), _make_tool("calc", "Calculate")] 81 prompt = build_react_system_prompt("You are helpful.", tools) 82 assert "search" in prompt 83 assert "calc" in prompt 84 assert "You are helpful." in prompt 85 assert "Action:" in prompt 86 assert "Final Answer:" in prompt 87 88 89 def test_build_react_system_prompt_empty_tools(): 90 prompt = build_react_system_prompt("You are helpful.", []) 91 assert "You have no tools available" in prompt 92 assert "Final Answer:" in prompt 93 94 95 def test_build_react_system_prompt_default_base(): 96 prompt = build_react_system_prompt("", [_make_tool("x")]) 97 assert "You are a helpful assistant." in prompt 98 99 100 # ---------- format_tool_for_react ---------- 101 102 103 def test_format_tool_for_react_output(): 104 tool = _make_tool( 105 "search", 106 "Search the web", 107 { 108 "type": "object", 109 "properties": {"q": {"type": "string"}, "limit": {"type": "integer"}}, 110 "required": ["q"], 111 }, 112 ) 113 output = format_tool_for_react(tool) 114 assert "search" in output 115 assert "Search the web" in output 116 assert "q:" in output 117 assert "limit?" in output # optional param has ? 118 119 120 def test_format_tool_for_react_no_params(): 121 tool = AdaptedTool( 122 name="noop", 123 description="Does nothing", 124 input_schema={"type": "object", "properties": {}, "required": []}, 125 fn=lambda: "ok", 126 ) 127 output = format_tool_for_react(tool) 128 assert "no arguments" in output