Skip to main content

Python client for Gnani's Vachana speech AI platform (STT, TTS, and more)

Project description

gnani-vachana

License: MIT

Official Python client for Vachana Speech APIs by Gnani.ai. Build multilingual voice workflows with Speech-to-Text (STT) and Text-to-Speech (TTS) across REST, SSE streaming, and real-time WebSockets.

Vachana is a production-ready speech platform with high-accuracy STT and low-latency TTS for 10+ Indian languages, with 6 voices, multilingual and code-switching scenarios.

Installation

pip install gnani-vachana

Requires Python 3.9+.

Quick Start

STT REST (file-based transcription)

from gnani.stt import GnaniSTTClient

client = GnaniSTTClient(api_key="your-api-key")

result = client.transcribe("audio.wav", language_code="hi-IN")
print(result["transcript"])

Realtime Streaming (WebSocket)

import asyncio
from gnani.stt import GnaniSTTStreamClient, StreamTranscriptEvent

async def main():
    async with GnaniSTTStreamClient(api_key="your-api-key", language_code="hi-IN") as stream:
        # Send audio chunks (raw PCM, 16-bit LE, 16 kHz, mono)
        with open("audio.pcm", "rb") as f:
            while chunk := f.read(1024):
                await stream.send_audio(chunk)
                await asyncio.sleep(0.032)  # real-time pacing (32 ms per frame)

        # Iterate over events
        async for event in stream:
            if isinstance(event, StreamTranscriptEvent):
                print(event.text)

asyncio.run(main())

TTS REST (single response)

from gnani.tts import GnaniTTSClient, AudioConfig

client = GnaniTTSClient(api_key="your-api-key")
audio = client.synthesize(
    "नमस्ते, आप कैसे हैं?",
    voice="Karan",
    audio_config=AudioConfig(sample_rate=44100, encoding="linear_pcm", container="wav"),
)

with open("tts_output.wav", "wb") as f:
    f.write(audio)

TTS SSE Streaming (lower latency)

from gnani.tts import GnaniTTSStreamClient

client = GnaniTTSStreamClient(api_key="your-api-key")
audio = client.synthesize("Hello from Gnani TTS", voice="Karan", output_file="tts_sse.wav")

TTS Realtime WebSocket (lowest latency)

import asyncio
from gnani.tts import GnaniTTSRealtimeClient

async def main():
    async with GnaniTTSRealtimeClient(api_key="your-api-key") as client:
        audio = await client.synthesize_and_collect(
            "Hello from Gnani TTS", voice="Karan", output_file="tts_realtime.wav",
        )

asyncio.run(main())

Authentication

All APIs (STT REST, STT Realtime, TTS) require a single API key:

Parameter Header Description
api_key X-API-Key-ID API key identifier for authentication

Obtaining Credentials

Email speechstack@gnani.ai with your name, company, and use case. Credentials are typically provisioned within 1 business day, and all new accounts receive free credits -- no credit card required.

Passing Credentials

Option 1 -- Constructor argument:

from gnani.stt import GnaniSTTClient, GnaniSTTStreamClient
from gnani.tts import GnaniTTSClient, GnaniTTSRealtimeClient, GnaniTTSStreamClient

client = GnaniSTTClient(api_key="your-api-key")
stream = GnaniSTTStreamClient(api_key="your-api-key")
tts_rest = GnaniTTSClient(api_key="your-api-key")
tts_stream = GnaniTTSStreamClient(api_key="your-api-key")
tts_realtime = GnaniTTSRealtimeClient(api_key="your-api-key")

Option 2 -- Environment variable:

export GNANI_API_KEY="your-api-key"
from gnani.stt import GnaniSTTClient, GnaniSTTStreamClient
from gnani.tts import GnaniTTSClient

client = GnaniSTTClient()           # picks up GNANI_API_KEY
stream = GnaniSTTStreamClient()     # picks up GNANI_API_KEY
tts = GnaniTTSClient()              # picks up GNANI_API_KEY

Supported Languages

STT Languages (Speech-to-Text)

STT uses BCP-47 locale codes (e.g. hi-IN). For the full list of supported languages, see:

Code-switching — pass comma-separated codes for multilingual audio:

result = client.transcribe("meeting.wav", language_code="en-IN,hi-IN")

Auto-detect (streaming only):

from gnani.stt import GnaniSTTStreamClient

stream = GnaniSTTStreamClient(api_key="key", language_code=GnaniSTTStreamClient.AUTO_DETECT)

TTS Voices

Voice Gender Description
Karan Male Bold, Trustworthy
Simran Female Confident, Bright
Nara Female Gentle, Expressive
Riya Female Cheerful, Energetic
Viraj Male Commanding, Dynamic
Raju Male Grounded, Conversational

TTS Languages (Text-to-Speech)

For the full list of supported languages, see TTS — Supported Languages.

REST Usage

Transcribe a file by path

result = client.transcribe("meeting.wav", language_code="en-IN")
print(result["transcript"])

Transcribe from a file object

with open("meeting.mp3", "rb") as f:
    result = client.transcribe(f, language_code="ta-IN")

Transcribe raw bytes

audio_bytes = download_audio_from_somewhere()
result = client.transcribe_bytes(
    audio_bytes, filename="clip.wav", language_code="kn-IN"
)

Custom request ID

result = client.transcribe(
    "call.flac", language_code="hi-IN", request_id="my-trace-123"
)

List supported languages

for code, name in GnaniSTTClient.supported_languages().items():
    print(f"{code}: {name}")

Realtime Streaming Usage

Connection Flow

  1. Client opens a WebSocket connection to wss://api.vachana.ai/stt/v3/stream with auth headers.
  2. Server sends a connected event with the active configuration.
  3. Client sends binary PCM audio frames (1024 bytes each = 512 samples at 16-bit).
  4. Server detects speech via VAD and responds with processing and transcript events.
  5. Either side may close the connection at any time.

PCM Specification

All audio must be sent as raw PCM binary frames. No container format (WAV, MP3, etc.) is accepted mid-stream.

Property 16 kHz 8 kHz
Encoding PCM signed 16-bit little-endian PCM signed 16-bit little-endian
Sample Rate 16,000 Hz 8,000 Hz
Channels 1 (mono) 1 (mono)
Samples per chunk 512 512
Bytes per frame 1,024 bytes (512 samples × 2 bytes) 1,024 bytes (512 samples × 2 bytes)
Frame duration 32 ms 64 ms
  • Each binary frame must be exactly 1,024 bytes.
  • Frames must be sent at real-time cadence — one frame every 32 ms (16 kHz) or 64 ms (8 kHz). Do not buffer and burst; this degrades VAD accuracy.
  • For 44100 and 48000 Hz sources, the server resamples internally — still send 1,024-byte frames at the appropriate cadence.

For the full WebSocket protocol reference, see STT Realtime — PCM Specification.

Using the async context manager

import asyncio
from gnani.stt import GnaniSTTStreamClient, StreamTranscriptEvent

async def main():
    async with GnaniSTTStreamClient(
        api_key="your-api-key",
        language_code="hi-IN",
        sample_rate=16000,
    ) as stream:
        print(f"Connected! Sample rate: {stream.connected_config.sample_rate}")

        with open("audio.pcm", "rb") as f:
            while chunk := f.read(1024):
                await stream.send_audio(chunk)
                await asyncio.sleep(0.032)

        async for event in stream:
            if isinstance(event, StreamTranscriptEvent):
                print(f"[{event.segment_index}] {event.text}")
                print(f"  Duration: {event.audio_duration_ms}ms, Latency: {event.latency}ms")

asyncio.run(main())

Manual connect / close

import asyncio
from gnani.stt import GnaniSTTStreamClient, StreamTranscriptEvent

