feat(plugin_system): 引入插件HTTP端点系统
引入了全新的 `BaseRouterComponent` 组件类型,允许插件开发者通过继承并实现 `register_endpoints` 方法来创建 FastAPI 路由。 - 插件系统现在可以自动发现并注册这些路由组件,并将它们挂载到主 FastAPI 应用的 `/plugins/<plugin_name>` 前缀下。 - 新增了全局配置 `[plugin_http_system]`,提供了总开关、API 速率限制和 API 密钥认证 (`X-API-Key`) 等功能,以确保端点的安全性和稳定性。 - 更新了 `hello_world_plugin` 插件,增加了一个简单的 `/greet` 端点作为实现示例。
This commit is contained in:
committed by
Windpicker-owo
parent
fea007b429
commit
717d4ba555
@@ -5,11 +5,15 @@ from pathlib import Path
|
||||
from re import Pattern
|
||||
from typing import Any, cast
|
||||
|
||||
from fastapi import Depends
|
||||
|
||||
from src.common.logger import get_logger
|
||||
from src.config.config import global_config as bot_config
|
||||
from src.plugin_system.base.base_action import BaseAction
|
||||
from src.plugin_system.base.base_chatter import BaseChatter
|
||||
from src.plugin_system.base.base_command import BaseCommand
|
||||
from src.plugin_system.base.base_events_handler import BaseEventHandler
|
||||
from src.plugin_system.base.base_http_component import BaseRouterComponent
|
||||
from src.plugin_system.base.base_interest_calculator import BaseInterestCalculator
|
||||
from src.plugin_system.base.base_prompt import BasePrompt
|
||||
from src.plugin_system.base.base_tool import BaseTool
|
||||
@@ -24,6 +28,7 @@ from src.plugin_system.base.component_types import (
|
||||
PluginInfo,
|
||||
PlusCommandInfo,
|
||||
PromptInfo,
|
||||
RouterInfo,
|
||||
ToolInfo,
|
||||
)
|
||||
from src.plugin_system.base.plus_command import PlusCommand, create_legacy_command_adapter
|
||||
@@ -40,6 +45,7 @@ ComponentClassType = (
|
||||
| type[BaseChatter]
|
||||
| type[BaseInterestCalculator]
|
||||
| type[BasePrompt]
|
||||
| type[BaseRouterComponent]
|
||||
)
|
||||
|
||||
|
||||
@@ -194,6 +200,10 @@ class ComponentRegistry:
|
||||
assert isinstance(component_info, PromptInfo)
|
||||
assert issubclass(component_class, BasePrompt)
|
||||
ret = self._register_prompt_component(component_info, component_class)
|
||||
case ComponentType.ROUTER:
|
||||
assert isinstance(component_info, RouterInfo)
|
||||
assert issubclass(component_class, BaseRouterComponent)
|
||||
ret = self._register_router_component(component_info, component_class)
|
||||
case _:
|
||||
logger.warning(f"未知组件类型: {component_type}")
|
||||
ret = False
|
||||
@@ -373,6 +383,48 @@ class ComponentRegistry:
|
||||
logger.debug(f"已注册Prompt组件: {prompt_name}")
|
||||
return True
|
||||
|
||||
def _register_router_component(self, router_info: RouterInfo, router_class: type[BaseRouterComponent]) -> bool:
|
||||
"""注册Router组件并将其端点挂载到主服务器"""
|
||||
# 1. 检查总开关是否开启
|
||||
if not bot_config.plugin_http_system.enable_plugin_http_endpoints:
|
||||
logger.info("插件HTTP端点功能已禁用,跳过路由注册")
|
||||
return True
|
||||
try:
|
||||
from src.common.security import get_api_key
|
||||
from src.common.server import get_global_server
|
||||
|
||||
router_name = router_info.name
|
||||
plugin_name = router_info.plugin_name
|
||||
|
||||
# 2. 实例化组件以触发其 __init__ 和 register_endpoints
|
||||
component_instance = router_class()
|
||||
|
||||
# 3. 获取配置好的 APIRouter
|
||||
plugin_router = component_instance.router
|
||||
|
||||
# 4. 获取全局服务器实例
|
||||
server = get_global_server()
|
||||
|
||||
# 5. 生成唯一的URL前缀
|
||||
prefix = f"/plugins/{plugin_name}"
|
||||
|
||||
# 6. 根据需要应用安全依赖项
|
||||
dependencies = []
|
||||
if router_info.auth_required:
|
||||
dependencies.append(Depends(get_api_key))
|
||||
|
||||
# 7. 注册路由,并使用插件名作为API文档的分组标签
|
||||
server.app.include_router(
|
||||
plugin_router, prefix=prefix, tags=[plugin_name], dependencies=dependencies
|
||||
)
|
||||
|
||||
logger.debug(f"成功将插件 '{plugin_name}' 的路由组件 '{router_name}' 挂载到: {prefix}")
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"注册路由组件 '{router_info.name}' 时出错: {e}", exc_info=True)
|
||||
return False
|
||||
|
||||
# === 组件移除相关 ===
|
||||
|
||||
async def remove_component(self, component_name: str, component_type: ComponentType, plugin_name: str) -> bool:
|
||||
@@ -616,6 +668,7 @@ class ComponentRegistry:
|
||||
| BaseChatter
|
||||
| BaseInterestCalculator
|
||||
| BasePrompt
|
||||
| BaseRouterComponent
|
||||
]
|
||||
| None
|
||||
):
|
||||
@@ -643,6 +696,8 @@ class ComponentRegistry:
|
||||
| type[PlusCommand]
|
||||
| type[BaseChatter]
|
||||
| type[BaseInterestCalculator]
|
||||
| type[BasePrompt]
|
||||
| type[BaseRouterComponent]
|
||||
| None,
|
||||
self._components_classes.get(namespaced_name),
|
||||
)
|
||||
@@ -867,6 +922,7 @@ class ComponentRegistry:
|
||||
plus_command_components: int = 0
|
||||
chatter_components: int = 0
|
||||
prompt_components: int = 0
|
||||
router_components: int = 0
|
||||
for component in self._components.values():
|
||||
if component.component_type == ComponentType.ACTION:
|
||||
action_components += 1
|
||||
@@ -882,6 +938,8 @@ class ComponentRegistry:
|
||||
chatter_components += 1
|
||||
elif component.component_type == ComponentType.PROMPT:
|
||||
prompt_components += 1
|
||||
elif component.component_type == ComponentType.ROUTER:
|
||||
router_components += 1
|
||||
return {
|
||||
"action_components": action_components,
|
||||
"command_components": command_components,
|
||||
@@ -891,6 +949,7 @@ class ComponentRegistry:
|
||||
"plus_command_components": plus_command_components,
|
||||
"chatter_components": chatter_components,
|
||||
"prompt_components": prompt_components,
|
||||
"router_components": router_components,
|
||||
"total_components": len(self._components),
|
||||
"total_plugins": len(self._plugins),
|
||||
"components_by_type": {
|
||||
|
||||
@@ -405,13 +405,14 @@ class PluginManager:
|
||||
plus_command_count = stats.get("plus_command_components", 0)
|
||||
chatter_count = stats.get("chatter_components", 0)
|
||||
prompt_count = stats.get("prompt_components", 0)
|
||||
router_count = stats.get("router_components", 0)
|
||||
total_components = stats.get("total_components", 0)
|
||||
|
||||
# 📋 显示插件加载总览
|
||||
if total_registered > 0:
|
||||
logger.info("🎉 插件系统加载完成!")
|
||||
logger.info(
|
||||
f"📊 总览: {total_registered}个插件, {total_components}个组件 (Action: {action_count}, Command: {command_count}, Tool: {tool_count}, PlusCommand: {plus_command_count}, EventHandler: {event_handler_count}, Chatter: {chatter_count}, Prompt: {prompt_count})"
|
||||
f"📊 总览: {total_registered}个插件, {total_components}个组件 (Action: {action_count}, Command: {command_count}, Tool: {tool_count}, PlusCommand: {plus_command_count}, EventHandler: {event_handler_count}, Chatter: {chatter_count}, Prompt: {prompt_count}, Router: {router_count})"
|
||||
)
|
||||
|
||||
# 显示详细的插件列表
|
||||
@@ -452,6 +453,9 @@ class PluginManager:
|
||||
prompt_components = [
|
||||
c for c in plugin_info.components if c.component_type == ComponentType.PROMPT
|
||||
]
|
||||
router_components = [
|
||||
c for c in plugin_info.components if c.component_type == ComponentType.ROUTER
|
||||
]
|
||||
|
||||
if action_components:
|
||||
action_details = [format_component(c) for c in action_components]
|
||||
@@ -478,6 +482,9 @@ class PluginManager:
|
||||
if prompt_components:
|
||||
prompt_details = [format_component(c) for c in prompt_components]
|
||||
logger.info(f" 📝 Prompt组件: {', '.join(prompt_details)}")
|
||||
if router_components:
|
||||
router_details = [format_component(c) for c in router_components]
|
||||
logger.info(f" 🌐 Router组件: {', '.join(router_details)}")
|
||||
|
||||
# 权限节点信息
|
||||
if plugin_instance := self.loaded_plugins.get(plugin_name):
|
||||
|
||||
Reference in New Issue
Block a user