178 lines
5.5 KiB
Python
178 lines
5.5 KiB
Python
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_")
|