From 40c447695d94840013320248fd6f34514c36b15d Mon Sep 17 00:00:00 2001 From: laurentbarontini Date: Mon, 27 Apr 2026 16:19:02 +0200 Subject: [PATCH] Inject context --- app.py | 97 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- memory.py | 63 ++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+), 5 deletions(-) diff --git a/app.py b/app.py index bba1749..ecccd5c 100644 --- a/app.py +++ b/app.py @@ -16,7 +16,9 @@ from memory import ( add_repository, add_task, close_task, + get_memory_snapshot, init_db, + list_clients, search_memory, upsert_client, upsert_project, @@ -69,6 +71,7 @@ Actions disponibles : {"action": "ajouter_repo", "nom": "...", "projet": "...", "chemin": "...", "url": "...", "branche": "..."} {"action": "ajouter_note", "contenu": "...", "sujet": "...", "client": "...", "projet": "...", "type": "note"} {"action": "chercher_memoire", "requete": "..."} +{"action": "lister_clients"} Si une phrase contient plusieurs choses a enregistrer, utilise : {"actions": [{...}, {...}]} @@ -96,10 +99,66 @@ def sauvegarder_contacts(contacts): ) -def construire_system_prompt(): +def construire_system_prompt(session_id=None): contacts = charger_contacts() lignes = [f"- {nom.title()} : {email}" for nom, email in contacts.items()] - return SYSTEM_PROMPT + "\n\nContacts connus :\n" + "\n".join(lignes) + return ( + SYSTEM_PROMPT + + "\n\nContacts connus :\n" + + "\n".join(lignes) + + "\n\nMemoire professionnelle actuelle :\n" + + construire_contexte_memoire(session_id) + ) + + +def construire_contexte_memoire(session_id=None): + snapshot = get_memory_snapshot() + contexte = session_contexts.get(session_id or "", {}) + lignes = [] + + clients_mem = snapshot["clients"] + if clients_mem: + lignes.append("Clients connus et taches ouvertes :") + for client_mem in clients_mem: + lignes.append(f"- {client_mem['name']} : {client_mem['open_task_count']} tache(s) ouverte(s)") + else: + lignes.append("Aucun client connu.") + + if snapshot["tasks"]: + lignes.append("Taches ouvertes recentes :") + for tache in snapshot["tasks"][:15]: + sujet = tache.get("client_name") or tache.get("project_name") or "general" + lignes.append(f"- #{tache['id']} {sujet} : {tache['title']}") + + if snapshot["projects"]: + lignes.append("Projets recents :") + for projet in snapshot["projects"][:10]: + client_nom = projet.get("client_name") or "sans client" + statut = projet.get("status") or "actif" + lignes.append(f"- {projet['name']} ({client_nom}) : {statut}") + + if snapshot["repositories"]: + lignes.append("Depots Git associes :") + for repo in snapshot["repositories"][:10]: + cible = repo.get("project_name") or "sans projet" + chemin = repo.get("local_path") or repo.get("remote_url") or "chemin non renseigne" + lignes.append(f"- {repo['name']} -> {cible} : {chemin}") + + if contexte: + lignes.append("Contexte de la conversation en cours :") + if contexte.get("last_client"): + lignes.append(f"- dernier client mentionne : {contexte['last_client']}") + if contexte.get("last_project"): + lignes.append(f"- dernier projet mentionne : {contexte['last_project']}") + if contexte.get("tasks"): + ids = ", ".join(f"#{tache['id']} {tache['title']}" for tache in contexte["tasks"][:5]) + lignes.append(f"- dernieres taches affichees : {ids}") + + lignes.append( + "Utilise ce contexte pour repondre aux questions globales. " + "Pour modifier la memoire, reponds avec une action JSON." + ) + return "\n".join(lignes) def resoudre_contact(destinataire): @@ -132,6 +191,7 @@ ACTIONS = { "ajouter_repo", "ajouter_note", "chercher_memoire", + "lister_clients", } @@ -171,7 +231,7 @@ def demander_au_llm(session_id, texte): reponse = client.chat.completions.create( model=CHAT_MODEL, - messages=[{"role": "system", "content": construire_system_prompt()}] + historique, + messages=[{"role": "system", "content": construire_system_prompt(session_id)}] + historique, temperature=0.45, max_tokens=900, ) @@ -221,8 +281,8 @@ def detecter_action_directe(texte, session_id=None): "requete": recherche_match.group(1).strip(" ?.,;:"), } - if re.search(r"\b(liste|affiche|montre)\b.*\bclients?\b", texte_min): - return {"action": "chercher_memoire", "requete": ""} + if detecter_liste_clients_demandee(texte_min): + return {"action": "lister_clients"} if re.search(r"\b(liste|affiche|montre)\b.*\b(projets?|taches?|repos?|depots?)\b", texte_min): return {"action": "chercher_memoire", "requete": ""} @@ -351,6 +411,18 @@ def detecter_suppression_tache(texte, session_id): return {"action": "terminer_tache", "id": task_id} +def detecter_liste_clients_demandee(texte_min): + if not re.search(r"\bclients?\b", texte_min): + return False + return bool( + re.search(r"\b(liste|affiche|montre|donne)\b.*\bclients?\b", texte_min) + or re.search(r"\bclients?\b.*\b(liste|affiche|montre|donne)\b", texte_min) + or re.search(r"\b(quels?|qui)\s+sont\b.*\bclients?\b", texte_min) + or re.search(r"\btous\s+mes\s+clients?\b", texte_min) + or re.search(r"\bmes\s+clients?\b", texte_min) + ) + + def extraire_liste_clients(texte): if not re.search(r"\bclients?\b", texte, flags=re.IGNORECASE): return [] @@ -533,6 +605,14 @@ def executer_action(action): resultat = search_memory(action.get("requete", "")) return formater_memoire(resultat), {"memoryRead": True, "memoryResult": resultat, "memoryQuery": action.get("requete", "")} + if nom_action == "lister_clients": + clients_mem = list_clients() + return formater_liste_clients(clients_mem), { + "memoryRead": True, + "memoryResult": {"clients": clients_mem, "projects": [], "tasks": [], "repositories": [], "memories": []}, + "memoryQuery": "", + } + return "", {} @@ -630,6 +710,13 @@ def formater_memoire(resultat): return "Voici ce que j'ai trouve :\n" + "\n".join(f"- {ligne}" for ligne in lignes[:10]) +def formater_liste_clients(clients_mem): + if not clients_mem: + return "Je n'ai encore aucun client dans la memoire professionnelle." + noms = [client_mem["name"] for client_mem in clients_mem] + return "Voici tes clients :\n" + "\n".join(f"- {nom}" for nom in noms) + + def traiter_message(session_id, texte, avec_audio=False): action_directe = detecter_action_directe(texte, session_id) reponse = json.dumps(action_directe, ensure_ascii=False) if action_directe else demander_au_llm(session_id, texte) diff --git a/memory.py b/memory.py index b5618a1..61ff53c 100644 --- a/memory.py +++ b/memory.py @@ -195,6 +195,69 @@ def add_task(title, client_name="", project_name="", details="", due_at=""): return row_to_dict(db.execute("SELECT * FROM tasks WHERE id = ?", (cursor.lastrowid,)).fetchone()) +def list_clients(limit=100): + with connect() as db: + rows = db.execute( + "SELECT * FROM clients ORDER BY name COLLATE NOCASE LIMIT ?", + (limit,), + ).fetchall() + return [row_to_dict(row) for row in rows] + + +def get_memory_snapshot(limit=25): + with connect() as db: + clients = db.execute( + """ + SELECT c.*, + COUNT(t.id) AS open_task_count + FROM clients c + LEFT JOIN tasks t ON t.client_id = c.id AND t.done = 0 + GROUP BY c.id + ORDER BY c.name COLLATE NOCASE + LIMIT ? + """, + (limit,), + ).fetchall() + tasks = db.execute( + """ + SELECT t.*, c.name AS client_name, p.name AS project_name + FROM tasks t + LEFT JOIN clients c ON c.id = t.client_id + LEFT JOIN projects p ON p.id = t.project_id + WHERE t.done = 0 + ORDER BY t.created_at DESC + LIMIT ? + """, + (limit,), + ).fetchall() + projects = db.execute( + """ + SELECT p.*, c.name AS client_name + FROM projects p + LEFT JOIN clients c ON c.id = p.client_id + ORDER BY p.updated_at DESC + LIMIT ? + """, + (limit,), + ).fetchall() + repos = db.execute( + """ + SELECT r.*, p.name AS project_name + FROM repositories r + LEFT JOIN projects p ON p.id = r.project_id + ORDER BY r.updated_at DESC + LIMIT ? + """, + (limit,), + ).fetchall() + return { + "clients": [row_to_dict(row) for row in clients], + "tasks": [row_to_dict(row) for row in tasks], + "projects": [row_to_dict(row) for row in projects], + "repositories": [row_to_dict(row) for row in repos], + } + + def close_task(task_id): with connect() as db: row = db.execute(