Metadata-Version: 2.4
Name: koldan-websocket-sdk
Version: 9.1.3
Summary: Official Python SDK for Koldan real-time speech recognition over WebSocket.
Author: Dixilang
License-Expression: LicenseRef-Proprietary
Project-URL: Documentation, https://docs.dixilang.com/developers/sdk/python-websocket/
Keywords: asr,koldan,speech-recognition,speech-to-text,streaming,websocket
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: websockets<17,>=16.1.1
Provides-Extra: microphone
Requires-Dist: sounddevice<0.6,>=0.5.5; extra == "microphone"
Provides-Extra: dev
Requires-Dist: build<2,>=1.4; extra == "dev"
Requires-Dist: mypy<3,>=2.3; extra == "dev"
Requires-Dist: ruff<1,>=0.16; extra == "dev"
Requires-Dist: twine<7,>=6.2; extra == "dev"

# Koldan WebSocket SDK for Python

`koldan-websocket-sdk` is the official asyncio-based Python SDK for Koldan
real-time speech recognition. It handles WebSocket authentication, session
lifecycle, protocol messages, model discovery, audio validation, and typed
recognition events.

## Requirements

- Python 3.10 or newer
- Raw signed 16-bit little-endian mono PCM audio
- Usually 16 kHz audio, depending on the selected Koldan model

The SDK has one required runtime dependency:
[`websockets`](https://websockets.readthedocs.io/). WAV parsing, model
discovery, and audio framing use the Python standard library.

## Install

From PyPI after publication:

```bash
python -m pip install koldan-websocket-sdk
```

From an extracted Koldan SDK archive:

```bash
python -m pip install .
```

For the optional microphone example:

```bash
python -m pip install "koldan-websocket-sdk[microphone]"
```

## Quick start

The high-level `transcribe()` API sends audio and receives events concurrently,
then sends `session.end` automatically when the source is exhausted.

```python
import asyncio
import os

from koldan_websocket import (
    ErrorMessage,
    FinalResult,
    KoldanStreamingClient,
    PartialResult,
    StreamingSessionOptions,
    wav_file_frames,
)


async def main() -> None:
    client = KoldanStreamingClient(
        base_url=os.environ["KOLDAN_BASE_URL"],
        api_key=os.environ["KOLDAN_API_KEY"],
    )
    options = StreamingSessionOptions(
        model=os.environ.get("KOLDAN_MODEL", "general"),
        language="he-IL",
    )

    async with client:
        async for event in client.transcribe(
            wav_file_frames("speech-16kHz.wav"),
            options,
            frame_interval_seconds=0.1,
        ):
            if isinstance(event, PartialResult):
                print(f"\r{event.transcript}", end="", flush=True)
            elif isinstance(event, FinalResult):
                print(f"\nFinal[{event.segment_index}]: {event.transcript}")
            elif isinstance(event, ErrorMessage):
                print(f"\n{event.code}: {event.message}")


asyncio.run(main())
```

`wav_file_frames()` accepts multichannel PCM WAV input. Set `channel=0` for
the first channel, `channel=1` for the second, and so on:

```python
audio = wav_file_frames("stereo.wav", channel=0)
```

The helper validates that WAV input is uncompressed 16-bit PCM at 16 kHz. It
does not silently resample audio.

## Discover streaming models

Model discovery uses the same credential as the WebSocket session and returns
only aliases that advertise `supportsStreaming: true`.

```python
models = await client.list_streaming_models()
for model in models:
    print(model.id, model.capabilities.languages)
```

The API key requires the `speech:model-aliases:read` scope for discovery and
the `speech:sessions:write` scope to start a stream.

## Session options

```python
from koldan_websocket import DiarizationOptions, StreamingSessionOptions

options = StreamingSessionOptions(
    model="general",
    language="he-IL",
    enable_endpoint_detection=True,
    record_audio=True,
    inverse_text_normalization=True,
    diarization=DiarizationOptions(
        enabled=True,
        max_speakers=-1,  # Automatic speaker-count detection.
    ),
    metadata={
        "externalId": "case-4821",
        "department": "radiology",
    },
)
```

`max_speakers` may be omitted, set to `-1` for automatic detection, or set to
a positive integer.

## Events and errors

The async iterator yields these immutable, typed events:

| Event | Meaning |
|---|---|
| `SessionInfo` | Session ID, resolved model, and fallback status |
| `PartialResult` | Interim transcript and word timings |
| `FinalResult` | Stable segment index, transcript, confidence, timings, and speaker tags |
| `SessionCompleted` | Final segment and word totals |
| `ErrorMessage` | Server error or a documented non-fatal warning |

A repeated `FinalResult.segment_index` is a revision of that segment. Replace
the earlier segment instead of appending a duplicate.

`RECORDING_UNAVAILABLE` is a non-fatal warning: the session continues without
recording. Other `ErrorMessage` values end the event stream. Errors received
before startup completes raise `KoldanServerError`, whose `.error` property
contains the typed `ErrorMessage`.

The SDK exception hierarchy is rooted at `KoldanError`:

- `KoldanConfigurationError` for invalid options and audio frames
- `KoldanProtocolError` for invalid state or server messages
- `KoldanServerError` when the server rejects startup
- `KoldanWebSocketError` for WebSocket transport failures
- `KoldanRestError` for model-discovery failures

## Lower-level session API

Use a session directly when a capture pipeline needs independent producer and
consumer tasks:

```python
import asyncio

session = await client.start_session(options)


async def send_audio() -> None:
    for frame in wav_file_frames("speech-16kHz.wav"):
        await session.send_audio(frame)
        await asyncio.sleep(0.1)
    await session.finish()


sender = asyncio.create_task(send_audio())
try:
    async for event in session:
        print(event)
finally:
    await session.close()
    await sender
```

Only one coroutine may receive from a session. Sending and receiving from
different coroutines is supported.

## Audio framing

At 16 kHz, 16-bit mono, a 100 ms frame is 3,200 bytes. The server accepts a
maximum binary frame size of 65,536 bytes.

```python
from koldan_websocket import pcm_file_frames, pcm_frame_size

assert pcm_frame_size(duration_ms=100) == 3200
frames = pcm_file_frames("speech.pcm", frame_bytes=3200)
```

File input must be paced in real time. Microphone capture is naturally paced
by the device and should use the default `frame_interval_seconds=None`.

## Authentication and TLS

Use either `api_key` or `bearer_token`. If both are supplied, the API key takes
precedence. Credentials are sent only inside `session.start`, never in the URL.

TLS certificate and hostname verification are enabled by default. For a private
certificate authority, create an `ssl.SSLContext` with that CA and pass it as
`ssl_context`. Do not disable verification in production.

See the complete guide at
[docs.dixilang.com](https://docs.dixilang.com/developers/sdk/python-websocket/).
