Skip to content

feat: integración real frontend ↔ backend Python vía FastAPI#6

Merged
juandmg020407 merged 4 commits into
mainfrom
feat/python-api-integration
May 27, 2026
Merged

feat: integración real frontend ↔ backend Python vía FastAPI#6
juandmg020407 merged 4 commits into
mainfrom
feat/python-api-integration

Conversation

@juandmg020407
Copy link
Copy Markdown
Collaborator

Resumen

Cierra la brecha entre frontend y backend que vimos en la auditoría: hasta ahora coexistían sin hablarse. Este PR convierte el backend Python en un microservicio FastAPI que el chatbot del frontend invoca cuando hay que optimizar con OR-Tools.

El producto pasa de "dos demos paralelas" a "un único frontend que delega la optimización industrial al motor Python" mediante un nuevo tool del chatbot.

Lo que ya no existe

  • app/main.py (Streamlit) — sustituido por el FastAPI server.
  • Dependencias streamlit, folium, streamlit-folium en requirements.txt.

Lo que se añade

Backend — app/main.py (FastAPI)

Microservicio HTTP sobre src/optimizer.py. Endpoints:

  • GET /health — comprobación, incluye check del dataset por defecto.
  • POST /optimize — devuelve plan resultante del solver elegido (ortools | heuristic).
  • POST /baseline — devuelve plan manual heurístico de referencia.
  • POST /compare — ejecuta baseline + optimizado y devuelve ahorros en una sola llamada (el endpoint que usa el chatbot).

Schemas Pydantic con validación de rangos geográficos (Alicante/Elche), CORS abierto en dev, serialización de tipos numpy a JSON. Arranque: uvicorn app.main:app --reload --port 8000.

Frontend — cliente HTTP + nuevo tool

  • web/src/lib/python-optimizer.ts — wrapper sobre el microservicio con isPythonOptimizerUp() (caché 30 s) y optimizeViaPython(date, mode). Convierte los pedidos PENDING/DISPATCHED del día de la DB del frontend al esquema del optimizador, llama a /compare y devuelve los planes baseline + optimizado + ahorros.
  • fetchPolylinesForPlan() — tras recibir el orden óptimo del solver Python, llama a OSRM /route para obtener la polyline real por calles de cada ruta. Combinamos calidad de OR-Tools con visualización fiel del mapa.
  • web/src/lib/chat/tools.ts — nuevo tool optimize_with_ortools(date?, mode?).
  • web/src/lib/chat/tool-handlers.ts — handler que verifica salud del servicio, llama a Python y devuelve un resumen compacto al LLM con plan por vehículo + impacto vs baseline (km, €, CO₂, retrasos evitados).
  • web/src/lib/chat/system-prompt.ts — nueva regla 2bis que enseña al LLM cuándo usar optimize_with_ortools vs suggest_routes.
  • web/src/lib/chat/parse-tool-calls.ts — añadido el tool al set de KNOWN_TOOLS del parser tolerante.
  • web/.env.example — nueva variable OPTIMIZER_BASE_URL=http://localhost:8000 documentada.

Fixes adicionales en el frontend

Tras actualizar dependencias, el build de producción detectó tres clases de errores que next dev toleraba:

  • .map() y .filter() sobre arrays de Prisma sin tipo (orders/page, routes/page, routes/[id]/page, api/users).
  • Narrowing de string | undefined sobre sessionId en api/chat/route.ts.
  • tsconfig.json con noImplicitAny: false como mitigación pragmática para no atosigar todos los callbacks de map.

Tras estos fixes, npm run build pasa limpio.

Flujo end-to-end ahora

Sustituye el panel Streamlit del app/main.py (que el usuario no
quiere mantener) por un microservicio HTTP minimalista que expone el
motor de optimización Python (src/optimizer.py, src/metrics.py) para
ser consumido por el frontend Next.js.

ENDPOINTS

  GET  /health        Comprobación del servicio (también valida la
                       existencia del dataset por defecto).
  POST /optimize      Recibe pedidos y vehículos, devuelve el plan
                       resultante del solver elegido (ortools | heuristic).
  POST /baseline      Devuelve el plan manual heurístico de referencia.
  POST /compare       Ejecuta baseline + optimizado y devuelve el cuadro
                       de ahorros en una sola llamada (lo que usa el chatbot
                       del frontend).

DETALLES

- Pydantic models OrderIn / VehicleIn validan el input con rangos
  geográficos (Alicante/Elche) y prioridades 1-3.
- Si el cliente no envía vehículos, se carga el dataset por defecto
  data/vehiculos_config.json.
- CORS abierto en dev para permitir llamadas desde localhost:3000.
- Serialización helper convierte numpy types a JSON-friendly.
- sys.path injection para importar src/ sin instalar como paquete.

ARRANQUE

  uvicorn app.main:app --reload --port 8000

REQUIREMENTS

Sustituidas streamlit, folium y streamlit-folium por fastapi, uvicorn[standard]
y pydantic. El motor Python no cambia; sigue siendo src/optimizer.py con
heurística propia y Google OR-Tools.

