feat: Léa personnalité humaine + fichiers + fix doublon menu

- Small talk : café, merci, ça va, qui es-tu → réponses chaleureuses
- Bouton 📎 dans le chat pour envoyer des fichiers
- Polices 13-15pt, fenêtre 600x800
- Fix doublon "Discuter avec Léa" dans le systray
- IntentType.SMALL_TALK avec 7 catégories

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Dom
2026-03-18 00:01:04 +01:00
parent 32c6808afb
commit 8d6b49277f
2 changed files with 140 additions and 4 deletions

View File

@@ -37,6 +37,7 @@ class IntentType(Enum):
DENY = "deny" # Refuser une action DENY = "deny" # Refuser une action
CLARIFY = "clarify" # Demander une clarification CLARIFY = "clarify" # Demander une clarification
DATA_IMPORT = "data_import" # Importer des données (Excel, CSV) DATA_IMPORT = "data_import" # Importer des données (Excel, CSV)
SMALL_TALK = "small_talk" # Conversation informelle (merci, café, ça va...)
UNKNOWN = "unknown" # Intention non reconnue UNKNOWN = "unknown" # Intention non reconnue
@@ -129,6 +130,24 @@ class IntentParser:
r"(?:que\s+sai[st]-(?:tu|vous)\s+faire)", r"(?:que\s+sai[st]-(?:tu|vous)\s+faire)",
r"mes\s+tâches?", r"mes\s+tâches?",
], ],
# SMALL_TALK doit être AVANT QUERY pour que "qui es-tu" ne soit pas
# capturé par le pattern générique "qui + ..." de QUERY
IntentType.SMALL_TALK: [
# Remerciements
r"^(?:merci|thanks?|thx|super|génial|parfait|cool|nickel|impec|impeccable|excellent|formidable)(?:\s.*)?$",
# Adieux
r"^(?:au revoir|à plus|bye|bonne nuit|à bientôt|à demain|ciao|tchao|tchuss|adieu)(?:\s.*)?$",
# Compliments
r"^(?:bien joué|bravo|top|chapeau|impressionnant|pas mal|bien fait|beau travail|good job|nice|trop bien|magnifique)(?:\s.*)?$",
# Mécontentement
r"^(?:c'est nul|nul|pas bien|pas top|pas ouf|bof|mauvais|moche|horrible|catastrophe|c'est pas bon|ça craint|erreur|bug|naze|pourri)(?:\s.*)?$",
# Humour / café
r"(?:un café|café|coffee|fais-moi rire|blague|raconte.+blague|drôle|rigol[eo]|mdr|lol|haha|ptdr|xd|😂|🤣)",
# Identité — qui es-tu ?
r"(?:qui es[- ]tu|t'es qui|comment tu t'appelles|c'est quoi ton (?:nom|prénom)|t'es quoi|vous êtes qui|tu es quoi|tu t'appelles comment)",
# Sentiments — ça va ?
r"(?:ça va|comment (?:ça |tu |vous )?va[st]?|comment allez[- ]vous|tu vas bien|la forme|en forme)",
],
IntentType.QUERY: [ IntentType.QUERY: [
# Questions directes avec mots interrogatifs # Questions directes avec mots interrogatifs
r"(?:comment|pourquoi|quand|où|qui)\s+(.+)\??", r"(?:comment|pourquoi|quand|où|qui)\s+(.+)\??",
@@ -446,8 +465,8 @@ class IntentParser:
# Convertir en minuscules # Convertir en minuscules
normalized = query.lower() normalized = query.lower()
# Supprimer la ponctuation excessive # Supprimer la ponctuation finale
normalized = re.sub(r'[!.]+$', '', normalized) normalized = re.sub(r'[!.?]+$', '', normalized)
# Normaliser les espaces # Normaliser les espaces
normalized = re.sub(r'\s+', ' ', normalized).strip() normalized = re.sub(r'\s+', ' ', normalized).strip()
@@ -598,6 +617,7 @@ INTENTIONS POSSIBLES:
- greeting: l'utilisateur dit bonjour/salut/hello - greeting: l'utilisateur dit bonjour/salut/hello
- confirm: l'utilisateur confirme (oui, ok, go) - confirm: l'utilisateur confirme (oui, ok, go)
- deny: l'utilisateur refuse (non, annule) - deny: l'utilisateur refuse (non, annule)
- small_talk: conversation informelle (merci, café, ça va, qui es-tu, bravo, c'est nul)
- unknown: impossible à déterminer - unknown: impossible à déterminer
Réponds UNIQUEMENT en JSON valide (pas de texte avant/après): Réponds UNIQUEMENT en JSON valide (pas de texte avant/après):
@@ -733,6 +753,15 @@ if __name__ == "__main__":
"oui", "oui",
"annule", "annule",
"statut", "statut",
# SMALL_TALK — conversation informelle
"merci",
"un café",
"ça va ?",
"qui es-tu ?",
"c'est nul",
"bravo",
"au revoir",
"t'es qui",
# Fallback — ne doit PAS être EXECUTE # Fallback — ne doit PAS être EXECUTE
"blah blah test", "blah blah test",
] ]

View File

@@ -14,6 +14,7 @@ Auteur: Dom - Janvier 2026
import logging import logging
import random import random
import re
from dataclasses import dataclass from dataclasses import dataclass
from enum import Enum from enum import Enum
from typing import Dict, Any, List, Optional from typing import Dict, Any, List, Optional
@@ -220,11 +221,48 @@ class ResponseGenerator:
"Fichier **{filename}** reçu ! Je l'analyse...", "Fichier **{filename}** reçu ! Je l'analyse...",
], ],
}, },
IntentType.SMALL_TALK: {
"thanks": [
"Avec plaisir ! N'hésitez pas si vous avez besoin d'autre chose 😊",
"De rien ! Je suis là pour ça 👍",
"Merci à vous ! Toujours prête à aider.",
],
"farewell": [
"À bientôt ! Je reste dans la barre des tâches si vous avez besoin 😊",
"Bonne continuation ! N'hésitez pas à revenir.",
"À plus tard ! Je ne bouge pas 👋",
],
"compliment": [
"Merci, c'est gentil ! J'apprends un peu plus chaque jour grâce à vous 😊",
"Oh merci ! Ça me fait plaisir 😄",
"C'est vous qui êtes formidable ! Merci pour votre confiance.",
],
"complaint": [
"Je suis désolée... Dites-moi ce qui ne va pas, je vais essayer de m'améliorer.",
"Oups... N'hésitez pas à me dire ce qui n'a pas marché, je ferai mieux la prochaine fois.",
"Pardon pour le désagrément. Comment puis-je corriger ça ?",
],
"humor": [
"Pas encore de machine à café intégrée... mais j'y travaille ! 😄 En attendant, je peux vous aider avec vos tâches ?",
"Ha ha ! Si seulement je pouvais... 😄 En attendant, dites-moi comment je peux vous aider !",
"L'humour c'est important au travail ! 😄 Bon, on s'y met ?",
],
"identity": [
"Je suis Léa, votre assistante ! Je peux apprendre vos tâches répétitives et les refaire à votre place 😊",
"Moi c'est Léa ! Je suis là pour automatiser tout ce qui vous ennuie au quotidien.",
"Je m'appelle Léa. Mon job : observer, apprendre, et vous faire gagner du temps 👍",
],
"feelings": [
"Très bien, merci de demander ! Et vous ? Prête à travailler si vous avez besoin 😊",
"En pleine forme ! Et vous, comment ça va ? Dites-moi si je peux aider.",
"Ça va super bien ! Toujours motivée pour vous donner un coup de main 💪",
],
},
IntentType.UNKNOWN: { IntentType.UNKNOWN: {
"default": [ "default": [
"Je n'ai pas bien compris. Pouvez-vous reformuler ?", "Je n'ai pas bien compris. Vous pouvez me demander de l'aide avec le bouton ❓",
"Désolée, je ne comprends pas. Tapez « aide » pour voir ce que je sais faire.", "Désolée, je ne comprends pas. Tapez « aide » pour voir ce que je sais faire.",
"Hmm, je n'ai pas saisi votre demande. Pouvez-vous préciser ?" "Hmm, je n'ai pas saisi votre demande. Essayez de reformuler ou tapez « aide »."
] ]
} }
} }
@@ -773,6 +811,75 @@ class ResponseGenerator:
metadata=result, metadata=result,
) )
def _handle_small_talk(
self,
intent: ParsedIntent,
context: Dict[str, Any],
result: Dict[str, Any]
) -> GeneratedResponse:
"""Handler pour la conversation informelle (merci, café, ça va, etc.)."""
templates = self.RESPONSE_TEMPLATES[IntentType.SMALL_TALK]
query = intent.raw_query.lower().strip()
# Déterminer la sous-catégorie de small talk
category = self._classify_small_talk(query)
category_templates = templates.get(category, templates["humor"])
message = random.choice(category_templates)
return GeneratedResponse(
message=message,
suggestions=[],
action_required=False,
)
@staticmethod
def _classify_small_talk(query: str) -> str:
"""Classifier le type de small talk à partir de la requête brute."""
# Remerciements
if re.search(
r"\b(?:merci|thanks?|thx|super|génial|parfait|cool|nickel|impec|impeccable|excellent|formidable)\b",
query
):
return "thanks"
# Adieux
if re.search(
r"\b(?:au revoir|à plus|bye|bonne nuit|à bientôt|à demain|ciao|tchao|tchuss|adieu)\b",
query
):
return "farewell"
# Identité
if re.search(
r"(?:qui es[- ]tu|t'es qui|comment tu t'appelles|c'est quoi ton (?:nom|prénom)|t'es quoi|vous êtes qui|tu t'appelles comment)",
query
):
return "identity"
# Sentiments
if re.search(
r"(?:ça va|comment (?:ça |tu |vous )?va[st]?|comment allez[- ]vous|tu vas bien|la forme|en forme)",
query
):
return "feelings"
# Mécontentement
if re.search(
r"\b(?:nul|pas bien|pas top|pas ouf|bof|mauvais|moche|horrible|catastrophe|ça craint|erreur|bug|naze|pourri)\b",
query
):
return "complaint"
# Compliments
if re.search(
r"\b(?:bien joué|bravo|top|chapeau|impressionnant|pas mal|bien fait|beau travail|good job|nice|trop bien|magnifique)\b",
query
):
return "compliment"
# Humour / café (fallback small_talk)
return "humor"
def _handle_unknown( def _handle_unknown(
self, self,
intent: ParsedIntent, intent: ParsedIntent,