f88031b0c9
* Refactor: using sqlmodel(sqlchemy+pydantic) as ORM framework and switch to async-based sqlite operation (#2294) * stage * stage * refactor: using sqlchemy as ORM framework, switch to async-based sqlite operation - using sqlmodel as ORM(based on sqlchemy and pydantic) - add Persona, Preference, PlatformMessageHistory table * fix: conversation * fix: remove redundant explicit session.commit, and fix some type error * fix: conversation context issue * chore: remove comments * chore: remove exclude_content param * Fix: 当多个相同消息平台实例部署时上下文可能混乱(共享) (#2298) * perf: update astrbot event session format, using platfrom id to ensure uniqueness fixes: #1000 * fix: 更新 MessageSession 类以使用 platform_id 作为唯一标识符,并调整相关方法以确保一致性 * fix: 更新 MessageSession 文档以明确 platform_id 的赋值规则,并调整 get_platform 和 get_platform_inst 方法的返回类型 * Improve: 引入全新的人格管理模式以及重构函数工具管理器 (#2305) * feat: add persona management * refactor: 重构函数工具管理器,引入 ToolSet,并让 Persona 支持绑定 Tools * feat: 更新 Persona 工具选择逻辑,支持全选和指定工具的切换 * feat: 更新 BaseDatabase 中的 persona 方法返回类型,支持返回 None * fix: platform id * feat: add support to sync mcp servers from ModelScope (#2313) * fix: 修复访问令牌的空格问题 * chore: 移除 MCP 市场相关逻辑 (#2314) * chore: 移除 MCP 市场相关路由 * Refactor: 重构配置文件管理,以支持更灵活的、会话粒度的(基于 umo part)配置文件隔离 (#2328) * refactor: 重构配置文件管理,以支持更灵活的、基于 umo part 的配置文件隔离 * Refactor: 重构配置前端页面,新增数个配置项 (#2331) * refactor: 重构配置前端页面,新增数个配置项 * feat: 完善多配置文件结构 * perf: 系统配置入口 * fix: normal config item list not display * fix: 修复 axios 请求中的上下文引用问题 * chore: remove status checking in chat page * fix: 修复 stage 在不同 pipeline 中被重复使用的问题和 persona 相关问题 * Feature: 增加图片转述提供商配置、支持用户自定义模型模态能力 (#2422) * feat: 增加图片转述提供商配置、支持用户自定义模型模态能力 * fix: 修复 LLMRequestSubStage 中会话管理方法参数不一致的问题,简化方法调用 * Feature: 优化 WebSearch 的爬取网页速度并且支持使用 Tavily 作为搜索引擎 (#2427) * feat: 优化了 websearch 的速度;支持 Tavily 作为搜索引擎 * fix: 优化日志记录格式,修复搜索结果处理中的索引和内容显示问题 * feat: 添加对话选中状态管理,优化默认对话加载逻辑 * feat: 支持通过解析URL 的方式导入网页数据到知识库 (#2280) * feat:为webchat页面添加一个手动上传文件按钮(目前只处理图片) * fix:上传后清空value,允许触发change事件以多次上传同一张图片 * perf:webchat页面消息发送后清空图片预览缩略图,维持与文本信息行为一致 * perf:将文件输入的值重置为空字符串以提升浏览器兼容性 * feat:webchat文件上传按钮支持多选文件上传 * fix:释放blob URL以防止内存泄漏 * perf:并行化sendMessage中的图片获取逻辑 * feat:完成从url获取部分的UI * feat: 添加从URL导入功能的组件 * fix: 优化导入结果处理,添加整体摘要和主题摘要的文件命名 * perf: 更新url导入选项添加默认值 * perf: 在导入url的部分配置项未启用时隐藏暂不使用的下拉框选项 * feat: 添加上传前提提示信息至导入url至知识库功能 * feat: 更新导入功能提示信息,添加上传状态通知 * fix: 优化url转知识库错误处理 * feat: 合并知识库的上传文件和 URL 标签页 * feat: 删除导入URL至知识库功能的相关组件 --------- Co-authored-by: Soulter <905617992@qq.com> * feat: 添加条件显示逻辑以优化插件配置项的可见性管理 (#2433) * Feature: 支持在 WebUI 配置文件页中配置默认知识库 (#2437) * feat: 支持配置默认知识库 * chore: clean code * refactor: 重构 Function Tool 管理并初步引入 Multi Agent 及 Agent Handsoff 机制 (#2454) * stage * refactor: 重构 Function Tool 管理并引入 multi agent handsoff 机制 - Updated `star_request.py` to use the global `call_handler` instead of context-specific calls. - Modified `entities.py` to remove the dependency on `FunctionToolManager` and streamline the function tool handling. - Refactored `func_tool_manager.py` to simplify the `FunctionTool` class and its methods, removing deprecated code and enhancing clarity. - Adjusted `provider.py` to align with the new function tool structure, removing unnecessary type unions. - Enhanced `star_handler.py` to support agent registration and tool association, introducing `RegisteringAgent` for better encapsulation. - Updated `star_manager.py` to handle tool registration for agents, ensuring proper binding of handlers. - Revised `main.py` in the web searcher package to utilize the new agent registration system for web search tools. * chore: websearch * perf: 减少嵌套 * chore: 移除未使用的 mcp 导入 * feat: 添加 WebUI 迁移助手以及相关迁移方法 (#2477) * fix: 修复迁移对话时的一些问题 * feat: 增加工具使用模型能力选项 * feat: 添加知识库插件更新检查和更新功能 * perf: 调整 WebUI sidebar 顺序 * refactor: 重构 SharedPreference 类并采用数据库存储替换 json 存储 (#2482) * perf: 使用 run_coroutine_threadsafe Co-authored-by: Raven95676 <raven95676@gmail.com> * Feature: 支持配置重排序模型(vLLM API 格式)用于 score 任务 (#2496) * feat: 支持添加重排序模型(vLLM API 格式)用于 score 任务 * fix: update rerank API base URL to use localhost * feat: 知识库支持配置重排序模型 * fix: remove debug print statement for reranked results in FaissVecDB * fix: 移除知识库中的提示文本 * Feature: 支持在配置文件配置可用的插件组 (#2505) * feat: 增加可用插件集合配置项 * remove: 旧版平台可用性配置 已经基于多配置文件实现。 * feat: 应用配置文件插件可用性配置 * perf: hoist if from if * feat: llm_tool 装饰器返回值支持返回 mcp 库中 tool 的返回值类型(mcp.type.CallToolResult) (#2507) * fix: add type definition for migrationDialog and ensure open method exists before calling * chore: update project version to 4.0.0 * feat: 多 t2i 服务的随机负载均衡 (#2529) * fix: bugfixes * Improve: 扩大配置文件生效范围的自定义程度到会话粒度 (#2532) * feat: 扩大配置文件生效范围的自定义程度 * perf: 冲突检测 * refactor: simplify config form validation and improve conflict message clarity * chore: clean code * feat: 插件配置支持多个快捷魔法配置项 * chore: 修复当自动更新 webchat title 时,history 被重置的问题 * bugfixes * feat: add custom T2I template editor (#2581) * perf: add option to clear provider selection in ProviderSelector component * 📦 release: bump verstion to v4.0.0-beta.1 * chore: delete uv.lock --------- Co-authored-by: RC-CHN <67079377+RC-CHN@users.noreply.github.com> Co-authored-by: Raven95676 <raven95676@gmail.com>
168 lines
6.8 KiB
Python
168 lines
6.8 KiB
Python
import traceback
|
|
import asyncio
|
|
from astrbot.core.config.astrbot_config import AstrBotConfig
|
|
from .platform import Platform
|
|
from typing import List
|
|
from asyncio import Queue
|
|
from .register import platform_cls_map
|
|
from astrbot.core import logger
|
|
from .sources.webchat.webchat_adapter import WebChatAdapter
|
|
|
|
|
|
class PlatformManager:
|
|
def __init__(self, config: AstrBotConfig, event_queue: Queue):
|
|
self.platform_insts: List[Platform] = []
|
|
"""加载的 Platform 的实例"""
|
|
|
|
self._inst_map = {}
|
|
|
|
self.platforms_config = config["platform"]
|
|
self.settings = config["platform_settings"]
|
|
"""NOTE: 这里是 default 的配置文件,以保证最大的兼容性;
|
|
这个配置中的 unique_session 需要特殊处理,
|
|
约定整个项目中对 unique_session 的引用都从 default 的配置中获取"""
|
|
self.event_queue = event_queue
|
|
|
|
async def initialize(self):
|
|
"""初始化所有平台适配器"""
|
|
for platform in self.platforms_config:
|
|
try:
|
|
await self.load_platform(platform)
|
|
except Exception as e:
|
|
logger.error(f"初始化 {platform} 平台适配器失败: {e}")
|
|
|
|
# 网页聊天
|
|
webchat_inst = WebChatAdapter({}, self.settings, self.event_queue)
|
|
self.platform_insts.append(webchat_inst)
|
|
asyncio.create_task(
|
|
self._task_wrapper(asyncio.create_task(webchat_inst.run(), name="webchat"))
|
|
)
|
|
|
|
async def load_platform(self, platform_config: dict):
|
|
"""实例化一个平台"""
|
|
# 动态导入
|
|
try:
|
|
if not platform_config["enable"]:
|
|
return
|
|
|
|
logger.info(
|
|
f"载入 {platform_config['type']}({platform_config['id']}) 平台适配器 ..."
|
|
)
|
|
match platform_config["type"]:
|
|
case "aiocqhttp":
|
|
from .sources.aiocqhttp.aiocqhttp_platform_adapter import (
|
|
AiocqhttpAdapter, # noqa: F401
|
|
)
|
|
case "qq_official":
|
|
from .sources.qqofficial.qqofficial_platform_adapter import (
|
|
QQOfficialPlatformAdapter, # noqa: F401
|
|
)
|
|
case "qq_official_webhook":
|
|
from .sources.qqofficial_webhook.qo_webhook_adapter import (
|
|
QQOfficialWebhookPlatformAdapter, # noqa: F401
|
|
)
|
|
case "wechatpadpro":
|
|
from .sources.wechatpadpro.wechatpadpro_adapter import (
|
|
WeChatPadProAdapter, # noqa: F401
|
|
)
|
|
case "lark":
|
|
from .sources.lark.lark_adapter import LarkPlatformAdapter # noqa: F401
|
|
case "dingtalk":
|
|
from .sources.dingtalk.dingtalk_adapter import (
|
|
DingtalkPlatformAdapter, # noqa: F401
|
|
)
|
|
case "telegram":
|
|
from .sources.telegram.tg_adapter import TelegramPlatformAdapter # noqa: F401
|
|
case "wecom":
|
|
from .sources.wecom.wecom_adapter import WecomPlatformAdapter # noqa: F401
|
|
case "weixin_official_account":
|
|
from .sources.weixin_official_account.weixin_offacc_adapter import (
|
|
WeixinOfficialAccountPlatformAdapter, # noqa
|
|
)
|
|
case "discord":
|
|
from .sources.discord.discord_platform_adapter import (
|
|
DiscordPlatformAdapter, # noqa: F401
|
|
)
|
|
case "slack":
|
|
from .sources.slack.slack_adapter import SlackAdapter # noqa: F401
|
|
except (ImportError, ModuleNotFoundError) as e:
|
|
logger.error(
|
|
f"加载平台适配器 {platform_config['type']} 失败,原因:{e}。请检查依赖库是否安装。提示:可以在 管理面板->控制台->安装Pip库 中安装依赖库。"
|
|
)
|
|
except Exception as e:
|
|
logger.error(f"加载平台适配器 {platform_config['type']} 失败,原因:{e}。")
|
|
|
|
if platform_config["type"] not in platform_cls_map:
|
|
logger.error(
|
|
f"未找到适用于 {platform_config['type']}({platform_config['id']}) 平台适配器,请检查是否已经安装或者名称填写错误"
|
|
)
|
|
return
|
|
cls_type = platform_cls_map[platform_config["type"]]
|
|
inst: Platform = cls_type(platform_config, self.settings, self.event_queue)
|
|
self._inst_map[platform_config["id"]] = {
|
|
"inst": inst,
|
|
"client_id": inst.client_self_id,
|
|
}
|
|
self.platform_insts.append(inst)
|
|
|
|
asyncio.create_task(
|
|
self._task_wrapper(
|
|
asyncio.create_task(
|
|
inst.run(),
|
|
name=f"platform_{platform_config['type']}_{platform_config['id']}",
|
|
)
|
|
)
|
|
)
|
|
|
|
async def _task_wrapper(self, task: asyncio.Task):
|
|
try:
|
|
await task
|
|
except asyncio.CancelledError:
|
|
pass
|
|
except Exception as e:
|
|
logger.error(f"------- 任务 {task.get_name()} 发生错误: {e}")
|
|
for line in traceback.format_exc().split("\n"):
|
|
logger.error(f"| {line}")
|
|
logger.error("-------")
|
|
|
|
async def reload(self, platform_config: dict):
|
|
await self.terminate_platform(platform_config["id"])
|
|
if platform_config["enable"]:
|
|
await self.load_platform(platform_config)
|
|
|
|
# 和配置文件保持同步
|
|
config_ids = [provider["id"] for provider in self.platforms_config]
|
|
for key in list(self._inst_map.keys()):
|
|
if key not in config_ids:
|
|
await self.terminate_platform(key)
|
|
|
|
async def terminate_platform(self, platform_id: str):
|
|
if platform_id in self._inst_map:
|
|
logger.info(f"正在尝试终止 {platform_id} 平台适配器 ...")
|
|
|
|
# client_id = self._inst_map.pop(platform_id, None)
|
|
info = self._inst_map.pop(platform_id, None)
|
|
client_id = info["client_id"]
|
|
inst = info["inst"]
|
|
try:
|
|
self.platform_insts.remove(
|
|
next(
|
|
inst
|
|
for inst in self.platform_insts
|
|
if inst.client_self_id == client_id
|
|
)
|
|
)
|
|
except Exception:
|
|
logger.warning(f"可能未完全移除 {platform_id} 平台适配器")
|
|
|
|
if getattr(inst, "terminate", None):
|
|
await inst.terminate()
|
|
|
|
async def terminate(self):
|
|
for inst in self.platform_insts:
|
|
if getattr(inst, "terminate", None):
|
|
await inst.terminate()
|
|
|
|
def get_insts(self):
|
|
return self.platform_insts
|