Universidad del Valle · Germán David Murillas Mondragón
53 tests PHPUnit · 58 tests Playwright · 6 bugs arreglados · 8 modelos LLM · 15 módulos · 1 rama definitiva
| Modelo | Tamaño | Contexto | Velocidad | Costo | Estado |
|---|---|---|---|---|---|
gemma3:12b | 8.1 GB | 256K ⭐ | 3-5 tok/s | Gratis | Activo |
qwen3:14b | 9.3 GB | 128K | 2-4 tok/s | Gratis | Activo |
gemma3:4b | 3.3 GB | 8K | 8-12 tok/s | Gratis | Activo |
gemma3:27b | 17 GB | 128K | <1 tok/s | Gratis | Lento |
qwen3:30b-a3b | 18 GB | 128K | <1 tok/s | Gratis | Lento |
smollm2:360m | 725 MB | 2K | 30+ tok/s | Gratis | Activo |
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.
| Modelo | Tipo | Contenido Real | Costo |
|---|---|---|---|
qwen3.7-plus | Razonamiento | ✅ Sí — responde directo | $0 |
minimax-m3 | Razonamiento + <think> | ⚠️ OK con strip | $0 |
kimi-k2.6 | Razonamiento + <think> | ⚠️ OK con strip | $0 |
hy3-preview | Experimental | ⚠️ Intermitente | $0 |
deepseek-v4-flash | Razonamiento | ❌ Solo reasoning_content | $0 |
glm-5.1 | Razonamiento | ❌ Solo reasoning_content | $0 |
qwen3.7-plus (responde directo, sin Thinking, costo $0)<think> tags y tiene fallback automático a Ollama si el modelo solo devuelve razonamiento.Captura tomada con Playwright contra Titan en producción (Jun 11, 2026). El header muestra el modelo activo.
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).
1-2 segundos por respuesta. Sin límite de RAM. Sin costo.
| # | Pregunta | Respuesta | T |
|---|---|---|---|
| 1 | ¿Lote que vence primero? | Pintura Epóxica Azul Marín (LT-QUIM-099), vence 2026-05-15 | 2s |
| 2 | ¿Lotes activos? | 21 lotes activos | 1s |
| 3 | ¿Lotes vencidos? | 2 productos vencidos: Pintura Epóxica y Queso Crema | 2s |
| 4 | ¿Valor total? | $68,575,000 COP | 1s |
| 5 | ¿Bodega más ocupada? | Bodega Central con 29% | 1s |
| 6 | ¿Lotes críticos? | 1 lote crítico FEFO | 1s |
| 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 junio | 2s |
| 10 | ¿Plan 3 más críticos? | P1: Pintura Epóxica (vencida). P2: Queso Crema (vencido). P3: Harina de Trigo (Jun 5) | 2s |
Más lento por ser local + ARM sin GPU. Preciso. Ideal para contexto largo (256K tokens).
| # | Respuesta | T |
|---|---|---|
| 1 | Pintura Epóxica Azul Marín (LT-QUIM-099) | 221s* |
| 2 | 21 | 10s |
| 3 | Pintura Epóxica, Queso Crema | 36s |
| 4 | 8,575,000 COP | 14s |
| 5 | Bodega Central | 11s |
*221s en la primera pregunta (carga del modelo en RAM). Preguntas subsiguientes: 10-36s.
| # | Pregunta | Respuesta | T |
|---|---|---|---|
| 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 productos | 2s |
| 3 | ¿Hay vencidos? | Pintura Epóxica Azul Marín y Queso Crema están vencidos | 3s |
| 4 | ¿Valor total? | $68,575,000 | 2s |
| 5 | ¿Bodega ocupada? | Bodega Central es la de mayor ocupación | 2s |
Respuestas correctas pero a veces incluye "razonamiento" en la salida (filtrado por el backend).
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.
# 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 &
| Problema | Solución | Commit |
|---|---|---|
| 6 paneles importados pero nunca renderizados (ghost code) | Cableados LOG_MAESTRO, ESCANER, SCAN_HISTORY, TRANSFERENCIAS, PURCHASE_ORDERS, LABELS_PRINT | a43d97f |
| FigmaSettings + FigmaKanban duplicados con Settings.tsx + Kanban.tsx | Eliminados tabs CONFIGURACION y PROYECTO. 1 implementación por módulo. | b167eef |
| Kanban solo mostraba 2 items en sidebar | Restaurados 15 items con href funcionales | 3d633b6 |
| Settings usaba mode=dashboard con botones muertos | Cambiado a mode=kanban para Links navegables | b167eef |
| Tooltips inexistentes en sidebar | 15 tooltips con descripciones vía title= | f023b8e |
Kanban con sidebar de 15 items. Todos navegables. Drag & drop funcional.
| Problema | Solución |
|---|---|
| Modelos reasoning mostraban "Thinking. 1. Analyze..." al usuario | Filtro <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 restrictivo | Simplificado 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 destructivas | confirm() en QR scanner y transferencias |
| Problema | Solución |
|---|---|
| DragOverlay rotado (rotate-2) — desalineado visualmente | Eliminado rotate-2, ancho fijo 340px |
| Opacidad 0.4 dejaba ver tarjeta original debajo | Reducida a 0.3 + zIndex condicional |
| Reorder bug: mover al final producía posición incorrecta | where('id', '!=', item->id) en el incremento |
| Feature | Detalle |
|---|---|
| Providers CRUD | Agregar, editar, eliminar y toggle (enable/disable) proveedores desde Settings → API Keys |
| Tabla DB | Migración create_llm_providers_table (key, label, base_url, enabled, models) |
| Config + DB merge | 5 providers por defecto en config/llm_providers.php + personalizados vía DB |
| Select de modelos | Dropdown con TODOS los modelos del provider (no datalist filtrado). Ollama: dinámico desde API |
| Base URL auto-fill | Al seleccionar provider, la URL se llena automáticamente desde config |
| "EN USO" badge | Badge verde si la key coincide con llm_source activo del RAG |
| Activo exclusivo | Activar una key desactiva las demás del mismo tipo (single-active-per-type) |
| Toggle providers | Botón Power: deshabilita providers (desaparecen del selector). Usa updateOrInsert |
| Enmascarado | Keys visibles como sk-y••••Bu2f — cifradas en DB con cast encrypted |
| Endpoints | GET/POST /api/llm-providers + PUT/DELETE /api/llm-providers/{key} |
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ón | Contenido |
|---|---|
| Proveedor + Key activa | Selector de origen con todos los providers habilitados + indicador de qué key está en uso |
| Modelo | Dropdown agrupado: Ollama local / modelos del provider / keys activas. Botón Refrescar |
| Parámetros | Temperatura (0-2), Max Tokens, Contexto Lotes |
| Probar RAG | Input + botón que envía una pregunta real al endpoint /chat-rag y muestra la respuesta en vivo con el modelo usado |
Cada key ahora tiene un botón azul 🔄 que prueba la conexión contra el provider real.
| Feature | Detalle |
|---|---|
| Endpoint | POST /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 |
| Timeout | 10 segundos — no bloquea la UI |
| CSRF refresh | El botón "Nueva Key" ahora refresca el token CSRF antes de enviar y muestra errores si falla |
| Problema | Solució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 togglear | Merge config+DB: si DB tiene models vacío, usa los del config |
| Datalist solo mostraba 1 modelo | Cambiado a <select> con todos los modelos visibles |
Modelos descargados y verificados en Titan (ARM 4 OCPU, 24GB). Aparecen automáticamente en el selector RAG vía GET /api/llm-models.
| Modelo | Tamaño | Contexto | Velocidad | Uso |
|---|---|---|---|---|
gemma3:12b | 8.1 GB | 256K ⭐ | 3-5 tok/s | Sweet spot: contexto enorme + velocidad aceptable |
gemma3:27b | 17 GB | 128K | <1 tok/s | Máxima calidad, requiere pausar keepalive |
qwen3:30b-a3b | 18 GB (MoE q4) | 128K | <1 tok/s | MoE 3B activos; rozando el límite de RAM |
qwen3:14b | 9.3 GB | 128K | 2-4 tok/s | Razonamiento fuerte, programación |
gemma3:4b | 3.3 GB | 8K | 8-12 tok/s | Rápido, tareas simples |
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.| Herramienta | Versión | Detalle | Estado |
|---|---|---|---|
| Hermes Agent (Nous Research) | v0.16.0 | 192k ⭐ GitHub · 28 tools, 88 skills · 20 skills migrados de OpenClaw · Providers: OpenCode (7 modelos) + Ollama (6 modelos) · Instalado en WSL (~/.hermes/) | Activo |
| Nginx Proxy Manager | Docker | UI visual para gestionar proxy hosts, SSL (Let's Encrypt), redirecciones · npm.pymetory.com · Dashboard en reuniones/nginx/ | Activo |
Dashboard — KPIs + FEFO + Kardex
Kanban — @dnd-kit + 15 items sidebar + drag funcional
Kanban — tarjetas visibles con drag handle
Settings — API Keys + Users CRUD
| # | Bug | Detectado por | Impacto |
|---|---|---|---|
| 1 | Lote::is_critical excluía lotes vencidos (días < 0) | ExpiredLoteRegressionTest | Dashboard no mostraba vencidos reales |
| 2 | TransferController undefined array key "reason" | TransferTest | Error 500 en transferencias |
| 3 | InventoryController undefined array key "description" | InventoryStoreTest | Error 500 al crear material sin descripción |
| 4 | Kanban::reorder posiciones incorrectas mismo-columna | KanbanReorderTest | Tarjetas desordenadas al arrastrar |
| 5 | Dashboard lotesCriticos no contaba vencidos | DashboardCriticosConsistencyTest | Métrica "Críticos FEFO" incorrecta |
| 6 | Migración after('nombre') → columna es name | MigrationsSchemaSanityTest | MySQL-killer silencioso en SQLite |
| # | Principio | Antes | Después | Mejora |
|---|---|---|---|---|
| 1 | Visibilidad de estado | 8 | 8 | — |
| 2 | Sistema ↔ mundo real | 9 | 9 | — |
| 3 | Control del usuario | 4 | 6 | +2 (confirm() en mutaciones) |
| 4 | Consistencia | 8 | 9 | +1 (sidebar unificada 15 items) |
| 5 | Prevención de errores | 7 | 8 | +1 (validaciones client-side) |
| 6 | Reconocer vs recordar | 7 | 7 | — |
| 7 | Flexibilidad / eficiencia | 4 | 4 | — |
| 8 | Diseño minimalista | 8 | 9 | +1 (sin ghost code, sin duplicados) |
| 9 | Reconocer errores | 7 | 7 | — |
| 10 | Ayuda y documentación | 3 | 5 | +2 (tooltips + mini-glosario) |
Mejora de +0.7 puntos tras correcciones de UX
# 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)