fix(agent): v4.13.1 - actions contenu document (resume/traduire/ameliorer/meeting) sans outils, apercu + appliquer/rejeter, mock ignore contexte
FlowDeck CI / test (push) Failing after 14s
FlowDeck CI / docker (push) Skipped

This commit is contained in:
2026-09-06 01:14:52 -04:00
parent 0b63ed17bc
commit 75b7f29826
8 changed files with 291 additions and 18 deletions
+31 -8
View File
@@ -190,6 +190,11 @@ class LLMClient:
async def _mock_complete(self, messages, model, tools) -> LLMResponse:
user_content = self._last_user_content(messages)
sys_content = self._system_content(messages)
# Only the user's objective drives the planner. The engine appends the
# workspace/document snapshot under "# Contexte"; that text must never
# trigger keyword heuristics (a doc mentioning "recherche"/"collection"
# used to misroute content requests into tool calls).
objective = user_content.split("\n# Contexte")[0]
# Once tool results are already in the conversation, we have acted:
# stop issuing new tool calls and conclude.
@@ -200,7 +205,7 @@ class LLMClient:
)
# Skill-driven: if the objective names a known skill, mirror its template.
skill_hint = self._extract_skill_hint(user_content)
skill_hint = self._extract_skill_hint(objective)
if skill_hint == "sprint":
return LLMResponse(
@@ -218,13 +223,13 @@ class LLMClient:
# before the tool-intent heuristics and scoped to the user objective
# only, so an injected "# Contexte" that happens to mention "collection"
# can't misroute a writing request into a create-collection action.
draft = self._draft_reply(user_content.split("\n# Contexte")[0])
draft = self._draft_reply(objective)
if draft:
return LLMResponse(text=draft, model=model)
# Exact keyword → tool intent resolution.
# Exact keyword → tool intent resolution (objective only).
for regex, builder in _CREATE_PATTERNS:
m = regex.search(user_content)
m = regex.search(objective)
if m:
return LLMResponse(
tool_calls=[dict(name=name, arguments=self._bind_placeholders(args, sys_content)) for name, args in [builder(m)]],
@@ -233,7 +238,7 @@ class LLMClient:
)
for regex, builder in _SEARCH_PATTERNS:
m = regex.search(user_content)
m = regex.search(objective)
if m:
return LLMResponse(
tool_calls=[dict(name=name, arguments=args) for name, args in [builder(m)]],
@@ -242,9 +247,9 @@ class LLMClient:
)
# Loose "collection <X>" detection → treat as a create intent.
low = user_content.lower()
low = objective.lower()
if "collection" in low:
m = _COLLECTION_LINE.search(user_content)
m = _COLLECTION_LINE.search(objective)
if m:
name = m.group(1).strip()
return LLMResponse(
@@ -256,7 +261,7 @@ class LLMClient:
# Plain conversational objective → final answer (no tool).
return LLMResponse(
text=self._summarize(user_content),
text=self._summarize(objective),
model=model,
)
@@ -335,6 +340,24 @@ class LLMClient:
"• Planifier le suivi et archiver ce compte-rendu.\n"
)
# ── Traduction / analyse du document (hors-ligne) ──
if re.search(r"traduis|traduit|translate", low):
return (
"⚠️ **Traduction non disponible en mode hors-ligne** (aucun modèle d'IA "
"connecté).\n\n"
"Connectez un fournisseur dans **Paramètres → Agent & IA**, puis relancez "
"« Traduire cette page » : le document traduit apparaîtra ici, avec un aperçu "
"à approuver ou à rejeter avant application."
)
if re.search(r"r[ée]sum|am[ée]lior|sugg[èe]re des|propose des", low):
return (
"⚠️ **Cette action nécessite un modèle d'IA connecté** pour analyser le "
"document.\n\n"
"Configurez une clé API dans **Paramètres → Agent & IA**, puis relancez "
"l'action : l'agent générera la proposition ici, avec un aperçu à approuver "
"ou à rejeter avant application."
)
# ── Page / document draft (contextual "Ask AI") ──
if title:
t = title[:80]