async def main():
    stream = GnaniSTTStreamClient(api_key="your-api-key")
    config = await stream.connect()
    print(f"Server ready: {config.message}")

    await stream.send_audio(audio_chunk)
    transcripts = await stream.close()

    for t in transcripts:
        print(t.text)

asyncio.run(main())

High-level stream_audio helper with callbacks

import asyncio
from gnani.stt import GnaniSTTStreamClient, StreamTranscriptEvent, StreamProcessingEvent

async def main():
    async with GnaniSTTStreamClient(api_key="your-api-key") as stream:
        with open("audio.pcm", "rb") as f:
            transcripts = await stream.stream_audio(
                f,
                on_transcript=lambda t: print(f"Transcript: {t.text}"),
                on_processing=lambda p: print(f"Processing at {p.timestamp}..."),
                realtime_pace=True,
            )

    print(f"Total segments: {len(transcripts)}")

asyncio.run(main())

Using 8 kHz audio

stream = GnaniSTTStreamClient(
    api_key="your-api-key",
    language_code="en-IN",
    sample_rate=8000,
)

Event Types

All events are typed dataclasses:

Event Fields Description
StreamConnectedEvent message, timestamp, sample_rate, chunk_size, raw Handshake confirmation with server config
StreamProcessingEvent timestamp, raw VAD detected end-of-speech, transcribing
StreamTranscriptEvent text, audio_duration_ms, segment_id, segment_index, latency, timestamp, raw Completed transcription for a speech segment
StreamErrorEvent message, timestamp, raw Server-side error

Accessing the raw JSON payload

Every event includes a raw field with the full server JSON:

async for event in stream:
    print(event.raw)  # dict with the complete server response

Text-to-Speech Usage

TTS REST

from gnani.tts import GnaniTTSClient

client = GnaniTTSClient(api_key="your-api-key")
audio = client.synthesize("यह एक टेस्ट है", voice="Karan")
with open("tts_rest.wav", "wb") as f:
    f.write(audio)

TTS Streaming (SSE)

Lower latency than REST — audio is streamed via Server-Sent Events.

from gnani.tts import GnaniTTSStreamClient, AudioConfig

client = GnaniTTSStreamClient(api_key="your-api-key")

# synthesize() collects all SSE chunks and returns a valid WAV file
audio = client.synthesize(
    "Streaming TTS response",
    voice="Raju",
    audio_config=AudioConfig(sample_rate=16000, encoding="linear_pcm", container="wav"),
    output_file="tts_sse.wav",
)

For raw PCM streaming (e.g. real-time playback), use synthesize_stream():

for pcm_chunk in client.synthesize_stream("Hello!", voice="Karan"):
    play_audio(pcm_chunk)  # raw PCM, no WAV header

TTS Realtime (WebSocket)

Lowest latency — audio is streamed over a persistent WebSocket connection.

import asyncio
from gnani.tts import GnaniTTSRealtimeClient, AudioConfig

async def main():
    async with GnaniTTSRealtimeClient(api_key="your-api-key") as client:
        # synthesize_and_collect() returns a valid WAV file
        audio = await client.synthesize_and_collect(
            "Realtime TTS response",
            voice="Simran",
            audio_config=AudioConfig(sample_rate=16000, encoding="linear_pcm", container="wav"),
            output_file="tts_realtime.wav",
        )

asyncio.run(main())

For raw PCM streaming (e.g. real-time playback):

async with GnaniTTSRealtimeClient(api_key="your-api-key") as client:
    async for pcm_chunk in client.synthesize("Hello!", voice="Karan"):
        play_audio(pcm_chunk)  # raw PCM, no WAV header

TTS Voices

6 voices are available. List them programmatically:

from gnani.tts import GnaniTTSClient

print(GnaniTTSClient.supported_voices())

Available voices: Karan, Simran, Nara, Riya, Viraj, Raju.

Audio Requirements

STT REST

Constraint Value
Formats WAV, MP3, FLAC, OGG, M4A
Max duration 60 seconds
Channels Mono or stereo
Sample rate Automatically converted to 16 kHz mono

