Add AI usage logs for Gemini and Whisper
- 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>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
ff109a5608
commit
44297aa0bb
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use App\Models\AiUsageLog;
|
||||
use App\Models\ChatConfig;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
@@ -20,13 +21,14 @@ class GeminiIntentService
|
||||
* Detecta la intención del texto del usuario y la mapea a una acción del sistema.
|
||||
* Retorna ['action' => string, 'data' => array] o null si no se puede detectar.
|
||||
*/
|
||||
public function detectar(string $texto): ?array
|
||||
public function detectar(string $texto, ?int $usuarioId = null, string $canal = 'web'): ?array
|
||||
{
|
||||
if (! $this->apiKey || ChatConfig::get('gemini_habilitado', '0') !== '1') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$prompt = $this->buildPrompt($texto);
|
||||
$inicio = microtime(true);
|
||||
|
||||
try {
|
||||
$response = Http::withHeaders(['Content-Type' => 'application/json'])
|
||||
@@ -39,16 +41,50 @@ class GeminiIntentService
|
||||
],
|
||||
]);
|
||||
|
||||
$tiempoMs = (int) ((microtime(true) - $inicio) * 1000);
|
||||
|
||||
if (! $response->successful()) {
|
||||
Log::warning('[Gemini Intent] API error: ' . $response->body());
|
||||
AiUsageLog::registrar([
|
||||
'servicio' => 'gemini_intent',
|
||||
'canal' => $canal,
|
||||
'usuario_id'=> $usuarioId,
|
||||
'tiempo_ms' => $tiempoMs,
|
||||
'resultado' => 'error',
|
||||
'detalle' => ['error' => substr($response->body(), 0, 300), 'texto' => $texto],
|
||||
]);
|
||||
return null;
|
||||
}
|
||||
|
||||
$text = $response->json('candidates.0.content.parts.0.text', '');
|
||||
return $this->parseRespuesta($text);
|
||||
$inputTokens = $response->json('usageMetadata.promptTokenCount', 0);
|
||||
$outputTokens = $response->json('usageMetadata.candidatesTokenCount', 0);
|
||||
$text = $response->json('candidates.0.content.parts.0.text', '');
|
||||
$resultado = $this->parseRespuesta($text);
|
||||
|
||||
AiUsageLog::registrar([
|
||||
'servicio' => 'gemini_intent',
|
||||
'canal' => $canal,
|
||||
'usuario_id' => $usuarioId,
|
||||
'input_tokens' => $inputTokens,
|
||||
'output_tokens'=> $outputTokens,
|
||||
'tiempo_ms' => $tiempoMs,
|
||||
'resultado' => 'ok',
|
||||
'detalle' => ['texto' => $texto, 'accion' => $resultado['action'] ?? null],
|
||||
]);
|
||||
|
||||
return $resultado;
|
||||
|
||||
} catch (\Throwable $e) {
|
||||
$tiempoMs = (int) ((microtime(true) - $inicio) * 1000);
|
||||
Log::warning('[Gemini Intent] Excepcion: ' . $e->getMessage());
|
||||
AiUsageLog::registrar([
|
||||
'servicio' => 'gemini_intent',
|
||||
'canal' => $canal,
|
||||
'usuario_id'=> $usuarioId,
|
||||
'tiempo_ms' => $tiempoMs,
|
||||
'resultado' => 'error',
|
||||
'detalle' => ['error' => $e->getMessage(), 'texto' => $texto],
|
||||
]);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user