Coverage for /home/admin/Documents/AI/applications/lexigram-dev/lexigram/experimental/ai/lexigram-ai-skills/src/lexigram/ai/skills/registry/core.py: 45%
38 statements
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 07:19 +0800
« prev ^ index » next coverage.py v7.15.4, created at 2026-08-25 07:19 +0800
1"""SkillRegistry — central catalog for registering and discovering skills."""
3from __future__ import annotations
5from typing import TYPE_CHECKING, Any
7from lexigram.ai.skills.exceptions import SkillAlreadyRegisteredError
8from lexigram.logging import (
9 get_logger,
10)
12if TYPE_CHECKING:
13 from lexigram.contracts.ai.agents import ToolProtocol
14 from lexigram.contracts.ai.skills import SkillDefinition, SkillProtocol
16logger = get_logger(__name__)
19class SkillRegistry:
20 """Central registry for all available skills.
22 Supports registration, lookup by name, category-based listing,
23 permission-filtered listing, and OpenAI function-calling schema export.
24 """
26 def __init__(self) -> None:
27 """Initialise an empty registry."""
28 self._skills: dict[str, SkillProtocol] = {}
29 self._categories: dict[str, list[str]] = {}
31 def register(self, skill: SkillProtocol) -> None:
32 """Register a skill.
34 Args:
35 skill: The skill to register.
37 Raises:
38 SkillAlreadyRegisteredError: If a skill with the same name is already registered.
39 """
40 name = skill.definition.name
41 if name in self._skills:
42 raise SkillAlreadyRegisteredError(name)
43 self._skills[name] = skill
44 cat = skill.definition.category
45 self._categories.setdefault(cat, []).append(name)
46 logger.debug("skill_registered", name=name, category=cat)
48 def register_tool(self, tool: ToolProtocol) -> None:
49 """Wrap *tool* as a :class:`~lexigram.ai.skills.base.ToolSkillAdapter` and register it.
51 Args:
52 tool: Any object satisfying ToolProtocol.
53 """
54 from lexigram.ai.skills.base import ToolSkillAdapter
56 self.register(ToolSkillAdapter(tool))
58 def get(self, name: str) -> SkillProtocol | None:
59 """Return the skill registered under *name*, or ``None``.
61 Args:
62 name: Skill name to look up.
64 Returns:
65 The skill or None if not found.
66 """
67 return self._skills.get(name)
69 def list_skills(
70 self,
71 category: str | None = None,
72 permissions: list[str] | None = None,
73 ) -> list[SkillDefinition]:
74 """List registered skill definitions with optional filters.
76 Args:
77 category: Return only skills in this category.
78 permissions: Return only skills whose required permissions are a
79 subset of the supplied permission set.
81 Returns:
82 Matching skill definitions.
83 """
84 skills = list(self._skills.values())
86 if category is not None:
87 names_in_cat = set(self._categories.get(category, []))
88 skills = [s for s in skills if s.definition.name in names_in_cat]
90 if permissions is not None:
91 user_perms = set(permissions)
92 skills = [
93 s for s in skills if set(s.definition.permissions).issubset(user_perms)
94 ]
96 return [s.definition for s in skills]
98 def get_schemas(self) -> list[dict[str, Any]]:
99 """Return OpenAI function-calling compatible schemas for all skills.
101 Returns:
102 List of schema dicts in OpenAI ``tools`` format.
103 """
104 return [
105 {
106 "type": "function",
107 "function": {
108 "name": s.definition.name,
109 "description": s.definition.description,
110 "parameters": s.definition.parameters_schema,
111 },
112 }
113 for s in self._skills.values()
114 ]
116 def __len__(self) -> int:
117 return len(self._skills)
119 def __contains__(self, name: str) -> bool:
120 return name in self._skills
123__all__ = ["SkillRegistry"]