- OcrExtractionService: registra cada llamada en ai_usage_logs (servicio=ocr,
costo=5 COP/foto) y ahora tambien loggea cuando SI funciona, no solo
cuando falla (antes era imposible confirmar por log que el OCR corrio).
- GeminiVisionService: el log decia siempre "[Gemini Vision]" sin importar
si la llamada fue de imagen o de texto post-OCR, haciendo imposible
distinguir cual camino se uso realmente. Ahora usa el servicio real.
- GeminiVisionService: agrega thinkingConfig.thinkingBudget=0 (igual que
GeminiIntentService) para que el modelo no gaste tokens narrando su
razonamiento en texto plano antes del JSON final -- se estaba colando
ese razonamiento en la respuesta y comiendose el presupuesto de tokens.
- GeminiIntentService: resultado ya no queda forzado a 'ok'; ahora filtra
bloques 'thought' igual que vision, y guarda tokens/raw en el detalle
para poder diagnosticar sin acceso al servidor.
- ShowAiLogs: card y filtro de costo OCR en el dashboard de consumo.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- ValidarPagoWebJob: avisa al usuario si la solicitud ya no esta pendiente
en vez de saltar en silencio a "Menu principal".
- PublicChat: guard para no despachar dos jobs de validacion del mismo
comprobante si Livewire dispara updatedFotoComprobante() dos veces
(causaba que apareciera "Menu principal" y luego se corrigiera solo).
- PublicChat: "comprar hbo" caia en historial.ver porque compra(s)? sin \b
matcheaba dentro de "comprar". Agregado limite de palabra.
- PublicChat: "comprar plan de netflix" caia en el listado generico de
servicios por la palabra "plan". Ahora, si el mensaje nombra un servicio
concreto, se abre ese servicio directo en vez del listado.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Antes, si Gemini Vision fallaba (cuota/API), el 100% de los comprobantes
del chat web quedaban sin poder leerse. Ahora el job intenta primero un
servicio OCR externo configurable (imagen -> texto) y le pasa el texto
a Gemini para que solo lo estructure en JSON, más barato y sin la cuota
de visión. Si el OCR no está habilitado o falla, cae al método anterior
(Gemini leyendo la imagen directamente) sin romper nada.
Agrega configuración en el panel (URL/token/habilitado + probar conexión)
y la spec del servicio OCR a desplegar en docs/ocr-service-spec.md.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
El upload del comprobante hacía todo dentro de un solo request de Livewire:
Gemini (hasta 80s) más la lectura IMAP (20-60s) superaban el límite del
gateway y el usuario recibía un 504. Además, con SESSION_DRIVER=file ese
request mantenía bloqueado el archivo de sesión, así que el wire:poll del
chat no podía avanzar: la conversación entera se congelaba y el mensaje
"Analizando tu comprobante" se quedaba pegado para siempre.
Ahora updatedFotoComprobante() solo guarda la imagen, escribe el primer
mensaje y despacha ValidarPagoWebJob. Termina en ~1s.
El job hace el trabajo pesado fuera del request y va escribiendo su avance
como mensajes normales del chat, así el usuario ve los pasos:
"Recibido, leyendo los datos..." -> "Datos leídos, buscando en el banco..."
-> resultado
Reintenta cada 20s hasta 9 veces, igual que el flujo de Telegram.
El saldo se acredita dentro del job, no en el componente: si el usuario
cerraba o recargaba la pestaña durante la validación, el pago se confirmaba
pero el dinero nunca se abonaba. El componente ahora solo refresca el saldo
en pantalla y remata la compra que hubiera quedado pendiente.
Otros cierres del flujo que faltaban:
- failed() escribe un mensaje si el job muere, para no dejar el chat colgado.
- revisarPago() se rinde a los 6 min si el worker está caído.
- Los estados terminales dejan siempre un botón de menú.
- El botón Reintentar reencola en vez de validar dentro del request.
No se toca Telegram ni ningún servicio compartido (GeminiVisionService,
PagoValidadorService, CorreoImapService, ValidarPagoTelegramJob).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>