feat: tools remove_relation + move_relation (gestion des aretes du graphe)

- trilium_api.py : remove_relation_safe (retrait via attributeId, idempotent),
  move_relation_safe (reconnexion = add nouvelle puis remove ancienne, ordre sur)
- mcp_server.py : 2 tools MCP exposes (22 tools au total)
Comble le manque revele par la fusion render_engine.py : le systeme savait
creer une relation mais pas la retirer ni la reconnecter. Rend triviales les
corrections de mesusage et les fusions de hub. Teste de bout en bout.
This commit is contained in:
2026-07-04 08:14:44 +02:00
parent da9c6319c7
commit 7c56f125ad
2 changed files with 71 additions and 1 deletions
+34
View File
@@ -227,6 +227,40 @@ def add_relation_safe(source_id, nom, cible_id):
return (True, "Relation creee: %s ~%s %s" % (source_id, nom, cible_id))
def remove_relation_safe(source_id, nom, cible_id):
"""Retire la relation source ~nom-> cible si elle existe (via son attributeId).
Idempotent : si absente, ne fait rien et le signale.
Retourne (True, message) ou (False, message)."""
src = get_note(source_id)
if not src:
return (False, "Note source introuvable: %s" % source_id)
aid = None
for a in src.get("attributes", []):
if a.get("type") == "relation" and a.get("name") == nom and a.get("value") == cible_id:
aid = a.get("attributeId")
break
if aid is None:
return (True, "Relation absente (rien a retirer): %s ~%s %s" % (source_id, nom, cible_id))
try:
_ea().delete_attribute(aid)
except Exception as e:
return (False, "Echec retrait relation: %s" % e)
return (True, "Relation retiree: %s ~%s %s" % (source_id, nom, cible_id))
def move_relation_safe(source_id, nom, ancienne_cible, nouvelle_cible):
"""Reconnecte une relation : cree source ~nom-> nouvelle_cible puis retire
source ~nom-> ancienne_cible. Geste de fusion en une operation.
Retourne (True, message) ou (False, message)."""
ok_add, msg_add = add_relation_safe(source_id, nom, nouvelle_cible)
if not ok_add:
return (False, "Echec (creation nouvelle cible) : %s" % msg_add)
ok_rm, msg_rm = remove_relation_safe(source_id, nom, ancienne_cible)
if not ok_rm:
return (False, "Nouvelle cible creee MAIS echec retrait ancienne : %s" % msg_rm)
return (True, "Relation deplacee: %s ~%s de %s vers %s" % (source_id, nom, ancienne_cible, nouvelle_cible))
def _est_editable(note):
"""Nouveau critere de garde-fou : une note est editable/supprimable si elle
porte un label projet (note de projet, propriete du projet), ou si son type