fix(tts): use multiple api keys to mitigate ratelimits
Build and Push Image / build-backend (push) Successful in 16s
Build and Push Image / build-frontend (push) Successful in 1m19s
Build and Push Image / push (push) Successful in 44s

This commit is contained in:
2026-06-08 12:36:16 +07:00 Unverified
parent 8ab55c7a2e
commit 953e3ae08e
+32 -17
View File
@@ -2,10 +2,16 @@ import { GoogleGenAI } from "@google/genai";
import wav from "wav";
import { Readable, PassThrough } from "stream";
import z from "zod";
import { env } from "process";
import { redis } from "@/lib/db/redis";
import { hash } from "bun";
const { DONATE_WIDGET_KEY } = process.env as Record<string, string>;
const { DONATE_WIDGET_KEY, GEMINI_TTS_API_KEY } = process.env as Record<
string,
string
>;
const API_KEYS = GEMINI_TTS_API_KEY.split(",")
.map((k) => k.trim())
.filter(Boolean);
const Schema = z.object({
message: z.string().max(1000),
@@ -31,25 +37,34 @@ export async function GET(request: Request) {
headers: { "Content-Type": "audio/wav" },
});
const client = new GoogleGenAI({ apiKey: env.GEMINI_TTS_API_KEY });
let audioData: string | undefined;
const response = await client.models.generateContent({
model: "gemini-2.5-flash-preview-tts",
contents: [{ parts: [{ text: message }] }],
config: {
responseModalities: ["AUDIO"],
speechConfig: {
voiceConfig: {
prebuiltVoiceConfig: {
voiceName: voices[Math.floor(Math.random() * voices.length)],
for (const apiKey of API_KEYS) {
const client = new GoogleGenAI({ apiKey });
try {
const response = await client.models.generateContent({
model: "gemini-2.5-flash-preview-tts",
contents: [{ parts: [{ text: message }] }],
config: {
responseModalities: ["AUDIO"],
speechConfig: {
voiceConfig: {
prebuiltVoiceConfig: {
voiceName: voices[Math.floor(Math.random() * voices.length)],
},
},
},
},
},
},
});
});
audioData = response.candidates?.[0].content?.parts?.[0].inlineData?.data;
if (audioData) break;
} catch (e) {
console.error(`TTS API key failed: ${apiKey.slice(0, 8)}...`, e);
}
}
const audioData =
response.candidates?.[0].content?.parts?.[0].inlineData?.data;
if (!audioData)
return Response.json({ error: "Response is empty" }, { status: 500 });
queueMicrotask(() => {