Update: 2026-05-08 02:11:29
This commit is contained in:
@@ -103,6 +103,71 @@ class AI
|
||||
return null;
|
||||
}
|
||||
|
||||
// Track token usage from Gemini response
|
||||
$usage = $result['usageMetadata'] ?? [];
|
||||
if (!empty($usage)) {
|
||||
self::logTokenUsage($usage);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Log AI token usage to the database for cost tracking
|
||||
*/
|
||||
private static function logTokenUsage(array $usage): void
|
||||
{
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
|
||||
$inputTokens = (int)($usage['promptTokenCount'] ?? 0);
|
||||
$outputTokens = (int)($usage['candidatesTokenCount'] ?? 0);
|
||||
$totalTokens = (int)($usage['totalTokenCount'] ?? ($inputTokens + $outputTokens));
|
||||
|
||||
// Gemini Flash Lite pricing: $0.075/1M input, $0.30/1M output
|
||||
$inputCost = ($inputTokens / 1000000) * 0.075;
|
||||
$outputCost = ($outputTokens / 1000000) * 0.30;
|
||||
$totalCostUsd = $inputCost + $outputCost;
|
||||
$totalCostJod = $totalCostUsd * 0.709; // 1 USD ≈ 0.709 JOD
|
||||
|
||||
$db->prepare("
|
||||
INSERT INTO ai_usage_log (id, input_tokens, output_tokens, total_tokens, cost_usd, cost_jod, model, created_at)
|
||||
VALUES (UUID(), ?, ?, ?, ?, ?, 'gemini-flash-lite', NOW())
|
||||
")->execute([
|
||||
$inputTokens,
|
||||
$outputTokens,
|
||||
$totalTokens,
|
||||
round($totalCostUsd, 8),
|
||||
round($totalCostJod, 8),
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
// Never crash the main flow for logging
|
||||
error_log("[AI] Token usage log failed: " . $e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get aggregated AI usage stats
|
||||
*/
|
||||
public static function getUsageStats(?string $tenantId = null): array
|
||||
{
|
||||
try {
|
||||
$db = Database::getInstance();
|
||||
$stmt = $db->query("
|
||||
SELECT
|
||||
COUNT(*) as total_requests,
|
||||
COALESCE(SUM(input_tokens), 0) as total_input_tokens,
|
||||
COALESCE(SUM(output_tokens), 0) as total_output_tokens,
|
||||
COALESCE(SUM(total_tokens), 0) as total_tokens,
|
||||
COALESCE(SUM(cost_usd), 0) as total_cost_usd,
|
||||
COALESCE(SUM(cost_jod), 0) as total_cost_jod,
|
||||
COALESCE(AVG(total_tokens), 0) as avg_tokens_per_request,
|
||||
COALESCE(AVG(cost_jod), 0) as avg_cost_jod_per_request
|
||||
FROM ai_usage_log
|
||||
");
|
||||
return $stmt->fetch() ?: [];
|
||||
} catch (\Exception $e) {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
80
app/modules_app/dashboard/ai_usage.php
Normal file
80
app/modules_app/dashboard/ai_usage.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/**
|
||||
* AI Usage Statistics
|
||||
* GET /v1/dashboard/ai-usage
|
||||
* Returns token consumption and cost breakdown
|
||||
*/
|
||||
|
||||
use App\Core\AI;
|
||||
use App\Core\Database;
|
||||
use App\Middleware\RoleMiddleware;
|
||||
|
||||
$decoded = RoleMiddleware::require(['super_admin', 'admin']);
|
||||
$db = Database::getInstance();
|
||||
|
||||
try {
|
||||
// Overall stats
|
||||
$overall = AI::getUsageStats();
|
||||
|
||||
// Today's usage
|
||||
$todayStmt = $db->query("
|
||||
SELECT
|
||||
COUNT(*) as requests,
|
||||
COALESCE(SUM(total_tokens), 0) as tokens,
|
||||
COALESCE(SUM(cost_jod), 0) as cost_jod
|
||||
FROM ai_usage_log
|
||||
WHERE DATE(created_at) = CURDATE()
|
||||
");
|
||||
$today = $todayStmt->fetch();
|
||||
|
||||
// This month
|
||||
$monthStmt = $db->query("
|
||||
SELECT
|
||||
COUNT(*) as requests,
|
||||
COALESCE(SUM(total_tokens), 0) as tokens,
|
||||
COALESCE(SUM(cost_jod), 0) as cost_jod
|
||||
FROM ai_usage_log
|
||||
WHERE MONTH(created_at) = MONTH(NOW()) AND YEAR(created_at) = YEAR(NOW())
|
||||
");
|
||||
$month = $monthStmt->fetch();
|
||||
|
||||
// Daily breakdown (last 30 days)
|
||||
$dailyStmt = $db->query("
|
||||
SELECT
|
||||
DATE(created_at) as date,
|
||||
COUNT(*) as requests,
|
||||
SUM(total_tokens) as tokens,
|
||||
SUM(cost_jod) as cost_jod
|
||||
FROM ai_usage_log
|
||||
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
|
||||
GROUP BY DATE(created_at)
|
||||
ORDER BY date DESC
|
||||
");
|
||||
$daily = $dailyStmt->fetchAll();
|
||||
|
||||
json_success([
|
||||
'overall' => [
|
||||
'total_requests' => (int)($overall['total_requests'] ?? 0),
|
||||
'total_tokens' => (int)($overall['total_tokens'] ?? 0),
|
||||
'total_cost_usd' => round((float)($overall['total_cost_usd'] ?? 0), 4),
|
||||
'total_cost_jod' => round((float)($overall['total_cost_jod'] ?? 0), 4),
|
||||
'avg_tokens_per_invoice' => round((float)($overall['avg_tokens_per_request'] ?? 0)),
|
||||
'avg_cost_per_invoice_jod' => round((float)($overall['avg_cost_jod_per_request'] ?? 0), 6),
|
||||
],
|
||||
'today' => [
|
||||
'requests' => (int)($today['requests'] ?? 0),
|
||||
'tokens' => (int)($today['tokens'] ?? 0),
|
||||
'cost_jod' => round((float)($today['cost_jod'] ?? 0), 4),
|
||||
],
|
||||
'this_month' => [
|
||||
'requests' => (int)($month['requests'] ?? 0),
|
||||
'tokens' => (int)($month['tokens'] ?? 0),
|
||||
'cost_jod' => round((float)($month['cost_jod'] ?? 0), 4),
|
||||
],
|
||||
'daily_breakdown' => $daily,
|
||||
], 'إحصائيات استخدام الذكاء الاصطناعي');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
error_log("AI Usage Stats Error: " . $e->getMessage());
|
||||
json_error('خطأ في جلب إحصائيات AI', 500);
|
||||
}
|
||||
Reference in New Issue
Block a user