重构适配器系统并增强插件架构

- 在mofox_bus中,将BaseAdapter重命名为AdapterBase以提高清晰度。
- 引入了AdapterInfo类来封装适配器组件信息。
- 增强的PluginManager,支持核心消息接收器配置和适配器注册。
- 实现了EnvelopeConverter,用于将MessageEnvelope转换为内部消息格式。
- 创建了BaseAdapter类来管理插件的生命周期、配置和健康检查。
- 开发了AdapterManager,用于管理适配器实例和子流程。
- 添加了一个示例适配器插件,以展示与新适配器系统的集成。
- 删除了过时的Phi插件文档。
This commit is contained in:
Windpicker-owo
2025-11-22 12:49:37 +08:00
parent fee7611e99
commit 7c579e6ee4
12 changed files with 1130 additions and 133 deletions

View File

@@ -0,0 +1,252 @@
"""
插件系统 Adapter 基类
提供插件化的适配器支持,包装 mofox_bus.AdapterBase
添加插件生命周期、配置管理、自动启动等特性。
"""
from __future__ import annotations
import asyncio
from abc import ABC, abstractmethod
from pathlib import Path
from typing import TYPE_CHECKING, Any, Dict, Optional
from mofox_bus import AdapterBase as MoFoxAdapterBase, CoreMessageSink, MessageEnvelope
if TYPE_CHECKING:
from src.plugin_system.base.base_plugin import BasePlugin
from src.common.logger import get_logger
logger = get_logger("plugin.adapter")
class BaseAdapter(MoFoxAdapterBase, ABC):
"""
插件系统的 Adapter 基类
相比 mofox_bus.AdapterBase增加了以下特性
1. 插件生命周期管理 (on_adapter_loaded, on_adapter_unloaded)
2. 配置管理集成
3. 自动重连与健康检查
4. 子进程启动支持
"""
# 适配器元数据
adapter_name: str = "unknown_adapter"
adapter_version: str = "0.0.1"
adapter_author: str = "Unknown"
adapter_description: str = "No description"
# 是否在子进程中运行
run_in_subprocess: bool = True
# 子进程启动脚本路径(相对于插件目录)
subprocess_entry: Optional[str] = None
def __init__(
self,
core_sink: CoreMessageSink,
plugin: Optional[BasePlugin] = None,
**kwargs
):
"""
Args:
core_sink: 核心消息接收器
plugin: 所属插件实例(可选)
**kwargs: 传递给 AdapterBase 的其他参数
"""
super().__init__(core_sink, **kwargs)
self.plugin = plugin
self._config: Dict[str, Any] = {}
self._health_check_task: Optional[asyncio.Task] = None
self._running = False
@property
def config(self) -> Dict[str, Any]:
"""获取适配器配置"""
if self.plugin and hasattr(self.plugin, "config"):
return self.plugin.config
return self._config
@config.setter
def config(self, value: Dict[str, Any]) -> None:
"""设置适配器配置"""
self._config = value
async def start(self) -> None:
"""启动适配器"""
logger.info(f"启动适配器: {self.adapter_name} v{self.adapter_version}")
# 调用生命周期钩子
await self.on_adapter_loaded()
# 调用父类启动
await super().start()
# 启动健康检查
if self.config.get("enable_health_check", False):
self._health_check_task = asyncio.create_task(self._health_check_loop())
self._running = True
logger.info(f"适配器 {self.adapter_name} 启动成功")
async def stop(self) -> None:
"""停止适配器"""
logger.info(f"停止适配器: {self.adapter_name}")
self._running = False
# 停止健康检查
if self._health_check_task and not self._health_check_task.done():
self._health_check_task.cancel()
try:
await self._health_check_task
except asyncio.CancelledError:
pass
# 调用父类停止
await super().stop()
# 调用生命周期钩子
await self.on_adapter_unloaded()
logger.info(f"适配器 {self.adapter_name} 已停止")
async def on_adapter_loaded(self) -> None:
"""
适配器加载时的钩子
子类可重写以执行初始化逻辑
"""
pass
async def on_adapter_unloaded(self) -> None:
"""
适配器卸载时的钩子
子类可重写以执行清理逻辑
"""
pass
async def _health_check_loop(self) -> None:
"""健康检查循环"""
interval = self.config.get("health_check_interval", 30)
while self._running:
try:
await asyncio.sleep(interval)
# 执行健康检查
is_healthy = await self.health_check()
if not is_healthy:
logger.warning(f"适配器 {self.adapter_name} 健康检查失败,尝试重连...")
await self.reconnect()
except asyncio.CancelledError:
break
except Exception as e:
logger.error(f"适配器 {self.adapter_name} 健康检查异常: {e}", exc_info=True)
async def health_check(self) -> bool:
"""
健康检查
子类可重写以实现自定义检查逻辑
Returns:
bool: 是否健康
"""
# 默认检查 WebSocket 连接状态
if self._ws and not self._ws.closed:
return True
return False
async def reconnect(self) -> None:
"""
重新连接
子类可重写以实现自定义重连逻辑
"""
try:
await self.stop()
await asyncio.sleep(2) # 等待一段时间再重连
await self.start()
except Exception as e:
logger.error(f"适配器 {self.adapter_name} 重连失败: {e}", exc_info=True)
def get_subprocess_entry_path(self) -> Optional[Path]:
"""
获取子进程启动脚本的完整路径
Returns:
Path | None: 脚本路径,如果不存在则返回 None
"""
if not self.subprocess_entry:
return None
if not self.plugin:
return None
# 获取插件目录
plugin_dir = Path(self.plugin.__file__).parent
entry_path = plugin_dir / self.subprocess_entry
if entry_path.exists():
return entry_path
logger.warning(f"子进程入口脚本不存在: {entry_path}")
return None
@classmethod
def get_adapter_info(cls) -> "AdapterInfo":
"""获取适配器的信息
Returns:
AdapterInfo: 适配器组件信息
"""
from src.plugin_system.base.component_types import AdapterInfo
return AdapterInfo(
name=getattr(cls, "adapter_name", cls.__name__.lower().replace("adapter", "")),
version=getattr(cls, "adapter_version", "1.0.0"),
platform=getattr(cls, "platform", "unknown"),
description=getattr(cls, "adapter_description", ""),
enabled=True,
run_in_subprocess=getattr(cls, "run_in_subprocess", False),
subprocess_entry=getattr(cls, "subprocess_entry", None),
)
@abstractmethod
def from_platform_message(self, raw: Any) -> MessageEnvelope:
"""
将平台原始消息转换为 MessageEnvelope
子类必须实现此方法
Args:
raw: 平台原始消息
Returns:
MessageEnvelope: 统一的消息信封
"""
raise NotImplementedError
async def _send_platform_message(self, envelope: MessageEnvelope) -> None:
"""
发送消息到平台
如果使用了 WebSocketAdapterOptions 或 HttpAdapterOptions
此方法会自动处理。否则子类需要重写此方法。
Args:
envelope: 要发送的消息信封
"""
# 如果配置了自动传输,调用父类方法
if self._transport_config:
await super()._send_platform_message(envelope)
else:
raise NotImplementedError(
f"适配器 {self.adapter_name} 未配置自动传输,必须重写 _send_platform_message 方法"
)
__all__ = ["BaseAdapter"]