"""Tool / command catalog API endpoints. Exposes the agent's user-invocable slash-command catalog so the Golang backend can cache it and the frontend can render its "/" command menu WITHOUT calling the AI agent for every list (Golang GETs + caches `list_tools`). Scope (2026-06-24, KM-674): the FE slash-command catalog is now just the two FE-callable skills — `/help` and `/report`. Naming: verb-first, kebab-case, `/` prefix; each command maps 1:1 to a real internal tool/intent `name` (the dispatch key). The analytics + data-access tools (analyze_*, check_*, retrieve_*) and the retired `/problem-statement` skill are kept COMMENTED in the catalog below, NOT deleted — they still exist and run via the router/Planner, and check_data/check_knowledge are served by Golang; they are simply not surfaced in the FE slash menu for now. Slash invocation bypasses the router to the tool directly, so re-exposing one is a matter of un-commenting its entry. Stateless and deterministic — safe for the Golang backend to cache. """ from typing import Literal from fastapi import APIRouter from pydantic import BaseModel from src.middlewares.logging import get_logger, log_execution logger = get_logger("tools_api") router = APIRouter(prefix="/api/v1", tags=["Tools"]) CommandType = Literal["skill", "analytics", "data_access"] class CommandResponse(BaseModel): command: str # FE-facing slash command, e.g. "/analyze-descriptive" name: str # internal handler/tool name, e.g. "analyze_descriptive" type: CommandType description: str class ListToolsResponse(BaseModel): count: int tools: list[CommandResponse] # Single source of truth for the FE slash-command catalog. Order = display order. # Keep `command` in Harry's convention (verb-first, kebab-case, `/`); `name` is the # internal route/tool name used by the orchestrator. # # 2026-06-24 (KM-674 batch): the FE-callable skills are ONLY /help + /report. The rest # below are COMMENTED OUT — NOT deleted — on purpose: # - /problem-statement is retired (objective + business_questions now live in the # New-Analysis form, not a slash skill). # - check_data / check_knowledge stay available but are served by Golang, not exposed # in the FE slash menu. # - the analytics + data-access tools still exist and run via the router/Planner; they # are simply not surfaced as FE slash commands here. # Re-enable any line if the FE slash menu is later widened back out. _COMMAND_CATALOG: list[CommandResponse] = [ CommandResponse( command="/help", name="help", type="skill", description="Show what the assistant can do and guide your next step.", ), CommandResponse( command="/report", name="report", type="skill", description="Generate a versioned analysis report (background, EDA, " "key findings, insights).", ), # CommandResponse( # command="/problem-statement", # name="problem_statement", # type="skill", # description="Define and validate your analysis goal (objective + metric) " # "before exploring data.", # ), # CommandResponse( # command="/analyze-descriptive", # name="analyze_descriptive", # type="analytics", # description="Summary statistics for selected columns (count, mean, min, max, …).", # ), # CommandResponse( # command="/analyze-aggregate", # name="analyze_aggregate", # type="analytics", # description="Group and aggregate values (sum, count, average) by dimension.", # ), # CommandResponse( # command="/analyze-correlation", # name="analyze_correlation", # type="analytics", # description="Correlation strength between numeric columns.", # ), # CommandResponse( # command="/analyze-trend", # name="analyze_trend", # type="analytics", # description="Trend of a value over time at a chosen frequency.", # ), # CommandResponse( # command="/check-data", # name="check_data", # type="data_access", # description="Inventory of the available structured data sources.", # ), # CommandResponse( # command="/check-knowledge", # name="check_knowledge", # type="data_access", # description="Inventory of the available knowledge / uploaded documents.", # ), # CommandResponse( # command="/retrieve-data", # name="retrieve_data", # type="data_access", # description="Pull rows from a structured source for analysis.", # ), # CommandResponse( # command="/retrieve-knowledge", # name="retrieve_knowledge", # type="data_access", # description="Retrieve relevant passages from your uploaded documents.", # ), ] @router.get("/tools/list", response_model=ListToolsResponse) @log_execution(logger) async def list_tools() -> ListToolsResponse: """List the user-invocable slash-command catalog (skills + tools). Static per deployment — safe for the Golang backend to cache. pr/5 Phase 2: moved from `GET /api/v1/tools` to `GET /api/v1/tools/list` so the skills group is `/tools/list` · `/tools/help` · `/tools/report`. """ return ListToolsResponse(count=len(_COMMAND_CATALOG), tools=_COMMAND_CATALOG)