feat(assistant): #91 steps humains en direct + outils web_search/fetch_url (labels backend, SSE step, garde SSRF)
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
"""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"),
|
||||
}
|
||||
|
||||
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."""
|
||||
cleaned = " ".join((text or "").split())
|
||||
return {"key": "thought", "params": {"value": cleaned[:400]}}
|
||||
Reference in New Issue
Block a user