Universidad del Valle · Germán David Murillas Mondragón

Evidencia de Pruebas · Reuniones 11-13

53 tests PHPUnit · 58 tests Playwright · 6 bugs arreglados · 8 modelos LLM · 15 módulos · 1 rama definitiva

PHPUnit 53/53 Playwright 58/58 CI/CD Verde Nielsen 6.5
53
Tests PHPUnit
58
Tests Playwright
6
Bugs Arreglados
8
Modelos LLM
15
Módulos Sidebar
$0
Costo OpenCode

🧠 Benchmarks de Modelos LLM

Modelos Locales (Ollama · Titan ARM 4 OCPU · 24GB)

ModeloTamañoContextoVelocidadCostoEstado
gemma3:12b8.1 GB256K ⭐3-5 tok/sGratisActivo
qwen3:14b9.3 GB128K2-4 tok/sGratisActivo
gemma3:4b3.3 GB8K8-12 tok/sGratisActivo
gemma3:27b17 GB128K<1 tok/sGratisLento
qwen3:30b-a3b18 GB128K<1 tok/sGratisLento
smollm2:360m725 MB2K30+ tok/sGratisActivo

Velocidades estimadas en ARM64 sin GPU. El punto dulce es gemma3:12b (256K contexto) y qwen3:14b (razonamiento). Los modelos >17GB requieren pausar el keepalive de Oracle.

Modelos Remotos (OpenCode API · Gratis $0)

ModeloTipoContenido RealCosto
qwen3.7-plusRazonamiento✅ Sí — responde directo$0
minimax-m3Razonamiento + <think>⚠️ OK con strip$0
kimi-k2.6Razonamiento + <think>⚠️ OK con strip$0
hy3-previewExperimental⚠️ Intermitente$0
deepseek-v4-flashRazonamiento❌ Solo reasoning_content$0
glm-5.1Razonamiento❌ Solo reasoning_content$0
Modelo default en Titan: qwen3.7-plus (responde directo, sin Thinking, costo $0)
Fix aplicado: El backend filtra <think> tags y tiene fallback automático a Ollama si el modelo solo devuelve razonamiento.

Evidencia Visual — Modelo Respondiendo en Vivo

Captura tomada con Playwright contra Titan en producción (Jun 11, 2026). El header muestra el modelo activo.

Dashboard con RAG funcional

Dashboard con Motor RAG. Verificado vía API: POST /chat-rag → gemma3:4b respondió "Hola." (200 OK). También: "Hay 6 lotes." (49s, Ollama local).

🔬 10 Preguntas Encadenadas — Benchmarks Reales

qwen3.7-plus (Remoto · OpenCode · $0) ⭐ Recomendado

1-2 segundos por respuesta. Sin límite de RAM. Sin costo.

#PreguntaRespuestaT
1¿Lote que vence primero?Pintura Epóxica Azul Marín (LT-QUIM-099), vence 2026-05-152s
2¿Lotes activos?21 lotes activos1s
3¿Lotes vencidos?2 productos vencidos: Pintura Epóxica y Queso Crema2s
4¿Valor total?$68,575,000 COP1s
5¿Bodega más ocupada?Bodega Central con 29%1s
6¿Lotes críticos?1 lote crítico FEFO1s
7¿Productos >300u?Harina de Trigo(312u), Azúcar(315u), Esencia Vainilla(432u), Queso Crema(495u), Cemento(2150u)2s
8¿Vencen en 7 días?Harina de Trigo (Jun 5), Esencia Vainilla (Jun 9), Azúcar Refinada (Jun 11)1s
9¿Punto reorden Harina?Reordenar a 100u. Consumir stock actual antes del 5 de junio2s
10¿Plan 3 más críticos?P1: Pintura Epóxica (vencida). P2: Queso Crema (vencido). P3: Harina de Trigo (Jun 5)2s
Modelo recomendado para la defensa. Respuestas rápidas, precisas, usa datos reales del inventario, costo $0.

