Files
OpenOPC/opc/layer0_interaction/message_bus.py
T
2026-07-01 17:56:31 +08:00

85 lines
3.0 KiB
Python

"""Unified message bus — routes messages between channels and the system."""
from __future__ import annotations
import asyncio
from typing import Any, Callable, Coroutine, Optional
from loguru import logger
from opc.core.models import SystemMessage, UserMessage
InboundHandler = Callable[[UserMessage], Coroutine[Any, Any, Optional[SystemMessage]]]
class MessageBus:
"""Async message bus for routing between interaction channels and the OPC engine.
Channels publish inbound messages; the engine processes them and publishes outbound responses.
"""
def __init__(self) -> None:
self._inbound_queue: asyncio.Queue[UserMessage] | None = None
self._outbound_queue: asyncio.Queue[SystemMessage] | None = None
self._inbound_handler: InboundHandler | None = None
self._running = False
def _inbound(self) -> asyncio.Queue[UserMessage]:
if self._inbound_queue is None:
self._inbound_queue = asyncio.Queue()
return self._inbound_queue
def _outbound(self) -> asyncio.Queue[SystemMessage]:
if self._outbound_queue is None:
self._outbound_queue = asyncio.Queue()
return self._outbound_queue
def set_handler(self, handler: InboundHandler) -> None:
self._inbound_handler = handler
async def publish_inbound(self, message: UserMessage) -> None:
await self._inbound().put(message)
async def publish_outbound(self, message: SystemMessage) -> None:
await self._outbound().put(message)
async def get_response(self, timeout: float = 600.0) -> SystemMessage | None:
try:
return await asyncio.wait_for(self._outbound().get(), timeout=timeout)
except asyncio.TimeoutError:
return None
async def start(self) -> None:
"""Start processing inbound messages."""
self._running = True
while self._running:
try:
msg = await asyncio.wait_for(self._inbound().get(), timeout=1.0)
except asyncio.TimeoutError:
continue
if self._inbound_handler:
try:
response = await self._inbound_handler(msg)
if response:
await self._outbound().put(response)
except Exception as e:
logger.error(f"Message handler error: {e}")
await self._outbound().put(SystemMessage(
channel=msg.channel,
user_id=msg.user_id,
session_id=msg.session_id,
content=f"Error processing message: {e}",
message_type="reply",
))
def stop(self) -> None:
self._running = False
async def process_single(self, message: UserMessage) -> SystemMessage | None:
"""Process a single message synchronously (for CLI use)."""
if self._inbound_handler:
return await self._inbound_handler(message)
return None