Skip to content

feat: add smallest ai asr and tts extensions#2203

Open
harshitajain165 wants to merge 3 commits into
TEN-framework:mainfrom
harshitajain165:feat/smallest-ai-integration
Open

feat: add smallest ai asr and tts extensions#2203
harshitajain165 wants to merge 3 commits into
TEN-framework:mainfrom
harshitajain165:feat/smallest-ai-integration

Conversation

@harshitajain165

Copy link
Copy Markdown

What

Adds two new vendor extensions for Smallest AI:

  • smallest_asr_python — real-time speech-to-text using the
    Pulse
    live WebSocket API (wss://api.smallest.ai/waves/v1/stt/live). 38 languages,
    64 ms time-to-first-transcript.
  • smallest_tts_python — text-to-speech using the
    Lightning
    SSE streaming endpoint (/waves/v1/tts/live). ~100 ms to first audio chunk,
    12 languages, voice cloning; lightning_v3.1 and lightning_v3.1_pro models.

Implementation notes

ASR (extends AsyncASRBaseExtension):

  • Streams raw PCM16 binary frames; interim results with final=false, finals on
    Pulse's is_final.
  • asr_finalize maps to Pulse's {"type": "finalize"} control message (session
    stays open).
  • word_timestamps=true by default so final results carry accurate
    start_ms/duration_ms.
  • Reconnect with exponential backoff (5 attempts) before reporting a fatal error.

TTS (extends AsyncTTS2HttpExtension):

  • SSE frames (data: {"audio": "<base64>"} / {"done": true}) decoded
    incrementally; a partial line is never split across chunk boundaries.
  • output_format pinned to pcm: Lightning's raw PCM is already signed 16-bit
    LE mono, matching the pcm_frame contract with no conversion, and skipping the
    container header keeps time-to-first-audio low.
  • 401/403 and invalid_api_key classified as INVALID_KEY_ERROR (fatal);
    everything else non-fatal.

Both authenticate via params.api_key / SMALLEST_API_KEY and send
X-Source: ten-framework for API-side attribution. All other params keys pass
through verbatim (query string for ASR, request body for TTS).

Testing

  • Extension-local mocked test suites included for both (no API key needed):
    ASR — result shape, finalize, dump, metrics, reconnect, vendor error, invalid
    params; TTS — basic/dump/flush, error classification, metrics, params/URL
    resolution, robustness, state machine.
  • python -m black --check --line-length 80 clean; syntax verified.
  • task asr-guarder-test EXTENSION=smallest_asr_python — 10/10 passed against the live API
  • task tts-guarder-test EXTENSION=smallest_tts_python — 15/15 passed (+1 optional skip) against the live API
  • End-to-end voice test — live conversation via the playground on the voice_assistant_smallest graph (Pulse STT + GPT-4o + Lightning TTS over Agora RTC), including barge-in/interruption

Supersedes #2200 (auto-closed by a force-push during a history cleanup).

Add two new vendor extensions for Smallest AI:

- smallest_asr_python: real-time speech-to-text via the Pulse live
  WebSocket API (binary PCM16 in, interim/final transcripts out,
  finalize control message, reconnect with exponential backoff).
- smallest_tts_python: text-to-speech via the Lightning SSE streaming
  endpoint (base64 PCM16 chunks decoded on the fly, ~100 ms to first
  audio, output_format pinned to pcm).

Both include mocked extension-local test suites, test configs, and
README docs. Configured via params.api_key or SMALLEST_API_KEY.
@harshitajain165 harshitajain165 force-pushed the feat/smallest-ai-integration branch from 63273f3 to e2f2b42 Compare July 7, 2026 07:11
…sults

- guard _send_finalize against a down websocket and complete the
  finalize handshake locally so asr_finalize_end is always emitted
  and the turn cannot stall
- reset the reconnect retry budget on received transcriptions instead
  of right after the websocket handshake, so an accept-then-close
  failure hits the max-attempts fatal error instead of looping forever
- reformat _handle_error_message to be black-clean
- tts: stop substring-matching 401/403 in exception text; real auth
  failures are already classified by the HTTP status-code branch, so
  transport-level exceptions are always plain errors now
- asr: latch the utterance start when its first transcript arrives and
  reuse it for word-less results, so interim start_ms stays monotonic
  instead of pointing at the end of the audio stream
@harshitajain165 harshitajain165 marked this pull request as ready for review July 7, 2026 08:43
@harshitajain165

Copy link
Copy Markdown
Author

@halajohn @plutoless
Requesting review whenever you find a chance, thanks in advance!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant