- System prompt: SIEMPRE devuelve search_query en ingles - Diccionario de traducciones (balatas→Brake Pad, etc.) - Busca directo sin preguntar mas info - Fallback: extrae keywords si AI no da search_query Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
92 lines
3.5 KiB
Python
92 lines
3.5 KiB
Python
# /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.
|
|
|
|
IMPORTANTE: Responde SIEMPRE en formato JSON valido con esta estructura:
|
|
{
|
|
"message": "Tu respuesta al usuario en español",
|
|
"search_query": "termino de busqueda EN INGLES para el catalogo",
|
|
"vehicle": {"brand": "TOYOTA", "model": "Corolla", "year": 2020}
|
|
}
|
|
|
|
Reglas OBLIGATORIAS:
|
|
1. "search_query" SIEMPRE debe tener un valor cuando el usuario menciona una parte. NUNCA dejes null si el usuario pide algo.
|
|
2. "search_query" debe estar EN INGLES porque el catalogo TecDoc tiene nombres en ingles. Traducciones comunes:
|
|
- Balatas/Pastillas de freno = "Brake Pad"
|
|
- Discos de freno = "Brake Disc"
|
|
- Amortiguador = "Shock Absorber"
|
|
- Filtro de aceite = "Oil Filter"
|
|
- Filtro de aire = "Air Filter"
|
|
- Bujias = "Spark Plug"
|
|
- Banda serpentina = "V-Belt" o "Serpentine Belt"
|
|
- Bomba de agua = "Water Pump"
|
|
- Alternador = "Alternator"
|
|
- Radiador = "Radiator"
|
|
- Sensor de oxigeno = "Oxygen Sensor"
|
|
- Terminal de direccion = "Tie Rod End"
|
|
- Bomba de gasolina = "Fuel Pump"
|
|
- Clutch/Embrague = "Clutch Kit"
|
|
- Mofle/Escape = "Exhaust"
|
|
- Inyector = "Injector"
|
|
3. "vehicle" extrae marca, modelo y ano. La marca en MAYUSCULAS.
|
|
4. Nombres mexicanos: Tsuru = TSURU, Aveo = AVEO, Jetta = JETTA, Pointer = POINTER, Chevy = CORSA, Vocho = BEETLE.
|
|
5. No preguntes mas info si ya puedes buscar. Si el usuario dice "balatas para Tsuru 2015", busca directo.
|
|
6. "message" es breve y directo: "Buscando balatas para Nissan Tsuru 2015..."
|
|
"""
|
|
|
|
|
|
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,
|
|
}
|