mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-09 05:50:44 +08:00
重构仓库目录为统一的 runtime 分层并清理历史 openclaw 残留。
本次迁移将网关/通道/工具/技能/脚本与协议资源集中到新结构,统一路径常量与脚本转发机制,减少顶层噪音并保证运行与测试行为一致。 Made-with: Cursor
This commit is contained in:
parent
ba3836f00f
commit
4a23b715a2
498 changed files with 2760 additions and 2200 deletions
134
runtime/tools/plugin_loader.py
Normal file
134
runtime/tools/plugin_loader.py
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Callable
|
||||
|
||||
from oclaw.runtime.tools.base import ToolSpec
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
ToolFactory = Callable[[], ToolSpec]
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class PluginTool:
|
||||
tool: ToolSpec
|
||||
plugin_name: str
|
||||
plugin_version: str
|
||||
entry_point: str
|
||||
|
||||
|
||||
def _iter_entry_points() -> list[Any]:
|
||||
try:
|
||||
from importlib.metadata import entry_points # py3.10+
|
||||
except Exception:
|
||||
return []
|
||||
try:
|
||||
eps = entry_points()
|
||||
# py3.10+ returns EntryPoints with .select
|
||||
groups = []
|
||||
for g in ("ai_ops_assistant.tools", "chatgpt.tools"):
|
||||
try:
|
||||
groups.extend(list(eps.select(group=g)))
|
||||
except Exception:
|
||||
pass
|
||||
return groups
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
|
||||
def discover_plugin_tool_factories() -> list[tuple[str, str, str, ToolFactory]]:
|
||||
"""Return (plugin_name, plugin_version, ep_name, factory)."""
|
||||
out: list[tuple[str, str, str, ToolFactory]] = []
|
||||
for ep in _iter_entry_points():
|
||||
ep_name = f"{getattr(ep, 'group', '')}:{getattr(ep, 'name', '')}"
|
||||
try:
|
||||
obj = ep.load()
|
||||
except Exception as exc:
|
||||
logger.warning("skip plugin entry point %s: %s", ep_name, exc)
|
||||
continue
|
||||
factory: ToolFactory | None = None
|
||||
if callable(obj):
|
||||
factory = obj # type: ignore[assignment]
|
||||
if not factory:
|
||||
logger.warning("skip plugin entry point %s: not callable", ep_name)
|
||||
continue
|
||||
dist_name = ""
|
||||
dist_ver = ""
|
||||
try:
|
||||
dist = getattr(ep, "dist", None)
|
||||
if dist is not None:
|
||||
dist_name = str(getattr(dist, "name", "") or "")
|
||||
dist_ver = str(getattr(dist, "version", "") or "")
|
||||
except Exception:
|
||||
dist_name = ""
|
||||
dist_ver = ""
|
||||
out.append((dist_name or "unknown", dist_ver or "", ep_name, factory))
|
||||
return out
|
||||
|
||||
|
||||
def materialize_plugin_tools() -> list[PluginTool]:
|
||||
tools: list[PluginTool] = []
|
||||
for plugin_name, plugin_version, ep_name, factory in discover_plugin_tool_factories():
|
||||
try:
|
||||
spec = factory()
|
||||
except Exception as exc:
|
||||
logger.warning("skip plugin tool %s (%s): %s", ep_name, plugin_name, exc)
|
||||
continue
|
||||
if not isinstance(spec, ToolSpec):
|
||||
logger.warning("skip plugin tool %s (%s): factory did not return ToolSpec", ep_name, plugin_name)
|
||||
continue
|
||||
# Tag plugin tools and default them to higher risk unless explicitly configured otherwise.
|
||||
try:
|
||||
spec = ToolSpec(
|
||||
name=spec.name,
|
||||
description=spec.description,
|
||||
parameters=spec.parameters,
|
||||
handler=spec.handler,
|
||||
tags=frozenset(set(spec.tags) | {"plugin"}),
|
||||
version=getattr(spec, "version", "v1"),
|
||||
risk_level=getattr(spec, "risk_level", "high") or "high",
|
||||
timeout_s=getattr(spec, "timeout_s", None),
|
||||
rate_limit=getattr(spec, "rate_limit", None),
|
||||
required_permissions=getattr(spec, "required_permissions", frozenset()),
|
||||
execution_mode=getattr(spec, "execution_mode", "in_process"),
|
||||
read_only=bool(spec.is_read_only()),
|
||||
)
|
||||
except Exception:
|
||||
# If wrapping fails, keep original spec.
|
||||
pass
|
||||
tools.append(
|
||||
PluginTool(
|
||||
tool=spec,
|
||||
plugin_name=plugin_name,
|
||||
plugin_version=plugin_version,
|
||||
entry_point=ep_name,
|
||||
)
|
||||
)
|
||||
return tools
|
||||
|
||||
|
||||
def sync_plugin_metadata(store: Any) -> int:
|
||||
"""Persist discovered plugin metadata into store (best-effort)."""
|
||||
count = 0
|
||||
try:
|
||||
rows = discover_plugin_tool_factories()
|
||||
except Exception:
|
||||
rows = []
|
||||
for plugin_name, plugin_version, ep_name, _factory in rows:
|
||||
try:
|
||||
store.upsert_tool_plugin(
|
||||
plugin_name=plugin_name,
|
||||
plugin_version=plugin_version,
|
||||
entry_point=ep_name,
|
||||
enabled=True,
|
||||
)
|
||||
count += 1
|
||||
except Exception:
|
||||
continue
|
||||
return count
|
||||
|
||||
|
||||
__all__ = ["PluginTool", "discover_plugin_tool_factories", "materialize_plugin_tools", "sync_plugin_metadata"]
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue