Update Saqel Platform: 2026-09-08 23:29:32

This commit is contained in:
Hamza-Ayed
2026-09-08 23:29:32 +03:00
parent 590326baa0
commit 90487a42ad
3 changed files with 57 additions and 24 deletions
@@ -18,9 +18,36 @@ namespace App\Services;
use App\Core\Database;
use App\Core\Security;
use App\Core\RedisClient;
class AiVideoAnalyzerService
{
/**
* Select a Gemini key from the configured comma-separated pool. Redis
* provides a process-safe round robin counter across PHP-FPM workers.
* No key material is logged or returned to callers.
*/
private static function nextGeminiApiKey(): string
{
$raw = trim((string)(getenv('GEMINI_API_KEYS') ?: getenv('GEMINI_API_KEY') ?: ''));
$keys = array_values(array_filter(array_map('trim', explode(',', $raw))));
if (empty($keys)) {
return '';
}
if (count($keys) === 1) {
return $keys[0];
}
try {
$turn = (int)RedisClient::getInstance()->incr('saqel:gemini:round_robin');
return $keys[($turn - 1) % count($keys)];
} catch (\Throwable $e) {
// The analysis can still proceed during a Redis outage. The time
// bucket alternates keys without exposing either value.
return $keys[(int)(floor(time() / 60) % count($keys))];
}
}
/**
* Fail-closed admission gate for teacher uploads. The media stays in PHP's
* temporary upload area until Gemini and technical checks approve it.
@@ -50,7 +77,7 @@ class AiVideoAnalyzerService
if (!$hasVideo || !$hasAudio || $duration <= 0 || $duration > 1500) {
return ['decision' => 'rejected', 'reason' => 'الفيديو يجب أن يحتوي صورة وصوتاً وأن لا يتجاوز 25 دقيقة', 'duration_seconds' => $duration];
}
$key = trim((string)getenv('GEMINI_API_KEY'));
$key = self::nextGeminiApiKey();
if ($key === '') return ['decision' => 'needs_manual_review', 'reason' => 'Gemini غير مهيأ؛ لم يتم رفع الفيديو', 'duration_seconds' => $duration];
// Extract a representative frame; Gemini receives real media evidence,
@@ -200,7 +227,7 @@ class AiVideoAnalyzerService
public static function evaluatePedagogicalQuality(int $lessonId, string $lessonTitle, int $duration, array $curriculum): array
{
try {
$geminiKey = getenv('GEMINI_API_KEY');
$geminiKey = self::nextGeminiApiKey();
$alignmentScore = 0.0;
$clarityScore = 0.0;
$outcomes = [
@@ -312,7 +339,7 @@ class AiVideoAnalyzerService
*/
private static function generateAnalysis(string $lessonTitle, int $duration, array $curriculum): array
{
$geminiKey = getenv('GEMINI_API_KEY');
$geminiKey = self::nextGeminiApiKey();
if (!empty($geminiKey)) {
try {