feat(F): F-CTX context_builder, F-STR agent_stream, F-DEF agent definition fields, F-SKILL skill_registry, F-TOOL agent_tools
Check Cross-Plugin Imports / check (push) Has been cancelled
Check Cross-Plugin Imports / check (push) Has been cancelled
- F-CTX: app/ai/context_builder.py (282 lines) — build_agent_context() + ReActSystemPromptBuilder - F-STR: app/ai/agent_stream.py (155 lines) — stream_react_loop() with SSE events (step, status, done, error) - F-DEF: AgentDefinition fields added (temperature, max_tokens, max_steps, trace_mode, skill_ids, trigger_config, ai_use_case_metadata) + migration 0122 - F-SKILL: app/ai/skill_registry.py (82 lines) — SkillDefinition + SkillRegistry singleton - F-TOOL: app/ai/agent_tools.py (117 lines) — get_agent_tools() with permission intersection - Skill CRUD routes: app/plugins/builtins/automation/skill_routes.py - Tests: test_skill_registry.py (97 lines), test_agent_tools.py (219 lines) - All Python compile checks pass, tests require PostgreSQL (infra issue, not code bug)
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
"""Tests for the Small Skill Registry (app/ai/skill_registry.py)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from app.ai.skill_registry import SkillDefinition, get_skill_registry
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _clean_registry():
|
||||
"""Reset the singleton registry between tests."""
|
||||
registry = get_skill_registry()
|
||||
for skill in list(registry.list_all()):
|
||||
registry.unregister(skill.name)
|
||||
yield
|
||||
for skill in list(registry.list_all()):
|
||||
registry.unregister(skill.name)
|
||||
|
||||
|
||||
def _make_skill(
|
||||
name: str = "mail_reader",
|
||||
allowed_tool_ids: list[str] | None = None,
|
||||
category: str = "general",
|
||||
) -> SkillDefinition:
|
||||
return SkillDefinition(
|
||||
name=name,
|
||||
description="Read and summarize emails",
|
||||
instructions="Use the mail tools to read and summarize emails.",
|
||||
allowed_tool_ids=allowed_tool_ids or ["mail_read", "mail_list"],
|
||||
context_policy={"include_recent": True},
|
||||
category=category,
|
||||
)
|
||||
|
||||
|
||||
class TestSkillDefinition:
|
||||
def test_defaults(self):
|
||||
skill = SkillDefinition(
|
||||
name="s", description="d", instructions="i", allowed_tool_ids=["t"]
|
||||
)
|
||||
assert skill.context_policy is None
|
||||
assert skill.category == "general"
|
||||
|
||||
def test_to_dict(self):
|
||||
skill = _make_skill()
|
||||
data = skill.to_dict()
|
||||
assert data["name"] == "mail_reader"
|
||||
assert data["allowed_tool_ids"] == ["mail_read", "mail_list"]
|
||||
assert data["context_policy"] == {"include_recent": True}
|
||||
assert data["category"] == "general"
|
||||
|
||||
|
||||
class TestSkillRegistry:
|
||||
def test_register_and_get(self):
|
||||
registry = get_skill_registry()
|
||||
skill = _make_skill()
|
||||
registry.register(skill)
|
||||
assert registry.get("mail_reader") is skill
|
||||
|
||||
def test_get_missing_returns_none(self):
|
||||
registry = get_skill_registry()
|
||||
assert registry.get("nope") is None
|
||||
|
||||
def test_get_by_names_skips_unknown(self):
|
||||
registry = get_skill_registry()
|
||||
a = _make_skill("a")
|
||||
b = _make_skill("b")
|
||||
registry.register(a)
|
||||
registry.register(b)
|
||||
resolved = registry.get_by_names(["a", "b", "missing"])
|
||||
assert len(resolved) == 2
|
||||
assert {s.name for s in resolved} == {"a", "b"}
|
||||
|
||||
def test_list_all_and_list_for_api(self):
|
||||
registry = get_skill_registry()
|
||||
registry.register(_make_skill("a"))
|
||||
registry.register(_make_skill("b"))
|
||||
assert len(registry.list_all()) == 2
|
||||
api = registry.list_for_api()
|
||||
assert len(api) == 2
|
||||
assert all("name" in item and "instructions" in item for item in api)
|
||||
|
||||
def test_unregister(self):
|
||||
registry = get_skill_registry()
|
||||
registry.register(_make_skill())
|
||||
registry.unregister("mail_reader")
|
||||
assert registry.get("mail_reader") is None
|
||||
|
||||
def test_register_replaces_existing(self):
|
||||
registry = get_skill_registry()
|
||||
registry.register(_make_skill("a", allowed_tool_ids=["t1"]))
|
||||
registry.register(_make_skill("a", allowed_tool_ids=["t2"]))
|
||||
assert registry.get("a").allowed_tool_ids == ["t2"]
|
||||
assert len(registry.list_all()) == 1
|
||||
|
||||
def test_singleton(self):
|
||||
assert get_skill_registry() is get_skill_registry()
|
||||
Reference in New Issue
Block a user