refactor: fusion glossaire vers concept (code API + MCP)

- api_context.py : ConceptCreate/Patch, routes /api/concepts, briefing 'Concepts'
- mcp_server.py : get_concepts (ex get_glossaire), tool_get_note garde-fou lecture retire
- redirige type=termeGlossaire vers type=concept partout (lecture, creation, briefing)
- section briefing filtree sur concepts ayant une definition (role lexical)
Corrige le 'glossaire vide' vu au test Phase 3 (code cherchait un type mort).
This commit is contained in:
2026-07-01 21:10:19 +02:00
parent 3631f11362
commit 9ab6bbf116
3 changed files with 45 additions and 44 deletions
+32 -30
View File
@@ -124,15 +124,15 @@ class ConversationPatch(BaseModel):
synthese_cloture: Optional[str] = Field(None, max_length=500) synthese_cloture: Optional[str] = Field(None, max_length=500)
statut: Optional[str] = Field(None, pattern="^(en-cours|clos)$") statut: Optional[str] = Field(None, pattern="^(en-cours|clos)$")
class GlossaireCreate(BaseModel): class ConceptCreate(BaseModel):
projet: str projet: str
terme: str titre: str
definition: str
synonymes_eviter: Optional[str] = None
class GlossairePatch(BaseModel):
definition: Optional[str] = None definition: Optional[str] = None
synonymes_eviter: Optional[str] = None source: Optional[str] = None
class ConceptPatch(BaseModel):
definition: Optional[str] = None
source: Optional[str] = None
class SkillCreate(BaseModel): class SkillCreate(BaseModel):
titre: str titre: str
@@ -416,40 +416,41 @@ def patch_conversation(note_id: str, body: ConversationPatch, llm: str = Depends
return {"id": note_id, "updated_by": llm} return {"id": note_id, "updated_by": llm}
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Routes — Glossaire # Routes — Concepts
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@app.get("/api/glossaire/{projet}", tags=["Glossaire"]) @app.get("/api/concepts/{projet}", tags=["Concepts"])
def list_glossaire(projet: str, llm: str = Depends(get_llm)): def list_concepts(projet: str, llm: str = Depends(get_llm)):
from trilium_api import search_by_label, get_label_value from trilium_api import search_by_label, get_label_value
notes = search_by_label("type", "termeGlossaire") notes = search_by_label("type", "concept")
termes = [n for n in notes concepts = [n for n in notes
if get_label_value(n["noteId"], "projet") == projet] if get_label_value(n["noteId"], "projet") == projet]
return [note_to_dict(n, with_content=True) for n in termes] return [note_to_dict(n, with_content=True) for n in concepts]
@app.post("/api/glossaire", tags=["Glossaire"], status_code=201) @app.post("/api/concepts", tags=["Concepts"], status_code=201)
def create_glossaire(body: GlossaireCreate, llm: str = Depends(get_llm)): def create_concept(body: ConceptCreate, llm: str = Depends(get_llm)):
import json import json
from trilium_api import create_note, get_note_id, set_label from trilium_api import create_note, get_note_id, set_label
with open(os.path.expanduser("~/App/Context_continuity/trilium_ids.json")) as f: with open(os.path.expanduser("~/App/Context_continuity/trilium_ids.json")) as f:
ids = json.load(f) ids = json.load(f)
content = f"<p><b>Définition</b> : {body.definition}</p>" content = f"<p><b>Définition</b> : {body.definition}</p>" if body.definition else " "
if body.synonymes_eviter: res = create_note(ids["Concepts"], body.titre, content=content)
content += f"<p><b>Synonymes à éviter</b> : {body.synonymes_eviter}</p>"
res = create_note(ids["Glossaire"], body.terme, content=content)
nid = get_note_id(res) nid = get_note_id(res)
set_label(nid, "type", "termeGlossaire") set_label(nid, "type", "concept")
set_label(nid, "projet", body.projet) set_label(nid, "projet", body.projet)
set_label(nid, "definition", body.definition) if body.definition:
return {"id": nid, "terme": body.terme, "created_by": llm} set_label(nid, "definition", body.definition)
if body.source:
set_label(nid, "source", body.source)
return {"id": nid, "titre": body.titre, "created_by": llm}
@app.patch("/api/glossaire/{note_id}", tags=["Glossaire"]) @app.patch("/api/concepts/{note_id}", tags=["Concepts"])
def patch_glossaire(note_id: str, body: GlossairePatch, llm: str = Depends(get_llm)): def patch_concept(note_id: str, body: ConceptPatch, llm: str = Depends(get_llm)):
from trilium_api import get_note, set_label, get_note_content, update_note_content from trilium_api import get_note, set_label, get_note_content, update_note_content
try: try:
get_note(note_id) get_note(note_id)
except Exception: except Exception:
raise HTTPException(status_code=404, detail="Terme introuvable") raise HTTPException(status_code=404, detail="Concept introuvable")
if body.definition: if body.definition:
set_label(note_id, "definition", body.definition) set_label(note_id, "definition", body.definition)
content = get_note_content(note_id) content = get_note_content(note_id)
@@ -489,9 +490,10 @@ def get_contexte(projet: str, llm_cible: str = "LLM",
if get_label_value(b["noteId"], "projet") == projet if get_label_value(b["noteId"], "projet") == projet
and get_label_value(b["noteId"], "statut") not in ("fait", "abandonne")] and get_label_value(b["noteId"], "statut") not in ("fait", "abandonne")]
glossaire = search_by_label("type", "termeGlossaire") glossaire = search_by_label("type", "concept")
glossaire = [g for g in glossaire glossaire = [g for g in glossaire
if get_label_value(g["noteId"], "projet") == projet] if get_label_value(g["noteId"], "projet") == projet
and get_label_value(g["noteId"], "definition")]
date = datetime.now().strftime("%d/%m/%Y") date = datetime.now().strftime("%d/%m/%Y")
lines = [ lines = [
@@ -513,7 +515,7 @@ def get_contexte(projet: str, llm_cible: str = "LLM",
lines.append("") lines.append("")
if glossaire: if glossaire:
lines += ["## Glossaire"] lines += ["## Concepts"]
for g in glossaire: for g in glossaire:
defn = get_label_value(g["noteId"], "definition") or "(voir note)" defn = get_label_value(g["noteId"], "definition") or "(voir note)"
lines.append(f"- **{g.get('title','?')}** : {defn}") lines.append(f"- **{g.get('title','?')}** : {defn}")
+11 -12
View File
@@ -86,8 +86,8 @@ TOOLS = [
}, },
}, },
{ {
"name": "get_glossaire", "name": "get_concepts",
"description": "Liste les termes du glossaire d'un projet avec leur definition.", "description": "Liste les concepts d'un projet avec leur definition (l'ancien glossaire, fusionne dans concept).",
"inputSchema": { "inputSchema": {
"type": "object", "type": "object",
"properties": {"projet": {"type": "string"}}, "properties": {"projet": {"type": "string"}},
@@ -305,9 +305,9 @@ def tool_get_historique(projet):
if get_label_value(n["noteId"], "projet") == projet if get_label_value(n["noteId"], "projet") == projet
and get_label_value(n["noteId"], "encoreValide") == "true"] and get_label_value(n["noteId"], "encoreValide") == "true"]
def tool_get_glossaire(projet): def tool_get_concepts(projet):
notes = search_by_label("type", "termeGlossaire") notes = search_by_label("type", "concept")
return [{"id": n["noteId"], "terme": n.get("title", ""), return [{"id": n["noteId"], "titre": n.get("title", ""),
"definition": get_label_value(n["noteId"], "definition") or ""} for n in notes "definition": get_label_value(n["noteId"], "definition") or ""} for n in notes
if get_label_value(n["noteId"], "projet") == projet] if get_label_value(n["noteId"], "projet") == projet]
@@ -321,8 +321,9 @@ def tool_get_contexte(projet, llm_cible="LLM"):
backlog = [n for n in search_by_label("type", "backlogItem") backlog = [n for n in search_by_label("type", "backlogItem")
if get_label_value(n["noteId"], "projet") == projet if get_label_value(n["noteId"], "projet") == projet
and get_label_value(n["noteId"], "statut") not in ("fait", "abandonne")] and get_label_value(n["noteId"], "statut") not in ("fait", "abandonne")]
glossaire = [n for n in search_by_label("type", "termeGlossaire") glossaire = [n for n in search_by_label("type", "concept")
if get_label_value(n["noteId"], "projet") == projet] if get_label_value(n["noteId"], "projet") == projet
and get_label_value(n["noteId"], "definition")]
date = datetime.now().strftime("%d/%m/%Y") date = datetime.now().strftime("%d/%m/%Y")
lines = [f"# REPRISE DE CONTEXTE — {projet}{date}", lines = [f"# REPRISE DE CONTEXTE — {projet}{date}",
f"**LLM cible : {llm_cible}**", ""] f"**LLM cible : {llm_cible}**", ""]
@@ -336,7 +337,7 @@ def tool_get_contexte(projet, llm_cible="LLM"):
lines.append(f"- [{t}] {h.get('title','?')}") lines.append(f"- [{t}] {h.get('title','?')}")
lines.append("") lines.append("")
if glossaire: if glossaire:
lines += ["## Glossaire"] lines += ["## Concepts"]
for g in glossaire: for g in glossaire:
d = get_label_value(g["noteId"], "definition") or "(voir note)" d = get_label_value(g["noteId"], "definition") or "(voir note)"
lines.append(f"- **{g.get('title','?')}** : {d}") lines.append(f"- **{g.get('title','?')}** : {d}")
@@ -449,13 +450,11 @@ def _type_systeme(note):
return None return None
def tool_get_note(note_id): def tool_get_note(note_id):
from trilium_api import TYPES_SYSTEME, get_note_content from trilium_api import get_note_content
note = get_note(note_id) note = get_note(note_id)
if not note: if not note:
return {"error": "Note introuvable"} return {"error": "Note introuvable"}
tv = _type_systeme(note) tv = _type_systeme(note)
if tv not in TYPES_SYSTEME:
return {"error": "Refus : note hors type systeme (type=%s)" % tv}
return {"id": note_id, "titre": note.get("title", ""), "type": tv, return {"id": note_id, "titre": note.get("title", ""), "type": tv,
"contenu": get_note_content(note_id)} "contenu": get_note_content(note_id)}
@@ -514,7 +513,7 @@ def tool_delete_note(note_id):
DISPATCH = { DISPATCH = {
"get_backlog": tool_get_backlog, "get_decisions": tool_get_decisions, "get_backlog": tool_get_backlog, "get_decisions": tool_get_decisions,
"get_historique": tool_get_historique, "get_glossaire": tool_get_glossaire, "get_historique": tool_get_historique, "get_concepts": tool_get_concepts,
"get_contexte": tool_get_contexte, "add_decision": tool_add_decision, "get_contexte": tool_get_contexte, "add_decision": tool_add_decision,
"add_history": tool_add_history, "add_backlog": tool_add_backlog, "add_history": tool_add_history, "add_backlog": tool_add_backlog,
"delete_note": tool_delete_note, "delete_note": tool_delete_note,
+2 -2
View File
@@ -5,11 +5,11 @@
"Backlog": "tSE6NSAxThpE", "Backlog": "tSE6NSAxThpE",
"Decisions": "JcraxD7XwhWa", "Decisions": "JcraxD7XwhWa",
"Historique": "S4NBIOBcmnY8", "Historique": "S4NBIOBcmnY8",
"Glossaire": "nDj8Z4zEkZXZ",
"ContextesReprise": "XN2uR5Y7ZEYF", "ContextesReprise": "XN2uR5Y7ZEYF",
"Skills": "ol1h09MhksPw", "Skills": "ol1h09MhksPw",
"ModelesOntologie": "z5a9JmEyiAlF", "ModelesOntologie": "z5a9JmEyiAlF",
"ProcessWorkflows": "Cw3CkUKi2W8N", "ProcessWorkflows": "Cw3CkUKi2W8N",
"EnvironnementTechnique": "aUZ4qIUVEbcz", "EnvironnementTechnique": "aUZ4qIUVEbcz",
"Methodes": "ZRVaYgdw19t2" "Methodes": "ZRVaYgdw19t2",
"Concepts": "uLLYs4GV9hh6"
} }