"""Module Registry - 模块注册中心 提供功能模块的动态注册、发现和管理能力。所有扩展模块(如 AI 配音、 多平台发布、去重检测等)通过此注册中心声明自身能力、依赖和配额消耗规则。 设计原则: - 模块自描述:每个模块声明自己的能力、依赖、配额规则 - 松耦合:模块间通过注册中心间接通信,不直接依赖 - 可扩展:新模块只需注册即可被系统识别,无需修改核心代码 """ from __future__ import annotations import logging from dataclasses import dataclass, field from enum import Enum from typing import Any, Callable, Dict, List, Optional logger = logging.getLogger(__name__) class ModuleStatus(str, Enum): """模块运行状态""" REGISTERED = "registered" # 已注册,未激活 ACTIVE = "active" # 已激活,可用 DISABLED = "disabled" # 已禁用(管理员/Feature Flag 控制) ERROR = "error" # 注册或初始化出错 @dataclass class QuotaRule: """模块定义的配额消耗规则 描述该模块消耗哪些配额维度,以及每个操作消耗多少。 例如:AI 配音模块每生成一条配音消耗 1 个 ai_voice_credit。 """ dimension: str # 配额维度名,如 "ai_voice_credits", "storage_gb" per_operation: float # 每次操作消耗量 description: str = "" # 人类可读描述 @dataclass class ModuleCapability: """模块声明的一个能力 能力是模块对外暴露的可调用功能单元。 """ name: str # 能力名,如 "generate_voice" description: str = "" # 人类可读描述 quota_rules: List[QuotaRule] = field(default_factory=list) # 该能力消耗的配额规则 metadata: Dict[str, Any] = field(default_factory=dict) # 扩展元数据 @dataclass class Module: """模块注册信息 每个扩展模块通过 Module 描述自身,注册到 ModuleRegistry。 """ name: str # 模块唯一标识,如 "ai_voice" version: str = "1.0.0" # 模块版本 description: str = "" # 人类可读描述 capabilities: List[ModuleCapability] = field(default_factory=list) dependencies: List[str] = field(default_factory=list) # 依赖的其他模块名 status: ModuleStatus = ModuleStatus.REGISTERED config: Dict[str, Any] = field(default_factory=dict) # 模块配置 _init_func: Optional[Callable] = field(default=None, repr=False) # 初始化回调 def activate(self) -> None: """激活模块""" if self.status == ModuleStatus.ERROR: logger.error(f"Cannot activate module '{self.name}': in error state") return self.status = ModuleStatus.ACTIVE logger.info(f"Module '{self.name}' v{self.version} activated") def disable(self) -> None: """禁用模块""" self.status = ModuleStatus.DISABLED logger.info(f"Module '{self.name}' disabled") class ModuleRegistry: """模块注册中心 单例模式,全局唯一实例(module_registry)。 提供模块注册、发现、依赖检查、能力查询等功能。 Usage: from packages.infrastructure.module_registry import module_registry # 注册模块 module_registry.register(Module( name="ai_voice", version="1.0.0", description="AI 配音生成模块", capabilities=[ ModuleCapability( name="generate_voice", description="文本转配音", quota_rules=[QuotaRule("ai_voice_credits", 1.0, "每次配音消耗 1 积分")], ), ], )) # 查询 mod = module_registry.get("ai_voice") has_cap = module_registry.has_capability("generate_voice") """ def __init__(self) -> None: self._modules: Dict[str, Module] = {} def register(self, module: Module) -> None: """注册一个模块 Args: module: 要注册的模块 Raises: ValueError: 模块名已存在 """ if module.name in self._modules: raise ValueError(f"Module '{module.name}' already registered") # 检查依赖是否已注册 for dep in module.dependencies: if dep not in self._modules: logger.warning( f"Module '{module.name}' depends on '{dep}' which is not yet registered. " f"Call check_dependencies() after all modules are registered." ) self._modules[module.name] = module logger.info(f"Module '{module.name}' v{module.version} registered") # 自动尝试激活 if self.check_dependencies(module.name): module.activate() def unregister(self, name: str) -> None: """注销一个模块""" if name not in self._modules: raise KeyError(f"Module '{name}' not found") # 检查是否有其他模块依赖它 dependents = [m.name for m in self._modules.values() if name in m.dependencies and m.name != name] if dependents: raise ValueError(f"Cannot unregister module '{name}': depended on by {dependents}") del self._modules[name] logger.info(f"Module '{name}' unregistered") def get(self, name: str) -> Optional[Module]: """按名称获取模块,不存在返回 None""" return self._modules.get(name) def list_modules(self, status: Optional[ModuleStatus] = None) -> List[Module]: """列出所有模块,可按状态过滤""" modules = list(self._modules.values()) if status is not None: modules = [m for m in modules if m.status == status] return modules def has_capability(self, capability_name: str) -> bool: """检查是否有任何已激活模块提供指定能力""" for module in self._modules.values(): if module.status != ModuleStatus.ACTIVE: continue for cap in module.capabilities: if cap.name == capability_name: return True return False def get_capability(self, capability_name: str) -> Optional[ModuleCapability]: """获取指定能力的定义,返回第一个匹配的""" for module in self._modules.values(): if module.status != ModuleStatus.ACTIVE: continue for cap in module.capabilities: if cap.name == capability_name: return cap return None def get_quota_rules(self, capability_name: str) -> List[QuotaRule]: """获取指定能力的配额消耗规则""" cap = self.get_capability(capability_name) if cap is None: return [] return cap.quota_rules def check_dependencies(self, module_name: str) -> bool: """检查指定模块的所有依赖是否都已注册且激活""" module = self._modules.get(module_name) if module is None: return False for dep in module.dependencies: dep_module = self._modules.get(dep) if dep_module is None or dep_module.status != ModuleStatus.ACTIVE: return False return True def get_active_capabilities(self) -> Dict[str, List[str]]: """获取所有已激活模块的能力列表 Returns: {module_name: [capability_name, ...]} """ result = {} for module in self._modules.values(): if module.status == ModuleStatus.ACTIVE and module.capabilities: result[module.name] = [cap.name for cap in module.capabilities] return result def clear(self) -> None: """清空所有注册(仅用于测试)""" self._modules.clear() # 全局单例 module_registry = ModuleRegistry()