feat(nabrah): add Nabrah Arabic STT plugin - #7337
MagdiWaleed wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Devin Review found 2 potential issues.
2 flags not posted on this PR by your GitHub settings — view them in Devin Review. (Configure)
| async for data in self._input_ch: | ||
| if isinstance(data, rtc.AudioFrame): | ||
| audio_bytes = data.data.tobytes() | ||
| if audio_bytes: | ||
| self._audio_position += data.samples_per_channel / data.sample_rate | ||
| await ws.send_bytes(audio_bytes) |
There was a problem hiding this comment.
🟡 Stream flush requests are ignored
When callers invoke flush(), _send_task discards its sentinel instead of ending the current segment. Text remains unfinalized until provider EOT or stream closure.
Learn more
RecognizeStream.flush() marks the end of the current input segment. The input channel represents this call with _FlushSentinel, which this loop currently consumes without action because it handles only audio frames. Unlike provider-managed streams that explicitly document and warn about unsupported flushes, this plugin silently keeps the segment open.
Example: A direct streaming caller pushes "مرحبا", calls flush(), and waits for a final transcript. Without an EOT token, no final event arrives and later speech joins the same turn.
Recommended fix: Handle _FlushSentinel using Nabrah's provider-side finalize operation, then drain the corresponding transcript before emitting the segment boundary. If Nabrah cannot finalize on demand, explicitly override or reject flush() rather than silently accepting it.
Was this helpful? React with 👍 or 👎 to provide feedback.
| except ( | ||
| aiohttp.ClientConnectorError, | ||
| aiohttp.WSServerHandshakeError, | ||
| asyncio.TimeoutError, | ||
| ) as e: | ||
| raise APIConnectionError("failed to connect to nabrah STT") from e |
There was a problem hiding this comment.
🟡 Permanent handshake failures are retried
When the handshake rejects, _connect_ws marks every HTTP status retryable. Invalid endpoints repeat until the retry budget expires, delaying failure.
Learn more
aiohttp.WSServerHandshakeError carries the HTTP status returned while upgrading the connection. Wrapping every such error in the default retryable APIConnectionError erases the distinction between permanent client failures and transient server failures. The base stream retries retryable API errors.
Example: A caller supplies a stale base_url that returns HTTP 404. The stream reconnects with the same URL until max_retry is exhausted instead of failing immediately with the 404.
Recommended fix: Catch WSServerHandshakeError separately and raise APIStatusError with its status, no response body, and the appropriate retryability. Keep transport and timeout failures as APIConnectionError.
Was this helpful? React with 👍 or 👎 to provide feedback.
Adds livekit-plugins-nabrah, a streaming speech-to-text plugin for Nabrah's Arabic recognizer over WebSocket, with interim/final transcripts, end-of-turn detection, word timings, and usage metrics.
c109d33 to
e38c0e9
Compare
Summary
Adds
livekit-plugins-nabrah, a streaming speech-to-text plugin for Nabrah's Arabic recognizer.wss://api.nabrah.ai/api/ext/stt/ws) at 16 kHz mono<eot>token or closing punctuation, with a configurable confirm delayRECOGNITION_USAGEmetricslanguage,recognition_model,priority_words,disable_number_normalization, silence and inactivity timeoutsnabrahextra inlivekit-agentsNeeds
NABRAH_API_KEY.Testing
uv run pytest tests/test_plugin_nabrah_stt.py: 10 passedmake check(format, lint, mypy strict includinglivekit.plugins.nabrah): passesReplaces #6873, rebuilt on current
main.