STT Streaming (WebSocket)

Constraint Value
Encoding Raw PCM, signed 16-bit little-endian
Sample rate 16,000 Hz or 8,000 Hz
Channels 1 (mono)
Frame size 1,024 bytes (512 samples)
Pacing Send frames at real-time cadence for best VAD accuracy

See PCM Specification for full details.

Response Format

REST

{
  "success": true,
  "request_id": "req_abc123",
  "timestamp": "20251226_143052.123",
  "transcript": "नमस्ते, आप कैसे हैं?"
}

Realtime Streaming

Connected:

{
  "type": "connected",
  "message": "STT service ready — VAD service connected",
  "timestamp": "2024-01-15T10:30:00.000Z",
  "config": { "sample_rate": 16000, "chunk_size": 512 }
}

Transcript:

{
  "type": "transcript",
  "timestamp": "2024-01-15T10:30:05.987Z",
  "text": "Hello, how are you today?",
  "audio_duration_ms": 2340,
  "segment_id": "<segment_id>",
  "segment_index": "<segment_index>",
  "latency": 320
}

Error Handling

from gnani.stt import (
    AuthenticationError,
    InvalidAudioError,
    APIError,
    StreamConnectionError,
    StreamClosedError,
    StreamError,
)

# REST errors
try:
    result = client.transcribe("audio.wav", language_code="hi-IN")
except AuthenticationError:
    print("Check your credentials")
except InvalidAudioError as e:
    print(f"Bad audio file: {e}")
except APIError as e:
    print(f"API error {e.status_code}: {e}")

# Streaming errors
try:
    async with GnaniSTTStreamClient(api_key="key") as stream:
        await stream.send_audio(chunk)
except StreamConnectionError as e:
    print(f"Connection failed: {e}")
except StreamClosedError as e:
    print(f"Stream already closed: {e}")
except StreamError as e:
    print(f"Server error: {e} (at {e.timestamp})")

Documentation

Full API reference and guides are available at docs.gnani.ai.

License

This project is licensed under the MIT License -- see the LICENSE file for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gnani_vachana-0.5.1.tar.gz (23.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gnani_vachana-0.5.1-py3-none-any.whl (22.7 kB view details)

Uploaded Python 3

File details

Details for the file gnani_vachana-0.5.1.tar.gz.

File metadata

  • Download URL: gnani_vachana-0.5.1.tar.gz
  • Upload date:
  • Size: 23.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for gnani_vachana-0.5.1.tar.gz
Algorithm Hash digest
SHA256 71607592313543672ebd45c168e3e5c73e247747f0444858fca3f906f5bb8b21
MD5 3cdc6d4cf3c5c9b9f3310c0b50d267c1
BLAKE2b-256 e76865b900916fe51e2fb1fc03ee4a735c23b43efddd41c2ac2c9d81118413f6

See more details on using hashes here.

Provenance

The following attestation bundles were made for gnani_vachana-0.5.1.tar.gz:

Publisher: workflow.yml on Gnani-AI-Mintlify/Gnani-Vachana

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file gnani_vachana-0.5.1-py3-none-any.whl.

File metadata

  • Download URL: gnani_vachana-0.5.1-py3-none-any.whl
  • Upload date:
  • Size: 22.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for gnani_vachana-0.5.1-py3-none-any.whl
Algorithm Hash digest
SHA256 3779f7de395cf0755f0b99299f9aeadcff58247977232a03846c271ea9e728b2
MD5 9192d9811b6bf431c7d9c2fa59dcabd8
BLAKE2b-256 f1b04ad3dfe66207b6b5f15f80cfc0b4b406d8c9defbfd2f0a142fc6b166e33c

See more details on using hashes here.

Provenance

The following attestation bundles were made for gnani_vachana-0.5.1-py3-none-any.whl:

Publisher: workflow.yml on Gnani-AI-Mintlify/Gnani-Vachana

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page