File size: 5,934 Bytes
0721bb4 17e31c6 0e02a0f 0721bb4 0e02a0f 17e31c6 0e02a0f 0721bb4 17e31c6 0e02a0f 0721bb4 3bacc1d 0721bb4 3bacc1d 0721bb4 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 | """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-07-09): the FE slash-command catalog is now just `/help`. `/report` was
removed — report generation is button-only (the Generate button in the Report panel);
typing `/report` no longer creates a report (the router sends it to `check`), so
surfacing it as a slash command would mislead. 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-07-09: the only FE-callable slash skill is now /help. The rest below are
# COMMENTED OUT — NOT deleted — on purpose:
# - /report is retired as a slash command: report generation is button-only (Generate
# button in the Report panel). Typing /report no longer generates a report (the router
# routes it to `check`), so exposing it here would misdirect users. The report HTTP
# endpoint (POST /api/v1/tools/report in report.py) stays — the Generate button calls it.
# - /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)
|