feat(agent-service): 添加直接模式下的消息处理支持

当代理服务处于非运行状态时,现在会使用process_direct方法来处理入站消息,
而不是依赖submit_direct方法。这使得服务能够在两种模式下都能正确处理消息。

添加了新的DirectModeInboundService和RunningInboundService测试类来验证
不同模式下的行为,并增加了相应的集成测试用例。
This commit is contained in:
2026-06-16 11:05:08 +08:00
parent 2cacff4a0f
commit 7020f2d67f
2 changed files with 38 additions and 3 deletions

View File

@ -1285,7 +1285,8 @@ class AgentService:
channel_identity = inbound.channel_identity channel_identity = inbound.channel_identity
try: try:
result = await self.submit_direct( runner = self.submit_direct if self.is_running else self.process_direct
result = await runner(
inbound.content, inbound.content,
session_id=inbound.session_id, session_id=inbound.session_id,
source=f"gateway:{inbound.channel}", source=f"gateway:{inbound.channel}",

View File

@ -53,6 +53,27 @@ class InvalidService:
is_running = True is_running = True
class DirectModeInboundService(AgentService):
@property
def is_running(self) -> bool:
return False
async def submit_direct(self, message: str, **kwargs: Any) -> FakeResult:
raise RuntimeError("AgentLoop.submit_direct() requires an active run() loop")
async def process_direct(self, message: str, **kwargs: Any) -> FakeResult:
return FakeResult(
session_id=kwargs.get("session_id") or "s1",
output_text=f"direct:{message}",
)
class RunningInboundService(AgentService):
@property
def is_running(self) -> bool:
return True
def test_gateway_routes_memory_channel_roundtrip(tmp_path) -> None: def test_gateway_routes_memory_channel_roundtrip(tmp_path) -> None:
async def run() -> None: async def run() -> None:
bus = MessageBus() bus = MessageBus()
@ -197,7 +218,7 @@ def test_gateway_fails_fast_for_service_without_handle_inbound_message() -> None
def test_agent_service_maps_inbound_error_to_structured_outbound() -> None: def test_agent_service_maps_inbound_error_to_structured_outbound() -> None:
async def run() -> None: async def run() -> None:
service = AgentService() service = RunningInboundService()
async def failing_submit_direct(message: str, **kwargs: Any) -> FakeResult: async def failing_submit_direct(message: str, **kwargs: Any) -> FakeResult:
raise RuntimeError("boom") raise RuntimeError("boom")
@ -217,7 +238,7 @@ def test_agent_service_maps_inbound_error_to_structured_outbound() -> None:
def test_agent_service_maps_stopped_runtime_to_stopped_outbound() -> None: def test_agent_service_maps_stopped_runtime_to_stopped_outbound() -> None:
async def run() -> None: async def run() -> None:
service = AgentService() service = RunningInboundService()
async def stopped_submit_direct(message: str, **kwargs: Any) -> FakeResult: async def stopped_submit_direct(message: str, **kwargs: Any) -> FakeResult:
raise RuntimeError("AgentLoop.submit_direct() is not accepting new tasks after stop()") raise RuntimeError("AgentLoop.submit_direct() is not accepting new tasks after stop()")
@ -233,6 +254,19 @@ def test_agent_service_maps_stopped_runtime_to_stopped_outbound() -> None:
asyncio.run(run()) asyncio.run(run())
def test_agent_service_handles_inbound_in_direct_mode() -> None:
async def run() -> None:
service = DirectModeInboundService()
outbound = await service.handle_inbound_message(
InboundMessage(channel="memory", content="hello", session_id="s1")
)
assert outbound.finish_reason == "stop"
assert outbound.content == "direct:hello"
asyncio.run(run())
def test_channel_manager_keeps_unknown_channel_outbound_undeliverable() -> None: def test_channel_manager_keeps_unknown_channel_outbound_undeliverable() -> None:
async def run() -> None: async def run() -> None:
bus = MessageBus() bus = MessageBus()