fix(ai): contexte applicatif de l'assistant, dossier vide et liens fiables (#88, BUG-041, BUG-042)
CI / lint (push) Successful in 1m16s
CI / security (push) Successful in 50s
CI / test (push) Successful in 3m0s
CI / build (push) Successful in 47s
CI / e2e (push) Successful in 10m58s

- BUG-041 : le contexte d'un dossier vide ne renvoie plus 404 ; il degrade
  vers le prompt General enrichi d'un bloc « Dossier vide ».
- BUG-042 : liens de fichiers deterministes (nom -> presse-papiers,
  dossier -> arborescence, chemin -> viewer) et resolution du chemin contre
  l'index du vault (exact -> suffixe -> basename) avant ouverture, fin des
  'File not found'.
- #88 : app_context (documents ouverts, repertoire/vault courants, recherche
  + resultats affiches) et fichiers recemment modifies injectes dans le
  prompt General.

Tests : pytest 963 passed / 6 skipped, ruff 0, mypy 0, frontend 52/52 +
validate-imports 36 modules.
This commit is contained in:
2026-09-14 11:28:30 -04:00
parent 05ed36240b
commit 314e37c433
11 changed files with 464 additions and 32 deletions
+73 -2
View File
@@ -499,13 +499,84 @@ Règles :
"""
def build_general_system_prompt(vaults: list[str] | None = None) -> str:
"""System prompt for the General assistant (app help + actions)."""
def _format_app_context(app_context: dict[str, Any] | None, recent_files: list[dict[str, Any]] | None) -> str:
"""Render the live application state for the General assistant prompt.
The General assistant has no document context; without this block it only
knows the app exists. Passing what the user currently sees (open documents,
current directory, active search, recently modified files) lets it answer
"résume ce que je fais / où j'en suis" style questions.
"""
app_context = app_context or {}
lines: list[str] = []
vault = app_context.get("vault") or app_context.get("current_vault")
if vault:
lines.append(f"- Vault sélectionné : {vault}")
directory = app_context.get("directory")
if directory:
lines.append(f"- Répertoire courant : {directory}")
current_path = app_context.get("current_path")
if current_path:
lines.append(f"- Document affiché dans le viewer : {current_path}")
docs = app_context.get("open_documents") or []
rendered_docs = []
for doc in docs:
if not isinstance(doc, dict) or not doc.get("path"):
continue
rendered_docs.append(f"{doc['path']} (vault {doc['vault']})" if doc.get("vault") else str(doc["path"]))
if rendered_docs:
lines.append("- Documents ouverts dans les onglets/panneaux : " + ", ".join(rendered_docs))
query = app_context.get("search_query")
if query:
total = app_context.get("search_total")
suffix = f" ({total} résultat(s))" if isinstance(total, int) else ""
lines.append(f"- Recherche en cours : « {query} »{suffix}")
results = app_context.get("search_results") or []
rendered_results = [str(r.get("path")) for r in results[:10] if isinstance(r, dict) and r.get("path")]
if rendered_results:
lines.append("- Premiers résultats affichés : " + ", ".join(rendered_results))
if recent_files:
rendered_recent = [
f"{f.get('vault')}/{f.get('path')}" for f in recent_files[:10] if f.get("path")
]
if rendered_recent:
lines.append("- Fichiers récemment modifiés : " + ", ".join(rendered_recent))
if not lines:
return ""
return (
"## Contexte applicatif actuel\n"
"Voici ce que l'utilisateur voit ou fait en ce moment dans ObsiGate. "
"Sers-t'en pour comprendre sa demande ; ne le répète pas inutilement.\n"
+ "\n".join(lines)
+ "\n"
)
def build_general_system_prompt(
vaults: list[str] | None = None,
app_context: dict[str, Any] | None = None,
recent_files: list[dict[str, Any]] | None = None,
) -> str:
"""System prompt for the General assistant (app help + actions).
``app_context`` carries the live UI state (open documents, current
directory, active search) and ``recent_files`` the last modified files, so
the assistant knows what the user is doing rather than answering blind.
"""
prompt = GENERAL_SYSTEM_PROMPT
if vaults:
prompt += "\nVaults disponibles : " + ", ".join(sorted(vaults)) + "\n"
else:
prompt += "\nAucun vault n'est actuellement configuré.\n"
block = _format_app_context(app_context, recent_files)
if block:
prompt += "\n" + block
return prompt