From 51a3e92980b67b4698a8bc594c3cc8447c44bafe Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Fri, 22 May 2026 15:42:30 +0300 Subject: [PATCH] Add Gemini audio model failover logic to handle HTTP 429 rate limits --- backend/app/Services/GeminiService.php | 93 ++++++++++++++------------ 1 file changed, 50 insertions(+), 43 deletions(-) diff --git a/backend/app/Services/GeminiService.php b/backend/app/Services/GeminiService.php index f79df78..9fb6f8b 100644 --- a/backend/app/Services/GeminiService.php +++ b/backend/app/Services/GeminiService.php @@ -218,59 +218,66 @@ class GeminiService */ public static function generateAudioResponse(string $apiKey, string $systemPrompt, string $userMessage, string $voiceName = 'Puck'): ?array { - $url = 'https://generativelanguage.googleapis.com/v1beta/models/gemini-3.1-flash-tts-preview:generateContent?key=' . $apiKey; + $models = [ + 'gemini-3.1-flash-tts-preview', + 'gemini-2.5-flash-preview-tts' + ]; - $parts = []; - if (!empty($systemPrompt)) { - $parts[] = ['text' => "System instruction: " . $systemPrompt]; - } - $parts[] = ['text' => $userMessage]; + foreach ($models as $model) { + $url = 'https://generativelanguage.googleapis.com/v1beta/models/' . $model . ':generateContent?key=' . $apiKey; - $payload = json_encode([ - 'contents' => [ - [ - 'role' => 'user', - 'parts' => $parts - ] - ], - 'generationConfig' => [ - 'responseModalities' => ['AUDIO'], - 'speechConfig' => [ - 'voiceConfig' => [ - 'prebuiltVoiceConfig' => [ - 'voiceName' => $voiceName + $parts = []; + if (!empty($systemPrompt)) { + $parts[] = ['text' => "System instruction: " . $systemPrompt]; + } + $parts[] = ['text' => $userMessage]; + + $payload = json_encode([ + 'contents' => [ + [ + 'role' => 'user', + 'parts' => $parts + ] + ], + 'generationConfig' => [ + 'responseModalities' => ['AUDIO'], + 'speechConfig' => [ + 'voiceConfig' => [ + 'prebuiltVoiceConfig' => [ + 'voiceName' => $voiceName + ] ] ] ] - ] - ]); + ]); - $ch = curl_init($url); - curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); - curl_setopt($ch, CURLOPT_POST, true); - curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); - curl_setopt($ch, CURLOPT_HTTPHEADER, [ - 'Content-Type: application/json' - ]); - curl_setopt($ch, CURLOPT_TIMEOUT, 30); + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $payload); + curl_setopt($ch, CURLOPT_HTTPHEADER, [ + 'Content-Type: application/json' + ]); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); - $response = curl_exec($ch); - $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); - curl_close($ch); + $response = curl_exec($ch); + $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE); + curl_close($ch); - if ($httpCode !== 200) { - error_log("[Gemini Audio API Error] HTTP " . $httpCode . " | Response: " . $response); - return null; + if ($httpCode === 200) { + $data = json_decode($response, true); + $part = $data['candidates'][0]['content']['parts'][0] ?? null; + if ($part && isset($part['inlineData'])) { + return [ + 'audio' => $part['inlineData']['data'], + 'mimeType' => $part['inlineData']['mimeType'] ?? 'audio/mp4' + ]; + } + } else { + error_log("[Gemini Audio API Error] Model " . $model . " failed with HTTP " . $httpCode . " | Response: " . $response); + } } - $data = json_decode($response, true); - $part = $data['candidates'][0]['content']['parts'][0] ?? null; - if ($part && isset($part['inlineData'])) { - return [ - 'audio' => $part['inlineData']['data'], - 'mimeType' => $part['inlineData']['mimeType'] ?? 'audio/mp4' - ]; - } return null; }