Coverage for agentos/plugins/registry.py: 0%
133 statements
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-02 09:59 +0800
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-02 09:59 +0800
1"""
2AgentOS v0.70 — 插件系统: 注册中心。
3基因来源: Docker插件体系 + VSCode扩展市场
5插件清单格式:
6- manifest.json: 插件元数据
7- 入口点: Python类路径
8- 依赖声明: 插件间依赖
9"""
11from __future__ import annotations
13from dataclasses import dataclass, field
14from enum import Enum
15from typing import Any, Callable
18class PluginType(str, Enum):
19 """插件类型枚举。"""
20 PROVIDER = "provider" # 模型provider (如GeminiAdapter)
21 TOOL = "tool" # 工具扩展
22 MIDDLEWARE = "middleware" # 请求/响应拦截器
23 SINK = "sink" # 输出后端 (观测/日志/存储)
24 HOOK = "hook" # 生命周期钩子
25 CUSTOM = "custom" # 自定义
28class PluginStatus(str, Enum):
30 """插件状态。"""
32 LOADED = "loaded"
33 INITIALIZED = "initialized"
34 ACTIVE = "active"
35 STOPPING = "stopping"
36 STOPPED = "stopped"
37 ERROR = "error"
40@dataclass
41class PluginManifest:
42 """插件清单 — 描述插件能力与依赖。"""
44 name: str
45 version: str
46 description: str = ""
47 author: str = ""
48 plugin_type: PluginType = PluginType.CUSTOM
49 entry_point: str = "" # fully qualified class path
50 dependencies: list[str] = field(default_factory=list)
51 optional_dependencies: list[str] = field(default_factory=list)
52 tags: list[str] = field(default_factory=list)
53 config_schema: dict = field(default_factory=dict)
54 priority: int = 50 # 0=最高, 100=最低
55 homepage: str = ""
56 license: str = "MIT"
58 def to_dict(self) -> dict:
59 return {
60 "name": self.name,
61 "version": self.version,
62 "plugin_type": self.plugin_type.value,
63 "entry_point": self.entry_point,
64 "dependencies": self.dependencies,
65 "tags": self.tags,
66 }
69@dataclass
70class RegisteredPlugin:
71 """已注册的插件实例。"""
73 manifest: PluginManifest
74 instance: Any = None
75 status: PluginStatus = PluginStatus.LOADED
76 load_time_ms: float = 0.0
77 error: str | None = None
80class PluginRegistry:
81 """
82 插件注册中心 — 统一管理所有已注册插件。
83 支持: CRUD、查询、按标签/类型检索、依赖解析。
84 """
86 def __init__(self):
87 self._plugins: dict[str, RegisteredPlugin] = {}
88 self._hooks: dict[str, list[Callable]] = {} # event → list of callbacks
90 # ── CRUD ─────────────────────────────────────
92 def register(self, manifest: PluginManifest, instance: Any = None) -> RegisteredPlugin:
93 """注册插件(覆盖已有同名插件)。"""
94 registered = RegisteredPlugin(manifest=manifest, instance=instance)
95 self._plugins[manifest.name] = registered
96 return registered
98 def unregister(self, name: str) -> bool:
99 if name in self._plugins:
100 del self._plugins[name]
101 return True
102 return False
104 def get(self, name: str) -> RegisteredPlugin | None:
105 return self._plugins.get(name)
107 def get_instance(self, name: str) -> Any:
108 """获取插件实例。"""
109 registered = self._plugins.get(name)
110 return registered.instance if registered else None
112 # ── Query ────────────────────────────────────
114 def list_all(self) -> list[RegisteredPlugin]:
115 return list(self._plugins.values())
117 def list_names(self) -> list[str]:
118 return list(self._plugins.keys())
120 def by_type(self, plugin_type: PluginType) -> list[RegisteredPlugin]:
121 return [p for p in self._plugins.values() if p.manifest.plugin_type == plugin_type]
123 def by_tag(self, tag: str) -> list[RegisteredPlugin]:
124 return [p for p in self._plugins.values() if tag in p.manifest.tags]
126 def by_status(self, status: PluginStatus) -> list[RegisteredPlugin]:
127 return [p for p in self._plugins.values() if p.status == status]
129 # ── Dependency Resolution ────────────────────
131 def resolve_order(self, names: list[str]) -> list[str]:
132 """拓扑排序解析插件加载顺序。"""
133 adj: dict[str, list[str]] = {n: [] for n in names}
134 for name in names:
135 p = self._plugins.get(name)
136 if p:
137 adj[name] = [d for d in p.manifest.dependencies if d in names]
139 # Kahn's algorithm
140 in_degree = {n: 0 for n in names}
141 for deps in adj.values():
142 for d in deps:
143 in_degree[d] += 1
145 queue = [n for n in names if in_degree[n] == 0]
146 order = []
147 while queue:
148 n = queue.pop(0)
149 order.append(n)
150 for dep in adj.get(n, []):
151 in_degree[dep] -= 1
152 if in_degree[dep] == 0:
153 queue.append(dep)
155 if len(order) != len(names):
156 missing = set(names) - set(order)
157 raise DependencyCycleError(f"循环依赖或缺失依赖: {missing}")
159 return order
161 def check_requirements(self, name: str) -> list[str]:
162 """检查某插件的依赖是否满足。返回缺失的依赖列表。"""
163 p = self._plugins.get(name)
164 if not p:
165 return [name]
166 missing = []
167 for dep in p.manifest.dependencies:
168 if dep not in self._plugins:
169 missing.append(dep)
170 return missing
172 # ── Hook System ──────────────────────────────
174 def register_hook(self, event: str, callback: Callable):
175 """注册事件钩子。"""
176 if event not in self._hooks:
177 self._hooks[event] = []
178 self._hooks[event].append(callback)
180 async def emit_hook(self, event: str, **kwargs) -> list[Any]:
181 """触发事件钩子,返回所有回调结果。"""
182 results = []
183 for cb in self._hooks.get(event, []):
184 try:
185 import asyncio
186 if asyncio.iscoroutinefunction(cb):
187 results.append(await cb(**kwargs))
188 else:
189 results.append(cb(**kwargs))
190 except Exception as e:
191 results.append({"error": str(e)})
192 return results
194 def hook_names(self) -> list[str]:
195 return list(self._hooks.keys())
197 # ── Info ─────────────────────────────────────
199 @property
200 def count(self) -> int:
201 return len(self._plugins)
203 def summary(self) -> str:
204 by_type = {}
205 for p in self._plugins.values():
206 t = p.manifest.plugin_type.value
207 by_type[t] = by_type.get(t, 0) + 1
208 lines = [f"共 {self.count} 个插件"]
209 for t, c in sorted(by_type.items()):
210 lines.append(f" {t}: {c}")
211 return "\n".join(lines)
214class DependencyCycleError(Exception):
216 """插件依赖循环异常。"""
218 pass