102 lines
4.0 KiB
Python
102 lines
4.0 KiB
Python
"""Human-readable labels for tool calls (Notion-style “steps” section).
|
|
|
|
The assistant's conversation window shows a collapsible « N steps » block
|
|
above each answer. Raw tool names (``search_fulltext``) are developer-centric;
|
|
this module maps every registered tool to an i18n message key plus the
|
|
argument worth surfacing (path, query, …), so the UI can render sentences like
|
|
« Recherche dans le vault : pizza » or « Fichier lu : notes/x.md ».
|
|
|
|
The label KEY is emitted with each ``tool`` SSE event; the client resolves it
|
|
against its locale dictionaries (``ai.step.<key>``). Unknown tools fall back to
|
|
a generic key with the raw name prettified, so a new tool never breaks the UI.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
# tool name -> (message-key, primary argument name or None)
|
|
# NOTE: argument names MUST match the pydantic input models in
|
|
# backend/tools/schemas.py (verified by tests/test_tool_labels.py).
|
|
_STEP_LABELS: dict[str, tuple[str, str | None]] = {
|
|
"list_vaults": ("vaults", None),
|
|
"list_directory": ("directory", "path"),
|
|
"list_all_files": ("files", "dir"),
|
|
"read_file": ("file_read", "path"),
|
|
"read_file_raw": ("file_read", "path"),
|
|
"get_backlinks": ("backlinks", "path"),
|
|
"list_backups": ("backups", "path"),
|
|
"diff_backup": ("backup_diff", "path"),
|
|
"get_graph": ("graph", None),
|
|
"search_fulltext": ("search", "q"),
|
|
"search_advanced": ("search", "q"),
|
|
"search_paths": ("search_paths", "q"),
|
|
"list_tags": ("tags", None),
|
|
"suggest_tags": ("tags_suggest", "q"),
|
|
"list_recent": ("recent", None),
|
|
"create_file": ("file_create", "path"),
|
|
"create_directory": ("dir_create", "path"),
|
|
"edit_file": ("file_edit", "path"),
|
|
"append_to_file": ("file_append", "path"),
|
|
"rename_file": ("file_rename", "path"),
|
|
"rename_directory": ("dir_rename", "path"),
|
|
"move_path": ("move", "source_path"),
|
|
"replace_in_files": ("replace", "find"),
|
|
"delete_file": ("file_delete", "path"),
|
|
"delete_directory": ("dir_delete", "path"),
|
|
"restore_backup": ("backup_restore", "path"),
|
|
"web_search": ("web_search", "query"),
|
|
"fetch_url": ("fetch_url", "url"),
|
|
"crawl_site": ("crawl", "url"),
|
|
"git_list_repos": ("git_repos", "provider"),
|
|
"git_search_issues": ("git_issues", "query"),
|
|
"git_get_file": ("git_file", "path"),
|
|
"create_xlsx": ("xlsx_create", "path"),
|
|
"create_docx": ("docx_create", "path"),
|
|
"create_csv": ("csv_create", "path"),
|
|
"create_pdf": ("pdf_create", "path"),
|
|
}
|
|
|
|
GENERIC_KEY = "generic"
|
|
|
|
|
|
def _prettify(name: str) -> str:
|
|
return name.replace("_", " ").strip()
|
|
|
|
|
|
def _primary_value(arguments: dict[str, Any], arg: str | None) -> str | None:
|
|
if not arg:
|
|
return None
|
|
value = arguments.get(arg)
|
|
if isinstance(value, str) and value.strip():
|
|
return value.strip()
|
|
return None
|
|
|
|
|
|
def tool_step_label(name: str, arguments: dict[str, Any] | None = None) -> dict[str, Any]:
|
|
"""Return ``{key, params}`` describing one tool call in human terms.
|
|
|
|
``key`` resolves client-side to ``ai.step.<key>``; ``params`` carries the
|
|
optional ``{value}`` placeholder (path, query, …). For an unknown tool the
|
|
generic key is used with the prettified name as the value.
|
|
"""
|
|
arguments = arguments or {}
|
|
key, arg = _STEP_LABELS.get(name, (GENERIC_KEY, None))
|
|
if key == GENERIC_KEY:
|
|
return {"key": GENERIC_KEY, "params": {"tool": _prettify(name)}}
|
|
value = _primary_value(arguments, arg)
|
|
params: dict[str, str] = {}
|
|
if value is not None:
|
|
params["value"] = value
|
|
return {"key": key, "params": params}
|
|
|
|
|
|
def thought_step_label(text: str) -> dict[str, Any]:
|
|
"""Step descriptor for one intermediate reasoning note of the model.
|
|
|
|
The note is shown expanded under a “Thought” sub-section in the UI, so it
|
|
is kept long enough to be readable (truncation is the safety net).
|
|
"""
|
|
cleaned = " ".join((text or "").split())
|
|
return {"key": "thought", "params": {"value": cleaned[:1200]}}
|