diff --git a/admin.php b/admin.php
index 37c1cbc..0f07c06 100644
--- a/admin.php
+++ b/admin.php
@@ -438,6 +438,7 @@ $appConfig = microsoft_get_app_config();
$companyJiraDomain = jira_get_company_domain();
$workdaySettings = get_workday_settings();
$aiSettings = get_ai_settings();
+$aiUsageStats = get_ai_usage_stats();
$sessionIdleTimeout = get_session_idle_timeout();
$calendarBlockReasons = get_calendar_block_reasons();
$brainstormCategories = get_brainstorm_categories();
@@ -1539,8 +1540,7 @@ require_once __DIR__ . '/includes/header.php';
@@ -1555,6 +1555,54 @@ require_once __DIR__ . '/includes/header.php';
+
+
+
+
+
+ AI Verbruik
+
+
+
Laatst gebruikt: = htmlspecialchars(date('d-m-Y H:i', strtotime($aiUsageStats['last_used_at']))) ?>
+
+
Nog geen verbruik geregistreerd
+
+
+
+
+
+ Totaal Aanroepen
+ = number_format((int)($aiUsageStats['total_requests'] ?? 0), 0, ',', '.') ?>
+
+
+
+ Totaal Tokens
+ = number_format((int)($aiUsageStats['total_tokens'] ?? 0), 0, ',', '.') ?>
+
+
+
+ Prompt / Invoer
+ = number_format((int)($aiUsageStats['total_prompt_tokens'] ?? 0), 0, ',', '.') ?>
+
+
+
+ Output / Gegenereerd
+ = number_format((int)($aiUsageStats['total_completion_tokens'] ?? 0), 0, ',', '.') ?>
+
+
+
+
+
+ Per model:
+ $mCount): ?>
+
+ = htmlspecialchars((string)$mName) ?>: = (int)$mCount ?>x
+
+
+
+
+
+
diff --git a/auth/login.php b/auth/login.php
index bad2905..078d228 100644
--- a/auth/login.php
+++ b/auth/login.php
@@ -34,7 +34,7 @@ if ($currentUser) {
$error = null;
$infoMessage = null;
if (isset($_GET['expired'])) {
- $infoMessage = "Uw sessie is beeïndigd. Log opnieuw in om verder te gaan.";
+ $infoMessage = "Uw sessie is beeïndigd";
}
if (isset($_GET['revoked'])) {
$infoMessage = "Uw sessie is op afstand beëindigd of ingetrokken. Log opnieuw in om verder te gaan.";
diff --git a/includes/ai.php b/includes/ai.php
index 240812a..0e0a810 100644
--- a/includes/ai.php
+++ b/includes/ai.php
@@ -337,12 +337,23 @@ function fociz_ai_chat_gemini(array $messages, array $options, array $aiSettings
}
}
+ $promptTokens = (int)($response['json']['usageMetadata']['promptTokenCount'] ?? 0);
+ $completionTokens = (int)($response['json']['usageMetadata']['candidatesTokenCount'] ?? 0);
+ if ($promptTokens === 0 && $completionTokens === 0) {
+ // Schatting obv lengte indien niet geretourneerd
+ $promptTokens = max(1, (int)(strlen(json_encode($payload)) / 4));
+ $completionTokens = max(1, (int)(strlen($cleanContent) / 4));
+ }
+
return [
- 'success' => true,
- 'content' => $cleanContent,
- 'json' => $parsedJson,
- 'model' => $successfulModel,
- 'provider' => 'gemini',
+ 'success' => true,
+ 'content' => $cleanContent,
+ 'json' => $parsedJson,
+ 'model' => $successfulModel,
+ 'provider' => 'gemini',
+ 'prompt_tokens' => $promptTokens,
+ 'completion_tokens' => $completionTokens,
+ 'total_tokens' => ($promptTokens + $completionTokens),
];
}
@@ -414,12 +425,22 @@ function fociz_ai_chat_openai(array $messages, array $options, array $aiSettings
$parsedJson = json_decode($rawContent, true);
}
+ $promptTokens = (int)($response['json']['usage']['prompt_tokens'] ?? 0);
+ $completionTokens = (int)($response['json']['usage']['completion_tokens'] ?? 0);
+ if ($promptTokens === 0 && $completionTokens === 0) {
+ $promptTokens = max(1, (int)(strlen(json_encode($payload)) / 4));
+ $completionTokens = max(1, (int)(strlen($rawContent) / 4));
+ }
+
return [
- 'success' => true,
- 'content' => $rawContent,
- 'json' => $parsedJson,
- 'model' => $response['json']['model'] ?? $model,
- 'provider' => 'openai',
+ 'success' => true,
+ 'content' => $rawContent,
+ 'json' => $parsedJson,
+ 'model' => $response['json']['model'] ?? $model,
+ 'provider' => 'openai',
+ 'prompt_tokens' => $promptTokens,
+ 'completion_tokens' => $completionTokens,
+ 'total_tokens' => ($promptTokens + $completionTokens),
];
}
@@ -452,10 +473,21 @@ function fociz_ai_chat(array $messages, array $options = []): array {
// Routering: als provider gemini is of model gemini bevat, stuur naar Google Gemini
if ($provider === 'gemini' || strpos($model, 'gemini') !== false) {
- return fociz_ai_chat_gemini($messages, $options, $aiSettings);
+ $res = fociz_ai_chat_gemini($messages, $options, $aiSettings);
+ } else {
+ $res = fociz_ai_chat_openai($messages, $options, $aiSettings);
}
- return fociz_ai_chat_openai($messages, $options, $aiSettings);
+ // Verbruiksstatistieken registreren bij geslaagde aanroep
+ if (!empty($res['success'])) {
+ $usedProvider = $res['provider'] ?? $provider;
+ $usedModel = $res['model'] ?? $model;
+ $pTokens = (int)($res['prompt_tokens'] ?? 0);
+ $cTokens = (int)($res['completion_tokens'] ?? 0);
+ record_ai_usage($usedProvider, $usedModel, $pTokens, $cTokens, $userId);
+ }
+
+ return $res;
}
/**
diff --git a/includes/settings.php b/includes/settings.php
index 2b325b0..8179abc 100644
--- a/includes/settings.php
+++ b/includes/settings.php
@@ -259,4 +259,79 @@ function get_brainstorm_categories(): array {
return $defaultCategories;
}
+/**
+ * Registreert AI verbruiksstatistieken (aanroepen, tokens, provider/model)
+ */
+function record_ai_usage(string $provider, string $model, int $promptTokens = 0, int $completionTokens = 0, ?int $userId = null): void {
+ try {
+ $stats = get_system_setting('ai_usage_stats', [
+ 'total_requests' => 0,
+ 'total_prompt_tokens' => 0,
+ 'total_completion_tokens' => 0,
+ 'total_tokens' => 0,
+ 'last_used_at' => null,
+ 'by_provider' => [],
+ 'by_model' => [],
+ 'by_date' => [],
+ ]);
+
+ $stats['total_requests'] = (int)($stats['total_requests'] ?? 0) + 1;
+ $stats['total_prompt_tokens'] = (int)($stats['total_prompt_tokens'] ?? 0) + $promptTokens;
+ $stats['total_completion_tokens'] = (int)($stats['total_completion_tokens'] ?? 0) + $completionTokens;
+ $stats['total_tokens'] = (int)($stats['total_tokens'] ?? 0) + ($promptTokens + $completionTokens);
+ $stats['last_used_at'] = date('Y-m-d H:i:s');
+
+ // Per provider
+ if (!isset($stats['by_provider'][$provider])) {
+ $stats['by_provider'][$provider] = 0;
+ }
+ $stats['by_provider'][$provider]++;
+
+ // Per model
+ if (!isset($stats['by_model'][$model])) {
+ $stats['by_model'][$model] = 0;
+ }
+ $stats['by_model'][$model]++;
+
+ // Per datum
+ $today = date('Y-m-d');
+ if (!isset($stats['by_date'][$today])) {
+ $stats['by_date'][$today] = ['requests' => 0, 'tokens' => 0];
+ }
+ $stats['by_date'][$today]['requests']++;
+ $stats['by_date'][$today]['tokens'] += ($promptTokens + $completionTokens);
+
+ // Maximaal 30 dagen historiek
+ if (count($stats['by_date']) > 30) {
+ ksort($stats['by_date']);
+ $stats['by_date'] = array_slice($stats['by_date'], -30, 30, true);
+ }
+
+ set_system_setting('ai_usage_stats', $stats);
+ } catch (Throwable $e) {
+ error_log("record_ai_usage fout: " . $e->getMessage());
+ }
+}
+
+/**
+ * Haalt de AI verbruiksstatistieken op
+ */
+function get_ai_usage_stats(): array {
+ $stats = get_system_setting('ai_usage_stats', null);
+ if (!is_array($stats)) {
+ return [
+ 'total_requests' => 0,
+ 'total_prompt_tokens' => 0,
+ 'total_completion_tokens' => 0,
+ 'total_tokens' => 0,
+ 'last_used_at' => null,
+ 'by_provider' => [],
+ 'by_model' => [],
+ 'by_date' => [],
+ ];
+ }
+ return $stats;
+}
+
+