feat(whatsapp): QWEN primary AI backend, Hermes fallback, conversation history, vehicle persistence, demo prompts

- Add QWEN (qwen3.6) as primary AI backend with short system prompt
- Hermes remains as fallback with 45s timeout
- Increase QWEN timeout to 35s, max_tokens to 4000
- Add conversation history loading from whatsapp_messages (last 4 msgs)
- Persist detected vehicle in whatsapp_sessions table
- Add 'limpiar chat' / 'nuevo chat' / 'reset' commands to clear history
- Fix CSS conflict: rename whatsapp chat-panel classes to wa-chat-panel
- Fix JS ID conflicts with chat.js widget (waChatPanel, waChatMessages, etc.)
- Improve no-stock response: conversational with alternatives
- Split search_query by | for multi-part lookups
- Add DEMO_PROMPTS.md and DEMO_PROMPTS_V2.md
This commit is contained in:
2026-05-06 20:27:14 +00:00
parent 371d72887e
commit ff45905b49
33 changed files with 3040 additions and 445 deletions

View File

@@ -65,13 +65,13 @@
// -- DOM refs --------------------------------------------------------------
var convList = document.getElementById('convList');
var chatMessages = document.getElementById('chatMessages');
var chatMessages = document.getElementById('waChatMessages') || document.getElementById('chatMessages');
var chatHeader = document.getElementById('chatHeaderPhone');
var chatInput = document.getElementById('chatInput');
var sendBtn = document.getElementById('sendBtn');
var chatInput = document.getElementById('waChatInput') || document.getElementById('chatInput');
var sendBtn = document.getElementById('waSendBtn') || document.getElementById('sendBtn');
var newChatBtn = document.getElementById('newChatBtn');
var emptyState = document.getElementById('emptyState');
var chatPanel = document.getElementById('chatPanel');
var chatPanel = document.getElementById('waChatPanel') || document.getElementById('chatPanel');
var statusDot = document.getElementById('statusDot');
var statusText = document.getElementById('statusText');
var connectSection = document.getElementById('connectSection');
@@ -275,6 +275,7 @@
activePhone = null;
chatPanel.style.display = 'none';
emptyState.style.display = '';
if (messengerArea) messengerArea.classList.remove('has-active-chat');
}
loadConversations();
} else {
@@ -300,42 +301,65 @@
var activeContactName = '';
function openConversation(phone, contactName) {
activePhone = phone;
// Use contact name if available; fall back to formatted phone
var isLid = phone.length > 13 || !/^(52|1|44|34)/.test(phone);
activeContactName = contactName || '';
chatHeader.textContent = activeContactName || (isLid ? 'Contacto WhatsApp' : fmtPhone(phone));
emptyState.style.display = 'none';
chatPanel.style.display = 'flex';
try {
console.log('[WA-UI] Opening conversation:', phone, contactName);
activePhone = phone;
// Use contact name if available; fall back to formatted phone
var isLid = phone.length > 13 || !/^(52|1|44|34)/.test(phone);
activeContactName = contactName || '';
chatHeader.textContent = activeContactName || (isLid ? 'Contacto WhatsApp' : fmtPhone(phone));
emptyState.style.display = 'none';
chatPanel.style.display = 'flex';
console.log('[WA-UI] chatPanel display set to flex. chatPanel element:', chatPanel ? 'exists' : 'null');
// Add has-active-chat class for mobile responsive layout
if (messengerArea) messengerArea.classList.add('has-active-chat');
convList.querySelectorAll('.conv-item').forEach(function (el) {
el.classList.toggle('is-active', el.getAttribute('data-phone') === phone);
});
convList.querySelectorAll('.conv-item').forEach(function (el) {
el.classList.toggle('is-active', el.getAttribute('data-phone') === phone);
});
loadMessages(phone);
startPolling();
loadMessages(phone);
startPolling();
} catch (e) {
console.error('[WA-UI] openConversation error:', e);
}
}
function loadMessages(phone) {
console.log('[WA-UI] loadMessages start:', phone);
api('GET', '/conversations/' + encodeURIComponent(phone)).then(function (data) {
console.log('[WA-UI] loadMessages response:', data);
if (data.error) {
console.error('[WA-UI] loadMessages error:', data.error);
chatMessages.innerHTML = '<div class="chat-empty">Error cargando mensajes: ' + escHtml(data.error) + '</div>';
return;
}
var msgs = data.messages || [];
console.log('[WA-UI] loadMessages messages count:', msgs.length);
renderMessages(msgs);
}).catch(function (err) {
console.error('[WA-UI] loadMessages network error:', err);
chatMessages.innerHTML = '<div class="chat-empty">Error de red al cargar mensajes</div>';
});
}
function renderMessages(msgs) {
console.log('[WA-UI] renderMessages called with', msgs.length, 'messages');
if (!chatMessages) {
console.error('[WA-UI] chatMessages element is null!');
return;
}
var html = '';
msgs.forEach(function (m) {
var cls = m.direction === 'outgoing' ? 'msg-bubble--out' : 'msg-bubble--in';
// Support both 'text' and 'message_text' keys (backend changed)
var text = m.message_text || m.text || '';
// Support both 'created_at' and 'date' keys
var time = m.created_at || m.date || '';
html += '<div class="msg-bubble ' + cls + '">'
+ '<div class="msg-bubble__text">' + escHtml(text).replace(/\n/g, '<br>') + '</div>'
+ '<div class="msg-bubble__meta">' + fmtTime(time) + '</div>'
+ '</div>';
});
console.log('[WA-UI] renderMessages HTML length:', html.length);
chatMessages.innerHTML = html || '<div class="chat-empty">Sin mensajes</div>';
chatMessages.scrollTop = chatMessages.scrollHeight;
}