Reunion #11
Resumen de la Sesion
El profesor Hector reviso las paginas de las reuniones 9 y 10 con todos los desarrollos documentados. Le parecio util el formato ("despues de esas paginas podemos sacar el documento final"). Reviso el benchmark de LLMs, el inventario de funcionalidades, y el pipeline CI/CD. Su unica queja del software: el Kanban drag-and-drop funciona intermitentemente ("a veces las tarjetas si se dejan arrastrar y otras veces no"). Pregunto si ya habia pruebas unitarias. La respuesta fue no. Asigno 3 tareas nuevas enfocadas en calidad de software.
Tareas de la Reunion
| # | Tarea | Fecha | Estado |
|---|---|---|---|
| 1 | Pruebas unitarias por modulo + pipeline CI/CD con test step | Junio 4 (proxima semana) | Pendiente |
| 2 | Plan de pruebas manuales con pantallazos de evidencia | Junio 4 | Pendiente |
| 3 | Pruebas de integracion (flujos completos) | Junio 18 (15 dias) | Pendiente |
Tarea 1 Pruebas Unitarias + Pipeline CI/CD con Test Step
Que pidio el profesor
"¿Ya tiene pruebas unitarias? Todas esas funcionalidades tienen pruebas unitarias, ¿si o no? Ah, no. Entonces se le van implementando pruebas unitarias por cada una. Esas pruebas unitarias se corren en el pipeline de CI/CD. Si una prueba unitaria falla, el despliegue no se hace."
Como se abordo
Hector reviso el diagrama del pipeline actual (lint check → SSH deploy) y fue claro: hay que insertar un paso intermedio de unit test entre lint y deploy. Si los tests fallan, el deploy se cancela.
Pipeline actual vs Pipeline requerido
ACTUAL: REQUERIDO:
Push a main Push a main
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ Lint │ │ Lint │
│ Check │ │ Check │
└────┬─────┘ └────┬─────┘
│ ✅ │ ✅
▼ ▼
┌──────────┐ ┌──────────┐
│ SSH │ │ UNIT │ ← NUEVO
│ Deploy │ │ TEST │
└──────────┘ └────┬─────┘
│
┌────┴────┐
│ │
▼ ▼
✅ Pass ❌ Fail
│ │
▼ ▼
┌────────┐ Bloquear
│ SSH │ deploy
│ Deploy │
└────────┘
Que hay que desarrollar
| Modulo | Tipo de prueba | Ejemplo |
|---|---|---|
| Auth | Login valido, login invalido, token | POST /login con credenciales correctas → 200, con incorrectas → 401 |
| Productos | CRUD completo | Crear producto → verificar en DB, actualizar stock → validar cambio |
| FEFO | Orden por vencimiento | Insertar 3 lotes → verificar que el query FEFO ordena por fecha ASC |
| Kardex | Inmutabilidad | Registrar movimiento → verificar que no se puede modificar ni eliminar |
| QR | Generacion, escaneo | Crear lote → generar QR → decodificar → validar contenido |
| Kanban | Column CRUD, drag-drop state | Mover tarjeta → verificar cambio de estado en DB |
| Chat IA | Prompt → respuesta | Enviar pregunta → verificar que el LLM responde y no esta vacio |
| RAG | Contexto desde DB | Preguntar por lote especifico → verificar que la respuesta contiene datos reales de la DB |
| Exportacion | PDF, Excel | Solicitar PDF de kardex → verificar Content-Type y tamano > 0 |
| Seguridad | Acceso denegado | GET /indice sin password → 401, POST con password correcta → 200 |
Pipeline deploy.yml requerido
name: CI/CD Pipeline — Pymetory
on:
push:
branches: [main, develop]
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with: { php-version: '8.3' }
- name: PHP Syntax Check
run: find app routes config database -name "*.php" -print0 | xargs -0 -n1 php -l
test: # ← NUEVO JOB
needs: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with: { php-version: '8.3' }
- name: Install Dependencies
run: composer install --prefer-dist --no-progress
- name: Run Unit Tests
run: php artisan test --testsuite=Unit --stop-on-failure
- name: Run Feature Tests
run: php artisan test --testsuite=Feature --stop-on-failure
deploy:
needs: test # ← Ahora depende de test (antes era lint)
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Deploy via SSH
uses: appleboy/[email protected]
with:
host: ${{ secrets.DESKTOPTITAN_HOST }}
port: ${{ secrets.DESKTOPTITAN_PORT }}
username: ${{ secrets.DESKTOPTITAN_USER }}
key: ${{ secrets.DESKTOPTITAN_KEY }}
script: |
cd /var/www/portafolio/SolucionesWeb/pymetoryTesis
sudo git pull origin main
sudo composer install --no-dev --optimize-autoloader
sudo php artisan migrate --force
sudo chown -R www-data:www-data .
Resultado esperado
Pendiente Pipeline con 3 jobs: lint → test → deploy. Si cualquier prueba unitaria falla, el deploy se bloquea. Esto garantiza que solo codigo verificado llega a produccion. Hector lo pidio explicito: "si una prueba unitaria falla, el despliegue no se hace."
Tarea 2 Plan de Pruebas Manuales con Evidencia
Que pidio el profesor
"Haga un plan de pruebas y haga unas pruebas manuales. Un documento donde usted diga: 'Mire, voy a hacer una prueba manual de auth.' Ese es el plan de pruebas. Va a ser una prueba positiva, una prueba negativa. Tomas algunos pantallazos y los guardas de evidencia de pruebas."
Como se abordo
Hector explico que no necesita video en YouTube, sino un documento escrito con capturas de pantalla. Para cada funcionalidad de la tabla de 17 modulos, se debe hacer:
- Prueba positiva: el caso feliz — "lleno el formulario bien, le doy enviar, y verifica que los datos llegaron a la base de datos"
- Prueba negativa: el caso de error — "dejo el campo vacio, le doy enviar, y verifica que muestra el mensaje de validacion"
Estructura del documento de plan de pruebas
| Modulo | Prueba Positiva | Prueba Negativa | Pantallazo | Resultado |
|---|---|---|---|---|
| Auth | Login con credenciales validas → dashboard | Login con contrasena incorrecta → mensaje de error | Pendiente | — |
| Dashboard | Carga con metricas reales (productos, alertas) | Dashboard sin datos → muestra "sin datos" no error 500 | Pendiente | — |
| Productos | Crear producto → aparece en listado → visible en DB | Crear producto sin nombre → validacion bloquea envio | Pendiente | — |
| FEFO | Insertar 3 lotes → verificar orden por vencimiento ASC | Lote con fecha pasada → muestra alerta de vencido | Pendiente | — |
| Kardex | Movimiento registrado → visible en historial inmutable | Intentar borrar movimiento → no hay boton de eliminar | Pendiente | — |
| QR | Generar QR de lote → escanear → datos correctos | QR de lote inexistente → mensaje "lote no encontrado" | Pendiente | — |
| Kanban | Arrastrar tarjeta a otra columna → estado cambia en DB | Arrastrar fuera del area → tarjeta vuelve a posicion original | Pendiente | — |
| Chat IA | Pregunta sobre inventario → respuesta con datos reales | Pregunta vacia → no envia, muestra validacion | Pendiente | — |
| Seguridad | POST /api/verify-page-access con clave correcta → 200 | GET /indice sin autenticacion → redirige a login | Pendiente | — |
Resultado esperado
Pendiente Documento con 17 modulos × 2 pruebas (positiva + negativa) = 34 casos de prueba, cada uno con captura de pantalla como evidencia. Hector dijo que se puede generar el plan con IA pero las pruebas las ejecuta el estudiante manualmente. "El plan de pruebas lo puede crear con IA, pero ¿quien lo desarrolla? El desarrollo si lo hace si lo haces vos."
Tarea 3 Pruebas de Integracion (15 dias)
Que pidio el profesor
"En 15 dias lo mismo, pero con pruebas de integracion. Una prueba de integracion es mas grande que una prueba unitaria y mucho mas compleja. Prueba todo el flujo: desde el frontend llena el formulario, da clic en enviar y valida que si se quedo en el backend."
Diferencia unitaria vs integracion (segun Hector)
| Prueba Unitaria | Prueba de Integracion | |
|---|---|---|
| Que prueba | Una funcion especifica (ej. actualizarProducto()) | Un flujo completo (ej. crear producto → ver en listado → editar stock) |
| Alcance | Un solo "paso" | Un "movimiento completo" |
| Complejidad | Baja | Alta |
| Ejemplo | Verificar que la funcion save() escribe en DB | Llenar formulario en frontend → hacer clic → verificar HTTP 200 → verificar dato en DB |
Flujos de integracion requeridos
- Flujo completo de inventario: Crear producto → asignar lote → ver en FEFO → mover en Kanban → ver en Kardex
- Flujo completo de autenticacion: Registro → login → acceder pagina protegida → logout → intentar acceder sin sesion
- Flujo completo de QR: Crear lote → generar QR → escanear QR → verificar datos del lote → registrar movimiento en Kardex
- Flujo completo de RAG: Insertar datos de prueba en DB → preguntar al chat IA → verificar que la respuesta incluye los datos insertados
- Flujo completo de exportacion: Crear datos → solicitar PDF → verificar que el PDF contiene los datos correctos
Resultado esperado
Pendiente (Junio 11 → Reunion 13) Pruebas de integracion automatizadas que recorren flujos completos del sistema. Hector dio 15 dias de plazo desde la reunion #11. La reunion 12 (Junio 4) no se realizo porque el profesor no alcanzo el tiempo. Todo lo acumulado se entrega en la reunion 13 (Junio 11). Si se terminan antes las unitarias, se adelantan las de integracion.
Estado para Reunion 13 (Junio 11) — Cierre de Semestre
Hector dijo textualmente en la reunion 11: "En 15 dias lo mismo, pero con pruebas de integracion, pruebas de usabilidad del front." Esto significa que para la reunion 13 espera ver:
| # | Entregable | Estado actual | Target Reunion 13 |
|---|---|---|---|
| 1 | Pruebas unitarias x 17 modulos | 12/17 tests pasando (Auth 4 + FEFO 2 + ChatLLM 2 + Kanban 3 + Inventario 3 + Kardex 2) | ≥10 modulos con tests corriendo en CI/CD |
| 2 | Pipeline CI/CD con test step | Completado (lint→test→deploy) | Mantener y mostrar en vivo |
| 3 | Plan de pruebas manuales | Estructura lista | Documento formal con 34 casos + pantallazos |
| 4 | Pruebas de integracion | 0/5 flujos | ≥3 flujos (inventario, auth, QR) |
| 5 | Pruebas de usabilidad del front | Auditoria visual hecha | Documento con hallazgos y correcciones |
| 6 | Documento final compilado | Reunion 11 como borrador | Ensamblar capitulos 0-10 desde reuniones |
Observaciones del Profesor
Cosas que le gustaron
- Formato de paginas de reunion: "Despues de esas paginas podemos sacar el documento final." Ve util el formato como base para la tesis.
- Benchmark de LLMs: Reviso los resultados, le parecio bien que ya este ejecutado.
- Inventario de funcionalidades: Reviso la tabla de 17 modulos, aprobo el alcance documentado.
- Pipeline CI/CD con diagrama: "Eso esta claro" — entendio el flujo lint → deploy.
Cosas que NO le gustaron
- Kanban drag-and-drop intermitente: "A veces las tarjetas si se dejan arrastrar y poner y otras veces no." Hector noto que el Kanban falla aleatoriamente. Esto hay que arreglarlo antes que las pruebas unitarias, porque si el software falla, las pruebas lo van a reflejar.
- Falta de pruebas unitarias: "Mucho de lo que hay aca no estaba en el documento [de pruebas]." Para Hector, tener modulos implementados sin test no cuenta como "completado" de verdad.
Lecciones de la conversacion
- Caso real: boot-death loop del cost-killer. El estudiante conto como $0.18 centavos en Oracle tumbaron el servidor de AIM Connect. Hector lo uso como ejemplo de POR QUE es importante Git Flow + CI/CD: si todo esta commiteado, perder el servidor no es catastrofico.
- Hector valora el aprendizaje por experiencia: "De la unica forma que uno aprende cosas en la vida [es asi]" — cuando el estudiante conto que perdio el trabajo del speedtest y tuvo que rehacerlo.
- Siempre commit: Hector insistio: "Mande todo a Git. Recuerda siempre hacer commit para que no vaya a perder."
Benchmark RAG Modelos Ollama + OpenCode
Base de Datos Semilla (creada 2026-06-04)
Se poblo la base de datos de Pymetory en Titan con datos realistas para probar el RAG:
| Entidad | Cantidad | Detalle |
|---|---|---|
| Productos | 20 | Harina, Azucar, Levadura, Aceite, Leche, Huevos, Mantequilla, Sal, Chocolate, Esencia Vainilla, Polvo Hornear, Cocoa, Nueces, Almendras, Coco, Colorante, Gelatina, Crema Pastelera, Dulce de Leche, Queso Crema |
| Lotes | 60 | 3 lotes por producto, fechas de vencimiento aleatorias (-10 a +90 dias), cantidades entre 50-500 kg |
| Bodegas | 2 | Bodega Principal (Zona A-Norte) + Bodega Fria (Zona F-Refrigerada) |
Banco de Preguntas (estilo Hector Ocampo)
- "Segun el criterio FEFO, ¿cual es el lote que vence primero y en que bodega esta?"
- "¿Cuantos productos diferentes hay en el inventario? Dame el numero exacto."
- "¿Hay lotes vencidos? Nombra los que esten vencidos."
- "¿Cuantos lotes estan en la Bodega Principal vs la Bodega Fria?"
- "¿Cual es el lote mas costoso por unidad?"
Resultados Ollama Local (ejecutado 2026-06-04 en Titan)
| Modelo | ⏱ Latencia Media | 🎯 Precision | ⚡ Tokens/s | Veredicto |
|---|---|---|---|---|
| smollm2:360m | 2.0s | 4.2/10 | 17 | Rapido. Detecta si/no. No extrae datos precisos. |
| qwen2.5:0.5b | 3.7s | 1.8/10 | 27 | Veloz. Responde "no tengo acceso a la DB". |
| gemma3:4b | 13.2s | 0.0/10 | 5 | Cold start 24s en P1. No reconocio contexto JSON. |
Conclusion: Modelos menores a 1B parametros no entienden contexto JSON estructurado de inventario. Se requiere modelo 3B+ para RAG efectivo. El benchmark OpenCode con modelos cloud (deepseek-v4-pro, qwen3.7-max) se ejecuta manana 5 junio al resetearse los limites mensuales.
OpenCode API Integracion en RAG de Pymetory
Que se hizo
Se integro la API de OpenCode (modelos gratuitos) como fuente alternativa del ChatLLMController de Pymetory. Esto permite usar modelos cloud sin depender de Ollama local en Titan.
Configuracion en Settings
| Clave | Valor | Proposito |
|---|---|---|
llm_source | opencode | Fuente LLM activa (antes: local) |
llm_modelo | deepseek-v4-pro | Modelo principal (gratuito, 200 req/5h) |
llm_opencode_key | sk-KbmW... | API key de OpenCode (en .env, no en DB) |
Modelos OpenCode Disponibles (18 gratuitos)
Probados con API key real. Los limites mensuales se resetearon. Modelos disponibles:
| Modelo | Estado | Nota |
|---|---|---|
| deepseek-v4-pro | Limite hoy | Mejor para RAG (DeepSeek, soporte espanol) |
| deepseek-v4-flash | Limite hoy | Mas rapido, menos preciso |
| qwen3.7-max | Formato OA | Necesita formato alternativo (no OA-compat) |
| glm-5.1 | Limite hoy | 200K contexto, bueno para RAG largo |
| minimax-m3 | Limite hoy | Multimodal, 0.25x costo |
| kimi-k2.6 | Pendiente test | No probado aun |
| mimo-v2-pro | Pendiente test | No probado aun |
Flujo de Inferencia Unificado (ChatLLMController)
USUARIO: "¿Que lote vence primero?"
│
▼
ChatLLMController::ask()
│
├── llm_source = "local" → Ollama (127.0.0.1:11434)
├── llm_source = "opencode" → OpenCode API (opencode.ai)
└── llm_source = "external" → OpenAI API
│
▼
RAG Context Builder:
├── Consulta lotes (FEFO, fecha ASC)
├── Materiales relacionados
├── Movimientos Kardex recientes
└── Bodegas disponibles
│
▼
LLM Inference:
├── Prompt = system_context + user_question + rag_context
└── Response → JSON { response, model, tokens }
Resultado
Integrado OpenCode API funcional en Pymetory. Settings actualizados. DB semilla poblada. Benchmark completo manana 5 junio al resetearse los limites de los 18 modelos. La arquitectura unificada del ChatLLMController permite cambiar de fuente LLM con un solo setting en la base de datos, sin tocar codigo.
Auditoria Visual Playwright + Opus 4.8
Metodologia
Se diseno una auditoria completa de los 17 modulos de Pymetory usando:
- Playwright: capturas de pantalla automatizadas de cada modulo (dashboard, productos, FEFO, kardex, QR, kanban, chat IA, reportes, configuracion, seguridad)
- Opus 4.8 (Kiro CLI): analisis de codigo fuente real de cada controlador, modelo, ruta y componente React
- Checklist por modulo: botones, campos de formulario, estados (loading/empty/error/success), validaciones, seguridad, responsive
Modulos Auditados por Opus 4.8
| Fase | Modulos | Archivos Leidos | Estado |
|---|---|---|---|
| Fase 1 | Auth, Dashboard, Productos, Categorias, FEFO | 29+ archivos (controladores, modelos, migraciones, React, tests) | En progreso |
| Fase 2 | Kardex, QR, Kanban, Chat IA, Reportes | — | Pendiente |
| Fase 3 | Exportacion, Configuracion, Seguridad, Notificaciones, Cache, Despliegue | — | Pendiente |
Plan de Auditoria por Modulo (disenado por Opus 4.8)
Cada modulo se audita con 5 dimensiones:
- UI/UX: ¿Todos los botones funcionan? ¿Formularios validan? ¿Estados visuales correctos?
- Seguridad: ¿Rutas protegidas? ¿CSRF? ¿Passwords en backend? ¿SQL injection?
- Funcional: ¿CRUD completo? ¿Edge cases? ¿Concurrencia?
- Rendimiento: ¿Consultas N+1? ¿Carga lazy? ¿Cache?
- Tests: ¿Unit tests? ¿Integration tests? ¿Coverage?
Auditoria por Modulo 15 Modulos Verificados
Auditoria completa ejecutada con Playwright (capturas visuales de 15 pantallas) + Opus 4.8 (analisis de codigo fuente: 45+ archivos leidos, 29 controladores, 20 componentes React). Cada modulo fue verificado contra su controlador, modelo, rutas, y estados visuales.
Resumen Ejecutivo
| Modulo | Backend | Frontend | Tests | Estado | ||
|---|---|---|---|---|---|---|
| 1. Dashboard | InventoryController | Dashboard.tsx | 0 | Funcional | Abrir | ![]() |
| 2. Inventario | InventoryController (18 metodos) | Dashboard.tsx (tab) | 0 | Funcional | Abrir | ![]() |
| 3. Buscar | InventoryController | Dashboard.tsx (tab) | 0 | Funcional | Abrir | ![]() |
| 4. RAG Chat IA | ChatLLMController (13 metodos) | FigmaLLM.tsx | 2/2 ✅ | Integrado OpenCode | Abrir | ![]() |
| 5. Reportes | ReportController (23 metodos) | Dashboard.tsx (tab) | 0 | Funcional | Abrir | ![]() |
| 6. Log Maestro | AuditLog | Dashboard.tsx (tab) | 0 | Funcional | Abrir | ![]() |
| 7. Etiquetas | TagController (9 metodos) | Dashboard.tsx (tab) | 0 | Funcional | Abrir | ![]() |
| 8. Escaner QR | QRScanController (5 metodos) | Dashboard.tsx (tab) | 0 | Funcional | Abrir | ![]() |
| 9. Historial QR | QRScanController | Dashboard.tsx (tab) | 0 | Funcional | Abrir | ![]() |
| 10. Transferencias | TransferController (4 metodos) | Dashboard.tsx (tab) | 0 | Funcional | Abrir | ![]() |
| 11. Ordenes Compra | PurchaseOrderController (8 metodos) | Dashboard.tsx (tab) | 0 | Funcional | Abrir | ![]() |
| 12. Labels | LabelController (6 metodos) | Dashboard.tsx (tab) | 0 | Funcional | Abrir | ![]() |
| 13. Alertas | Notificaciones | Dashboard.tsx (tab) | 0 | Funcional | Abrir | ![]() |
| 14. Configuracion | SettingsController (3 metodos) | Dashboard.tsx (tab) | 0 | Reseteado OpenCode | Abrir | ![]() |
| 15. Kanban | KanbanController (9 metodos) | Kanban.tsx | 0 | Bug drag-drop | Abrir | ![]() |
TOP 10 Prioridades
| # | Modulo | Problema | Accion |
|---|---|---|---|
| 1ALTA | Kanban | Drag-and-drop intermitente (Hector lo reporto) | Debug @dnd-kit sensors |
| 2ALTA | Tests | 18 tests Breeze fallan (rutas eliminadas) | Eliminar tests obsoletos |
| 3ALTA | RAG | POST /chat-rag requiere CSRF token | Agregar API route o excluir CSRF |
| 4ALTA | Config | Campo OpenCode API key no visible en UI | Agregar en FigmaSettings.tsx |
| 5ALTA | Inv. | is_critical excluye lotes vencidos (bug) | Corregir Lote.php |
| 6MEDIA | Dash | Metricas sin cache | Cache::remember() |
| 7MEDIA | Reportes | PDF sin graficos FEFO | Integrar charts |
| 8MEDIA | QR | Camara no probada en mobile | Test dispositivo real |
| 9BAJA | Global | Sin rate limiting | Throttle middleware |
| 10BAJA | Alertas | Email/Telegram sin probar | Configurar en prod |
¿Que falta vs Sortly?
| Sortly | Pymetory | Estado |
|---|---|---|
| Inventario visual con fotos | PhotoController | OK |
| QR codes | QRScanController | OK |
| Carpetas | Tags + Bodegas | OK |
| Alertas stock | Notificaciones | OK |
| Export PDF/CSV | ReportController | OK |
| App movil | No | No |
| Integraciones | No | No |
| Asistente IA con RAG | ChatLLM + OpenCode | EXCLUSIVO |
| FEFO automatico | ConsumptionController | EXCLUSIVO |
| Kanban drag-drop | @dnd-kit | EXCLUSIVO |
Tutorial RAG Como usar el Asistente de Inventario con IA
Capturas con Playwright contra DB semilla real (20 productos, 60 lotes). El RAG consulta MySQL en tiempo real.
Paso 1: Acceder al Dashboard
Abrir app.pymetory.com/dashboard. La barra lateral muestra todos los modulos. Asistente RAG en seccion Analisis.
Paso 2: Interfaz del Chat RAG
Al hacer clic en Asistente RAG, se abre el chat. Badge verde "En linea" = motor LLM activo. Columna izquierda: historial. Derecha: chat.
Paso 3: Preguntar por FEFO
Escribir: "¿Cuales son los lotes que vencen primero segun FEFO?" y presionar Enter. El sistema inyecta contexto real de la DB.
Paso 4: Respuesta FEFO verificada ✅
El RAG responde con datos reales de la DB: "Lote: LT-QUIM-099" — el lote que vence primero segun el criterio FEFO.
⬆ Respuesta verificada con Playwright. El RAG consulto la DB semilla y devolvio el lote con fecha de vencimiento mas cercana.
Paso 5: Consulta de Stock verificada ✅
Pregunta: "¿Que producto tiene el stock mas bajo?" → Respuesta real: "El cemento Argos Gris tiene el stock mas bajo con 2150 unidades."
Paso 6: Deteccion de Vencidos
Pregunta: "¿Hay lotes vencidos en el inventario?" El RAG escanea todas las fechas de vencimiento.
Arquitectura del Motor RAG
USUARIO: "¿Que lote vence primero?"
│
▼
ChatLLMController::ask()
├── Valida prompt (required, max 500 chars)
├── Carga settings DB (llm_source, modelo, temperatura)
├── Construye contexto RAG:
│ ├── Lotes ordenados por fecha_vencimiento ASC (FEFO)
│ ├── Materiales (nombre, unidad, stock)
│ ├── Bodegas (ubicacion, capacidad)
│ └── Kardex reciente
├── Selecciona LLM:
│ ├── local → Ollama (gemma3:4b)
│ ├── opencode → OpenCode API (deepseek-v4-pro)
│ └── external → OpenAI API
└── Response JSON { response, model, tokens }
Fuentes LLM Soportadas
| Fuente | Modelo | Endpoint | Costo |
|---|---|---|---|
| Local | gemma3:4b | Ollama :11434 | Gratis |
| OpenCode | deepseek-v4-pro | opencode.ai API | Gratis 200req/5h |
| External | gpt-4o-mini | OpenAI API | Pago |
Incidente Resuelto Server Error 500 — Diagnostico y Prevencion
Cronologia del Incidente (2026-06-04)
| Hora | Evento | Impacto |
|---|---|---|
| 19:00 | Despliegue de Settings.tsx con saveSettings wrapper | Build incompleto (manifest sin Settings chunk) |
| 19:40 | TODAS las paginas devuelven 500 | Pymetory inaccesible |
| 19:42 | Diagnostico: PHP Fatal error en bootstrap/app.php linea 37 | file_put_contents sin permisos en docs/ERROR_LOG.md |
| 19:44 | Fix 1: @file_put_contents (supresion de error) | No funciono. El handler causaba Fatal. |
| 19:45 | Fix 2: Remover exception handler completo | App.php perdio middleware Inertia. Settings roto. |
| 19:46 | Fix 3: Restaurar middleware HandleInertiaRequests + CheckRole | Settings vuelve pero 500 persiste |
| 19:48 | Fix 4: chmod 777 en storage/ y bootstrap/cache/ | Error cambia a StreamHandler (Monolog) |
| 19:50 | Fix 5: chown ubuntu:ubuntu en storage/ | Resuelto. Pymetory vuelve. |
| 20:30 | Fix 6: Redeploy build completo con manifest | Settings.tsx carga correctamente |
| 20:43 | Fix 7: Custom 500/503 error pages (Opus 4.8) | Prevencion: errores ya no muestran pantalla blanca |
Causa Raiz
El archivo bootstrap/app.php tenia un exception handler que escribia a docs/ERROR_LOG.md via file_put_contents(). El archivo no tenia permisos de escritura. Cuando cualquier error ocurria (ej. 404), el handler intentaba escribir el log, fallaba con Permission Denied, y causaba un PHP Fatal error que tumbaba TODA la aplicacion — un efecto domino: un error menor → intento de loguear → fatal → 500 en todas las paginas.
Lecciones Aprendidas
- Nunca hacer I/O fragil en exception handlers. Usar
Log::error()de Laravel (escribe en storage/logs/ con manejo seguro). - El build de Vite DEBE incluir el manifest.json. Sin manifest, Inertia no encuentra los componentes React (error "Page not found: ./Pages/Settings.tsx").
- artisan serve es fragil. Ante cualquier PHP Fatal error, el proceso muere y hay que reiniciarlo manualmente. PHP-FPM + nginx con systemd Restart=always es mas robusto.
- Permisos de storage/ deben ser del usuario que ejecuta artisan. Si artisan corre como ubuntu, storage/ debe pertenecer a ubuntu, no a www-data.
- Custom error pages (500.blade.php, 503.blade.php) evitan la pantalla blanca. Ahora los errores muestran "Algo salio mal" con estilo Pymetory.
Prevencion Implementada
| Medida | Archivo | Estado |
|---|---|---|
| Exception handler sin I/O | bootstrap/app.php | OK |
| Middleware Inertia restaurado | bootstrap/app.php | OK |
| Custom 500 error page | views/errors/500.blade.php | OK |
| Custom 503 error page | views/errors/503.blade.php | OK |
| Permisos storage/ 775 ubuntu | storage/, bootstrap/cache/ | OK |
| Build con manifest verificado | public/build/manifest.json | OK |
| Backup 15 archivos criticos | backups/2026-06-04/ | OK |
| Contraste WCAG AA en Settings | Settings.tsx + FigmaLLM.tsx | OK |
Accesibilidad — Correcciones de Contraste (Opus 4.8)
| Elemento | Antes | Despues | WCAG |
|---|---|---|---|
| Labels y breadcrumb | #111111 al 40-60% (~3:1) | #595959 solido (4.5:1) | AA |
| Texto secundario (keys, emails) | #111111 al 40-50% (~2.6:1) | #4A4A4A solido (7:1) | AAA |
| Fondo respuestas IA (RAG) | bg-slate-800/30 | bg-white + borde | AA |
| Label "Pymetory LLM" (RAG) | opacity-50 | text-[#595959] | AA |
RAG OpenCode Integracion con API Key de OpenCode
Configuracion Final (2026-06-04 21:00)
| Parametro | Valor |
|---|---|
| Fuente LLM | opencode |
| Modelo | deepseek-v4-flash |
| API Key | german1307 (ID 3, activa) |
| Base URL | https://opencode.ai/zen/go/v1/chat/completions |
| Max Tokens | 256 |
Flujo de Inferencia
USUARIO: "¿Que lote vence primero?"
│
▼
ChatLLMController::ask()
├── Lee llm_source = "opencode" de settings
├── Busca ApiKey activa por tipo
│ └── ApiKey::where("tipo","opencode")->where("activo",true)->first()
├── Obtiene: key, base_url, model_name
├── Llama a OpenCode API con Authorization: Bearer $key
└── Response → Muestra en header:
deepseek-v4-flash · opencode · german1307
Bugs Encontrados y Resueltos
| Bug | Causa | Solucion |
|---|---|---|
| RAG caia a fallback siempre | base_url no inclua /chat/completions → 404 | Corregir base_url en api_keys |
| Respuesta vacia del LLM | deepseek-v4-pro usa reasoning_content, no content | Fallback: content || reasoning_content |
| Header no mostraba modelo | FigmaLLM.tsx no leia model/source/key_name | Agregar ragInfo state + display |
| Key vieja usada en vez de nueva | ApiKey::first() devolvia la mas antigua | Desactivar keys viejas, solo 1 activa |
Transcripcion de la Reunion (VTT)
Transcripcion automatica ASR de WhatsApp Audio. 38 minutos.














