STT observability for Tuner — Deepgram and Speechmatics adapters.
Project description
tuner-stt-observer
STT observability for Tuner. Hooks into your existing STT provider's native event system to capture user transcript, turn timing, and STT latency — your WebSocket/streaming loop is completely untouched. This is a pure observer: it never intercepts audio or makes decisions that affect the call (e.g. barge-in/VAD) — that belongs in your own application.
Supported adapters
| Provider | Adapter | Status |
|---|---|---|
| Deepgram | DeepgramAdapter |
✅ Supported |
| Speechmatics | SpeechmaticsAdapter |
✅ Supported |
| AssemblyAI | — | SOON |
Need a provider that isn't listed? Extend BaseSTTAdapter — see
Custom providers below.
Installation
pip install tuner-core tuner-stt-observer
Each provider adapter lives behind an optional extra so you only pull in the SDKs you actually use:
pip install "tuner-stt-observer[deepgram]" # DeepgramAdapter
pip install "tuner-stt-observer[speechmatics]" # SpeechmaticsAdapter
Usage — Deepgram
DeepgramAdapter registers on Open, Transcript, and UtteranceEnd.
speech_final transcript fragments are buffered internally and flushed as a
single joined utterance when UtteranceEnd fires, so you get one complete
user turn per utterance instead of one per fragment.
from tuner_core import TunerConfig, TunerSession
from tuner_stt_observer import DeepgramAdapter
dg_connection = deepgram_client.listen.asyncwebsocket.v("1")
session = TunerSession(config=TunerConfig.from_env(), call_id=call_id)
adapter = DeepgramAdapter(connection=dg_connection)
adapter.on_utterance = lambda text: transcript_queue.put_nowait(text)
session.attach(adapter)
await dg_connection.start(options) # requires utterance_end_ms + vad_events=True
# In the audio receive loop, send to Deepgram as you normally would —
# the adapter only listens on the connection's events, it never touches audio:
async for chunk in websocket.iter_bytes():
await dg_connection.send(chunk)
await session.flush() # at call end
LiveOptions must set utterance_end_ms (e.g. "1000") and
vad_events=True — without them UtteranceEnd never fires and the fragment
buffer never flushes.
Need barge-in detection? That's call-affecting logic, not observation — build
it in your own application (e.g. a local VAD in your audio loop, or
Deepgram's own SpeechStarted event), not in this adapter.
Usage — Speechmatics
SpeechmaticsAdapter registers on ADD_TRANSCRIPT and END_OF_UTTERANCE.
Speechmatics-rt stacks event handlers rather than replacing them, so this
coexists safely with any handlers you register yourself (e.g. for barge-in
via ADD_PARTIAL_TRANSCRIPT).
from tuner_stt_observer import SpeechmaticsAdapter
adapter = SpeechmaticsAdapter(client=stt_client)
session.attach(adapter)
# Your own handlers still work — Speechmatics-rt stacks, not replaces:
stt_client.on(SMEvent.ADD_PARTIAL_TRANSCRIPT)(my_barge_in_handler)
Speechmatics reports times already relative to session start, so no timing
anchor is needed (unlike Deepgram's stream-relative start field).
Custom providers
For providers other than Deepgram/Speechmatics, extend BaseSTTAdapter from
tuner_stt_observer — its docstring documents the full contract
(_record_user_turn(), _record_stt_usage(), timestamp conversion) with a
worked example.
What gets captured automatically
| Signal | Deepgram | Speechmatics |
|---|---|---|
| Complete user utterance text | ✓ (fragments joined on UtteranceEnd) |
✓ (fragments joined on END_OF_UTTERANCE) |
| Turn start timestamp | ✓ | ✓ |
| Turn duration | ✓ | ✓ |
| STT latency | ✓ (last fragment's audio end → Transcript arrival) |
✓ (END_OF_UTTERANCE → flush) |
| STT audio usage | ✓ | ✓ |
Development
uv sync
uv run pytest
uv run ruff check .
uv run mypy
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file tuner_stt_observer-0.1.1.tar.gz.
File metadata
- Download URL: tuner_stt_observer-0.1.1.tar.gz
- Upload date:
- Size: 15.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
32f49d055d2234a718e60004682938bec888ddd90d7fbb7ef96c95076439e27c
|
|
| MD5 |
421b64044ecef331e23cc3d497595a56
|
|
| BLAKE2b-256 |
e7e23f8065c71a5c5f4185563eca30cdde1b20e58e73aa321e51dfde1e6416d4
|
File details
Details for the file tuner_stt_observer-0.1.1-py3-none-any.whl.
File metadata
- Download URL: tuner_stt_observer-0.1.1-py3-none-any.whl
- Upload date:
- Size: 12.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dc72dc7509f9f01cdabe7dfbc5b95c7c4264cbfaf3515d15b130df6afd24a243
|
|
| MD5 |
55729a9b7d4484111795250d00598661
|
|
| BLAKE2b-256 |
997d207a126272ee484518f281554b2b62a519c4608298977596470077b69d97
|