feat(app-instance): 集成Beaver后端并更新配置管理
集成新的Beaver后端服务到应用实例中,替换原有的nanobot实现。 主要变更包括: - 在Dockerfile和环境配置中添加Beaver相关路径和配置变量 - 更新工作目录结构从.nanobot到.beaver - 实现Beaver引擎加载器,支持配置文件加载和工具组装 - 添加内置工具如ListDirectoryTool、ReadFileTool、SearchFilesTool - 更新消息处理流程,支持通道适配器和网关模式 - 重构技能系统,支持显式工具提示和嵌入式检索 - 改进错误处理和生命周期管理 此变更使应用实例能够使用统一的Beaver后端进行AI代理运行时管理。
This commit is contained in:
@ -1,2 +1,7 @@
|
||||
"""Channel interfaces."""
|
||||
|
||||
from .base import ChannelAdapter
|
||||
from .manager import ChannelManager
|
||||
from .memory import MemoryChannelAdapter
|
||||
|
||||
__all__ = ["ChannelAdapter", "ChannelManager", "MemoryChannelAdapter"]
|
||||
|
||||
24
app-instance/backend/beaver/interfaces/channels/base.py
Normal file
24
app-instance/backend/beaver/interfaces/channels/base.py
Normal file
@ -0,0 +1,24 @@
|
||||
"""Channel adapter contracts for gateway-facing integrations."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Protocol
|
||||
|
||||
from beaver.foundation.events import MessageBus, OutboundMessage
|
||||
|
||||
|
||||
class ChannelAdapter(Protocol):
|
||||
"""Minimal contract every gateway channel must implement."""
|
||||
|
||||
name: str
|
||||
bus: MessageBus
|
||||
|
||||
async def start(self) -> None:
|
||||
"""Prepare the channel before messages are routed."""
|
||||
|
||||
async def stop(self) -> None:
|
||||
"""Stop accepting/routing channel messages."""
|
||||
|
||||
async def send(self, message: OutboundMessage) -> None:
|
||||
"""Deliver an outbound message to the concrete channel."""
|
||||
|
||||
76
app-instance/backend/beaver/interfaces/channels/manager.py
Normal file
76
app-instance/backend/beaver/interfaces/channels/manager.py
Normal file
@ -0,0 +1,76 @@
|
||||
"""Channel manager for routing gateway outbound messages."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from contextlib import suppress
|
||||
|
||||
from beaver.foundation.events import MessageBus, OutboundMessage
|
||||
|
||||
from .base import ChannelAdapter
|
||||
|
||||
|
||||
class ChannelManager:
|
||||
"""Start/stop channel adapters and dispatch outbound messages to them."""
|
||||
|
||||
def __init__(self, bus: MessageBus) -> None:
|
||||
self.bus = bus
|
||||
self.channels: dict[str, ChannelAdapter] = {}
|
||||
self.undeliverable: list[OutboundMessage] = []
|
||||
self.started = False
|
||||
|
||||
def register(self, channel: ChannelAdapter) -> None:
|
||||
if self.started:
|
||||
raise RuntimeError("Cannot register channels after ChannelManager.start()")
|
||||
if channel.name in self.channels:
|
||||
raise ValueError(f"Channel already registered: {channel.name}")
|
||||
if channel.bus is not self.bus:
|
||||
raise ValueError("Channel must share the same MessageBus as ChannelManager")
|
||||
self.channels[channel.name] = channel
|
||||
|
||||
async def start(self) -> None:
|
||||
started: list[ChannelAdapter] = []
|
||||
try:
|
||||
for channel in self.channels.values():
|
||||
await channel.start()
|
||||
started.append(channel)
|
||||
except BaseException:
|
||||
for channel in reversed(started):
|
||||
with suppress(BaseException):
|
||||
await channel.stop()
|
||||
raise
|
||||
else:
|
||||
self.started = True
|
||||
|
||||
async def stop(self) -> None:
|
||||
errors: list[BaseException] = []
|
||||
for channel in reversed(tuple(self.channels.values())):
|
||||
try:
|
||||
await channel.stop()
|
||||
except Exception as exc: # pragma: no cover - defensive cleanup path
|
||||
errors.append(exc)
|
||||
self.started = False
|
||||
if errors:
|
||||
raise RuntimeError(f"Failed to stop {len(errors)} channel(s)") from errors[0]
|
||||
|
||||
async def dispatch_outbound(self, stop_event: asyncio.Event) -> None:
|
||||
"""Route bus outbound messages until stopped and the queue is drained."""
|
||||
|
||||
while True:
|
||||
if stop_event.is_set() and self.bus.outbound_size == 0:
|
||||
break
|
||||
|
||||
try:
|
||||
message = await asyncio.wait_for(self.bus.consume_outbound(), timeout=0.25)
|
||||
except asyncio.TimeoutError:
|
||||
continue
|
||||
|
||||
channel = self.channels.get(message.channel)
|
||||
if channel is None:
|
||||
self.undeliverable.append(message)
|
||||
continue
|
||||
|
||||
try:
|
||||
await channel.send(message)
|
||||
except Exception: # pragma: no cover - defensive channel isolation
|
||||
self.undeliverable.append(message)
|
||||
57
app-instance/backend/beaver/interfaces/channels/memory.py
Normal file
57
app-instance/backend/beaver/interfaces/channels/memory.py
Normal file
@ -0,0 +1,57 @@
|
||||
"""In-memory channel adapter for tests and local gateway embedding."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
from beaver.foundation.events import InboundMessage, MessageBus, OutboundMessage
|
||||
|
||||
|
||||
class MemoryChannelAdapter:
|
||||
"""A local channel that stores outbound messages in memory."""
|
||||
|
||||
def __init__(self, bus: MessageBus, *, name: str = "memory") -> None:
|
||||
self.name = name
|
||||
self.bus = bus
|
||||
self.started = False
|
||||
self.sent_messages: list[OutboundMessage] = []
|
||||
|
||||
async def start(self) -> None:
|
||||
self.started = True
|
||||
|
||||
async def stop(self) -> None:
|
||||
self.started = False
|
||||
|
||||
async def send(self, message: OutboundMessage) -> None:
|
||||
self.sent_messages.append(message)
|
||||
|
||||
async def publish_text(
|
||||
self,
|
||||
content: str,
|
||||
*,
|
||||
session_id: str | None = None,
|
||||
user_id: str | None = None,
|
||||
title: str | None = None,
|
||||
execution_context: str | None = None,
|
||||
model: str | None = None,
|
||||
provider_name: str | None = None,
|
||||
embedding_model: str | None = None,
|
||||
metadata: dict[str, Any] | None = None,
|
||||
) -> InboundMessage:
|
||||
"""Publish a text message from this channel into the shared bus."""
|
||||
|
||||
message = InboundMessage(
|
||||
channel=self.name,
|
||||
content=content,
|
||||
session_id=session_id,
|
||||
user_id=user_id,
|
||||
title=title,
|
||||
execution_context=execution_context,
|
||||
model=model,
|
||||
provider_name=provider_name,
|
||||
embedding_model=embedding_model,
|
||||
metadata=metadata or {},
|
||||
)
|
||||
await self.bus.publish_inbound(message)
|
||||
return message
|
||||
|
||||
Reference in New Issue
Block a user