Files
xtrm-agent/xtrm_agent/bus.py
Kaloyan Danchev 378d599125 Initial implementation of xtrm-agent multi-agent system
Multi-agent AI automation system with shared message bus, specialized
roles (coder/researcher/reviewer), and deny-by-default security.

- Config system with Pydantic validation and YAML loading
- Async message bus with inter-agent delegation
- LLM providers: Anthropic (Claude) and LiteLLM (DeepSeek/Kimi/MiniMax)
- Tool system: registry, builtins (file/bash/web), approval engine, MCP client
- Agent engine with tool-calling loop and orchestrator for multi-agent management
- CLI channel (REPL) and Discord channel
- Docker + Dockge deployment config
- Typer CLI: chat, serve, status, agents commands

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 10:21:42 +02:00

93 lines
2.9 KiB
Python

"""Shared message bus — async queues for inter-component communication."""
from __future__ import annotations
import asyncio
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Any
@dataclass
class InboundMessage:
"""Message from a channel (user) heading to an agent."""
channel: str
sender_id: str
chat_id: str
content: str
target_agent: str | None = None
timestamp: datetime = field(default_factory=lambda: datetime.now(timezone.utc))
metadata: dict[str, Any] = field(default_factory=dict)
@dataclass
class OutboundMessage:
"""Message from an agent heading back to a channel."""
channel: str
chat_id: str
content: str
reply_to: str | None = None
metadata: dict[str, Any] = field(default_factory=dict)
@dataclass
class AgentMessage:
"""Inter-agent delegation message."""
from_agent: str
to_agent: str
task: str
request_id: str = ""
response: str | None = None
class MessageBus:
"""Async queue-based message bus."""
def __init__(self) -> None:
self.inbound: asyncio.Queue[InboundMessage] = asyncio.Queue()
self.outbound: asyncio.Queue[OutboundMessage] = asyncio.Queue()
self.agent_messages: asyncio.Queue[AgentMessage] = asyncio.Queue()
self._outbound_subscribers: dict[str, list[asyncio.Queue[OutboundMessage]]] = {}
async def publish_inbound(self, msg: InboundMessage) -> None:
await self.inbound.put(msg)
async def consume_inbound(self, timeout: float = 1.0) -> InboundMessage | None:
try:
return await asyncio.wait_for(self.inbound.get(), timeout=timeout)
except asyncio.TimeoutError:
return None
async def publish_outbound(self, msg: OutboundMessage) -> None:
await self.outbound.put(msg)
# Also dispatch to channel-specific subscribers
channel_queues = self._outbound_subscribers.get(msg.channel, [])
for q in channel_queues:
await q.put(msg)
async def consume_outbound(self, timeout: float = 1.0) -> OutboundMessage | None:
try:
return await asyncio.wait_for(self.outbound.get(), timeout=timeout)
except asyncio.TimeoutError:
return None
def subscribe_outbound(self, channel: str) -> asyncio.Queue[OutboundMessage]:
"""Subscribe to outbound messages for a specific channel."""
q: asyncio.Queue[OutboundMessage] = asyncio.Queue()
self._outbound_subscribers.setdefault(channel, []).append(q)
return q
async def publish_agent_message(self, msg: AgentMessage) -> None:
await self.agent_messages.put(msg)
async def consume_agent_message(
self, timeout: float = 1.0
) -> AgentMessage | None:
try:
return await asyncio.wait_for(self.agent_messages.get(), timeout=timeout)
except asyncio.TimeoutError:
return None