import os import sys from types import SimpleNamespace import pytest PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) if PROJECT_ROOT not in sys.path: sys.path.insert(0, PROJECT_ROOT) WORKSPACE_ROOT = os.path.join(PROJECT_ROOT, "workspace") import tools.requirements_tools as requirements_tools_module import tools.reference_image as reference_image_module from tools.requirements_tools import ( create_requirement_refinement_subagent_tools, make_user_query_more_detailed, ) def _tool_name(tool): if hasattr(tool, "_tool"): return tool._tool.name return getattr(tool, "name", getattr(tool, "__name__", None)) def test_requirement_refinement_subagent_tools_use_workspace_tools_only(): tools = create_requirement_refinement_subagent_tools() assert [_tool_name(tool) for tool in tools] == [ "execute_command", "sketch_pad_operations", "read_file", "grep", "sed", "echo_into", ] @pytest.mark.asyncio async def test_make_user_query_more_detailed_runs_specialist_subagent( monkeypatch, tmp_path ): monkeypatch.chdir(WORKSPACE_ROOT) calls = {"specialist": 0} stored = {} image_path = tmp_path / "query_image_001.png" image_path.write_bytes(b"fake-png-bytes") async def fake_run_subagent_with_events(**kwargs): calls["specialist"] += 1 request = kwargs["specialist_kwargs"]["message"] text = request[0]["text"] if isinstance(request, list) else request assert kwargs["specialist_kwargs"]["history"] == [] assert "SKILL.md" in text assert "Current working directory:" in text assert "Skill root: use the preferred skill root below." in text assert "Preferred skill root:" in text assert "references/docs/api/README.md" in text assert "## API Reference" in text assert "## Refined User Requirements" in text assert "## Parameter Table" in text assert "## Modeling Process" in text assert "## Notes" in text return ( "## API Reference\n- skill doc evidence\n\n" "## Refined User Requirements\n- refined requirements\n\n" "## Parameter Table\n- none\n\n" "## Modeling Process\n- step one\n\n" "## Notes\n- note" ) monkeypatch.setattr( requirements_tools_module, "run_subagent_with_events", fake_run_subagent_with_events, ) class FakeSketchPad: async def set_item(self, key, value, ttl=None, summary=None, tags=None): stored["key"] = key stored["value"] = value stored["tags"] = tags return key monkeypatch.setattr( requirements_tools_module, "get_current_sketch_pad", lambda: FakeSketchPad(), ) result = await make_user_query_more_detailed( query="Create a box", query_image_path=str(image_path), ) assert calls["specialist"] == 1 assert "SketchPad Key" in result assert stored["key"].startswith("req_") assert "Refined User Requirements" in stored["value"] @pytest.mark.asyncio async def test_make_user_query_more_detailed_uses_latest_uploaded_image_when_omitted( monkeypatch, tmp_path ): monkeypatch.chdir(WORKSPACE_ROOT) calls = {"specialist": 0} stored = {} image_path = tmp_path / "query_image_001.png" image_path.write_bytes(b"fake-png-bytes") async def fake_run_subagent_with_events(**kwargs): calls["specialist"] += 1 request = kwargs["specialist_kwargs"]["message"] assert isinstance(request, list) assert request[0]["type"] == "text" assert request[1]["type"] == "image_url" assert request[1]["image_url"]["url"].startswith("data:image/png;base64,") assert kwargs["status_payload"]["query_image_path"] == str(image_path.resolve()) return ( "## API Reference\n- skill doc evidence\n\n" "## Refined User Requirements\n- refined requirements\n\n" "## Parameter Table\n- none\n\n" "## Modeling Process\n- step one\n\n" "## Notes\n- note" ) monkeypatch.setattr( requirements_tools_module, "run_subagent_with_events", fake_run_subagent_with_events, ) class FakeContext: def retrieve_full_messages(self): return [ SimpleNamespace( role="user", content=[ {"type": "text", "text": "Create a box"}, { "type": "image_url", "image_url": { "url": "data:image/png;base64,abcd", "local_path": str(image_path), }, }, ], ) ] monkeypatch.setattr( reference_image_module, "get_current_context", lambda: FakeContext(), ) class FakeSketchPad: async def set_item(self, key, value, ttl=None, summary=None, tags=None): stored["key"] = key stored["value"] = value stored["tags"] = tags return key monkeypatch.setattr( requirements_tools_module, "get_current_sketch_pad", lambda: FakeSketchPad(), ) result = await make_user_query_more_detailed(query="Create a box") assert calls["specialist"] == 1 assert "SketchPad Key" in result assert stored["key"].startswith("req_")