JanMat / voice.py
BEASTZZZ's picture
Upload 11 files
135a99c verified
Raw History Blame Contribute Delete
2.83 kB
"""
Voice layer via Sarvam AI:
- transcribe_audio(): user's spoken question -> text (Saaras v3 STT)
- synthesize_speech(): the chatbot's answer -> spoken audio (Bulbul v3 TTS)
Docs: https://docs.sarvam.ai/api-reference/introduction
Both endpoints are synchronous REST calls suitable for short clips
(STT: files under ~30s; TTS: text under ~2500 chars per call — chunk longer
answers before calling synthesize_speech if needed).
"""
import base64
import logging
from typing import Optional
import requests
from config import (
SARVAM_API_KEY,
SARVAM_STT_MODEL,
SARVAM_TTS_MODEL,
SARVAM_DEFAULT_SPEAKER,
SARVAM_DEFAULT_LANGUAGE,
)
logger = logging.getLogger("janmat.voice")
STT_URL = "https://api.sarvam.ai/speech-to-text"
TTS_URL = "https://api.sarvam.ai/text-to-speech"
def _headers() -> dict:
if not SARVAM_API_KEY:
raise RuntimeError("SARVAM_API_KEY is not set")
return {"api-subscription-key": SARVAM_API_KEY}
def transcribe_audio(audio_bytes: bytes, filename: str = "audio.wav",
language_code: str = "unknown", mode: str = "transcribe") -> dict:
"""
Sends an audio clip to Sarvam's /speech-to-text (Saaras v3).
language_code="unknown" lets Sarvam auto-detect the spoken Indian
language; pass a BCP-47 code (e.g. "hi-IN") if you already know it.
Returns: {"transcript": str, "language_code": str}
"""
files = {"file": (filename, audio_bytes)}
data = {"model": SARVAM_STT_MODEL, "language_code": language_code, "mode": mode}
resp = requests.post(STT_URL, headers=_headers(), files=files, data=data, timeout=60)
resp.raise_for_status()
body = resp.json()
return {
"transcript": body.get("transcript", ""),
"language_code": body.get("language_code", language_code),
}
def synthesize_speech(text: str, language_code: str = SARVAM_DEFAULT_LANGUAGE,
speaker: str = SARVAM_DEFAULT_SPEAKER, pace: float = 1.0) -> bytes:
"""
Calls Sarvam's /text-to-speech (Bulbul v3). The REST endpoint accepts up
to ~2500 characters per call — split longer answers into chunks and
concatenate the resulting WAV bytes client-side if needed.
Returns raw WAV audio bytes (decoded from the base64 the API returns).
"""
payload = {
"text": text[:2500],
"target_language_code": language_code,
"speaker": speaker,
"pace": pace,
"model": SARVAM_TTS_MODEL,
}
headers = {**_headers(), "Content-Type": "application/json"}
resp = requests.post(TTS_URL, headers=headers, json=payload, timeout=60)
resp.raise_for_status()
body = resp.json()
audios = body.get("audios", [])
if not audios:
raise RuntimeError(f"Sarvam TTS returned no audio: {body}")
return base64.b64decode(audios[0])