feat(pos): chatbot IA con OpenRouter — busqueda de partes por lenguaje natural

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-02 07:18:55 +00:00
parent 32581739ad
commit 0a44fb5304
5 changed files with 776 additions and 0 deletions

83
pos/services/ai_chat.py Normal file
View File

@@ -0,0 +1,83 @@
# /home/Autopartes/pos/services/ai_chat.py
"""AI Chat service using OpenRouter for parts lookup assistance."""
import requests
import json
from config import OPENROUTER_API_KEY
OPENROUTER_URL = "https://openrouter.ai/api/v1/chat/completions"
MODEL = "anthropic/claude-haiku-4.5" # Fast + cheap for chat
SYSTEM_PROMPT = """Eres un asistente de refaccionaria automotriz mexicana. Tu trabajo es ayudar a encontrar autopartes.
Cuando el usuario describe lo que necesita, extrae:
1. Marca del vehiculo (si la menciona)
2. Modelo del vehiculo (si lo menciona)
3. Ano del vehiculo (si lo menciona)
4. Tipo de parte que busca
Responde en espanol, de forma breve y directa. Si puedes identificar el numero de parte OEM, incluyelo.
Si no tienes suficiente informacion, pregunta lo que falte.
IMPORTANTE: Responde SIEMPRE en formato JSON con esta estructura:
{
"message": "Tu respuesta al usuario",
"search_query": "texto para buscar en el catalogo" | null,
"vehicle": {"brand": "TOYOTA", "model": "Corolla", "year": 2020} | null
}
Reglas:
- "message" es tu respuesta conversacional al usuario.
- "search_query" es el texto clave para buscar partes en la base de datos (nombre de parte en ingles, numero OEM, etc). Usa null si no hay busqueda.
- "vehicle" extrae marca, modelo y ano si los menciona. Usa null si no hay vehiculo.
- La marca debe ir en MAYUSCULAS (NISSAN, TOYOTA, CHEVROLET, etc).
- Nombres comunes mexicanos: Tsuru = Sentra/Tsuru, Aveo, Jetta, Pointer, Chevy = Corsa, Vocho = Beetle.
"""
def chat(user_message, conversation_history=None):
"""Send a message to the AI and get a response with search suggestions."""
messages = [{"role": "system", "content": SYSTEM_PROMPT}]
if conversation_history:
messages.extend(conversation_history)
messages.append({"role": "user", "content": user_message})
try:
resp = requests.post(
OPENROUTER_URL,
headers={
"Authorization": f"Bearer {OPENROUTER_API_KEY}",
"Content-Type": "application/json",
},
json={
"model": MODEL,
"messages": messages,
"max_tokens": 500,
"temperature": 0.3,
},
timeout=15,
)
resp.raise_for_status()
data = resp.json()
content = data["choices"][0]["message"]["content"]
# Try to parse JSON response
try:
# Handle markdown-wrapped JSON (```json ... ```)
stripped = content.strip()
if stripped.startswith("```"):
lines = stripped.split("\n")
# Remove first and last lines (``` markers)
json_str = "\n".join(lines[1:-1])
parsed = json.loads(json_str)
else:
parsed = json.loads(stripped)
return parsed
except (json.JSONDecodeError, IndexError):
return {"message": content, "search_query": None, "vehicle": None}
except Exception as e:
return {
"message": f"Error de conexion: {str(e)}",
"search_query": None,
"vehicle": None,
}