Files
mai-bot/src/plugin_runtime/host/capability_service.py

86 lines
3.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""能力服务层
Host 端实现的能力服务,处理来自插件的 cap.* 请求。
每个能力方法被注册到 RPC Server接收 Runner 转发的请求并执行实际操作。
"""
from typing import Any, Callable, Dict, List, Coroutine, TYPE_CHECKING
from src.common.logger import get_logger
from src.plugin_runtime.protocol.envelope import CapabilityRequestPayload, CapabilityResponsePayload, Envelope
from src.plugin_runtime.protocol.errors import ErrorCode, RPCError
if TYPE_CHECKING:
from src.plugin_runtime.host.authorization import AuthorizationManager
logger = get_logger("plugin_runtime.host.capability_service")
# 能力实现函数类型: (plugin_id, capability, args) -> result
CapabilityImpl = Callable[[str, str, Dict[str, Any]], Coroutine[Any, Any, Any]]
class CapabilityService:
"""能力服务
负责:
1. 注册能力实现
2. 接收插件的能力调用请求
3. 通过策略引擎校验权限和限流
4. 执行实际操作并返回结果
"""
def __init__(self, authorization: "AuthorizationManager") -> None:
self._authorization = authorization
# capability_name -> implementation
self._implementations: Dict[str, CapabilityImpl] = {}
def register_capability(self, name: str, impl: CapabilityImpl) -> None:
"""注册一个能力实现
Args:
name: 能力名称,如 "send.text", "db.query", "llm.generate"
impl: 实现函数
"""
self._implementations[name] = impl
logger.debug(f"注册能力实现: {name}")
async def handle_capability_request(self, envelope: Envelope) -> Envelope:
"""处理能力调用请求(作为 RPC Server 的 method handler
从 envelope 中提取 capability 名称和参数,
校验权限后调用对应实现。
"""
plugin_id = envelope.plugin_id
try:
req = CapabilityRequestPayload.model_validate(envelope.payload)
except Exception as e:
return envelope.make_error_response(ErrorCode.E_BAD_PAYLOAD.value, f"能力调用 payload 格式错误: {e}")
capability = req.capability
# 1. 权限校验
allowed, reason = self._authorization.check_capability(plugin_id, capability)
if not allowed:
return envelope.make_error_response(ErrorCode.E_CAPABILITY_DENIED.value, reason)
# 2. 查找实现
impl = self._implementations.get(capability)
if impl is None:
return envelope.make_error_response(ErrorCode.E_METHOD_NOT_ALLOWED.value, f"未注册的能力: {capability}")
# 3. 执行
try:
result = await impl(plugin_id, capability, req.args)
resp_payload = CapabilityResponsePayload(success=True, result=result)
return envelope.make_response(payload=resp_payload.model_dump())
except RPCError as e:
return envelope.make_error_response(e.code.value, e.message, e.details)
except Exception as e:
logger.error(f"能力 {capability} 执行异常: {e}", exc_info=True)
return envelope.make_error_response(ErrorCode.E_CAPABILITY_FAILED.value, str(e))
def list_capabilities(self) -> List[str]:
"""列出所有已注册的能力"""
return list(self._implementations.keys())