refactor(tools): decorator-based tool registry replaces monolithic tools.py
Split 2566-line tools.py into a tools/ package with @tool decorator registration. Each tool's schema, metadata, and implementation live together. Briefing eligibility is now a briefing=True flag instead of a separate frozenset allowlist. Conditional inclusion (CalDAV, SearXNG) uses requires= metadata. Public API (get_tools_for_user, execute_tool) unchanged. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,62 +1,9 @@
|
||||
"""
|
||||
Curated read-only tool subset for agentic briefings.
|
||||
"""Briefing tool subset — delegates to the registry's ``briefing=True`` filter.
|
||||
|
||||
The main chat pipeline exposes 40+ tools via ``tools.get_tools_for_user``,
|
||||
including mutating tools (``create_task``, ``delete_note``) and
|
||||
external-search tools (``search_images``, ``search_web``). Neither is
|
||||
appropriate for a scheduled background job that generates briefings —
|
||||
briefings are read-only and should not reach out to the internet on the
|
||||
user's behalf, and leaving high-noise tools in the list increases the
|
||||
chance of spurious calls (e.g. ``search_images`` firing on "what
|
||||
meeting?").
|
||||
|
||||
This module maintains an explicit allowlist. New tools added to
|
||||
``tools.py`` are not automatically exposed to briefings — they must be
|
||||
opted in by name here.
|
||||
Tools are opted-in to briefings via ``@tool(briefing=True)`` in their
|
||||
respective module, so there is no separate allowlist to maintain here.
|
||||
"""
|
||||
|
||||
import logging
|
||||
from fabledassistant.services.tools import get_briefing_tools
|
||||
|
||||
from fabledassistant.services.tools import get_tools_for_user
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Explicit allowlist — tools a briefing is permitted to call. Read-only only.
|
||||
# Any tool not listed here is invisible to the briefing model.
|
||||
BRIEFING_TOOL_NAMES: frozenset[str] = frozenset({
|
||||
# Tasks — what's actionable today, overdue, or high priority
|
||||
"list_tasks",
|
||||
# Calendar — internal event store and CalDAV (if configured)
|
||||
"list_events",
|
||||
"search_events",
|
||||
# Weather — today's forecast for the user's configured locations
|
||||
"get_weather",
|
||||
# News — RSS items filtered by user preferences
|
||||
"get_rss_items",
|
||||
# Projects — context for prioritization and narrative continuity
|
||||
"list_projects",
|
||||
"search_projects",
|
||||
"get_project",
|
||||
# Notes — surface recent captures for "pick up where you left off"
|
||||
"list_notes",
|
||||
"get_note",
|
||||
})
|
||||
|
||||
|
||||
async def get_briefing_tools(user_id: int) -> list[dict]:
|
||||
"""Return the tool schemas a briefing run is permitted to call.
|
||||
|
||||
Builds the user's full tool list (so user-specific gating such as
|
||||
CalDAV availability still applies) and filters it down to the
|
||||
briefing allowlist.
|
||||
"""
|
||||
all_tools = await get_tools_for_user(user_id)
|
||||
filtered = [
|
||||
t for t in all_tools
|
||||
if t.get("function", {}).get("name") in BRIEFING_TOOL_NAMES
|
||||
]
|
||||
logger.debug(
|
||||
"Briefing tools for user %d: %d of %d selected",
|
||||
user_id, len(filtered), len(all_tools),
|
||||
)
|
||||
return filtered
|
||||
__all__ = ["get_briefing_tools"]
|
||||
|
||||
Reference in New Issue
Block a user