test: 更新 AgentScope 相关单元测试与集成测试

- 重命名 test_react_runner.py 为 test_runner.py
- 新增 test_utils.py 测试工具函数
- 更新现有测试用例适配新架构
This commit is contained in:
qzl
2026-03-16 16:11:06 +08:00
parent 36b104fa37
commit e55f12cdc1
15 changed files with 753 additions and 717 deletions
@@ -62,4 +62,4 @@ async def test_orchestrator_emits_run_lifecycle_events() -> None:
assert result["worker"]["answer"] == "done"
event_types = [item["event"]["type"] for item in pipeline.events]
assert event_types == ["run.started", "run.finished"]
assert event_types == ["RUN_STARTED", "RUN_FINISHED"]
@@ -0,0 +1,206 @@
from __future__ import annotations
import pytest
from ag_ui.core import RunAgentInput
from agentscope.message import Msg
from core.agentscope.runtime.runner import (
AgentScopeRunner,
StageExecutionResult,
SystemAgentRuntimeConfig,
)
from schemas.agent.runtime_models import (
RouterAgentOutput,
UiMode,
WorkerAgentOutputRich,
)
from schemas.agent.system_agent import AgentType, SystemAgentLLMConfig
from schemas.user.context import UserContext, parse_profile_settings
class _FakePipeline:
def __init__(self) -> None:
self.events: list[dict[str, object]] = []
async def emit(self, *, session_id: str, event: dict[str, object]) -> str:
self.events.append({"session_id": session_id, "event": event})
return "1-0"
class _FakeSessionCtx:
def __init__(self, session: object) -> None:
self._session = session
async def __aenter__(self) -> object:
return self._session
async def __aexit__(self, exc_type, exc, tb) -> None:
del exc_type, exc, tb
def _user_context() -> UserContext:
return UserContext(
id="00000000-0000-0000-0000-000000000001",
username="alice",
email="alice@example.com",
settings=parse_profile_settings(None),
)
def _run_input() -> RunAgentInput:
return RunAgentInput.model_validate(
{
"threadId": "00000000-0000-0000-0000-000000000010",
"runId": "run-1",
"state": {},
"messages": [{"id": "u1", "role": "user", "content": "hello"}],
"tools": [
{
"name": "calendar.read",
"description": "read",
"parameters": {"type": "object"},
},
{
"name": "calendar-write",
"description": "write",
"parameters": {"type": "object"},
},
],
"context": [],
"forwardedProps": {},
}
)
def _router_output(*, ui_mode: UiMode) -> RouterAgentOutput:
return RouterAgentOutput.model_validate(
{
"normalized_task_input": {
"user_text": "hello",
"multimodal_summary": [],
},
"key_entities": [],
"constraints": [],
"task_typing": {"primary": "knowledge", "secondary": []},
"execution_mode": "onestep",
"result_typing": {"primary": "direct_answer", "secondary": []},
"ui": {
"ui_mode": ui_mode.value,
"ui_decision_reason": "need structure"
if ui_mode == UiMode.RICH
else "plain text",
},
}
)
@pytest.mark.asyncio
async def test_execute_uses_router_ui_mode_to_select_worker_output_model(
monkeypatch: pytest.MonkeyPatch,
) -> None:
runner = AgentScopeRunner()
pipeline = _FakePipeline()
worker_model_holder: dict[str, type[object]] = {}
class _CommitSession:
async def commit(self) -> None:
return None
monkeypatch.setattr(
"core.agentscope.runtime.runner.AsyncSessionLocal",
lambda: _FakeSessionCtx(_CommitSession()),
)
monkeypatch.setattr(
runner,
"_build_toolkits",
lambda **kwargs: ("router-toolkit", "worker-toolkit"),
)
async def _load_system_agent_config(**kwargs):
return SystemAgentRuntimeConfig(
agent_type=kwargs["agent_type"],
model_code="qwen3.5-flash"
if kwargs["agent_type"] == AgentType.ROUTER
else "deepseek-chat",
llm_config=SystemAgentLLMConfig(
temperature=0.1, max_tokens=256, timeout_seconds=30
),
)
monkeypatch.setattr(runner, "_load_system_agent_config", _load_system_agent_config)
async def _run_router_stage(**kwargs):
return StageExecutionResult(
message=Msg(name="router", content="", role="assistant"),
payload=_router_output(ui_mode=UiMode.RICH).model_dump(mode="json"),
response_metadata={
"model": "qwen3.5-flash",
"inputTokens": 12,
"outputTokens": 6,
"cost": 0.001,
"latencyMs": 50,
},
)
monkeypatch.setattr(runner, "_run_router_stage", _run_router_stage)
async def _persist_router_message(**kwargs) -> None:
assert kwargs["model_code"] == "qwen3.5-flash"
monkeypatch.setattr(runner, "_persist_router_message", _persist_router_message)
async def _run_worker_stage(**kwargs):
worker_model_holder["model"] = kwargs["worker_output_model"]
return StageExecutionResult(
message=Msg(name="worker", content="done", role="assistant"),
payload=WorkerAgentOutputRich.model_validate(
{
"status": "success",
"answer": "done",
"key_points": [],
"result_type": "direct_answer",
"suggested_actions": [],
"error": None,
"ui_hints": None,
}
).model_dump(mode="json", exclude_none=True),
response_metadata={
"model": "deepseek-chat",
"inputTokens": 8,
"outputTokens": 4,
"cost": 0.002,
"latencyMs": 40,
},
)
monkeypatch.setattr(runner, "_run_worker_stage", _run_worker_stage)
result = await runner.execute(
user_context=_user_context(),
context_messages=[],
pipeline=pipeline,
run_input=_run_input(),
)
assert worker_model_holder["model"].__name__ == "WorkerAgentOutputRich"
event_types = []
for item in pipeline.events:
event = item.get("event")
if isinstance(event, dict):
event_types.append(event.get("type"))
assert event_types == [
"STEP_STARTED",
"STEP_FINISHED",
"STEP_STARTED",
"STEP_FINISHED",
]
assert result["router"]["ui"]["ui_mode"] == "rich"
assert result["worker"]["answer"] == "done"
def test_extract_tool_names_normalizes_client_tool_names() -> None:
runner = AgentScopeRunner()
names = runner._extract_tool_names(_run_input())
assert names == {"calendar_read", "calendar_write"}