Download voice.py from BEASTZZZ/JanMat: direct link, hf CLI and curl.
- Browser
- Download file 2.83 kB
-
https://huggingface.co/spaces/BEASTZZZ/JanMat/resolve/main/voice.py
- Command line
-
hf download hf://spaces/BEASTZZZ/JanMat/voice.py
-
curl -L -o voice.py https://huggingface.co/spaces/BEASTZZZ/JanMat/resolve/main/voice.py
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]) | |