mcpField
S
speech-to-text

speech-to-text

github.com/sarvamaiDevelopment72382 installsUpdated 6d ago
Repository

About speech-to-text

speech-to-text is a skill for developers using the Sarvam SDK to transcribe audio. It covers REST, batch, and WebSocket workflows, with model options and integration caveats.

Key features

  1. 0123 languages and five output modes
  2. 02REST, batch, and WebSocket transcription
  3. 03Speaker diarization for batch audio
  4. 04Partial results in realtime streams
  5. 05v4 keyterm prompting

Use cases

  1. 01Transcribe short audio files
  2. 02Process long recordings with speaker labels
  3. 03Show transcripts from live audio streams
  4. 04Improve recognition of domain-specific terms

Install

Copy the command and run it in a terminal.

$npx skills add https://github.com/sarvamai/skills --skill speech-to-text

After installing, type/speech-to-textin Claude Code to confirm it loaded.

Details

From the project README

Speech-to-Text — Saaras

Live in-chat transcription → sarvam-mcp (sarvam_tools_stt_*). This skill = SDK code.

Important

Auth: api-subscription-key header — NOT Authorization: Bearer. Base URL: https://api.sarvam.ai (NOT /v1 — that prefix is only for the OpenAI-compatible chat endpoint)

Model

saaras:v3 (default, recommended) — 23 languages, 5 output modes (transcribe, translate, verbatim, translit, codemix), auto language detection.

saaras:v4 (latest) — same modes/languages, plus Global English (not just Indian English) and Keyterm Prompting: pass keyterms=["Sarvam", "New Delhi", ...] (up to 50 terms, 64 chars each) on REST, Batch, or either WebSocket streaming endpoint to bias recognition toward domain-specific names/brands/terms. keyterms is saaras:v4-only — see Gotchas for a JS-specific caveat.

Quick Start (Python)

from sarvamai import SarvamAI
client = SarvamAI()

response = client.speech_to_text.transcribe(
    file=open("audio.wav", "rb"),
    model="saaras:v3",
    mode="transcribe"
)
print(response.transcript)

Quick Start (JavaScript/TypeScript)

import { SarvamAIClient } from "sarvamai";
import * as fs from "fs";

const client = new SarvamAIClient({ apiSubscriptionKey: "YOUR_SARVAM_API_KEY" });

const response = await client.speechToText.transcribe({
    file: fs.createReadStream("audio.wav"),
    model: "saaras:v3",
    mode: "transcribe"
});
console.log(response.transcript);

Batch API (Long Audio + Diarization)

job = client.speech_to_text_job.create_job(
    model="saaras:v3",
    mode="transcribe",
    language_code="hi-IN",
    with_diarization=True,
    num_speakers=2
)
job.upload_files(file_paths=["meeting.mp3"])
job.start()
job.wait_until_complete()
job.download_outputs(output_dir="./output")

Supports audio up to 2 hours per file, up to 20 files per job, up to 20 speakers (num_speakers), all 5 output modes.

WebSocket Streaming

import asyncio, base64
from sarvamai import AsyncSarvamAI

async def stream_audio():
    client = AsyncSarvamAI()
    async with client.speech_to_text_streaming.connect(
        model="saaras:v3",
        language_code="hi-IN",   # required — connect() now rejects a missing language_code
        high_vad_sensitivity=True,
        flush_signal=True
    ) as ws:
        with open("audio.wav", "rb") as f:
            audio_base64 = base64.b64encode(f.read()).decode("utf-8")
        await ws.transcribe(audio=audio_base64, encoding="audio/wav", sample_rate=16000)
        await ws.flush()
        response = await ws.recv()
        print(response)

asyncio.run(stream_audio())

No fixed session duration limit — but the connection closes after 60 seconds of inactivity. Use sample_rate=8000 for telephony audio.

Verified live: connect() currently raises TypeError: ... missing 1 required keyword-only argument: 'language_code' if you omit it — it is not optional in the installed SDK, despite not being marked required in some docs examples.

