Initial commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user