From e301579c31f994283ac5ad165a59f0d28943b04b Mon Sep 17 00:00:00 2001 From: Abhishek Kumar Date: Tue, 6 Jan 2026 15:26:07 +0530 Subject: [PATCH] add smart turn as provider --- evals/stt/README.md | 63 +++- evals/stt/audio/vad.m4a | Bin 0 -> 111765 bytes evals/stt/audio_streamer.py | 127 ++++++++ evals/stt/benchmark.py | 21 +- evals/stt/providers/__init__.py | 4 + evals/stt/providers/deepgram_flux_provider.py | 225 ++++++++++++++ evals/stt/providers/deepgram_provider.py | 213 ++++++++----- .../providers/local_smart_turn_provider.py | 285 ++++++++++++++++++ evals/stt/providers/speechmatics_provider.py | 200 +++++++----- 9 files changed, 960 insertions(+), 178 deletions(-) create mode 100644 evals/stt/audio/vad.m4a create mode 100644 evals/stt/audio_streamer.py create mode 100644 evals/stt/providers/deepgram_flux_provider.py create mode 100644 evals/stt/providers/local_smart_turn_provider.py diff --git a/evals/stt/README.md b/evals/stt/README.md index b176039..6e2802b 100644 --- a/evals/stt/README.md +++ b/evals/stt/README.md @@ -1,27 +1,29 @@ # STT Evaluation Benchmark -Benchmark for comparing Speech-to-Text providers with focus on: +Benchmark for comparing Speech-to-Text providers using **WebSocket streaming** with focus on: - **Speaker diarization** - identifying who said what - **Keyterm boosting** - improving recognition of specific terms (Deepgram) ## Providers -| Provider | Diarization | Keyterm Boost | Notes | -|----------|-------------|---------------|-------| -| Deepgram | Yes | Yes | `diarize=true`, `keyterm` param | -| Speechmatics | Yes | No | `diarization: "speaker"` config | +| Provider | Diarization | Keyterm Boost | Streaming | +|----------|-------------|---------------|-----------| +| Deepgram | Yes | Yes | WebSocket (v1/v2) | +| Speechmatics | Yes | Additional vocab | WebSocket RT | ## Setup ```bash -# Install dependencies (httpx is required) -pip install httpx +# Install dependencies +pip install websockets # Set API keys export DEEPGRAM_API_KEY="your-key" export SPEECHMATICS_API_KEY="your-key" ``` +**Note:** Requires `ffmpeg` installed for audio conversion to PCM16. + ## Usage Run from the project root directory: @@ -33,9 +35,12 @@ python -m evals.stt.benchmark audio/multi_speaker.m4a --diarize # Test only Deepgram python -m evals.stt.benchmark audio/multi_speaker.m4a --diarize --providers deepgram -# Test with keyterm boosting (Deepgram only) +# Test with keyterm boosting (Deepgram) python -m evals.stt.benchmark audio/multi_speaker.m4a --diarize --keyterms "Dograh" "Pipecat" +# Use different sample rate (default: 8000 Hz) +python -m evals.stt.benchmark audio/multi_speaker.m4a --diarize --sample-rate 16000 + # Show word-level timings python -m evals.stt.benchmark audio/multi_speaker.m4a --diarize --show-words @@ -50,8 +55,9 @@ python -m evals.stt.benchmark audio/multi_speaker.m4a --diarize --save | `audio_file` | Path to audio file (relative to evals/stt/ or absolute) | | `--providers` | Providers to test: `deepgram`, `speechmatics` (default: both) | | `--diarize` | Enable speaker diarization | -| `--keyterms` | Keywords to boost (Deepgram only) | +| `--keyterms` | Keywords to boost (Deepgram) / additional vocab (Speechmatics) | | `--language` | Language code (default: en) | +| `--sample-rate` | Audio sample rate for streaming (default: 8000) | | `--show-words` | Show individual word timings | | `--save` | Save results to JSON in `results/` | @@ -64,16 +70,33 @@ evals/stt/ ├── results/ # Saved benchmark results (JSON) ├── providers/ # STT provider implementations │ ├── base.py # Base classes -│ ├── deepgram_provider.py -│ └── speechmatics_provider.py +│ ├── deepgram_provider.py # WebSocket streaming +│ └── speechmatics_provider.py # WebSocket streaming +├── audio_streamer.py # PCM16 audio file streamer ├── benchmark.py # Main runner script └── README.md ``` +## How It Works + +1. **Audio Conversion**: The `AudioStreamer` converts any audio file to raw PCM16 using ffmpeg +2. **WebSocket Connection**: Providers connect to their respective WebSocket APIs +3. **Streaming**: Audio is sent in chunks (configurable sample rate, default 8kHz) +4. **Result Collection**: Transcripts and speaker info are collected from WebSocket responses +5. **Comparison**: Results are parsed into a common format for comparison + ## Output Example ``` +Audio file: /path/to/audio/multi_speaker.m4a +Providers: ['deepgram', 'speechmatics'] +Diarization: True +Sample rate: 8000 Hz + +============================================================ Provider: DEEPGRAM +============================================================ + Duration: 45.32s Speakers detected: 2 - ['0', '1'] @@ -84,17 +107,29 @@ Hello, welcome to the demo... [0.0s] Speaker 0: Hello, welcome to the demo. [2.5s] Speaker 1: Thanks for having me. ... + +============================================================ +COMPARISON SUMMARY +============================================================ + +Provider Duration Speakers Words +--------------------------------------------- +deepgram 45.32 2 312 +speechmatics 45.32 2 308 ``` ## Adding New Providers 1. Create a new file in `providers/` (e.g., `whisper_provider.py`) -2. Implement the `STTProvider` abstract class -3. Add to `providers/__init__.py` -4. Add to `benchmark.py` provider choices +2. Implement the `STTProvider` abstract class with WebSocket streaming +3. Use `AudioStreamer` for PCM16 conversion +4. Add to `providers/__init__.py` +5. Add to `benchmark.py` provider choices ## API Documentation +- Deepgram Streaming: https://developers.deepgram.com/docs/live-streaming-audio - Deepgram Diarization: https://developers.deepgram.com/docs/diarization - Deepgram Keyterms: https://developers.deepgram.com/docs/keyterm +- Speechmatics RT API: https://docs.speechmatics.com/rt-api-ref - Speechmatics Diarization: https://docs.speechmatics.com/features/diarization diff --git a/evals/stt/audio/vad.m4a b/evals/stt/audio/vad.m4a new file mode 100644 index 0000000000000000000000000000000000000000..e0488c7ac8882fae38ba5f4725bc6ed1ce3cd3e5 GIT binary patch literal 111765 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 !Z6sT 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 K-v0j)bN&mU&{(ek literal 0 HcmV?d00001 diff --git a/evals/stt/audio_streamer.py b/evals/stt/audio_streamer.py new file mode 100644 index 0000000..2eebc2d --- /dev/null +++ b/evals/stt/audio_streamer.py @@ -0,0 +1,127 @@ +"""Audio file streamer - converts audio files to PCM16 streams.""" + +import asyncio +import subprocess +from dataclasses import dataclass +from pathlib import Path +from typing import AsyncIterator + + +@dataclass +class AudioConfig: + """Audio streaming configuration.""" + + sample_rate: int = 8000 + channels: int = 1 + sample_width: int = 2 # 16-bit = 2 bytes + chunk_duration_ms: int = 80 # Send chunks every 80ms + + @property + def chunk_size(self) -> int: + """Bytes per chunk based on duration.""" + samples_per_chunk = int(self.sample_rate * self.chunk_duration_ms / 1000) + return samples_per_chunk * self.channels * self.sample_width + + +class AudioStreamer: + """Streams audio files as PCM16 chunks. + + Converts any audio format to raw PCM16 using ffmpeg and streams + in real-time chunks to simulate live audio. + """ + + def __init__(self, config: AudioConfig | None = None): + self.config = config or AudioConfig() + + def convert_to_pcm16(self, audio_path: Path) -> bytes: + """Convert audio file to raw PCM16 bytes using ffmpeg. + + Args: + audio_path: Path to input audio file + + Returns: + Raw PCM16 audio bytes + """ + cmd = [ + "ffmpeg", + "-i", + str(audio_path), + "-f", + "s16le", # signed 16-bit little-endian + "-acodec", + "pcm_s16le", + "-ar", + str(self.config.sample_rate), + "-ac", + str(self.config.channels), + "-", # output to stdout + ] + + result = subprocess.run( + cmd, + capture_output=True, + check=True, + ) + return result.stdout + + async def stream_file( + self, + audio_path: Path, + realtime: bool = True, + ) -> AsyncIterator[bytes]: + """Stream audio file as PCM16 chunks. + + Args: + audio_path: Path to audio file + realtime: If True, add delays to simulate real-time streaming + + Yields: + PCM16 audio chunks + """ + # Convert entire file to PCM16 + pcm_data = self.convert_to_pcm16(audio_path) + + chunk_size = self.config.chunk_size + delay = self.config.chunk_duration_ms / 1000.0 if realtime else 0 + + # Stream in chunks + for i in range(0, len(pcm_data), chunk_size): + chunk = pcm_data[i : i + chunk_size] + if chunk: + yield chunk + if realtime and delay > 0: + await asyncio.sleep(delay) + + async def stream_file_fast(self, audio_path: Path) -> AsyncIterator[bytes]: + """Stream audio file as fast as possible (no real-time delay). + + Args: + audio_path: Path to audio file + + Yields: + PCM16 audio chunks + """ + async for chunk in self.stream_file(audio_path, realtime=False): + yield chunk + + def get_duration(self, audio_path: Path) -> float: + """Get audio file duration in seconds. + + Args: + audio_path: Path to audio file + + Returns: + Duration in seconds + """ + cmd = [ + "ffprobe", + "-v", + "error", + "-show_entries", + "format=duration", + "-of", + "default=noprint_wrappers=1:nokey=1", + str(audio_path), + ] + result = subprocess.run(cmd, capture_output=True, text=True, check=True) + return float(result.stdout.strip()) diff --git a/evals/stt/benchmark.py b/evals/stt/benchmark.py index c1b5fed..7740cee 100644 --- a/evals/stt/benchmark.py +++ b/evals/stt/benchmark.py @@ -20,14 +20,23 @@ from datetime import datetime from pathlib import Path from typing import Any -from evals.stt.providers import DeepgramProvider, SpeechmaticsProvider, STTProvider, TranscriptionResult +from evals.stt.providers import ( + DeepgramProvider, + DeepgramFluxProvider, + SpeechmaticsProvider, + LocalSmartTurnProvider, + STTProvider, + TranscriptionResult, +) def get_provider(name: str) -> STTProvider: """Get provider instance by name.""" providers = { "deepgram": DeepgramProvider, + "deepgram-flux": DeepgramFluxProvider, "speechmatics": SpeechmaticsProvider, + "local-smart-turn": LocalSmartTurnProvider, } if name not in providers: raise ValueError(f"Unknown provider: {name}. Available: {list(providers.keys())}") @@ -145,7 +154,7 @@ Examples: "--providers", nargs="+", default=["deepgram", "speechmatics"], - choices=["deepgram", "speechmatics"], + choices=["deepgram", "deepgram-flux", "speechmatics", "local-smart-turn"], help="Providers to test (default: all)", ) parser.add_argument( @@ -163,6 +172,12 @@ Examples: default="en", help="Language code (default: en)", ) + parser.add_argument( + "--sample-rate", + type=int, + default=8000, + help="Audio sample rate for streaming (default: 8000)", + ) parser.add_argument( "--show-words", action="store_true", @@ -195,6 +210,7 @@ Examples: print(f"Audio file: {audio_path}") print(f"Providers: {args.providers}") print(f"Diarization: {args.diarize}") + print(f"Sample rate: {args.sample_rate} Hz") if args.keyterms: print(f"Keyterms: {args.keyterms}") @@ -209,6 +225,7 @@ Examples: diarize=args.diarize, keyterms=args.keyterms, language=args.language, + sample_rate=args.sample_rate, ) print_result(result, show_words=args.show_words) results.append(result) diff --git a/evals/stt/providers/__init__.py b/evals/stt/providers/__init__.py index d73a2b8..12bf3ab 100644 --- a/evals/stt/providers/__init__.py +++ b/evals/stt/providers/__init__.py @@ -1,11 +1,15 @@ from .base import STTProvider, TranscriptionResult, Word from .deepgram_provider import DeepgramProvider +from .deepgram_flux_provider import DeepgramFluxProvider from .speechmatics_provider import SpeechmaticsProvider +from .local_smart_turn_provider import LocalSmartTurnProvider __all__ = [ "STTProvider", "TranscriptionResult", "Word", "DeepgramProvider", + "DeepgramFluxProvider", "SpeechmaticsProvider", + "LocalSmartTurnProvider", ] diff --git a/evals/stt/providers/deepgram_flux_provider.py b/evals/stt/providers/deepgram_flux_provider.py new file mode 100644 index 0000000..bd7d16d --- /dev/null +++ b/evals/stt/providers/deepgram_flux_provider.py @@ -0,0 +1,225 @@ +"""Deepgram Flux STT provider with WebSocket streaming. + +Flux is Deepgram's conversational AI model with built-in turn detection. +It has a different API than Nova models - no language/punctuate/diarize params. +""" + +import asyncio +import json +import os +from pathlib import Path +from typing import Any +from urllib.parse import urlencode + +from loguru import logger + +from ..audio_streamer import AudioConfig, AudioStreamer +from .base import STTProvider, TranscriptionResult, Word + +try: + from websockets.asyncio.client import connect as websocket_connect +except ImportError: + raise ImportError("websockets required: pip install websockets") + + +class DeepgramFluxProvider(STTProvider): + """Deepgram Flux Speech-to-Text provider with WebSocket streaming. + + Flux is optimized for conversational AI with built-in turn detection. + + Key differences from Nova: + - Uses v2 API endpoint + - Only supports English (flux-general-en) + - No punctuate, diarize, or language params + - Has turn detection events (StartOfTurn, EndOfTurn, EagerEndOfTurn) + - Supports keyterm boosting + + API Docs: https://developers.deepgram.com/docs/ + """ + + WS_URL = "wss://api.deepgram.com/v2/listen" + + def __init__(self, api_key: str | None = None): + self.api_key = api_key or os.getenv("DEEPGRAM_API_KEY") + if not self.api_key: + raise ValueError( + "Deepgram API key required. Set DEEPGRAM_API_KEY env var or pass api_key." + ) + + @property + def name(self) -> str: + return "deepgram-flux" + + async def transcribe( + self, + audio_path: Path, + diarize: bool = False, # Ignored - Flux doesn't support diarization + keyterms: list[str] | None = None, + model: str = "flux-general-en", + sample_rate: int = 16000, + eot_threshold: float | None = None, + eot_timeout_ms: int | None = None, + eager_eot_threshold: float | None = None, + **kwargs: Any, + ) -> TranscriptionResult: + """Transcribe audio using Deepgram Flux WebSocket streaming. + + Args: + audio_path: Path to audio file + diarize: IGNORED - Flux does not support diarization + keyterms: List of keywords to boost recognition + model: Flux model (default: flux-general-en) + sample_rate: Audio sample rate (default: 16000 for Flux) + eot_threshold: End-of-turn confidence threshold (0-1, default 0.7) + eot_timeout_ms: Timeout in ms to force end of turn (default 5000) + eager_eot_threshold: Threshold for eager end-of-turn events + **kwargs: Additional Flux parameters + + Returns: + TranscriptionResult with transcript (no speaker info - Flux doesn't support diarization) + """ + if diarize: + logger.warning("Flux does not support diarization - ignoring diarize=True") + + # Build query params - Flux only supports specific params + params: dict[str, Any] = { + "model": model, + "encoding": "linear16", + "sample_rate": sample_rate, + } + + # Flux-specific turn detection params + if eot_threshold is not None: + params["eot_threshold"] = eot_threshold + if eot_timeout_ms is not None: + params["eot_timeout_ms"] = eot_timeout_ms + if eager_eot_threshold is not None: + params["eager_eot_threshold"] = eager_eot_threshold + + # Build URL with params + url_parts = [f"{k}={v}" for k, v in params.items()] + + # Add keyterms (repeated params) + if keyterms: + for term in keyterms: + url_parts.append(urlencode({"keyterm": term})) + + ws_url = f"{self.WS_URL}?{'&'.join(url_parts)}" + logger.debug(f"Flux WebSocket URL: {ws_url}") + + # Setup audio streamer + audio_config = AudioConfig(sample_rate=sample_rate) + streamer = AudioStreamer(audio_config) + + # Collect results + all_transcripts: list[dict[str, Any]] = [] + final_transcript = "" + duration = 0.0 + connected = asyncio.Event() + + async with websocket_connect( + ws_url, + additional_headers={"Authorization": f"Token {self.api_key}"}, + ) as ws: + + async def send_audio(): + """Send audio chunks to Deepgram Flux.""" + await connected.wait() + + chunk_no = 0 + async for chunk in streamer.stream_file(audio_path): + logger.debug(f"[deepgram-flux] Sent audio chunk {chunk_no}") + await ws.send(chunk) + chunk_no += 1 + + async def receive_messages(): + """Receive and collect Flux messages.""" + nonlocal all_transcripts, final_transcript, duration + + async for message in ws: + if isinstance(message, str): + data = json.loads(message) + msg_type = data.get("type") + logger.debug(f"[deepgram-flux] Received {msg_type}: {data}") + + if msg_type == "Connected": + logger.info("[deepgram-flux] Connected") + connected.set() + + elif msg_type == "TurnInfo": + event = data.get("event") + transcript = data.get("transcript", "") + words = data.get("words", []) + + if event == "EndOfTurn": + if transcript: + final_transcript += transcript + " " + if words: + all_transcripts.append({ + "transcript": transcript, + "words": words, + }) + # Get duration from last word + if words: + last_word = words[-1] + duration = max(duration, last_word.get("end", 0)) + + elif event == "TurnResumed": + logger.debug("TurnResumed") + + elif msg_type == "Error": + raise Exception(f"Deepgram Flux error: {data}") + + # Run send and receive concurrently + send_task = asyncio.create_task(send_audio()) + receive_task = asyncio.create_task(receive_messages()) + + await send_task + + logger.debug("[deepgram-flux] Send task done") + try: + await asyncio.wait_for(receive_task, timeout=10.0) + except asyncio.TimeoutError: + pass + + return self._parse_results( + all_transcripts, final_transcript.strip(), duration, params, keyterms + ) + + def _parse_results( + self, + transcripts: list[dict[str, Any]], + final_transcript: str, + duration: float, + params: dict[str, Any], + keyterms: list[str] | None, + ) -> TranscriptionResult: + """Parse collected Flux results into TranscriptionResult.""" + words = [] + + for turn in transcripts: + for w in turn.get("words", []): + words.append( + Word( + word=w.get("word", ""), + start=w.get("start", 0.0), + end=w.get("end", 0.0), + confidence=w.get("confidence", 0.0), + speaker=None, # Flux doesn't support diarization + speaker_confidence=None, + ) + ) + + stored_params = dict(params) + if keyterms: + stored_params["keyterms"] = keyterms + + return TranscriptionResult( + provider=self.name, + transcript=final_transcript, + words=words, + speakers=[], # Flux doesn't support diarization + duration=duration, + raw_response={"transcripts": transcripts}, + params=stored_params, + ) diff --git a/evals/stt/providers/deepgram_provider.py b/evals/stt/providers/deepgram_provider.py index c3b1ebd..8bb59eb 100644 --- a/evals/stt/providers/deepgram_provider.py +++ b/evals/stt/providers/deepgram_provider.py @@ -1,25 +1,38 @@ -"""Deepgram STT provider.""" +"""Deepgram STT provider with WebSocket streaming.""" +import asyncio +import json import os from pathlib import Path from typing import Any +from urllib.parse import urlencode -import httpx - +from ..audio_streamer import AudioConfig, AudioStreamer from .base import STTProvider, TranscriptionResult, Word +from loguru import logger + +try: + from websockets.asyncio.client import connect as websocket_connect +except ImportError: + raise ImportError("websockets required: pip install websockets") class DeepgramProvider(STTProvider): - """Deepgram Speech-to-Text provider. + """Deepgram Nova Speech-to-Text provider with WebSocket streaming. API Docs: https://developers.deepgram.com/docs/ Supports: - Speaker diarization via `diarize=true` - - Keyterm boosting via `keyterm` parameter (Nova-3 and Flux models) + - Keyterm boosting via `keyterm` parameter + - Real-time streaming via WebSocket + - Multiple languages + - Punctuation + + For Flux models, use DeepgramFluxProvider instead. """ - API_URL = "https://api.deepgram.com/v1/listen" + WS_URL = "wss://api.deepgram.com/v1/listen" def __init__(self, api_key: str | None = None): self.api_key = api_key or os.getenv("DEEPGRAM_API_KEY") @@ -37,113 +50,151 @@ class DeepgramProvider(STTProvider): audio_path: Path, diarize: bool = False, keyterms: list[str] | None = None, - model: str = "nova-3", + model: str = "nova-3-general", language: str = "en", + sample_rate: int = 8000, punctuate: bool = True, **kwargs: Any, ) -> TranscriptionResult: - """Transcribe audio using Deepgram API. + """Transcribe audio using Deepgram Nova WebSocket streaming. Args: audio_path: Path to audio file diarize: Enable speaker diarization keyterms: List of keywords to boost recognition - model: Deepgram model (nova-3, nova-2, etc.) + model: Deepgram Nova model (nova-3, nova-2, etc.) language: Language code + sample_rate: Audio sample rate for streaming punctuate: Add punctuation **kwargs: Additional Deepgram parameters Returns: TranscriptionResult with transcript and speaker info """ + # Build query params params: dict[str, Any] = { "model": model, "language": language, "punctuate": str(punctuate).lower(), + "encoding": "linear16", + "sample_rate": sample_rate, + "channels": 1, + "interim_results": "true", + "smart_format": "true", + "profanity_filter": "true", + "vad_events": "true" } if diarize: params["diarize"] = "true" - # Add keyterms (Deepgram uses repeated keyterm params) + # Build URL with params + url_parts = [f"{k}={v}" for k, v in params.items()] + + # Add keyterms (repeated params) if keyterms: - params["keyterm"] = keyterms + for term in keyterms: + url_parts.append(urlencode({"keyterm": term})) - # Add any extra kwargs - params.update(kwargs) + # Add extra kwargs + for k, v in kwargs.items(): + url_parts.append(f"{k}={v}") - # Read audio file - audio_data = audio_path.read_bytes() + ws_url = f"{self.WS_URL}?{'&'.join(url_parts)}" + logger.debug(f"Deepgram WebSocket URL: {ws_url}") - # Determine content type - suffix = audio_path.suffix.lower() - content_types = { - ".wav": "audio/wav", - ".mp3": "audio/mpeg", - ".m4a": "audio/mp4", - ".flac": "audio/flac", - ".ogg": "audio/ogg", - ".webm": "audio/webm", - } - content_type = content_types.get(suffix, "audio/wav") + # Setup audio streamer + audio_config = AudioConfig(sample_rate=sample_rate) + streamer = AudioStreamer(audio_config) - headers = { - "Authorization": f"Token {self.api_key}", - "Content-Type": content_type, - } + # Collect results + all_words: list[dict[str, Any]] = [] + final_transcript = "" + duration = 0.0 - async with httpx.AsyncClient(timeout=120.0) as client: - response = await client.post( - self.API_URL, - params=params, - headers=headers, - content=audio_data, - ) - response.raise_for_status() - data = response.json() + try: + async with websocket_connect( + ws_url, + additional_headers={"Authorization": f"Token {self.api_key}"}, + ) as ws: + # Create tasks for sending and receiving + send_complete = asyncio.Event() - return self._parse_response(data, params) + async def send_audio(): + """Send audio chunks to Deepgram.""" + chunk_no = 0 + async for chunk in streamer.stream_file(audio_path): + logger.debug(f"[deepgram] Sent audio chunk {chunk_no}") + await ws.send(chunk) + chunk_no += 1 + # Send close message + logger.debug(f"[deepgram] Sending CloseStream after {chunk_no} chunks") + await ws.send(json.dumps({"type": "CloseStream"})) + send_complete.set() - def _parse_response( - self, data: dict[str, Any], params: dict[str, Any] + async def receive_transcripts(): + """Receive and collect transcription results.""" + nonlocal all_words, final_transcript, duration + + async for message in ws: + if isinstance(message, str): + data = json.loads(message) + msg_type = data.get("type") + logger.debug(f"[deepgram] Received {msg_type}: {data}") + + if msg_type == "Results": + # Nova-style response + channel = data.get("channel", {}) + alternatives = channel.get("alternatives", []) + if alternatives: + alt = alternatives[0] + words = alt.get("words", []) + all_words.extend(words) + + # Check if final + if data.get("is_final"): + final_transcript += alt.get("transcript", "") + " " + duration = max( + duration, data.get("duration", 0) + data.get("start", 0) + ) + + elif msg_type == "Metadata": + # Get duration from metadata + duration = data.get("duration", duration) + + elif msg_type == "Error": + raise Exception(f"Deepgram error: {data}") + + # Run send and receive concurrently + send_task = asyncio.create_task(send_audio()) + receive_task = asyncio.create_task(receive_transcripts()) + + # Wait for send to complete, then wait a bit for final results + await send_task + try: + await asyncio.wait_for(receive_task, timeout=5.0) + except asyncio.TimeoutError: + pass # Normal - websocket closes after final results + except Exception as e: + logger.debug(e) + + return self._parse_results( + all_words, final_transcript.strip(), duration, params, keyterms + ) + + def _parse_results( + self, + raw_words: list[dict[str, Any]], + transcript: str, + duration: float, + params: dict[str, Any], + keyterms: list[str] | None, ) -> TranscriptionResult: - """Parse Deepgram API response.""" - results = data.get("results", {}) - channels = results.get("channels", []) - - if not channels: - return TranscriptionResult( - provider=self.name, - transcript="", - words=[], - speakers=[], - duration=0.0, - raw_response=data, - params=params, - ) - - # Get first channel, first alternative - channel = channels[0] - alternatives = channel.get("alternatives", []) - if not alternatives: - return TranscriptionResult( - provider=self.name, - transcript="", - words=[], - speakers=[], - duration=0.0, - raw_response=data, - params=params, - ) - - alt = alternatives[0] - transcript = alt.get("transcript", "") - - # Parse words with speaker info + """Parse collected results into TranscriptionResult.""" words = [] speakers_set: set[str] = set() - for w in alt.get("words", []): + for w in raw_words: speaker = str(w.get("speaker", "")) if "speaker" in w else None if speaker: speakers_set.add(speaker) @@ -159,9 +210,9 @@ class DeepgramProvider(STTProvider): ) ) - # Get duration from metadata - metadata = results.get("metadata", {}) - duration = metadata.get("duration", 0.0) + stored_params = dict(params) + if keyterms: + stored_params["keyterms"] = keyterms return TranscriptionResult( provider=self.name, @@ -169,6 +220,6 @@ class DeepgramProvider(STTProvider): words=words, speakers=sorted(speakers_set), duration=duration, - raw_response=data, - params=params, + raw_response={"words": raw_words}, + params=stored_params, ) diff --git a/evals/stt/providers/local_smart_turn_provider.py b/evals/stt/providers/local_smart_turn_provider.py new file mode 100644 index 0000000..3116ca0 --- /dev/null +++ b/evals/stt/providers/local_smart_turn_provider.py @@ -0,0 +1,285 @@ +"""Local Smart Turn provider for benchmarking end-of-turn detection. + +Uses the pipecat smart-turn-v3 ONNX model for local ML-based turn detection. +This is NOT an STT provider - it only detects when a speaker has finished talking. +""" + +import os +import time +from dataclasses import dataclass +from pathlib import Path +from typing import Any + +import numpy as np +from loguru import logger + +from ..audio_streamer import AudioConfig, AudioStreamer +from .base import STTProvider, TranscriptionResult, Word + +try: + import onnxruntime as ort + from transformers import WhisperFeatureExtractor +except ImportError: + raise ImportError( + "onnxruntime and transformers required: pip install onnxruntime transformers" + ) + + +@dataclass +class TurnEvent: + """Represents a detected turn event.""" + timestamp: float # Time in audio when turn was detected + probability: float # Model confidence + prediction: int # 1=complete, 0=incomplete + inference_time_ms: float + + +class LocalSmartTurnProvider(STTProvider): + """Local Smart Turn provider for end-of-turn detection benchmarking. + + Uses the smart-turn-v3 ONNX model to detect when speakers finish talking. + This is useful for comparing turn detection accuracy against cloud services + like Deepgram Flux's built-in turn detection. + + NOTE: This provider does NOT produce transcripts - only turn detection events. + """ + + # Smart turn model requires 16kHz audio + REQUIRED_SAMPLE_RATE = 16000 + # Model analyzes 8 seconds of audio + WINDOW_SECONDS = 8 + + def __init__( + self, + model_path: str | None = None, + cpu_count: int = 1, + ): + """Initialize the local smart turn provider. + + Args: + model_path: Path to ONNX model file. If None, uses bundled model. + cpu_count: Number of CPUs for inference (default: 1) + """ + self.model_path = model_path + self.cpu_count = cpu_count + self._session = None + self._feature_extractor = None + + def _load_model(self): + """Lazy load the ONNX model.""" + if self._session is not None: + return + + model_path = self.model_path + + if not model_path: + # Try to load bundled model from pipecat + model_name = "smart-turn-v3.1-cpu.onnx" + package_path = "pipecat.audio.turn.smart_turn.data" + + try: + import importlib_resources as impresources + model_path = str(impresources.files(package_path).joinpath(model_name)) + except Exception: + from importlib import resources as impresources + try: + with impresources.path(package_path, model_name) as f: + model_path = str(f) + except Exception: + model_path = str(impresources.files(package_path).joinpath(model_name)) + + logger.info(f"[local-smart-turn] Loading model from {model_path}") + + # Configure ONNX runtime + so = ort.SessionOptions() + so.execution_mode = ort.ExecutionMode.ORT_SEQUENTIAL + so.inter_op_num_threads = 1 + so.intra_op_num_threads = self.cpu_count + so.graph_optimization_level = ort.GraphOptimizationLevel.ORT_ENABLE_ALL + + self._feature_extractor = WhisperFeatureExtractor(chunk_length=8) + self._session = ort.InferenceSession(model_path, sess_options=so) + + logger.info("[local-smart-turn] Model loaded") + + @property + def name(self) -> str: + return "local-smart-turn" + + def _predict_endpoint(self, audio_array: np.ndarray) -> dict[str, Any]: + """Predict end-of-turn using the ONNX model. + + Args: + audio_array: Audio samples as float32 numpy array (16kHz) + + Returns: + Dict with prediction (0/1) and probability + """ + # Truncate to last 8 seconds or pad to 8 seconds + max_samples = self.WINDOW_SECONDS * self.REQUIRED_SAMPLE_RATE + if len(audio_array) > max_samples: + audio_array = audio_array[-max_samples:] + elif len(audio_array) < max_samples: + padding = max_samples - len(audio_array) + audio_array = np.pad(audio_array, (padding, 0), mode="constant", constant_values=0) + + # Process using Whisper's feature extractor + inputs = self._feature_extractor( + audio_array, + sampling_rate=self.REQUIRED_SAMPLE_RATE, + return_tensors="np", + padding="max_length", + max_length=self.WINDOW_SECONDS * self.REQUIRED_SAMPLE_RATE, + truncation=True, + do_normalize=True, + ) + + # Extract features for ONNX + input_features = inputs.input_features.squeeze(0).astype(np.float32) + input_features = np.expand_dims(input_features, axis=0) + + # Run inference + start_time = time.perf_counter() + outputs = self._session.run(None, {"input_features": input_features}) + inference_time = (time.perf_counter() - start_time) * 1000 + + # Extract probability (model returns sigmoid probabilities) + probability = outputs[0][0].item() + prediction = 1 if probability > 0.5 else 0 + + return { + "prediction": prediction, + "probability": probability, + "inference_time_ms": inference_time, + } + + async def transcribe( + self, + audio_path: Path, + diarize: bool = False, # Ignored - not applicable + keyterms: list[str] | None = None, # Ignored - not applicable + sample_rate: int = 16000, # Must be 16kHz for smart turn + analysis_interval_ms: int = 500, # How often to check for turn completion + **kwargs: Any, + ) -> TranscriptionResult: + """Analyze audio for turn detection events. + + NOTE: This does NOT produce transcripts. It detects when speakers + finish talking using ML-based turn detection. + + Args: + audio_path: Path to audio file + diarize: Ignored (not applicable for turn detection) + keyterms: Ignored (not applicable for turn detection) + sample_rate: Must be 16000 Hz for smart turn model + analysis_interval_ms: How often to run turn detection (ms) + **kwargs: Additional parameters (ignored) + + Returns: + TranscriptionResult with turn detection events in raw_response + """ + if sample_rate != self.REQUIRED_SAMPLE_RATE: + logger.warning( + f"[local-smart-turn] Sample rate must be {self.REQUIRED_SAMPLE_RATE}Hz, " + f"overriding {sample_rate}Hz" + ) + sample_rate = self.REQUIRED_SAMPLE_RATE + + # Load model if not already loaded + self._load_model() + + # Setup audio streamer at 16kHz + audio_config = AudioConfig(sample_rate=sample_rate) + streamer = AudioStreamer(audio_config) + + # Get audio duration + duration = streamer.get_duration(audio_path) + logger.info(f"[local-smart-turn] Processing {audio_path} ({duration:.2f}s)") + + # Collect all audio first (smart turn needs to analyze segments) + pcm_data = streamer.convert_to_pcm16(audio_path) + + # Convert to float32 for model + audio_int16 = np.frombuffer(pcm_data, dtype=np.int16) + audio_float32 = audio_int16.astype(np.float32) / 32768.0 + + # Analyze at intervals + turn_events: list[TurnEvent] = [] + samples_per_interval = int(sample_rate * analysis_interval_ms / 1000) + window_samples = self.WINDOW_SECONDS * sample_rate + + chunk_no = 0 + for end_sample in range(samples_per_interval, len(audio_float32), samples_per_interval): + # Get window of audio ending at current position + start_sample = max(0, end_sample - window_samples) + audio_window = audio_float32[start_sample:end_sample] + + current_time = end_sample / sample_rate + logger.debug(f"[local-smart-turn] Analyzing chunk {chunk_no} at {current_time:.2f}s") + + result = self._predict_endpoint(audio_window) + + turn_events.append(TurnEvent( + timestamp=current_time, + probability=result["probability"], + prediction=result["prediction"], + inference_time_ms=result["inference_time_ms"], + )) + + if result["prediction"] == 1: + logger.info( + f"[local-smart-turn] Turn complete at {current_time:.2f}s " + f"(prob={result['probability']:.3f})" + f"(inf time ms={result["inference_time_ms"]})" + ) + + chunk_no += 1 + + # Create result + # Convert turn events to word-like format for compatibility + words = [] + for event in turn_events: + if event.prediction == 1: + words.append(Word( + word=f"[END_OF_TURN prob={event.probability:.2f}]", + start=event.timestamp - 0.1, + end=event.timestamp, + confidence=event.probability, + speaker=None, + speaker_confidence=None, + )) + + # Count completed turns + completed_turns = sum(1 for e in turn_events if e.prediction == 1) + + params = { + "sample_rate": sample_rate, + "analysis_interval_ms": analysis_interval_ms, + "window_seconds": self.WINDOW_SECONDS, + } + + return TranscriptionResult( + provider=self.name, + transcript=f"[Turn detection only - {completed_turns} turns detected]", + words=words, + speakers=[], # Not applicable + duration=duration, + raw_response={ + "turn_events": [ + { + "timestamp": e.timestamp, + "probability": e.probability, + "prediction": e.prediction, + "inference_time_ms": e.inference_time_ms, + } + for e in turn_events + ], + "completed_turns": completed_turns, + "total_analyses": len(turn_events), + "avg_inference_time_ms": ( + sum(e.inference_time_ms for e in turn_events) / len(turn_events) + if turn_events else 0 + ), + }, + params=params, + ) diff --git a/evals/stt/providers/speechmatics_provider.py b/evals/stt/providers/speechmatics_provider.py index 8f6ee63..3f84a5c 100644 --- a/evals/stt/providers/speechmatics_provider.py +++ b/evals/stt/providers/speechmatics_provider.py @@ -1,38 +1,41 @@ -"""Speechmatics STT provider.""" +"""Speechmatics STT provider with WebSocket streaming.""" +import asyncio +import json import os from pathlib import Path from typing import Any -import httpx +from loguru import logger +from ..audio_streamer import AudioConfig, AudioStreamer from .base import STTProvider, TranscriptionResult, Word +try: + from websockets.asyncio.client import connect as websocket_connect +except ImportError: + raise ImportError("websockets required: pip install websockets") + class SpeechmaticsProvider(STTProvider): - """Speechmatics Speech-to-Text provider. + """Speechmatics Speech-to-Text provider with WebSocket streaming. API Docs: https://docs.speechmatics.com/ Supports: - Speaker diarization via `diarization: "speaker"` config - Speaker sensitivity tuning + - Real-time streaming via WebSocket """ - # EU and US endpoints available - API_URL = "https://asr.api.speechmatics.com/v2/jobs" - - def __init__(self, api_key: str | None = None, region: str = "eu1"): + def __init__(self, api_key: str | None = None, region: str = "eu2"): self.api_key = api_key or os.getenv("SPEECHMATICS_API_KEY") if not self.api_key: raise ValueError( "Speechmatics API key required. Set SPEECHMATICS_API_KEY env var or pass api_key." ) # Set region-specific endpoint - if region == "eu1": - self.api_url = "https://eu1.asr.api.speechmatics.com/v2/jobs" - else: - self.api_url = "https://asr.api.speechmatics.com/v2/jobs" + self.ws_url = f"wss://{region}.rt.speechmatics.com/v2" @property def name(self) -> str: @@ -45,27 +48,32 @@ class SpeechmaticsProvider(STTProvider): keyterms: list[str] | None = None, language: str = "en", operating_point: str = "enhanced", + sample_rate: int = 8000, speaker_sensitivity: float | None = None, + max_speakers: int | None = None, **kwargs: Any, ) -> TranscriptionResult: - """Transcribe audio using Speechmatics API. + """Transcribe audio using Speechmatics WebSocket streaming. Args: audio_path: Path to audio file diarize: Enable speaker diarization - keyterms: Not directly supported by Speechmatics (ignored) + keyterms: Additional vocabulary (limited support) language: Language code operating_point: "standard" or "enhanced" + sample_rate: Audio sample rate for streaming speaker_sensitivity: 0.0-1.0, higher = more speakers detected + max_speakers: Maximum number of speakers to detect **kwargs: Additional config parameters Returns: TranscriptionResult with transcript and speaker info """ - # Build transcription config + # Build transcription config for StartRecognition message transcription_config: dict[str, Any] = { "language": language, "operating_point": operating_point, + "enable_partials": False, } if diarize: @@ -74,13 +82,20 @@ class SpeechmaticsProvider(STTProvider): transcription_config["speaker_diarization_config"] = { "speaker_sensitivity": speaker_sensitivity } + if max_speakers is not None: + if "speaker_diarization_config" not in transcription_config: + transcription_config["speaker_diarization_config"] = {} + transcription_config["speaker_diarization_config"]["max_speakers"] = max_speakers - # Add any extra config - transcription_config.update(kwargs) + # Add additional vocabulary if provided + if keyterms: + transcription_config["additional_vocab"] = [{"content": term} for term in keyterms] - config = { - "type": "transcription", - "transcription_config": transcription_config, + # Audio format config + audio_format = { + "type": "raw", + "encoding": "pcm_s16le", + "sample_rate": sample_rate, } # Store params for result @@ -88,79 +103,104 @@ class SpeechmaticsProvider(STTProvider): "diarize": diarize, "language": language, "operating_point": operating_point, + "sample_rate": sample_rate, "speaker_sensitivity": speaker_sensitivity, + "max_speakers": max_speakers, } - headers = { - "Authorization": f"Bearer {self.api_key}", - } + # Setup audio streamer + audio_config = AudioConfig(sample_rate=sample_rate) + streamer = AudioStreamer(audio_config) - # Create job with multipart form - async with httpx.AsyncClient(timeout=300.0) as client: - # Submit job - with open(audio_path, "rb") as f: - files = { - "data_file": (audio_path.name, f, "audio/mpeg"), - "config": (None, str(config).replace("'", '"'), "application/json"), - } - response = await client.post( - self.api_url, - headers=headers, - files=files, - ) - response.raise_for_status() - job_data = response.json() + # Collect results + all_results: list[dict[str, Any]] = [] + recognition_started = asyncio.Event() + transcription_complete = asyncio.Event() - job_id = job_data.get("id") - if not job_id: - raise ValueError(f"No job ID in response: {job_data}") + async with websocket_connect( + self.ws_url, + additional_headers={"Authorization": f"Bearer {self.api_key}"}, + ) as ws: + # Send StartRecognition message + start_msg = { + "message": "StartRecognition", + "transcription_config": transcription_config, + "audio_format": audio_format, + } + await ws.send(json.dumps(start_msg)) - # Poll for completion - result_data = await self._wait_for_job(client, job_id, headers) + async def send_audio(): + """Send audio chunks after recognition starts.""" + await recognition_started.wait() - return self._parse_response(result_data, params) + chunk_no = 0 + async for chunk in streamer.stream_file(audio_path): + logger.debug(f"[speechmatics] Sent audio chunk {chunk_no}") + await ws.send(chunk) + chunk_no += 1 - async def _wait_for_job( - self, client: httpx.AsyncClient, job_id: str, headers: dict[str, str] - ) -> dict[str, Any]: - """Poll job status until complete.""" - import asyncio + # Signal end of audio with last sequence number + logger.debug(f"[speechmatics] Sending EndOfStream after {chunk_no} chunks") + await ws.send(json.dumps({"message": "EndOfStream", "last_seq_no": chunk_no})) - job_url = f"{self.api_url}/{job_id}" - transcript_url = f"{job_url}/transcript?format=json-v2" + async def receive_messages(): + """Receive and process messages.""" + nonlocal all_results - max_attempts = 120 # 10 minutes with 5s intervals - for _ in range(max_attempts): - # Check job status - status_response = await client.get(job_url, headers=headers) - status_response.raise_for_status() - status_data = status_response.json() + async for message in ws: + if isinstance(message, str): + data = json.loads(message) + msg_type = data.get("message") + logger.debug(f"[speechmatics] Received {msg_type}: {data}") - job_status = status_data.get("job", {}).get("status") + if msg_type == "RecognitionStarted": + logger.info("[speechmatics] Connected") + recognition_started.set() - if job_status == "done": - # Get transcript - transcript_response = await client.get(transcript_url, headers=headers) - transcript_response.raise_for_status() - return transcript_response.json() - elif job_status == "rejected": - raise ValueError(f"Job rejected: {status_data}") - elif job_status == "deleted": - raise ValueError(f"Job deleted: {status_data}") + elif msg_type == "AddTranscript": + # Final transcript segment + results = data.get("results", []) + all_results.extend(results) - await asyncio.sleep(5) + elif msg_type == "EndOfTranscript": + transcription_complete.set() + return - raise TimeoutError(f"Job {job_id} did not complete in time") + elif msg_type == "Error": + raise Exception(f"Speechmatics error: {data}") - def _parse_response( - self, data: dict[str, Any], params: dict[str, Any] + elif msg_type == "Warning": + logger.warning(f"[speechmatics] Warning: {data.get('reason')}") + + # Run send and receive concurrently + send_task = asyncio.create_task(send_audio()) + receive_task = asyncio.create_task(receive_messages()) + + # Wait for completion + await send_task + try: + await asyncio.wait_for(transcription_complete.wait(), timeout=30.0) + except asyncio.TimeoutError: + pass + + receive_task.cancel() + try: + await receive_task + except asyncio.CancelledError: + pass + + return self._parse_results(all_results, params) + + def _parse_results( + self, + results: list[dict[str, Any]], + params: dict[str, Any], ) -> TranscriptionResult: - """Parse Speechmatics API response.""" - results = data.get("results", []) - + """Parse Speechmatics results.""" words = [] speakers_set: set[str] = set() transcript_parts = [] + duration = 0.0 for item in results: item_type = item.get("type") @@ -176,27 +216,25 @@ class SpeechmaticsProvider(STTProvider): if speaker: speakers_set.add(speaker) + end_time = item.get("end_time", 0.0) + duration = max(duration, end_time) + if item_type == "word": words.append( Word( word=content, start=item.get("start_time", 0.0), - end=item.get("end_time", 0.0), + end=end_time, confidence=alt.get("confidence", 0.0), speaker=speaker, - speaker_confidence=None, # Not provided by Speechmatics + speaker_confidence=None, ) ) transcript_parts.append(content) elif item_type == "punctuation": - # Append punctuation to last word in transcript if transcript_parts: transcript_parts[-1] += content - # Get metadata - metadata = data.get("metadata", {}) - duration = metadata.get("duration", 0.0) - transcript = " ".join(transcript_parts) return TranscriptionResult( @@ -205,6 +243,6 @@ class SpeechmaticsProvider(STTProvider): words=words, speakers=sorted(speakers_set), duration=duration, - raw_response=data, + raw_response={"results": results}, params=params, )