- 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>
Con maxOutputTokens=512, los modelos con "thinking" (2.5/3.x) gastaban
parte del presupuesto en razonamiento interno y la respuesta JSON
llegaba incompleta (ej: {"banco": "Bancolombia", "valor": 9080 sin
cerrar), lo que Telegram/web reportaban como "no es JSON valido".
Subido a 2048 para dejar espacio de sobra.
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>
- GeminiVisionService: eliminar thinkingConfig (rompe modelos flash),
reducir maxOutputTokens 8192→512, timeout 20→40s, default gemini-2.0-flash
- PublicChat: detectar correctamente errores de Gemini (__error/__parse_error)
que antes pasaban el guard if(!$datosPago) por ser arrays truthy
- PagoValidadorService: tolerancia de hora 0→3 min, remitente >=2→>=1 palabra
- CorreoImapService: break→continue en correos fuera de ventana (IMAP no
garantiza orden cronológico), fetchSize *4→*2 para evitar timeout
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Con thinkingBudget=0 el modelo no consume tokens en razonamiento interno,
evitando que el JSON de respuesta quede truncado (bug: '{"banco":"Nu",...').
maxOutputTokens 2048→8192 en Vision, 1024→2048 en Intent.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Create pagos_confirmados table to track consumed email receipts
- PagoValidadorService now validates: valor + fecha + hora (±10min) + llave Bancolombia
- Each matched email is hashed (sha1 body+date) and marked as used; a second
match returns estado=ya_usado instead of confirmado
- Add partial remitente name matching against the registered user's name
- GeminiVisionService prompt now extracts llave (@xxx) from the receipt image
- TelegramBotService shows distinct message for ya_usado state
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
gemini-3.5-flash uses thinking tokens that share the same budget as output
tokens. With maxOutputTokens=200 (vision) or 100 (intent), the model would
exhaust the budget on thinking (~288-1043 tokens) and truncate the actual
response to 8-11 tokens, producing garbage output like "040825".
Fix: 2048 for vision (needs full JSON), 1024 for intent, 512 for connection test.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
gemini-3.x models return thinking blocks with thought:true flag in parts[].
Filter those out so only actual response text is used.
Also extract the first {...} JSON object from the text so stray content
before or after (like '040825') doesn't break parsing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rewrote extraerPago() to try v1 then v1beta for model compatibility
- Returns __error key with actual API message when both fail
- TelegramBotService shows that error in chat instead of generic message
- Also ensures model is read fresh from config (no hardcoded URL in constructor)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
gemini-2.5-flash and gemini-2.0-flash-lite are restricted/unavailable.
gemini-3.5-flash is confirmed working on this account.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The API returned: Unknown name "thinkingConfig": Cannot find field.
gemini-2.5-flash works with a plain generateContent request, no special config needed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- GeminiIntentService/VisionService: auto-select v1 for 2.5+ models, v1beta for older
- probarGemini: tries v1 then v1beta, shows actual API error message, filters model
list to only those supporting generateContent so the user sees usable options
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
thinkingConfig must be a top-level key in the request, NOT nested inside
generationConfig. This is required for gemini-2.5-flash and newer models.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
gemini-2.5-flash and newer require thinkingBudget:0 to disable thinking
mode, otherwise the API rejects the request or returns unexpected format.
Regex matches gemini-2.5-* and gemini-3.x-* automatically.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- gemini_model is now a saved config field (default: gemini-2.0-flash)
- Config panel shows a text input to set the model name
- probarGemini() reads the configured model and on error calls ListModels
to show exactly which model names are available for the account's API key
- GeminiIntentService and GeminiVisionService build the URL from config at runtime
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
gemini-1.5-flash was removed from the v1beta API. Updated all three
references (GeminiIntentService, GeminiVisionService, config test).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Migration + AiUsageLog model with tokens, duration, time, cost fields
- GeminiIntentService and GeminiVisionService log every API call
with input/output token counts from usageMetadata and response time
- TelegramBotService logs Whisper calls with audio duration (from Telegram)
and calculates cost at $1/second
- Whisper voice duration now passed from TelegramWebhookController
- Free text in Telegram now tries Gemini intent detection before showing menu
- /chat/ia-logs: Livewire component with summary cards + filterable table
- Whisper connection test button in config panel
- "Logs IA" link added to Chat/Bot nav section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Chat público en /chat con flujos de compra, recarga y credenciales
- Verificación de identidad por código OTP enviado al correo registrado
- Botones enriquecidos: cards, links, credenciales, validación de comprobante
- Gemini IA para detección de intención en texto libre
- Gemini Vision para extraer datos de foto de comprobante
- Validador de pagos cruzando IA con correos IMAP (Bancolombia)
- MercadoPago como pasarela de pago (reemplaza Wompi en el chat)
- Migraciones: payload en chat_messages, user_id en chat_contacts
- Config admin: API key Gemini, toggles IA y validación de foto
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>