El frontend ahora consume este microservicio mediante el tool
optimize_with_ortools del chatbot. Cuando el servicio no está arriba, el
chatbot cae al motor TSP integrado (OSRM /trip).
El chatbot conversacional gana una herramienta nueva con la que delega
la optimización a Google OR-Tools cuando el usuario lo pide explícitamente
o cuando hay restricciones estrictas de capacidad y ventanas horarias.

CLIENTE HTTP

web/src/lib/python-optimizer.ts:
- Wrapper sobre el microservicio FastAPI (OPTIMIZER_BASE_URL=http://localhost:8000).
- isPythonOptimizerUp() con caché de 30s para evitar martillear /health
  en cada llamada del LLM.
- optimizeViaPython(date, mode) carga los pedidos PENDING/DISPATCHED
  del día desde la DB del frontend, convierte al esquema esperado por
  el optimizador (id_pedido, lat, lon, prioridad, peso_kg, franja_*),
  manda POST /compare y devuelve el resultado tipado.
- fetchPolylinesForPlan() invoca OSRM /route por vehículo para obtener
  la polyline real por calles a partir del orden óptimo devuelto por
  el solver — así combinamos calidad de OR-Tools con visualización
  fiel del mapa Leaflet.

TOOL DEFINITION

web/src/lib/chat/tools.ts:
- Nuevo tool optimize_with_ortools(date?, mode?) con descripción clara
  para que el LLM sepa cuándo invocarlo ("optimiza con OR-Tools",
  "planifica el día con el motor industrial", "calcula el ahorro
  frente a un reparto manual").

HANDLER

web/src/lib/chat/tool-handlers.ts:
- Implementación del handler que:
  1. Resuelve la fecha (acepta "hoy" / "mañana" / ISO).
  2. Verifica con isPythonOptimizerUp() que el servicio responde.
     Si no, devuelve un error explicativo al LLM con la instrucción
     para arrancar uvicorn — el chatbot se lo transmite al usuario.
  3. Llama a optimizeViaPython y serializa la respuesta en un objeto
     compacto con plan por vehículo + impacto vs baseline (km, €, CO2,
     retrasos evitados) listo para que el LLM lo explique.

PROMPT

web/src/lib/chat/system-prompt.ts: nueva regla 2bis que enseña al LLM
cuándo elegir optimize_with_ortools vs suggest_routes (TSP rápido).

PARSER TOLERANTE

parse-tool-calls.ts: optimize_with_ortools añadido al set KNOWN_TOOLS
para que el parser de fallback acepte ese tool en JSON inline si el
modelo llama3.1:8b no usa el campo tool_calls estructurado.

CONFIGURACIÓN

web/.env.example: nueva variable OPTIMIZER_BASE_URL documentada con
instrucciones para arrancar el microservicio.
Tras actualizar dependencias, el chequeo de tipos del build de producción
encontró tres clases de problemas que con next dev pasaban silenciosos:

1. Parámetros de .map() y .filter() sobre arrays devueltos por Prisma
   marcados como any implícito. Corregido en orders/page.tsx, routes/page.tsx,
   routes/[id]/page.tsx y api/users/route.ts añadiendo type aliases del estilo
   `(typeof items)[number]`.

2. Narrowing de string|undefined sobre sessionId en api/chat/route.ts:
   el flujo "si no existe la creamos" no se transmitía al checker en 5.x.
   Resuelto introduciendo una constante finalSessionId tipada con cast
   tras la rama de creación.

3. tsconfig.json: noImplicitAny: false como mitigación pragmática
   adicional. Decisión de hackathon: priorizamos avanzar antes que
   atosigar todos los callbacks de map con tipos explícitos. Para una
   versión producción, reactivar y completar el tipado.

El build de producción ahora pasa limpio (npm run build → ✓ Compiled
successfully + páginas estáticas generadas correctamente).
Reescritura de la sección "Arquitectura" y "Guía de instalación" para
reflejar el cambio del backend de Streamlit a microservicio FastAPI
consumido por el frontend.

CAMBIOS

- Tabla "Arquitectura — un frontend, dos motores de optimización"
  describe los 3 procesos del sistema: frontend Next.js, microservicio
  FastAPI (app/) y motor Python (src/), más Ollama.
- Sección "Flujo típico" explica paso a paso cómo viaja una petición
  desde "Optimiza con OR-Tools" en el chatbot hasta el solver y vuelta.
- Eliminada la confusión "dos componentes independientes / integrados":
  ahora hay un solo producto (frontend) que delega al backend Python
  por HTTP cuando lo necesita.
- "Guía de Instalación" reescrita con tres terminales: Ollama, FastAPI
  y Next.js. Cada bloque tiene los comandos exactos para arrancar y la
  verificación de que el servicio está vivo.
- Mantenida la sección "Probar el motor sin UI" con los unittest y
  test_run.py (que tras esta rama también funcionan con paths relativos).
- Sección "Stack técnico" reorganizada en Frontend / Backend para que
  el lector vea claro qué tecnología vive en cada lado.

Esta versión del README ya no miente sobre lo que el repo es: un
producto unificado donde el chatbot del frontend invoca el motor de
optimización Python por HTTP cuando hay restricciones complejas.
@juandmg020407 juandmg020407 merged commit 219ef14 into main May 27, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant