open-swe/tests/middleware/test_subdir_agents_middleware.py

162 lines
5.4 KiB
Python
Raw Permalink Normal View History

"""Tests for subdirectory AGENTS.md auto-loading on read_file."""
from __future__ import annotations
from types import SimpleNamespace
from typing import Any
import pytest
from langchain_core.messages import ToolMessage
from agent.middleware.subdir_agents import SubdirAgentsReadMiddleware
from agent.utils import sandbox_state
class FakeReadResult:
def __init__(
self, *, content: str | None = None, encoding: str = "utf-8", error: str | None = None
) -> None:
self.error = error
self.file_data = None if content is None else {"content": content, "encoding": encoding}
class FakeBackend:
def __init__(self, results: dict[str, Any]) -> None:
self.results = results
self.reads: list[str] = []
async def aread(self, file_path: str, offset: int = 0, limit: int = 2000) -> Any:
self.reads.append(file_path)
result = self.results.get(file_path)
if isinstance(result, Exception):
raise result
if result is None:
return FakeReadResult(error="File not found")
return result
def _request(name: str, args: dict[str, Any], thread_id: str = "t1") -> Any:
return SimpleNamespace(
tool_call={"name": name, "args": args, "id": "call-1"},
runtime=SimpleNamespace(config={"configurable": {"thread_id": thread_id}}),
)
def _ok(name: str, content: str = "file content") -> ToolMessage:
return ToolMessage(content=content, tool_call_id="call-1", status="success", name=name)
@pytest.fixture
def register_backend():
registered: list[str] = []
def _register(thread_id: str, backend: Any) -> Any:
sandbox_state.SANDBOX_BACKENDS[thread_id] = backend
registered.append(thread_id)
return backend
yield _register
for thread_id in registered:
sandbox_state.SANDBOX_BACKENDS.pop(thread_id, None)
async def test_read_file_appends_applicable_agents_in_root_to_leaf_order(register_backend) -> None:
backend = FakeBackend(
{
"/repo/AGENTS.md": FakeReadResult(content="root rules"),
"/repo/pkg/AGENTS.md": FakeReadResult(content="pkg rules"),
}
)
register_backend("t1", backend)
request = _request("read_file", {"file_path": "/repo/pkg/src/app.py"})
async def handler(_req: Any) -> ToolMessage:
return _ok("read_file")
result = await SubdirAgentsReadMiddleware().awrap_tool_call(request, handler)
assert isinstance(result.content, str)
assert "<system-reminder>" in result.content
assert "Loaded applicable AGENTS.md instructions for `/repo/pkg/src/app.py`" in result.content
assert result.content.index("Instructions from: /repo/AGENTS.md") < result.content.index(
"Instructions from: /repo/pkg/AGENTS.md"
)
assert "root rules" in result.content
assert "pkg rules" in result.content
assert backend.reads == [
"/repo/AGENTS.md",
"/repo/pkg/AGENTS.md",
"/repo/pkg/src/AGENTS.md",
]
async def test_read_file_does_not_reload_same_agents_file(register_backend) -> None:
backend = FakeBackend({"/repo/pkg/AGENTS.md": FakeReadResult(content="pkg rules")})
register_backend("t1", backend)
middleware = SubdirAgentsReadMiddleware()
async def handler(_req: Any) -> ToolMessage:
return _ok("read_file")
first = await middleware.awrap_tool_call(
_request("read_file", {"file_path": "/repo/pkg/a.py"}), handler
)
second = await middleware.awrap_tool_call(
_request("read_file", {"file_path": "/repo/pkg/b.py"}), handler
)
assert isinstance(first.content, str)
assert "pkg rules" in first.content
assert isinstance(second.content, str)
assert "system-reminder" not in second.content
assert backend.reads == ["/repo/AGENTS.md", "/repo/pkg/AGENTS.md"]
async def test_reading_agents_md_directly_does_not_append_reminder(register_backend) -> None:
backend = FakeBackend({"/repo/pkg/AGENTS.md": FakeReadResult(content="pkg rules")})
register_backend("t1", backend)
request = _request("read_file", {"file_path": "/repo/pkg/AGENTS.md"})
async def handler(_req: Any) -> ToolMessage:
return _ok("read_file", "pkg rules")
result = await SubdirAgentsReadMiddleware().awrap_tool_call(request, handler)
assert result.content == "pkg rules"
assert backend.reads == []
async def test_non_read_file_tool_is_untouched(register_backend) -> None:
backend = FakeBackend({"/repo/AGENTS.md": FakeReadResult(content="root rules")})
register_backend("t1", backend)
request = _request("edit_file", {"file_path": "/repo/a.py"})
async def handler(_req: Any) -> ToolMessage:
return _ok("edit_file")
result = await SubdirAgentsReadMiddleware().awrap_tool_call(request, handler)
assert result.content == "file content"
assert backend.reads == []
async def test_missing_backend_is_graceful() -> None:
request = _request("read_file", {"file_path": "/repo/a.py"}, thread_id="absent-thread")
async def handler(_req: Any) -> ToolMessage:
return _ok("read_file")
result = await SubdirAgentsReadMiddleware().awrap_tool_call(request, handler)
assert result.content == "file content"
def test_sync_tool_call_is_not_supported() -> None:
request = _request("read_file", {"file_path": "/repo/a.py"})
def handler(_req: Any) -> ToolMessage:
return _ok("read_file")
with pytest.raises(NotImplementedError):
SubdirAgentsReadMiddleware().wrap_tool_call(request, handler)