sofhiaazzhr's picture
[NOTICKET] feat(api): add list_tools endpoint (GET /api/v1/tools)
4b7010a
Raw
History Blame
4.32 kB
"""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 confirmed: the catalog is the UNIFIED set of
everything the user can invoke via `/` —
spanning what the team internally splits into skills + analytics tools +
data-access tools. Naming : verb-first, kebab-case, `/` prefix.
Each command maps 1:1 to a real internal tool/intent `name` (the dispatch key);
the granular data-access tools (check_data, check_knowledge, retrieve_data,
retrieve_knowledge) are listed separately.
NOTE: the merged `check` intent still exists for natural-language routing — it is
NOT a slash command; slash invocation bypasses the router to the tool directly.
Deferred analytics tools (comparison/contribution/profile/segment) are NOT
exposed (not wired to the Planner).
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.
_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="/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", 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.
"""
return ListToolsResponse(count=len(_COMMAND_CATALOG), tools=_COMMAND_CATALOG)