gemma3:12b (Local · Ollama · 8.1GB · 256K contexto)

Más lento por ser local + ARM sin GPU. Preciso. Ideal para contexto largo (256K tokens).

#RespuestaT
1Pintura Epóxica Azul Marín (LT-QUIM-099)221s*
22110s
3Pintura Epóxica, Queso Crema36s
48,575,000 COP14s
5Bodega Central11s

*221s en la primera pregunta (carga del modelo en RAM). Preguntas subsiguientes: 10-36s.

deepseek-v4-flash (Remoto · OpenCode · $0)

#PreguntaRespuestaT
1¿Lote que vence primero?Pintura Epóxica Azul Marín vence 2026-05-15 (VENCIDO)3s
2¿Lotes activos?En el inventario hay 6 productos2s
3¿Hay vencidos?Pintura Epóxica Azul Marín y Queso Crema están vencidos3s
4¿Valor total?$68,575,0002s
5¿Bodega ocupada?Bodega Central es la de mayor ocupación2s

Respuestas correctas pero a veces incluye "razonamiento" en la salida (filtrado por el backend).

⚡ Infraestructura y Anti-Reclaim de Oracle

Cómo Titan mantiene 24GB disponibles para IA sin que Oracle lo reclame

Oracle Cloud Free Tier reclama VMs ARM si la RAM está por debajo del 20% por más de 7 días. Titan ejecuta keepalive_mem.py que reserva 5GB para mantener el uso por encima del umbral.

16 Gi
RAM Disponible (buff/cache)
21 Gi
RAM con Keepalive Pausado
Estrategia: Al usar modelos grandes (≥17GB), se pausa temporalmente el keepalive liberando 5GB extra. Oracle evalúa el percentil 95 sobre 7 días — pausarlo por horas o incluso un día completo es seguro. Además, la inferencia de modelos grandes dispara CPU+RAM, lo cual Oracle interpreta como "actividad" y no como idle.
# Pausar keepalive durante uso de IA
sudo pkill -f keepalive_mem.py

# Usar modelo grande (18-21GB disponibles)
ollama run qwen3:30b-a3b

# Reactivar keepalive al terminar
python3 /home/ubuntu/keepalive_mem.py &

🔧 Módulos Corregidos y Unificados

Sidebar — De Código Fantasma a Navegación Unificada

ProblemaSoluciónCommit
6 paneles importados pero nunca renderizados (ghost code)Cableados LOG_MAESTRO, ESCANER, SCAN_HISTORY, TRANSFERENCIAS, PURCHASE_ORDERS, LABELS_PRINTa43d97f
FigmaSettings + FigmaKanban duplicados con Settings.tsx + Kanban.tsxEliminados tabs CONFIGURACION y PROYECTO. 1 implementación por módulo.b167eef
Kanban solo mostraba 2 items en sidebarRestaurados 15 items con href funcionales3d633b6
Settings usaba mode=dashboard con botones muertosCambiado a mode=kanban para Links navegablesb167eef
Tooltips inexistentes en sidebar15 tooltips con descripciones vía title=f023b8e
Kanban con sidebar completa

Kanban con sidebar de 15 items. Todos navegables. Drag & drop funcional.

RAG — De "Thinking..." a Respuestas Reales

ProblemaSolución
Modelos reasoning mostraban "Thinking. 1. Analyze..." al usuarioFiltro <think> + fallback a Ollama si content vacío
Sin historial de conversación (cada msg era sesión nueva)Últimos 6 mensajes enviados al LLM
Prompt demasiado restrictivoSimplificado a "Responde de forma concisa y directa"
Selector de modelo hardcodeado (solo 2 opciones)Endpoint dinámico GET /api/llm-models consultando Ollama + api_keys
Sin confirmación en mutaciones destructivasconfirm() en QR scanner y transferencias

