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:
@@ -109,11 +109,30 @@ def confirm_quotation(tenant_conn, phone):
|
||||
return qid
|
||||
|
||||
|
||||
# ─── In-memory last-shown-part per phone ─────────────────────────────
|
||||
# ─── Persistent last-shown-part per phone ────────────────────────────
|
||||
# Tracks what part the bot last showed so "cotizar" knows what to add.
|
||||
# Key: phone (clean, no @lid). Value: dict with inventory item info.
|
||||
# Stored in tenant DB table whatsapp_sessions so it survives restarts.
|
||||
|
||||
_last_shown = {}
|
||||
_WHATSAPP_SESSIONS_SQL = """
|
||||
CREATE TABLE IF NOT EXISTS whatsapp_sessions (
|
||||
phone VARCHAR(50) PRIMARY KEY,
|
||||
last_shown JSONB,
|
||||
vehicle JSONB,
|
||||
updated_at TIMESTAMP DEFAULT NOW()
|
||||
);
|
||||
"""
|
||||
|
||||
|
||||
def _ensure_sessions_table(tenant_conn):
|
||||
cur = tenant_conn.cursor()
|
||||
cur.execute(_WHATSAPP_SESSIONS_SQL)
|
||||
# Migrate: add vehicle column if table already existed without it
|
||||
cur.execute("""
|
||||
ALTER TABLE whatsapp_sessions
|
||||
ADD COLUMN IF NOT EXISTS vehicle JSONB
|
||||
""")
|
||||
tenant_conn.commit()
|
||||
cur.close()
|
||||
|
||||
|
||||
def set_last_shown_part(phone, part_info):
|
||||
@@ -122,15 +141,110 @@ def set_last_shown_part(phone, part_info):
|
||||
part_info: dict with keys inventory_id, part_number, name, brand,
|
||||
price, stock, unit
|
||||
"""
|
||||
_last_shown[phone] = part_info
|
||||
# In-memory fallback for when tenant_conn is not available
|
||||
from tenant_db import get_tenant_conn
|
||||
try:
|
||||
conn = get_tenant_conn(11)
|
||||
_ensure_sessions_table(conn)
|
||||
cur = conn.cursor()
|
||||
import json
|
||||
cur.execute("""
|
||||
INSERT INTO whatsapp_sessions (phone, last_shown, updated_at)
|
||||
VALUES (%s, %s, NOW())
|
||||
ON CONFLICT (phone) DO UPDATE SET last_shown = EXCLUDED.last_shown, updated_at = NOW()
|
||||
""", (phone, json.dumps(part_info)))
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
print(f"[WA-SESSION] Failed to persist last_shown for {phone}: {e}")
|
||||
|
||||
|
||||
def get_last_shown_part(phone):
|
||||
return _last_shown.get(phone)
|
||||
from tenant_db import get_tenant_conn
|
||||
try:
|
||||
conn = get_tenant_conn(11)
|
||||
_ensure_sessions_table(conn)
|
||||
cur = conn.cursor()
|
||||
cur.execute("SELECT last_shown FROM whatsapp_sessions WHERE phone = %s", (phone,))
|
||||
row = cur.fetchone()
|
||||
cur.close()
|
||||
conn.close()
|
||||
if row and row[0]:
|
||||
return row[0]
|
||||
except Exception as e:
|
||||
print(f"[WA-SESSION] Failed to read last_shown for {phone}: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def clear_last_shown(phone):
|
||||
_last_shown.pop(phone, None)
|
||||
from tenant_db import get_tenant_conn
|
||||
try:
|
||||
conn = get_tenant_conn(11)
|
||||
_ensure_sessions_table(conn)
|
||||
cur = conn.cursor()
|
||||
cur.execute("DELETE FROM whatsapp_sessions WHERE phone = %s", (phone,))
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
print(f"[WA-SESSION] Failed to clear last_shown for {phone}: {e}")
|
||||
|
||||
|
||||
def set_vehicle(phone, vehicle):
|
||||
"""Store the detected vehicle for this phone number.
|
||||
|
||||
vehicle: dict with keys brand, model, year
|
||||
"""
|
||||
from tenant_db import get_tenant_conn
|
||||
try:
|
||||
conn = get_tenant_conn(11)
|
||||
_ensure_sessions_table(conn)
|
||||
cur = conn.cursor()
|
||||
import json
|
||||
cur.execute("""
|
||||
INSERT INTO whatsapp_sessions (phone, vehicle, updated_at)
|
||||
VALUES (%s, %s, NOW())
|
||||
ON CONFLICT (phone) DO UPDATE SET vehicle = EXCLUDED.vehicle, updated_at = NOW()
|
||||
""", (phone, json.dumps(vehicle)))
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
print(f"[WA-SESSION] Failed to persist vehicle for {phone}: {e}")
|
||||
|
||||
|
||||
def get_vehicle(phone):
|
||||
"""Retrieve the stored vehicle for this phone number."""
|
||||
from tenant_db import get_tenant_conn
|
||||
try:
|
||||
conn = get_tenant_conn(11)
|
||||
_ensure_sessions_table(conn)
|
||||
cur = conn.cursor()
|
||||
cur.execute("SELECT vehicle FROM whatsapp_sessions WHERE phone = %s", (phone,))
|
||||
row = cur.fetchone()
|
||||
cur.close()
|
||||
conn.close()
|
||||
if row and row[0]:
|
||||
return row[0]
|
||||
except Exception as e:
|
||||
print(f"[WA-SESSION] Failed to read vehicle for {phone}: {e}")
|
||||
return None
|
||||
|
||||
|
||||
def clear_session(phone):
|
||||
"""Clear all session data (last_shown + vehicle) for this phone."""
|
||||
from tenant_db import get_tenant_conn
|
||||
try:
|
||||
conn = get_tenant_conn(11)
|
||||
_ensure_sessions_table(conn)
|
||||
cur = conn.cursor()
|
||||
cur.execute("DELETE FROM whatsapp_sessions WHERE phone = %s", (phone,))
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
print(f"[WA-SESSION] Failed to clear session for {phone}: {e}")
|
||||
|
||||
|
||||
# ─── Quotation CRUD ─────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user