Inject context

This commit is contained in:
2026-04-27 16:19:02 +02:00
parent 3ff5bf4d25
commit 40c447695d
2 changed files with 155 additions and 5 deletions

97
app.py
View File

@@ -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)

View File

@@ -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(