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:
Dom
2026-03-17 07:18:51 +01:00
parent 97cb2957d5
commit 928b9e1065
8 changed files with 820 additions and 58 deletions

View File

@@ -447,6 +447,26 @@
color: var(--text-muted);
}
.attach-btn {
width: 48px;
height: 48px;
border-radius: 14px;
background: var(--bg-message-bot);
border: 1px solid var(--border);
color: var(--text-muted);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 20px;
transition: all 0.2s;
}
.attach-btn:hover {
color: var(--primary);
border-color: var(--primary);
}
.send-btn {
width: 48px;
height: 48px;
@@ -650,6 +670,10 @@
<div class="welcome-suggestion-title">📋 Voir les workflows</div>
<div class="welcome-suggestion-desc">Lister les workflows disponibles</div>
</div>
<div class="welcome-suggestion" onclick="sendSuggestion('Montre-moi les tables')">
<div class="welcome-suggestion-title">📊 Importer des données</div>
<div class="welcome-suggestion-desc">Importer un fichier Excel ou voir les tables existantes</div>
</div>
</div>
</div>
</div>
@@ -657,6 +681,10 @@
<!-- Input Area -->
<div class="input-area">
<div class="input-container">
<button class="attach-btn" onclick="document.getElementById('fileInput').click()" title="Joindre un fichier Excel">
<i class="bi bi-paperclip"></i>
</button>
<input type="file" id="fileInput" accept=".xlsx,.xls,.csv" style="display:none" onchange="handleFileUpload(event)">
<div class="input-wrapper">
<textarea
id="messageInput"
@@ -1041,7 +1069,11 @@
sessionId = data.session_id;
// Handle different response types
if (data.result?.needs_confirmation) {
if (data.result?.needs_confirmation && data.result?.preview) {
// Import de données — apercu avec demande de confirmation
addMessage(data.response.message);
addSuggestions(['oui', 'non']);
} else if (data.result?.needs_confirmation && data.result?.confirmation) {
pendingConfirmation = data.result.confirmation;
const card = createActionCard(
pendingConfirmation.workflow_name,
@@ -1084,6 +1116,18 @@
msg += `• **${w.name}**: ${w.description || 'Pas de description'}\n`;
});
addMessage(msg);
} else if (data.result?.imported) {
// Import de données réussi
addMessage(data.response.message);
if (data.response.suggestions?.length > 0) {
addSuggestions(data.response.suggestions);
}
} else if (data.result?.tables_list !== undefined || data.result?.table_info) {
// Liste des tables ou info table
addMessage(data.response.message);
if (data.response.suggestions?.length > 0) {
addSuggestions(data.response.suggestions);
}
} else {
addMessage(data.response.message);
}
@@ -1125,6 +1169,53 @@
addMessage("Demande d'annulation envoyée...");
}
// =====================================================
// File Upload
// =====================================================
async function handleFileUpload(event) {
const file = event.target.files[0];
if (!file) return;
// Afficher le message utilisateur
addMessage(`📎 ${file.name}`, 'user');
addTypingIndicator();
isProcessing = true;
updateInputState();
const formData = new FormData();
formData.append('file', file);
formData.append('session_id', sessionId || '');
try {
const response = await fetch('/api/chat/upload', {
method: 'POST',
body: formData
});
const data = await response.json();
removeTypingIndicator();
if (data.error && !data.success) {
addMessage(`Erreur : ${data.error}`);
} else if (data.message) {
addMessage(data.message);
if (data.needs_confirmation) {
addSuggestions(['oui', 'non']);
}
} else {
addMessage(`Fichier ${file.name} recu.`);
}
} catch (error) {
removeTypingIndicator();
addMessage(`Erreur d'upload : ${error.message}`);
}
isProcessing = false;
updateInputState();
// Reset le champ fichier pour permettre de re-uploader le meme fichier
event.target.value = '';
}
// =====================================================
// Copilot Mode
// =====================================================