Kanban — Drag & Drop Alineado

ProblemaSolución
DragOverlay rotado (rotate-2) — desalineado visualmenteEliminado rotate-2, ancho fijo 340px
Opacidad 0.4 dejaba ver tarjeta original debajoReducida a 0.3 + zIndex condicional
Reorder bug: mover al final producía posición incorrectawhere('id', '!=', item->id) en el incremento

API Keys + Providers — Gestión Unificada de Proveedores LLM

FeatureDetalle
Providers CRUDAgregar, editar, eliminar y toggle (enable/disable) proveedores desde Settings → API Keys
Tabla DBMigración create_llm_providers_table (key, label, base_url, enabled, models)
Config + DB merge5 providers por defecto en config/llm_providers.php + personalizados vía DB
Select de modelosDropdown con TODOS los modelos del provider (no datalist filtrado). Ollama: dinámico desde API
Base URL auto-fillAl seleccionar provider, la URL se llena automáticamente desde config
"EN USO" badgeBadge verde si la key coincide con llm_source activo del RAG
Activo exclusivoActivar una key desactiva las demás del mismo tipo (single-active-per-type)
Toggle providersBotón Power: deshabilita providers (desaparecen del selector). Usa updateOrInsert
EnmascaradoKeys visibles como sk-y••••Bu2f — cifradas en DB con cast encrypted
EndpointsGET/POST /api/llm-providers + PUT/DELETE /api/llm-providers/{key}
5 providers configurados: OpenCode (7 modelos, $0) · Ollama (local, dinámico) · OpenAI · Anthropic · Google AI. Verificado con Playwright: select muestra 8 opciones (— Modelo — + 7 modelos OpenCode)

Motor RAG — Configuración Centralizada

El módulo "Motor RAG" (Settings → primera sección) unifica toda la configuración del asistente IA en un solo lugar. Antes estaba dispersa en 3 sitios distintos.

SecciónContenido
Proveedor + Key activaSelector de origen con todos los providers habilitados + indicador de qué key está en uso
ModeloDropdown agrupado: Ollama local / modelos del provider / keys activas. Botón Refrescar
ParámetrosTemperatura (0-2), Max Tokens, Contexto Lotes
Probar RAGInput + botón que envía una pregunta real al endpoint /chat-rag y muestra la respuesta en vivo con el modelo usado
Flujo completo: Settings → Motor RAG → seleccionar provider → elegir modelo → ajustar params → "Probar RAG" con una pregunta real → ver respuesta del LLM en la misma página.

API Keys — Test de Conexión en Vivo

Cada key ahora tiene un botón azul 🔄 que prueba la conexión contra el provider real.

FeatureDetalle
EndpointPOST /api/api-keys/{id}/test — envía un mensaje de prueba al provider
Respuesta✅ "Conexión OK" si HTTP 200-299 · ❌ "HTTP 401" o el error real
Timeout10 segundos — no bloquea la UI
CSRF refreshEl botón "Nueva Key" ahora refresca el token CSRF antes de enviar y muestra errores si falla
Antes: el botón "Nueva Key" no hacía nada (419 CSRF). Ahora: refresca token + valida campos + captura errores + confirma "Key creada."

Toggle de Providers — Funcional

ProblemaSolución
Botón Power decorativo (no persistía)updateOrInsert en tabla llm_providers con label/base_url del config como fallback
Models se borraban al togglearMerge config+DB: si DB tiene models vacío, usa los del config
Datalist solo mostraba 1 modeloCambiado a <select> con todos los modelos visibles

🧠 Modelos LLM — Agregados en Titan (Ollama ARM)

Modelos descargados y verificados en Titan (ARM 4 OCPU, 24GB). Aparecen automáticamente en el selector RAG vía GET /api/llm-models.

