feat: costo OCR en dashboard, logs correctos y fix de thinking tokens

- 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>
This commit is contained in:
Lizandro
2026-08-11 01:52:59 +00:00
co-authored by Claude Sonnet 5
parent 7b21cd9773
commit 5e0c0367d9
6 changed files with 73 additions and 19 deletions
@@ -9,9 +9,10 @@
@php
$costoGemini = $resumen->costo_gemini ?? 0;
$costoWhisper = $resumen->costo_whisper ?? 0;
$costoTotal = $costoGemini + $costoWhisper;
$costoOcr = $resumen->costo_ocr ?? 0;
$costoTotal = $costoGemini + $costoWhisper + $costoOcr;
@endphp
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-5 gap-4">
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-6 gap-4">
<div class="bg-white rounded-2xl border border-gray-100 shadow p-4">
<p class="text-xs text-gray-400 mb-1">Total llamadas</p>
<p class="text-2xl font-bold text-gray-800">{{ number_format($resumen->total_llamadas ?? 0) }}</p>
@@ -38,6 +39,11 @@
<p class="text-2xl font-bold text-blue-700">${{ number_format($costoGemini, 0, ',', '.') }}</p>
<p class="text-xs text-gray-400 mt-1">$20.000 / millón tokens</p>
</div>
<div class="bg-white rounded-2xl border border-gray-100 shadow p-4">
<p class="text-xs text-gray-400 mb-1">Costo OCR</p>
<p class="text-2xl font-bold text-orange-600">${{ number_format($costoOcr, 0, ',', '.') }}</p>
<p class="text-xs text-gray-400 mt-1">{{ number_format($resumen->total_fotos_ocr ?? 0) }} fotos · $5/foto</p>
</div>
<div class="bg-white rounded-2xl border border-gray-100 shadow p-4">
<p class="text-xs text-gray-400 mb-1">Costo Total</p>
<p class="text-2xl font-bold text-emerald-700">${{ number_format($costoTotal, 0, ',', '.') }}</p>
@@ -75,6 +81,8 @@
<option value="">Todos</option>
<option value="gemini_intent">Gemini Intent</option>
<option value="gemini_vision">Gemini Vision</option>
<option value="gemini_text">Gemini Texto (post-OCR)</option>
<option value="ocr">OCR</option>
<option value="whisper">Whisper</option>
</select>
</div>
@@ -118,16 +126,20 @@
$badgeServicio = match($log->servicio) {
'gemini_intent' => 'bg-blue-100 text-blue-700',
'gemini_vision' => 'bg-indigo-100 text-indigo-700',
'gemini_text' => 'bg-sky-100 text-sky-700',
'ocr' => 'bg-orange-100 text-orange-700',
'whisper' => 'bg-purple-100 text-purple-700',
default => 'bg-gray-100 text-gray-600',
};
$labelServicio = match($log->servicio) {
'gemini_intent' => 'Gemini Intent',
'gemini_vision' => 'Gemini Vision',
'gemini_text' => 'Gemini Texto',
'ocr' => 'OCR',
'whisper' => 'Whisper',
default => $log->servicio,
};
$costoFila = in_array($log->servicio, ['gemini_intent', 'gemini_vision'])
$costoFila = in_array($log->servicio, ['gemini_intent', 'gemini_vision', 'gemini_text'])
? (($log->input_tokens ?? 0) + ($log->output_tokens ?? 0)) / 1000000 * 20000
: ($log->costo ?? 0);
@endphp