mirror of
https://github.com/Alkatrazz24/Funk-lab.git
synced 2026-07-09 01:44:43 +02:00
feat(stt): Phase 3 — actions admin pilotées par le LLM (admin_action) (#51)
Asa peut désormais AGIR sur le homelab quand on le demande explicitement, via un outil de la boucle agentique — mais jamais sans confirmation. - Outil admin_action(description) (contexte asa) : le LLM PROPOSE une action, n'exécute rien. brain.ask_with_tools gagne `confirm_tools` : un tel outil arrête la boucle et surface sa réponse (la question de confirmation). - _handle_agentic : stocke la proposition en pending par session ; au tour suivant « confirme » → agent.run_action → hermes-exec (hermes -z --yolo), « annule » → oubli. Réutilise le handshake + jeton du contexte agent. - admin_action n'est exposé que si _actions_available() (STT_ACTIONS_ENABLED + jeton) ; sinon retiré des schémas envoyés au modèle. - Factorisation du ctx_debug du visualiseur. 1 test unitaire (confirm_tools arrête la boucle). Serveur 0.9.0 ; doc stt.md + journal. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
dc7a2093ae
commit
3ea4c3c706
9 changed files with 163 additions and 28 deletions
|
|
@ -68,6 +68,40 @@ def test_no_schemas_falls_back_to_plain_ask(monkeypatch):
|
|||
assert reply == "réponse simple" and trace == []
|
||||
|
||||
|
||||
def test_confirm_tool_stops_loop(monkeypatch):
|
||||
"""Un outil de `confirm_tools` (ex. admin_action) arrête la boucle et surface sa réponse."""
|
||||
calls: list[dict] = []
|
||||
|
||||
async def fake_post(payload):
|
||||
calls.append(payload)
|
||||
return { # le modèle veut agir → appelle admin_action
|
||||
"role": "assistant", "content": "",
|
||||
"tool_calls": [{
|
||||
"id": "a1", "type": "function",
|
||||
"function": {"name": "admin_action",
|
||||
"arguments": '{"description": "redémarrer open-webui"}'},
|
||||
}],
|
||||
}
|
||||
|
||||
monkeypatch.setattr(brain, "_post", fake_post)
|
||||
|
||||
async def dispatch(name, args):
|
||||
# simule le handshake : renvoie la question de confirmation (pas d'exécution)
|
||||
return f"Tu veux que j'exécute : « {args['description']} » ? Dis « confirme »."
|
||||
|
||||
async def go():
|
||||
schemas = [{"type": "function", "function": {"name": "admin_action", "parameters": {}}}]
|
||||
return await brain.ask_with_tools(
|
||||
"redémarre open-webui", "sys",
|
||||
schemas=schemas, dispatch=dispatch, confirm_tools=("admin_action",),
|
||||
)
|
||||
|
||||
reply, trace = asyncio.run(go())
|
||||
assert "confirme" in reply
|
||||
assert len(calls) == 1 # boucle stoppée après 1 appel LLM (pas de 2e tour)
|
||||
assert trace[0]["name"] == "admin_action"
|
||||
|
||||
|
||||
def test_loop_forces_answer_after_max_iters(monkeypatch):
|
||||
"""Si le modèle boucle indéfiniment sur des outils, un dernier appel force une réponse."""
|
||||
calls: list[dict] = []
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue