| import unittest |
|
|
| from app.agents.browser_decision import _fallback_browser_decision |
| from app.agents.browser_tools import ( |
| _normalize_points, |
| execute_browser_tool_call, |
| parse_browser_json_response, |
| ) |
| from app.agents.graph.state import AgentState |
| from app.agents.llm_client import _parse_openrouter_responses_api |
| from app.agents.tooling import ToolCall, tool |
|
|
|
|
| @tool(description="Add two numbers together") |
| def add_numbers(left: int, right: int, note: str = "") -> dict: |
| return {"sum": left + right, "note": note} |
|
|
|
|
| class ToolingTests(unittest.TestCase): |
| def test_tool_schema_marks_required_and_optional_fields(self): |
| schema = add_numbers.schema |
| self.assertEqual(schema.name, "add_numbers") |
| self.assertEqual(schema.parameters["required"], ["left", "right"]) |
| self.assertEqual(schema.parameters["properties"]["note"]["type"], "string") |
| self.assertEqual(schema.parameters["properties"]["left"]["type"], "integer") |
|
|
| def test_execute_browser_tool_call_normalizes_search_decision(self): |
| result = execute_browser_tool_call( |
| ToolCall( |
| id="call_search", |
| name="search_web", |
| arguments={ |
| "query": "latest groq models", |
| "reason": "current page lacks the answer", |
| "known_facts": ["Groq exposes an OpenAI-compatible API"], |
| }, |
| ) |
| ) |
|
|
| self.assertEqual(result["action"], "SEARCH") |
| self.assertEqual(result["value"], "latest groq models") |
| self.assertEqual(result["reason"], "current page lacks the answer") |
| self.assertEqual(result["known_facts"], ["Groq exposes an OpenAI-compatible API"]) |
|
|
| def test_parse_browser_json_response_maps_legacy_fields(self): |
| parsed = parse_browser_json_response( |
| """ |
| { |
| "action": "navigate", |
| "url": "https://example.com/page", |
| "reason": "This looks like the source page", |
| "missing_points": ["exact launch date"] |
| } |
| """ |
| ) |
|
|
| self.assertEqual(parsed["action"], "NAVIGATE") |
| self.assertEqual(parsed["value"], "https://example.com/page") |
| self.assertEqual(parsed["reason"], "This looks like the source page") |
| self.assertEqual(parsed["missing_points"], ["exact launch date"]) |
|
|
| def test_parse_browser_json_response_rejects_scroll_when_mode_disallows_it(self): |
| with self.assertRaises(ValueError) as ctx: |
| parse_browser_json_response('{"action":"scroll"}', allow_scroll=False) |
|
|
| self.assertIn("SCROLL", str(ctx.exception)) |
|
|
| def test_normalize_points_treats_plain_string_as_single_item(self): |
| self.assertEqual( |
| _normalize_points("trump age still missing"), |
| ["trump age still missing"], |
| ) |
|
|
| def test_agent_state_update_research_progress_does_not_split_string_into_chars(self): |
| state = AgentState(task="qual idade do trump?") |
| state.update_research_progress(missing_points="trump age still missing") |
|
|
| self.assertEqual(state.missing_points, ["trump age still missing"]) |
|
|
| def test_fallback_browser_decision_does_not_leak_reasoning_as_done(self): |
| decision = _fallback_browser_decision( |
| task="qual idade do trump?", |
| current_url="https://en.wikipedia.org/wiki/Donald_Trump", |
| blocked=False, |
| allow_scroll=True, |
| links=[], |
| raw_text="We are on the Wikipedia page and should think step by step...", |
| ) |
|
|
| self.assertNotEqual(decision["action"], "DONE") |
| self.assertEqual(decision["action"], "SCROLL") |
|
|
| def test_fallback_browser_decision_uses_unseen_link_when_available(self): |
| decision = _fallback_browser_decision( |
| task="qual idade do trump?", |
| current_url="https://html.duckduckgo.com/html/?q=qual+idade+do+trump", |
| blocked=False, |
| allow_scroll=False, |
| links=["https://en.wikipedia.org/wiki/Donald_Trump"], |
| raw_text="Let us think carefully first...", |
| ) |
|
|
| self.assertEqual(decision["action"], "NAVIGATE") |
| self.assertEqual(decision["value"], "https://en.wikipedia.org/wiki/Donald_Trump") |
|
|
| def test_parse_openrouter_responses_api_separates_reasoning_from_content_and_tools(self): |
| parsed = _parse_openrouter_responses_api( |
| { |
| "model": "nvidia/nemotron-3-super-120b-a12b:free", |
| "status": "completed", |
| "output": [ |
| { |
| "type": "reasoning", |
| "summary": [ |
| "Need the current age", |
| "Should inspect the canonical page first", |
| ], |
| }, |
| { |
| "type": "function_call", |
| "id": "fc_1", |
| "call_id": "call_1", |
| "name": "navigate_to_url", |
| "arguments": "{\"url\":\"https://en.wikipedia.org/wiki/Donald_Trump\"}", |
| }, |
| { |
| "type": "message", |
| "role": "assistant", |
| "content": [ |
| { |
| "type": "output_text", |
| "text": "Vou abrir a página principal.", |
| } |
| ], |
| }, |
| ], |
| } |
| ) |
|
|
| self.assertEqual(parsed.reasoning, [ |
| "Need the current age", |
| "Should inspect the canonical page first", |
| ]) |
| self.assertEqual(parsed.content, "Vou abrir a página principal.") |
| self.assertEqual(len(parsed.tool_calls), 1) |
| self.assertEqual(parsed.tool_calls[0].name, "navigate_to_url") |
| self.assertEqual( |
| parsed.tool_calls[0].arguments, |
| {"url": "https://en.wikipedia.org/wiki/Donald_Trump"}, |
| ) |
|
|
|
|
| if __name__ == "__main__": |
| unittest.main() |
|
|