103 lines
3.3 KiB
PHP
103 lines
3.3 KiB
PHP
<?php
|
|
/**
|
|
* AI Usage Logger Service
|
|
* Records every AI API call with token counts and estimated cost.
|
|
*/
|
|
|
|
namespace App\Core;
|
|
|
|
class AiUsageLogger
|
|
{
|
|
/**
|
|
* Cost per 1M tokens (input/output) for each model.
|
|
* Update these when pricing changes.
|
|
*/
|
|
private const MODEL_PRICING = [
|
|
'gemini-1.5-flash' => [
|
|
'input' => 0.075, // $0.075 per 1M input tokens
|
|
'output' => 0.30, // $0.30 per 1M output tokens
|
|
],
|
|
'gemini-2.0-flash' => [
|
|
'input' => 0.10,
|
|
'output' => 0.40,
|
|
],
|
|
'gemini-1.5-pro' => [
|
|
'input' => 1.25,
|
|
'output' => 5.00,
|
|
],
|
|
'grok-2' => [
|
|
'input' => 2.00,
|
|
'output' => 10.00,
|
|
],
|
|
'whisper-large-v3' => [
|
|
'input' => 0.111, // $0.111 per 1M input tokens (Groq)
|
|
'output' => 0.0,
|
|
],
|
|
];
|
|
|
|
/**
|
|
* Log an AI usage event.
|
|
*
|
|
* @param string $tenantId
|
|
* @param string $actionType One of: invoice_extraction, voice_transcribe, voice_intent, report_generation, chatbot
|
|
* @param string $modelName e.g. gemini-1.5-flash
|
|
* @param int $promptTokens
|
|
* @param int $completionTokens
|
|
* @param string|null $userId
|
|
* @param string|null $companyId
|
|
* @param array|null $metadata Any extra info (invoice_id, etc.)
|
|
*/
|
|
public static function log(
|
|
string $tenantId,
|
|
string $actionType,
|
|
string $modelName,
|
|
int $promptTokens,
|
|
int $completionTokens,
|
|
?string $userId = null,
|
|
?string $companyId = null,
|
|
?array $metadata = null,
|
|
): void {
|
|
$totalTokens = $promptTokens + $completionTokens;
|
|
$estimatedCost = self::estimateCost($modelName, $promptTokens, $completionTokens);
|
|
|
|
try {
|
|
$db = Database::getInstance();
|
|
$stmt = $db->prepare(
|
|
"INSERT INTO ai_usage_log
|
|
(tenant_id, user_id, company_id, action_type, model_name,
|
|
prompt_tokens, completion_tokens, total_tokens, estimated_cost,
|
|
request_metadata, created_at)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, NOW())"
|
|
);
|
|
$stmt->execute([
|
|
$tenantId,
|
|
$userId,
|
|
$companyId,
|
|
$actionType,
|
|
$modelName,
|
|
$promptTokens,
|
|
$completionTokens,
|
|
$totalTokens,
|
|
$estimatedCost,
|
|
$metadata ? json_encode($metadata, JSON_UNESCAPED_UNICODE) : null,
|
|
]);
|
|
} catch (\Exception $e) {
|
|
// Logging should never break the main flow
|
|
error_log('[AiUsageLogger] Failed to log: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Estimate cost in USD based on model pricing.
|
|
*/
|
|
private static function estimateCost(string $model, int $inputTokens, int $outputTokens): float
|
|
{
|
|
$pricing = self::MODEL_PRICING[$model] ?? ['input' => 0.10, 'output' => 0.40];
|
|
|
|
$inputCost = ($inputTokens / 1_000_000) * $pricing['input'];
|
|
$outputCost = ($outputTokens / 1_000_000) * $pricing['output'];
|
|
|
|
return round($inputCost + $outputCost, 6);
|
|
}
|
|
}
|