Realtime Streaming (New — saaras:v3-realtime / saaras:v4)

A newer, separate WebSocket API — not a replacement for the "WebSocket Streaming" above, which still works. Use this one for true interim results and finer turn control.

import asyncio, base64
from sarvamai import AsyncSarvamAI, RealtimeAudioInput, RealtimeEnd

async def transcribe(audio_chunks):
    client = AsyncSarvamAI()
    async with client.speech_to_text_realtime_streaming.connect(
        language_code="hi-IN",
        stream_type="fast",   # "fast" | "balanced" (default) | "simulated"
    ) as ws:
        async def send_audio():
            async for chunk in audio_chunks:
                await ws.send_realtime_audio_input(
                    RealtimeAudioInput(audio=base64.b64encode(chunk).decode("utf-8"))
                )
            await ws.send_realtime_end(RealtimeEnd())

        async def receive_events():
            async for message in ws:
                if message.event == "transcript.partial":
                    print(f"partial: {message.text}")
                elif message.event == "transcript.final":
                    print(f"final: {message.text}")
                elif message.event == "error":
                    print(f"error ({message.code}): {message.message}")

        await asyncio.gather(send_audio(), receive_events())

JS: client.speechToTextRealtimeStreaming.connect({ language_code, stream_type }), then socket.sendRealtimeAudioInput({...}) / socket.sendRealtimeEnd({...}), with events on socket.on("message", ...).

Gotchas

GotchaDetail
REST: 30s limitAudio >30s fails. Use Batch API or WebSocket for longer files.
JS method nameclient.speechToText.transcribe({...}) — camelCase, NOT speech_to_text. File via fs.createReadStream().
WebSocket codecsOnly wav, pcm_s16le, pcm_l16, pcm_raw. MP3/AAC/OGG NOT supported for streaming. PCM input is 16kHz only.
WebSocket audioMust be base64-encoded. Use sample_rate=8000 for telephony audio.
WebSocket idle timeoutConnection closes after 60s of inactivity. For long-running sessions, send periodic silent (near-zero amplitude) audio chunks as keep-alive.
Flush signalflush_signal=True + await ws.flush() forces immediate transcription boundary.
VAD eventsvad_signals=True emits START_SPEECH/END_SPEECH events alongside transcripts. high_vad_sensitivity=True for automatic end-of-speech detection.
Short audio detectionSet language_code explicitly for audio <3 seconds — auto-detection needs more signal.
keyterms is v4-onlyAccepted with model="saaras:v4" on REST, Batch, and both WebSocket streaming endpoints (legacy /speech-to-text/ws and realtime). Verified live: saaras:v3 + keyterms returns 400 — "'keyterms' is only supported by model 'saaras:v4', got 'saaras:v3'." — a hard error, not a silent ignore. Format: JSON list of strings, max 50 terms, 64 chars each — don't use the older keyterm/hotwords fields.
keyterms JS supportDocs (as of this writing) say JS speechToText.transcribe({..., keyterms}) hasn't shipped to npm, with only speechToTextJob.createJob({..., keyterms}) (Batch) working. Verified live against [email protected]: REST transcribe({..., keyterms}) worked without error — the docs note may already be stale. Don't trust either source blindly; the gap may be closed by the time you read this — test the actual call once. Python needs sarvamai>=0.1.33a1 (REST) / >=0.1.33a3 (Batch) either way.
Realtime vs legacy streaming are different endpointsLegacy WebSocket (/speech-to-text/ws, speech_to_text_streaming) has no interim results and needs a reconnect to change params. Realtime (/speech-to-text-realtime/ws, speech_to_text_realtime_streaming) adds transcript.partial events, millisecond VAD params (threshold, silence_duration_ms, min_speech_duration_ms), and live config.update — don't mix the two APIs' parameter names.
Realtime sample rateOnly 8000 or 16000 Hz — any other value closes the connection with code 4000.

Full Docs

Fetch streaming protocol, batch API SDK examples, and codec details from:

Comments

Markdown supported