87 lines
2.6 KiB
Python
Executable File
87 lines
2.6 KiB
Python
Executable File
"""Registry loader -- discovers and merges per-layer registry modules.
|
|
|
|
On startup the loader scans ``ai_lsc.registry.layers`` for every
|
|
``.py`` file that exports a ``TOOLS`` dict, and merges them into a
|
|
single unified registry dict.
|
|
|
|
This replaces the monolithic ``DEFAULT_REGISTRY`` dict with a
|
|
zero-merge-conflict modular approach: each layer lives in its own
|
|
file and is independently editable.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib
|
|
import pkgutil
|
|
from typing import Any
|
|
|
|
|
|
def load_merged_registry() -> dict[str, dict[str, Any]]:
|
|
"""Discover and merge all layer TOOLS dicts into one registry dict.
|
|
|
|
Each layer module should export ``TOOLS: dict[str, dict]`` where
|
|
keys are tool IDs and values are the full metadata dicts.
|
|
|
|
Returns the merged ``{tool_id: metadata}`` dictionary, with later
|
|
files overriding earlier ones on key collision (which should never
|
|
happen if layers are well-separated).
|
|
"""
|
|
merged: dict[str, dict[str, Any]] = {}
|
|
|
|
# Import the layers package
|
|
try:
|
|
layers_pkg = importlib.import_module("ai_lsc.registry.layers")
|
|
except ImportError:
|
|
return merged
|
|
|
|
for importer, modname, ispkg in pkgutil.iter_modules(
|
|
layers_pkg.__path__, prefix=layers_pkg.__name__ + "."
|
|
):
|
|
if ispkg:
|
|
continue
|
|
try:
|
|
mod = importlib.import_module(modname)
|
|
except ImportError:
|
|
continue
|
|
|
|
tools = getattr(mod, "TOOLS", None)
|
|
|
|
if isinstance(tools, dict):
|
|
# Dict format: {tool_id: metadata, ...}
|
|
_merge_tools_dict(merged, tools)
|
|
elif isinstance(tools, list):
|
|
# Legacy list format: [{...}, ...]
|
|
_merge_tools_list(merged, tools)
|
|
|
|
return merged
|
|
|
|
|
|
def _merge_tools_dict(
|
|
target: dict[str, dict[str, Any]],
|
|
tools: dict[str, dict[str, Any]],
|
|
) -> None:
|
|
"""Merge a ``TOOLS`` dict into the target registry dict."""
|
|
for tool_id, entry in tools.items():
|
|
if not isinstance(entry, dict):
|
|
continue
|
|
# Inject tool_id into entry if missing
|
|
if "tool_id" not in entry:
|
|
entry = {**entry, "tool_id": tool_id}
|
|
target[tool_id] = entry
|
|
|
|
|
|
def _merge_tools_list(
|
|
target: dict[str, dict[str, Any]],
|
|
tools: list[dict[str, Any]],
|
|
) -> None:
|
|
"""Merge a legacy ``TOOLS`` list into the target registry dict."""
|
|
for entry in tools:
|
|
if not isinstance(entry, dict):
|
|
continue
|
|
tool_id = entry.get("tool_id")
|
|
if tool_id is None:
|
|
name = entry.get("name", "")
|
|
tool_id = name.lower().replace(" ", "_").replace("/", "_")
|
|
entry["tool_id"] = tool_id
|
|
target[tool_id] = entry
|