86 lines
2.5 KiB
Python
86 lines
2.5 KiB
Python
from fastapi import APIRouter, Depends, Request, HTTPException
|
|
from app.api.deps import get_current_module
|
|
from app.models.module import Module
|
|
from sqlalchemy.orm import Session
|
|
from app.core.database import get_db
|
|
from app.core.limiter import limiter
|
|
from app.core.config import settings
|
|
from pydantic import BaseModel
|
|
import httpx
|
|
|
|
router = APIRouter()
|
|
|
|
GEMINI_API_BASE = "https://generativelanguage.googleapis.com/v1beta"
|
|
GEMINI_LIVE_WSS_URI = (
|
|
"wss://generativelanguage.googleapis.com/ws/"
|
|
"google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent"
|
|
)
|
|
|
|
|
|
class LiveTokenRequest(BaseModel):
|
|
model: str = "gemini-2.0-flash-live-001"
|
|
system_instruction: str
|
|
voice_name: str | None = "Puck"
|
|
|
|
|
|
@router.post("/live-token")
|
|
@limiter.limit(settings.RATE_LIMIT)
|
|
async def generate_live_token(
|
|
request: Request,
|
|
body: LiveTokenRequest,
|
|
module: Module = Depends(get_current_module),
|
|
db: Session = Depends(get_db),
|
|
):
|
|
if not settings.GOOGLE_API_KEY or settings.GOOGLE_API_KEY == "your-google-api-key":
|
|
return {
|
|
"token": "mock-ephemeral-token",
|
|
"expires_at": "2026-04-02T02:00:00Z",
|
|
"websocket_uri": GEMINI_LIVE_WSS_URI,
|
|
}
|
|
|
|
payload = {
|
|
"systemInstruction": {
|
|
"parts": [{"text": body.system_instruction}]
|
|
},
|
|
"generationConfig": {
|
|
"responseModalities": ["AUDIO", "TEXT"],
|
|
},
|
|
}
|
|
|
|
if body.voice_name:
|
|
payload["generationConfig"]["speechConfig"] = {
|
|
"voiceConfig": {
|
|
"prebuiltVoiceConfig": {"voiceName": body.voice_name}
|
|
}
|
|
}
|
|
|
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
response = await client.post(
|
|
f"{GEMINI_API_BASE}/models/{body.model}:generateEphemeralToken",
|
|
json=payload,
|
|
params={"key": settings.GOOGLE_API_KEY},
|
|
)
|
|
|
|
if response.status_code != 200:
|
|
raise HTTPException(
|
|
status_code=502,
|
|
detail=f"Gemini token API error {response.status_code}: {response.text}",
|
|
)
|
|
|
|
data = response.json()
|
|
|
|
# Response name is "ephemeralTokens/{token_value}"
|
|
token_value = data.get("name", "").split("/")[-1]
|
|
expires_at = data.get("expireTime", "")
|
|
|
|
if module:
|
|
module.ingress_tokens += 1
|
|
module.total_tokens += 1
|
|
db.commit()
|
|
|
|
return {
|
|
"token": token_value,
|
|
"expires_at": expires_at,
|
|
"websocket_uri": GEMINI_LIVE_WSS_URI,
|
|
}
|