feat: import Excel via chat Léa, suppression nœuds VWB, fix temperature 0.1
- Chat Léa : "importe patients.xlsx" → preview → confirmation → table SQLite Bouton 📎 pour upload fichier, "montre les tables", "info table X" - VWB : suppression nœuds via touche Suppr/Backspace + bouton croix rouge - Fix : toutes les températures VLM à 0.1 (qwen3-vl bloque à 0.0) - Fix : capture VWB avec DISPLAY=:1 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -180,6 +180,44 @@ class ResponseGenerator:
|
||||
"{question}",
|
||||
]
|
||||
},
|
||||
IntentType.DATA_IMPORT: {
|
||||
"preview": [
|
||||
"J'ai trouvé le fichier **{filename}** — {total_rows} lignes, colonnes : {columns}. Je l'importe dans la table '{table_name}' ?",
|
||||
"Fichier **{filename}** prêt : {total_rows} lignes avec les colonnes {columns}. On crée la table '{table_name}' ?",
|
||||
],
|
||||
"imported": [
|
||||
"Table **'{table_name}'** créée avec {row_count} lignes et {col_count} colonnes ({columns}). Vous pouvez maintenant utiliser 'Pour chaque ligne' dans un workflow !",
|
||||
"Import réussi ! Table **'{table_name}'** : {row_count} lignes, {col_count} colonnes ({columns}).",
|
||||
],
|
||||
"list_tables": [
|
||||
"Voici les tables disponibles :\n{tables_list}",
|
||||
"Tables dans la base :\n{tables_list}",
|
||||
],
|
||||
"no_tables": [
|
||||
"Aucune table n'a été importée pour l'instant. Envoyez-moi un fichier Excel pour commencer !",
|
||||
"La base est vide. Importez un fichier Excel pour créer votre première table.",
|
||||
],
|
||||
"table_info": [
|
||||
"La table **'{table_name}'** contient {row_count} lignes et {col_count} colonnes :\n{columns_detail}",
|
||||
],
|
||||
"folder_list": [
|
||||
"J'ai trouvé {count} fichiers Excel dans le dossier :\n{files_list}\n\nDites-moi lequel importer !",
|
||||
],
|
||||
"folder_empty": [
|
||||
"Aucun fichier Excel trouvé dans le dossier '{folder}'. Vérifiez le chemin.",
|
||||
],
|
||||
"file_not_found": [
|
||||
"Je n'ai pas trouvé le fichier '{file_path}'. Vérifiez le chemin ou envoyez-le via le bouton 📎.",
|
||||
"Fichier introuvable : '{file_path}'. Vous pouvez aussi glisser un fichier dans le chat.",
|
||||
],
|
||||
"error": [
|
||||
"Erreur lors de l'import : {error}",
|
||||
"L'import a échoué : {error}",
|
||||
],
|
||||
"uploaded": [
|
||||
"Fichier **{filename}** reçu ! Je l'analyse...",
|
||||
],
|
||||
},
|
||||
IntentType.UNKNOWN: {
|
||||
"default": [
|
||||
"Je n'ai pas compris. Pouvez-vous reformuler ?",
|
||||
@@ -209,6 +247,15 @@ class ResponseGenerator:
|
||||
"facturer client X",
|
||||
"liste des workflows",
|
||||
"aide"
|
||||
],
|
||||
"after_import": [
|
||||
"montre les tables",
|
||||
"importer un autre fichier",
|
||||
"aide"
|
||||
],
|
||||
"after_table_list": [
|
||||
"importer un fichier Excel",
|
||||
"aide"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -623,6 +670,111 @@ class ResponseGenerator:
|
||||
action_required=False
|
||||
)
|
||||
|
||||
def _handle_data_import(
|
||||
self,
|
||||
intent: ParsedIntent,
|
||||
context: Dict[str, Any],
|
||||
result: Dict[str, Any]
|
||||
) -> GeneratedResponse:
|
||||
"""Handler pour les imports de données (Excel/CSV)."""
|
||||
templates = self.RESPONSE_TEMPLATES[IntentType.DATA_IMPORT]
|
||||
|
||||
if result.get("file_not_found"):
|
||||
template = random.choice(templates["file_not_found"])
|
||||
message = template.format(file_path=result.get("file_path", "?"))
|
||||
suggestions = ["aide"]
|
||||
|
||||
elif result.get("preview"):
|
||||
# Aperçu avant import
|
||||
template = random.choice(templates["preview"])
|
||||
preview = result["preview"]
|
||||
cols_str = ", ".join(preview.get("columns", [])[:8])
|
||||
if len(preview.get("columns", [])) > 8:
|
||||
cols_str += f"... (+{len(preview['columns']) - 8})"
|
||||
message = template.format(
|
||||
filename=result.get("filename", "?"),
|
||||
total_rows=preview.get("total_rows", 0),
|
||||
columns=cols_str,
|
||||
table_name=result.get("table_name", "?"),
|
||||
)
|
||||
suggestions = ["oui", "non"]
|
||||
|
||||
elif result.get("imported"):
|
||||
# Import réussi
|
||||
template = random.choice(templates["imported"])
|
||||
imp = result["imported"]
|
||||
cols_str = ", ".join(list(imp.get("columns", {}).keys())[:6])
|
||||
if len(imp.get("columns", {})) > 6:
|
||||
cols_str += "..."
|
||||
message = template.format(
|
||||
table_name=imp.get("table_name", "?"),
|
||||
row_count=imp.get("row_count", 0),
|
||||
col_count=imp.get("column_count", 0),
|
||||
columns=cols_str,
|
||||
)
|
||||
suggestions = self.CONTEXTUAL_SUGGESTIONS["after_import"]
|
||||
|
||||
elif result.get("tables_list") is not None:
|
||||
tables = result["tables_list"]
|
||||
if tables:
|
||||
lines = []
|
||||
for t in tables:
|
||||
lines.append(f" **{t['name']}** ({t['row_count']} lignes)")
|
||||
template = random.choice(templates["list_tables"])
|
||||
message = template.format(tables_list="\n".join(lines))
|
||||
suggestions = self.CONTEXTUAL_SUGGESTIONS["after_table_list"]
|
||||
else:
|
||||
message = random.choice(templates["no_tables"])
|
||||
suggestions = ["importer un fichier Excel"]
|
||||
|
||||
elif result.get("table_info"):
|
||||
info = result["table_info"]
|
||||
cols_detail = "\n".join(
|
||||
f" {c['name']} ({c['type']})" for c in info.get("columns", [])
|
||||
if c["name"] not in ("_rowid", "_imported_at")
|
||||
)
|
||||
template = random.choice(templates["table_info"])
|
||||
message = template.format(
|
||||
table_name=info.get("table_name", "?"),
|
||||
row_count=info.get("row_count", 0),
|
||||
col_count=len([c for c in info.get("columns", []) if c["name"] not in ("_rowid", "_imported_at")]),
|
||||
columns_detail=cols_detail,
|
||||
)
|
||||
suggestions = self.CONTEXTUAL_SUGGESTIONS["after_table_list"]
|
||||
|
||||
elif result.get("folder_files") is not None:
|
||||
files = result["folder_files"]
|
||||
if files:
|
||||
files_list = "\n".join(f" {f}" for f in files)
|
||||
template = random.choice(templates["folder_list"])
|
||||
message = template.format(count=len(files), files_list=files_list)
|
||||
else:
|
||||
template = random.choice(templates["folder_empty"])
|
||||
message = template.format(folder=result.get("folder", "?"))
|
||||
suggestions = ["aide"]
|
||||
|
||||
elif result.get("uploaded"):
|
||||
template = random.choice(templates["uploaded"])
|
||||
message = template.format(filename=result.get("filename", "?"))
|
||||
suggestions = []
|
||||
|
||||
elif result.get("error"):
|
||||
template = random.choice(templates["error"])
|
||||
message = template.format(error=result["error"])
|
||||
suggestions = self.CONTEXTUAL_SUGGESTIONS["after_error"]
|
||||
|
||||
else:
|
||||
message = "Je n'ai pas compris la demande d'import. Précisez le fichier ou dites 'montre les tables'."
|
||||
suggestions = ["montre les tables", "aide"]
|
||||
|
||||
return GeneratedResponse(
|
||||
message=message,
|
||||
suggestions=suggestions,
|
||||
action_required=result.get("needs_confirmation", False),
|
||||
action_type="data_import_confirm" if result.get("needs_confirmation") else None,
|
||||
metadata=result,
|
||||
)
|
||||
|
||||
def _handle_unknown(
|
||||
self,
|
||||
intent: ParsedIntent,
|
||||
|
||||
Reference in New Issue
Block a user