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:
148
pos/blueprints/chat_bp.py
Normal file
148
pos/blueprints/chat_bp.py
Normal file
@@ -0,0 +1,148 @@
|
||||
# /home/Autopartes/pos/blueprints/chat_bp.py
|
||||
"""Chat blueprint: AI-powered parts lookup via natural language.
|
||||
|
||||
Endpoints (all under /pos/api/chat):
|
||||
POST / — send a message, get AI response + catalog search results
|
||||
"""
|
||||
|
||||
from flask import Blueprint, request, jsonify, g
|
||||
from middleware import require_auth
|
||||
from tenant_db import get_master_conn, get_tenant_conn
|
||||
from services import catalog_service, ai_chat
|
||||
|
||||
chat_bp = Blueprint("chat", __name__, url_prefix="/pos/api/chat")
|
||||
|
||||
|
||||
@chat_bp.route("", methods=["POST"])
|
||||
@require_auth("catalog.view")
|
||||
def chat():
|
||||
body = request.get_json(force=True)
|
||||
user_message = (body.get("message") or "").strip()
|
||||
if not user_message:
|
||||
return jsonify({"error": "message required"}), 400
|
||||
|
||||
history = body.get("history") or []
|
||||
|
||||
# Call AI
|
||||
ai_response = ai_chat.chat(user_message, history)
|
||||
|
||||
search_results = []
|
||||
vehicle_match = None
|
||||
|
||||
master = None
|
||||
tenant = None
|
||||
try:
|
||||
# If AI suggests a search query, run it against the catalog
|
||||
search_query = ai_response.get("search_query")
|
||||
vehicle = ai_response.get("vehicle")
|
||||
|
||||
if search_query or vehicle:
|
||||
master = get_master_conn()
|
||||
tenant = get_tenant_conn(g.tenant_id)
|
||||
branch_id = g.branch_id
|
||||
|
||||
# Try to resolve vehicle to MYE
|
||||
if vehicle and master:
|
||||
vehicle_match = _resolve_vehicle(master, vehicle)
|
||||
|
||||
# Run catalog search if we have a search query
|
||||
if search_query and master and tenant:
|
||||
try:
|
||||
results = catalog_service.smart_search(
|
||||
master, search_query, tenant, branch_id, limit=10
|
||||
)
|
||||
search_results = results if results else []
|
||||
except Exception:
|
||||
pass # search failure is non-fatal
|
||||
|
||||
except Exception:
|
||||
pass # DB failure is non-fatal for chat
|
||||
finally:
|
||||
if master:
|
||||
try:
|
||||
master.close()
|
||||
except Exception:
|
||||
pass
|
||||
if tenant:
|
||||
try:
|
||||
tenant.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
return jsonify(
|
||||
{
|
||||
"response": ai_response.get("message", ""),
|
||||
"search_results": search_results,
|
||||
"vehicle": vehicle_match or ai_response.get("vehicle"),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
def _resolve_vehicle(master_conn, vehicle):
|
||||
"""Try to resolve AI-extracted vehicle info to brand_id/model_id in DB."""
|
||||
brand_name = (vehicle.get("brand") or "").upper().strip()
|
||||
model_name = (vehicle.get("model") or "").strip()
|
||||
year = vehicle.get("year")
|
||||
|
||||
if not brand_name:
|
||||
return vehicle
|
||||
|
||||
cur = master_conn.cursor()
|
||||
result = dict(vehicle)
|
||||
|
||||
try:
|
||||
# Find brand
|
||||
cur.execute(
|
||||
"SELECT id_brand, name_brand FROM brands WHERE UPPER(name_brand) = %s",
|
||||
(brand_name,),
|
||||
)
|
||||
brand_row = cur.fetchone()
|
||||
if brand_row:
|
||||
result["brand_id"] = brand_row[0]
|
||||
result["brand"] = brand_row[1]
|
||||
|
||||
# Find model
|
||||
if model_name:
|
||||
cur.execute(
|
||||
"""SELECT m.id_model, m.name_model
|
||||
FROM models m
|
||||
WHERE m.brand_id = %s
|
||||
AND UPPER(m.name_model) LIKE %s
|
||||
ORDER BY m.name_model
|
||||
LIMIT 5""",
|
||||
(brand_row[0], f"%{model_name.upper()}%"),
|
||||
)
|
||||
model_row = cur.fetchone()
|
||||
if model_row:
|
||||
result["model_id"] = model_row[0]
|
||||
result["model"] = model_row[1]
|
||||
|
||||
# Find year -> MYE
|
||||
if year:
|
||||
cur.execute(
|
||||
"""SELECT mye.id_mye, y.year_car, e.name_engine, mye.trim_level
|
||||
FROM model_year_engine mye
|
||||
JOIN years y ON y.id_year = mye.year_id
|
||||
JOIN engines e ON e.id_engine = mye.engine_id
|
||||
WHERE mye.model_id = %s AND y.year_car = %s
|
||||
ORDER BY e.name_engine
|
||||
LIMIT 10""",
|
||||
(model_row[0], int(year)),
|
||||
)
|
||||
mye_rows = cur.fetchall()
|
||||
if mye_rows:
|
||||
result["mye_options"] = [
|
||||
{
|
||||
"mye_id": r[0],
|
||||
"year": r[1],
|
||||
"engine": r[2],
|
||||
"trim": r[3],
|
||||
}
|
||||
for r in mye_rows
|
||||
]
|
||||
except Exception:
|
||||
pass
|
||||
finally:
|
||||
cur.close()
|
||||
|
||||
return result
|
||||
Reference in New Issue
Block a user