Metadata-Version: 2.4
Name: ringglabs
Version: 0.0.2
Summary: Production-grade Python SDK for RinggLabs STT over REST and WebSocket.
Author-email: RinggLabs <support@ringg.ai>
License-Expression: MIT
Project-URL: Homepage, https://github.com/Stonkr/ringglabs-sdk
Project-URL: Documentation, https://github.com/Stonkr/ringglabs-sdk/tree/main/docs
Project-URL: Repository, https://github.com/Stonkr/ringglabs-sdk
Project-URL: Issues, https://github.com/Stonkr/ringglabs-sdk/issues
Keywords: stt,speech-to-text,asr,websocket,ringglabs
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
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: Operating System :: Microsoft :: Windows
Classifier: Operating System :: MacOS :: MacOS X
Classifier: Operating System :: POSIX :: Linux
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx<1.0.0,>=0.27.0
Requires-Dist: websockets<16.0,>=12.0
Provides-Extra: dev
Requires-Dist: pytest<9.0,>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio<1.0,>=0.23; extra == "dev"
Dynamic: license-file

<p align="center">
  <img src="assets/logo/image.png" alt="RinggLabs" width="1000" />
</p>

# RinggLabs Python SDK

`ringglabs` is the Python SDK for RinggLabs-compatible STT services over REST and WebSocket.

## What it supports

- Sync and async clients
- Stream modes: `stream` and `on_final`
- Optional client VAD event signaling
- Typed response/event objects with full raw server payloads
- Configurable HTTP and WebSocket timeouts
- No implicit retries (opt-in retry helpers are available)

## Compatibility

- Python: `3.10+`
- Windows: `10+`
- macOS: `11+`
- Ubuntu: `20.04+`

## Install

```bash
pip install ringglabs
```

## API reference

### Core clients

```python
from ringglabs.stt import Client, AsyncClient
```

`Client` constructor:

```python
Client(
    base_url: str | None = None,
    rest_endpoint: str | None = None,
    ws_endpoint: str | None = None,
    api_key: str | None = None,
    timeout: TimeoutConfig | None = None,
    rest_path: str = "/parrot-stt/v1/transcriptions",
    ws_path: str = "/parrot-stt/v1/stream",
    default_headers: dict[str, str] | None = None,
)
```

`AsyncClient` has the same constructor parameters.

`Client` public methods:

```python
transcribe(...)
stream(...)
health(...)
close()
```

`AsyncClient` public methods:

```python
await transcribe(...)
stream(...)
await health(...)
await close()
```

### Health check example

```python
from ringglabs.stt import Client

client = Client(base_url="http://localhost:7860")
health = client.health()
print(health)
```

## Streaming parameters (public API)

These are the parameters accepted by `stream(...)` on both `Client` and `AsyncClient`.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `sample_rate` | `int` | `16000` | Audio sample rate for stream config. |
| `encoding` | `str` | `"int16"` | Audio encoding (`int16`, `linear16`, `float32`, `int32`). |
| `language` | `str` | `"hi"` | Language sent to server. |
| `mode` | `str` | `"stream"` | Streaming mode (`stream` or `on_final`). |
| `vad_tail_sil_ms` | `int` | `200` | Server VAD tail silence duration in ms. |
| `vad_confidence` | `float` | `0.55` | Server VAD confidence threshold. |
| `enable_cap_punc` | `bool` | `True` | Request punctuation/capitalization from server. |
| `accept_client_vad_events` | `bool` | `False` | Whether client sends speaking boundary events. |
| `api_key` | `str \| None` | `None` | Per-session API key override. |

### Streaming behavior and client VAD events

- `mode="stream"`:
  - Server emits segment-level final transcripts during the session.
- `mode="on_final"`:
  - Server emits partial transcript updates and a final transcript at finalization boundaries.
- `accept_client_vad_events=False`:
  - You only send audio chunks and `end()`.
  - Server-side logic controls segmentation/finalization behavior.
- `accept_client_vad_events=True`:
  - Call `start_speaking()` when user starts speech and `stop_speaking()` when user stops.
  - This explicitly marks speech spans and improves control over finalization timing.

## Streaming examples

### Sync streaming (`mode="stream"`, no client VAD events)

```python
from ringglabs.stt import Client

client = Client(
    ws_endpoint="wss://your-host/parrot-stt/v1/stream",
    api_key="rk_live_xxx",
)

with client.stream(
    language="en",
    mode="stream",
    accept_client_vad_events=False,
) as session:
    session.send_audio(b"...pcm chunk 1...")
    session.send_audio(b"...pcm chunk 2...")
    session.end()

    for event in session.events():
        print(event.type, event.raw)
```

### Sync streaming (`mode="on_final"` with client VAD events)

