mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-08-18 18:16:38 +03:00
refactor: move MCP lifecycle out of AgentLoop (#5343)
This commit is contained in:
@@ -34,8 +34,7 @@ AUTH_HEADERS = {"Authorization": f"Bearer {API_KEY}"}
|
||||
def _make_mock_agent(response_text: str = "mock response") -> MagicMock:
|
||||
agent = MagicMock()
|
||||
agent.process_direct = AsyncMock(return_value=response_text)
|
||||
agent._connect_mcp = AsyncMock()
|
||||
agent.close_mcp = AsyncMock()
|
||||
agent.aclose = AsyncMock()
|
||||
agent._last_usage = {"prompt_tokens": 100, "completion_tokens": 50}
|
||||
return agent
|
||||
|
||||
@@ -149,6 +148,59 @@ async def test_api_routes_allow_requests_without_configured_api_key(aiohttp_clie
|
||||
mock_agent.process_direct.assert_called_once()
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAS_AIOHTTP, reason="aiohttp not installed")
|
||||
@pytest.mark.asyncio
|
||||
async def test_api_prepares_application_resources_before_each_turn(aiohttp_client) -> None:
|
||||
events: list[str] = []
|
||||
agent = _make_mock_agent()
|
||||
|
||||
async def prepare_agent() -> None:
|
||||
events.append("prepare")
|
||||
|
||||
async def process_direct(**_kwargs):
|
||||
events.append("process")
|
||||
return "ready"
|
||||
|
||||
agent.process_direct = process_direct
|
||||
app = create_app(agent, prepare_agent=prepare_agent)
|
||||
client = await aiohttp_client(app)
|
||||
|
||||
response = await client.post(
|
||||
"/v1/chat/completions",
|
||||
json={"messages": [{"role": "user", "content": "hello"}]},
|
||||
)
|
||||
|
||||
assert response.status == 200
|
||||
assert events == ["prepare", "process"]
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAS_AIOHTTP, reason="aiohttp not installed")
|
||||
@pytest.mark.asyncio
|
||||
async def test_api_preparation_is_bounded_by_request_timeout(aiohttp_client) -> None:
|
||||
agent = _make_mock_agent()
|
||||
started = asyncio.Event()
|
||||
|
||||
async def prepare_agent() -> None:
|
||||
started.set()
|
||||
await asyncio.Event().wait()
|
||||
|
||||
app = create_app(
|
||||
agent,
|
||||
request_timeout=0.01,
|
||||
prepare_agent=prepare_agent,
|
||||
)
|
||||
client = await aiohttp_client(app)
|
||||
|
||||
response = await client.post(
|
||||
"/v1/chat/completions",
|
||||
json={"messages": [{"role": "user", "content": "hello"}]},
|
||||
)
|
||||
|
||||
assert started.is_set()
|
||||
assert response.status == 504
|
||||
agent.process_direct.assert_not_awaited()
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAS_AIOHTTP, reason="aiohttp not installed")
|
||||
@pytest.mark.asyncio
|
||||
async def test_no_user_message_returns_400(aiohttp_client, app) -> None:
|
||||
@@ -275,8 +327,7 @@ async def test_followup_requests_share_same_session_key(aiohttp_client) -> None:
|
||||
|
||||
agent = MagicMock()
|
||||
agent.process_direct = fake_process
|
||||
agent._connect_mcp = AsyncMock()
|
||||
agent.close_mcp = AsyncMock()
|
||||
agent.aclose = AsyncMock()
|
||||
agent._last_usage = {}
|
||||
|
||||
app = create_app(agent, model_name="m", api_key=API_KEY)
|
||||
@@ -315,8 +366,7 @@ async def test_fixed_session_requests_are_serialized(aiohttp_client) -> None:
|
||||
|
||||
agent = MagicMock()
|
||||
agent.process_direct = slow_process
|
||||
agent._connect_mcp = AsyncMock()
|
||||
agent.close_mcp = AsyncMock()
|
||||
agent.aclose = AsyncMock()
|
||||
agent._last_usage = {}
|
||||
|
||||
app = create_app(agent, model_name="m", api_key=API_KEY)
|
||||
@@ -433,8 +483,7 @@ async def test_empty_response_falls_back_without_retry(aiohttp_client) -> None:
|
||||
|
||||
agent = MagicMock()
|
||||
agent.process_direct = always_empty
|
||||
agent._connect_mcp = AsyncMock()
|
||||
agent.close_mcp = AsyncMock()
|
||||
agent.aclose = AsyncMock()
|
||||
agent._last_usage = {}
|
||||
|
||||
app = create_app(agent, model_name="m", api_key=API_KEY)
|
||||
@@ -457,7 +506,6 @@ async def test_process_direct_accepts_media() -> None:
|
||||
from nanobot.bus.runtime_events import RuntimeEventPublisher
|
||||
|
||||
loop = AgentLoop.__new__(AgentLoop)
|
||||
loop._connect_mcp = AsyncMock()
|
||||
loop._session_locks = {}
|
||||
loop.runtime_event_publisher = RuntimeEventPublisher()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user