Update: 2026-05-08 00:26:39
This commit is contained in:
67
app/modules_app/ai-usage/log.php
Normal file
67
app/modules_app/ai-usage/log.php
Normal file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
/**
|
||||
* AI Usage Log Endpoint
|
||||
* GET /api/v1/ai-usage/log
|
||||
*
|
||||
* Returns paginated log of all AI requests.
|
||||
*/
|
||||
|
||||
use App\Core\Database;
|
||||
use App\Middleware\RoleMiddleware;
|
||||
|
||||
$decoded = RoleMiddleware::require(['super_admin', 'admin']);
|
||||
|
||||
$db = Database::getInstance();
|
||||
|
||||
$page = max(1, (int) ($_GET['page'] ?? 1));
|
||||
$perPage = min(50, max(10, (int) ($_GET['per_page'] ?? 20)));
|
||||
$offset = ($page - 1) * $perPage;
|
||||
$tenantId = $decoded['tenant_id'];
|
||||
$isSuperAdmin = $decoded['role'] === 'super_admin';
|
||||
|
||||
$tenantCondition = $isSuperAdmin ? "" : "WHERE a.tenant_id = ?";
|
||||
$params = $isSuperAdmin ? [] : [$tenantId];
|
||||
|
||||
// Count
|
||||
$countSql = "SELECT COUNT(*) FROM ai_usage_log a $tenantCondition";
|
||||
$countStmt = $db->prepare($countSql);
|
||||
$countStmt->execute($params);
|
||||
$total = (int) $countStmt->fetchColumn();
|
||||
|
||||
// Fetch
|
||||
$sql = "SELECT
|
||||
a.id, a.action_type, a.model_name,
|
||||
a.prompt_tokens, a.completion_tokens, a.total_tokens,
|
||||
a.estimated_cost, a.created_at
|
||||
FROM ai_usage_log a
|
||||
$tenantCondition
|
||||
ORDER BY a.created_at DESC
|
||||
LIMIT $perPage OFFSET $offset";
|
||||
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$logs = $stmt->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
// Translate action types
|
||||
$actionLabels = [
|
||||
'invoice_extraction' => 'استخراج فاتورة',
|
||||
'voice_transcribe' => 'تحويل صوت لنص',
|
||||
'voice_intent' => 'تحليل أمر صوتي',
|
||||
'report_generation' => 'توليد تقرير',
|
||||
'chatbot' => 'محادثة ذكية',
|
||||
];
|
||||
|
||||
foreach ($logs as &$log) {
|
||||
$log['action_label'] = $actionLabels[$log['action_type']] ?? $log['action_type'];
|
||||
$log['estimated_cost'] = round((float) $log['estimated_cost'], 6);
|
||||
}
|
||||
|
||||
json_success([
|
||||
'logs' => $logs,
|
||||
'pagination' => [
|
||||
'page' => $page,
|
||||
'per_page' => $perPage,
|
||||
'total' => $total,
|
||||
'pages' => ceil($total / $perPage),
|
||||
],
|
||||
]);
|
||||
103
app/modules_app/ai-usage/stats.php
Normal file
103
app/modules_app/ai-usage/stats.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
/**
|
||||
* AI Usage Stats Endpoint
|
||||
* GET /api/v1/ai-usage/stats
|
||||
*
|
||||
* Returns AI token consumption stats for the current tenant.
|
||||
* Super admin sees system-wide; admin sees their tenant only.
|
||||
*/
|
||||
|
||||
use App\Core\Database;
|
||||
use App\Middleware\RoleMiddleware;
|
||||
|
||||
$decoded = RoleMiddleware::require(['super_admin', 'admin']);
|
||||
|
||||
$db = Database::getInstance();
|
||||
|
||||
$period = $_GET['period'] ?? 'month'; // day, week, month, all
|
||||
$tenantId = $decoded['tenant_id'];
|
||||
$isSuperAdmin = $decoded['role'] === 'super_admin';
|
||||
|
||||
// Date range
|
||||
$dateCondition = match ($period) {
|
||||
'day' => "AND a.created_at >= DATE_SUB(NOW(), INTERVAL 1 DAY)",
|
||||
'week' => "AND a.created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)",
|
||||
'month' => "AND a.created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)",
|
||||
default => "",
|
||||
};
|
||||
|
||||
$tenantCondition = $isSuperAdmin ? "" : "AND a.tenant_id = ?";
|
||||
$params = $isSuperAdmin ? [] : [$tenantId];
|
||||
|
||||
// Totals
|
||||
$sql = "SELECT
|
||||
COUNT(*) as total_requests,
|
||||
COALESCE(SUM(a.prompt_tokens), 0) as total_prompt_tokens,
|
||||
COALESCE(SUM(a.completion_tokens), 0) as total_completion_tokens,
|
||||
COALESCE(SUM(a.total_tokens), 0) as total_tokens,
|
||||
COALESCE(SUM(a.estimated_cost), 0) as total_cost
|
||||
FROM ai_usage_log a
|
||||
WHERE 1=1 $tenantCondition $dateCondition";
|
||||
|
||||
$stmt = $db->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$totals = $stmt->fetch(\PDO::FETCH_ASSOC);
|
||||
|
||||
// Breakdown by action type
|
||||
$sql2 = "SELECT
|
||||
a.action_type,
|
||||
COUNT(*) as requests,
|
||||
COALESCE(SUM(a.total_tokens), 0) as tokens,
|
||||
COALESCE(SUM(a.estimated_cost), 0) as cost
|
||||
FROM ai_usage_log a
|
||||
WHERE 1=1 $tenantCondition $dateCondition
|
||||
GROUP BY a.action_type
|
||||
ORDER BY tokens DESC";
|
||||
|
||||
$stmt2 = $db->prepare($sql2);
|
||||
$stmt2->execute($params);
|
||||
$breakdown = $stmt2->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
// Breakdown by model
|
||||
$sql3 = "SELECT
|
||||
a.model_name,
|
||||
COUNT(*) as requests,
|
||||
COALESCE(SUM(a.total_tokens), 0) as tokens,
|
||||
COALESCE(SUM(a.estimated_cost), 0) as cost
|
||||
FROM ai_usage_log a
|
||||
WHERE 1=1 $tenantCondition $dateCondition
|
||||
GROUP BY a.model_name
|
||||
ORDER BY tokens DESC";
|
||||
|
||||
$stmt3 = $db->prepare($sql3);
|
||||
$stmt3->execute($params);
|
||||
$modelBreakdown = $stmt3->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
// Daily trend (last 30 days)
|
||||
$sql4 = "SELECT
|
||||
DATE(a.created_at) as date,
|
||||
COALESCE(SUM(a.total_tokens), 0) as tokens,
|
||||
COALESCE(SUM(a.estimated_cost), 0) as cost,
|
||||
COUNT(*) as requests
|
||||
FROM ai_usage_log a
|
||||
WHERE 1=1 $tenantCondition AND a.created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
|
||||
GROUP BY DATE(a.created_at)
|
||||
ORDER BY date ASC";
|
||||
|
||||
$stmt4 = $db->prepare($sql4);
|
||||
$stmt4->execute($params);
|
||||
$dailyTrend = $stmt4->fetchAll(\PDO::FETCH_ASSOC);
|
||||
|
||||
json_success([
|
||||
'period' => $period,
|
||||
'totals' => [
|
||||
'requests' => (int) $totals['total_requests'],
|
||||
'prompt_tokens' => (int) $totals['total_prompt_tokens'],
|
||||
'completion_tokens' => (int) $totals['total_completion_tokens'],
|
||||
'total_tokens' => (int) $totals['total_tokens'],
|
||||
'estimated_cost_usd' => round((float) $totals['total_cost'], 4),
|
||||
],
|
||||
'by_action' => $breakdown,
|
||||
'by_model' => $modelBreakdown,
|
||||
'daily_trend' => $dailyTrend,
|
||||
]);
|
||||
Reference in New Issue
Block a user