feat(maya): add Maya Research TTS plugin - #6899
Open
saicherry93479 wants to merge 3 commits into
Open
Conversation
saicherry93479
force-pushed
the
maya-tts
branch
from
August 19, 2026 08:20
868553f to
adda8f5
Compare
Streaming speech for ten Indian languages and Indian English, every voice speaking all eleven. One websocket carries a whole conversation, and each sentence is sent as the LLM writes it, so the agent starts speaking before the reply is finished. Interrupting it stops generation at the server rather than only muting playback, which keeps a reused connection from carrying a turn nobody is listening to into the next one. Voice, language and model are passed through as given rather than checked against a fixed list, so values Maya adds later work without a release here. Leaving the language unset lets Maya detect it per sentence, which is what code-switched Hinglish needs.
Start the receive timeout when a sentence has actually gone out, rather than when the turn opens: until then Maya has nothing to answer, so the timeout was measuring how long the LLM took to write its first sentence and a slow one failed the whole reply. Separate sentences with a space. The tokenizer strips its tokens, so the last word of one sentence reached the model glued to the first word of the next. Skip the closing frame for a turn that produced no text, on both the streaming and one-shot paths. A context only exists once it has been given text, so closing one that was never opened is rejected rather than answered. Cancel an abandoned one-shot turn and ignore frames belonging to another context. Pooled connections outlive a turn, so a turn dropped before its terminator could otherwise leave the server generating and its audio would be read as part of whatever turn borrowed the connection next.
saicherry93479
force-pushed
the
maya-tts
branch
from
August 20, 2026 18:45
f800d68 to
d72b4f2
Compare
The connection pool only takes ownership once the handshake returns, so a socket abandoned there had nothing left to close it. Only a timeout and the two validation branches closed it; a dropped connection mid-handshake, a malformed reply, or cancellation left it open.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
livekit-plugins-maya(livekit-plugins/livekit-plugins-maya/), a WebSocket TTS provider covering ten Indian languages plus Indian English, with every voice speaking all eleven. Connections come fromutils.ConnectionPool, so one socket carries a whole conversation and the handshake is paid once rather than per utterance._connect_wssends{"type": "start", "v2": true, ...}and waits for themetadatareply before returning the socket — turns sent before that are rejected rather than served in the older frame shape, so the pool must never hand out a socket that hasn't completed it. A rejected handshake raisesAPIErrorinstead of yielding one that would fail every turn.SynthesizeStreammints acontext_id, sends each tokenized sentence under it withcontinue: true, and closes with an emptycontinue: falseframe, yielding exactly oneend. Sentences are not gated on the previous one's audio, so a multi-sentence reply streams continuously.cancelbefore the socket returns to the pool. Without it the server keeps generating and the next borrower of that connection receives audio from a dead turn.errorframe, so values it adds later work without a release here.ChunkedStreamruns over the same websocket rather than Maya's HTTP endpoint, so the plugin has one transport and one auth path.mayaoptional dependency, and an entry in the cross-providerSYNTHESIZE_TTSsuite.version.pytracks thelivekit-agentsversion (1.6.10), matching the sibling plugins so thelivekit-plugins-maya>=1.6.10pin resolves.Example usage
The API key is read from
MAYA_API_KEY.Test plan
uv run pytest tests/test_plugin_maya_tts.py— 25 tests against a local server speaking the v2 protocol, covering the handshake contents, the turn model, connection reuse across turns, cancel-on-interrupt, no cancel for a turn that finished, a rejected handshake, and error framesuv run ruff check && uv run ruff format --checkuv run mypy livekit-plugins/livekit-plugins-maya/livekit/plugins/maya/Happy to adjust anything to match house style.