ModeloTamañoContextoVelocidadUso
gemma3:12b8.1 GB256K ⭐3-5 tok/sSweet spot: contexto enorme + velocidad aceptable
gemma3:27b17 GB128K<1 tok/sMáxima calidad, requiere pausar keepalive
qwen3:30b-a3b18 GB (MoE q4)128K<1 tok/sMoE 3B activos; rozando el límite de RAM
qwen3:14b9.3 GB128K2-4 tok/sRazonamiento fuerte, programación
gemma3:4b3.3 GB8K8-12 tok/sRápido, tareas simples
Estrategia anti-reclaim Oracle: Titan corre keepalive_mem.py que reserva ~5 GB para mantener RAM > 20% (Oracle reclama VMs ARM <20% por 7 días). Al usar modelos grandes (≥17GB), se pausa el keepalive liberando ~18-19 GB usables. Oracle evalúa percentil 95 sobre 7 días — pausarlo horas o un día entero es seguro. Además, la inferencia dispara CPU+RAM = explícitamente NO-idle.

🤖 Hermes Agent + Nginx Proxy Manager

HerramientaVersiónDetalleEstado
Hermes Agent (Nous Research)v0.16.0192k ⭐ GitHub · 28 tools, 88 skills · 20 skills migrados de OpenClaw · Providers: OpenCode (7 modelos) + Ollama (6 modelos) · Instalado en WSL (~/.hermes/)Activo
Nginx Proxy ManagerDockerUI visual para gestionar proxy hosts, SSL (Let's Encrypt), redirecciones · npm.pymetory.com · Dashboard en reuniones/nginx/Activo

📸 Evidencia Visual — Módulos en Producción

Dashboard

Dashboard — KPIs + FEFO + Kardex

Kanban con 15 items sidebar

Kanban — @dnd-kit + 15 items sidebar + drag funcional

Kanban

Kanban — tarjetas visibles con drag handle

Settings

Settings — API Keys + Users CRUD

🐛 6 Bugs Arreglados por las Pruebas

#BugDetectado porImpacto
1Lote::is_critical excluía lotes vencidos (días < 0)ExpiredLoteRegressionTestDashboard no mostraba vencidos reales
2TransferController undefined array key "reason"TransferTestError 500 en transferencias
3InventoryController undefined array key "description"InventoryStoreTestError 500 al crear material sin descripción
4Kanban::reorder posiciones incorrectas mismo-columnaKanbanReorderTestTarjetas desordenadas al arrastrar
5Dashboard lotesCriticos no contaba vencidosDashboardCriticosConsistencyTestMétrica "Críticos FEFO" incorrecta
6Migración after('nombre') → columna es nameMigrationsSchemaSanityTestMySQL-killer silencioso en SQLite

📊 Evaluación Heurística de Nielsen

#PrincipioAntesDespuésMejora
1Visibilidad de estado88
2Sistema ↔ mundo real99
3Control del usuario46+2 (confirm() en mutaciones)
4Consistencia89+1 (sidebar unificada 15 items)
5Prevención de errores78+1 (validaciones client-side)
6Reconocer vs recordar77
7Flexibilidad / eficiencia44
8Diseño minimalista89+1 (sin ghost code, sin duplicados)
9Reconocer errores77
10Ayuda y documentación35+2 (tooltips + mini-glosario)
6.5 → 7.2

Mejora de +0.7 puntos tras correcciones de UX

🖥️ Comandos Reproducibles

# Suite completa PHPUnit
php artisan test
# Resultado: 53 passed, 1 skipped, 0 failed, 128 assertions

# Smoke tests (CI rápido)
npx playwright test --grep @smoke
# Resultado: 5 passed (17s)

# Suite visual completa
npx playwright test
# Resultado: 58 passed (~3 min), 65+ screenshots

# Casos manuales (34/34)
npx playwright test tests/usabilidad/casos-manuales.spec.cjs

# Modelos disponibles
ollama list            # locales
curl /api/llm-models   # dinámico (local + opencode)