```python
from ringglabs.stt import Client

client = Client(
    ws_endpoint="wss://your-host/parrot-stt/v1/stream",
    api_key="rk_live_xxx",
)

with client.stream(
    language="hi",
    mode="on_final",
    vad_tail_sil_ms=200,
    vad_confidence=0.55,
    enable_cap_punc=True,
    accept_client_vad_events=True,
) as session:
    session.start_speaking()
    session.send_audio(b"...speech chunk 1...")
    session.send_audio(b"...speech chunk 2...")
    session.stop_speaking()
    session.end()

    for event in session.events():
        print(event.type, event.raw)
```

### Async streaming (`mode="on_final"` with client VAD events)

```python
import asyncio
from ringglabs.stt import AsyncClient

async def main():
    client = AsyncClient(
        ws_endpoint="wss://your-host/parrot-stt/v1/stream",
        api_key="rk_live_xxx",
    )
    async with client.stream(
        language="en",
        mode="on_final",
        vad_tail_sil_ms=200,
        vad_confidence=0.55,
        accept_client_vad_events=True,
    ) as session:
        await session.start_speaking()
        await session.send_audio(b"...speech chunk...")
        await session.stop_speaking()
        await session.end()

        async for event in session.events():
            print(event.type, event.raw)

asyncio.run(main())
```

## REST parameters (public API)

These parameters are accepted by `transcribe(...)`.

| Parameter | Type | Default | Description |
|---|---|---|---|
| `language` | `str` | `"hi"` | Language sent to server. |
| `enable_cap_punc` | `bool` | `True` | Request punctuation/capitalization in output. |
| `api_key` | `str \| None` | `None` | Per-request API key override. |
| `content_type` | `str` | `"audio/wav"` | Multipart file content type. |
| `filename` | `str \| None` | Auto-detected, fallback `"audio.wav"` | Multipart filename used in upload. |

`source` accepted by `transcribe(...)`:
- file path (`str` / `pathlib.Path`)
- raw bytes (`bytes` / `bytearray`)
- file-like object (`BinaryIO`)

## REST `/file` examples

### Sync REST file transcription

```python
from ringglabs.stt import Client

client = Client(
    rest_endpoint="https://your-host/parrot-stt/v1/transcriptions",
    api_key="rk_live_xxx",
)

result = client.transcribe(
    "sample.wav",
    language="en",
    enable_cap_punc=True,
)

print(result.transcription)
print(result.request_id)
print(result.raw)  # full server response
```

### Async REST file transcription

```python
import asyncio
from ringglabs.stt import AsyncClient

async def main():
    async with AsyncClient(
        rest_endpoint="https://your-host/parrot-stt/v1/transcriptions",
        api_key="rk_live_xxx",
    ) as client:
        result = await client.transcribe(
            "sample.wav",
            language="hi",
            enable_cap_punc=True,
        )
        print(result.transcription)
        print(result.request_id)
        print(result.raw)

asyncio.run(main())
```

## Response reference

### REST result (`RestTranscriptionResult`)

- `status`
- `transcription`
- `is_final`
- `language`
- `duration_seconds`
- `processing_time_seconds`
- `request_id`
- `raw` (full server JSON payload)

### WebSocket event types

- `ready`
- `transcript`
- `ack`
- `pong`
- `error`

All parsed events include `raw` with the original server payload.

`transcript` events can include:
- `transcription`
- `is_final`
- `language`
- `request_id`
- `segment_idx`
- `segments`
- `compute_latency_ms`
- `audio_duration_sec`
- `transcribed_audio_duration_sec`
- `processing_time_ms`

## Error handling

```python
from ringglabs.stt import Client, ApiError, TimeoutError, TransportError

client = Client(rest_endpoint="https://your-host/parrot-stt/v1/transcriptions", api_key="...")

try:
    result = client.transcribe("sample.wav")
except TimeoutError:
    print("request timed out")
except TransportError:
    print("network/connection failure")
except ApiError as exc:
    print(exc.status_code, exc.code, exc.message)
```

## Notes

- SDK forwards request parameters to server; server is source of truth for validation.
- Configure connection budgets via `TimeoutConfig` (`connect`, `read`, `write`, `pool`, `ws_open`, `ws_recv`, `ws_close`).
- SDK does not auto-retry by default; use explicit retry helpers only where safe.

## E2E test script

Use the bundled script to validate all SDK flows (sync/async REST + sync/async WS `stream` + sync/async WS `on_final` with client VAD events).

```bash
python examples/test_stt_e2e.py \
  --ws-url ws://localhost:7860/parrot-stt/v1/stream \
  --rest-url http://localhost:7860/parrot-stt/v1/transcriptions \
  --audio-dir /path/to/wav/audio_dir \
  --max-files 3
```

Optional auth:

```bash
RINGGLABS_API_KEY=<your_key> python examples/test_stt_e2e.py
```

Or set audio dir via env:

```bash
RINGGLABS_AUDIO_DIR=/path/to/wav/audio_dir python examples/test_stt_e2e.py
```

## Additional docs

- [Production patterns](docs/production-patterns.md)
- [Public PyPI release guide](docs/release-public-pypi.md)
