fishaudio: reuse streaming websocket and prebuffer initial chunks#2017
Open
rosetta-livekit-bot[bot] wants to merge 1 commit into
Open
fishaudio: reuse streaming websocket and prebuffer initial chunks#2017rosetta-livekit-bot[bot] wants to merge 1 commit into
rosetta-livekit-bot[bot] wants to merge 1 commit into
Conversation
🦋 Changeset detectedLatest commit: 363246c The changes in this PR will be included in the next version bump. This PR includes changesets to release 36 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
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
Port livekit-plugins-fishaudio pooled websocket reuse and initial audio prebuffering from livekit/agents#6368.
/v1/tts/livewebsocket connections through the existing JSConnectionPool.TTS.prewarm()and close pooled sockets fromTTS.close().modelchanges because FishAudio sends model as a websocket handshake header.Source diff coverage
Coverage
livekit-plugins/livekit-plugins-fishaudio/livekit/plugins/fishaudio/tts.pyplugins/fishaudio/src/tts.ts. The pooled/v1/tts/livewebsocket behavior maps to the existing JSConnectionPool;prewarm(), pool close, model-change invalidation, and first-N chunk prebuffering were implemented. Adaptations are TypeScript/NodewsAPI differences, millisecond duration units (300_000ms), and the target stream/frame queue conventions.Verification
pnpm --filter @livekit/agents-plugin-fishaudio... buildpnpm --filter @livekit/agents-plugin-fishaudio buildpnpm --filter @livekit/agents-plugin-fishaudio lintpnpm test -- plugins/fishaudio(FishAudio test skipped becauseFISH_API_KEY/OPENAI_API_KEYare not set)pnpm buildpnpm lint(passes with pre-existing warnings in unrelated packages)cue-clitext-mode run against builtwarm-transferexample: assertedconversation_item_added(.item.message.role="ASSISTANT"); FishAudio provider was not exercised becauseFISH_API_KEYis not set in this environment.Notes
ConnectionPoolinfrastructure, so no infrastructure gap was left unported.pnpm --filter @livekit/agents-plugin-fishaudio api:checkstill fails due existing package API report/release-tag warnings (plugins/fishaudio/etcAPI report is absent); this PR does not introduce that gap.Ported from livekit/agents#6368
Original PR description
two changes to the fish plugin:
reuse/pre-warm the /v1/tts/live websocket via utils.ConnectionPool (same
pattern as cartesia/rime). the plugin used to open a fresh WSS per utterance,
paying ~330ms of TLS+WS handshake on every reply. now one socket is reused per
session and pre-warmed via prewarm(), so the first reply skips the handshake.
general latency win, independent of the crackle fix.
prebuffer the first two audio chunks before starting playout. fish streams a
small first chunk (~460ms) then, ~250ms later, a much larger second chunk.
starting webrtc's real-time playout at the first chunk leaves only ~190ms of
buffer headroom; app/network jitter tips it into an underrun the receiver
conceals as an audible click/gap ~0.5s into the first utterance (repro'd ~1 in
5). waiting for the second chunk starts playout with ~1.3s buffered and
eliminates it.
the prebuffer is an internal constant (_PREBUFFER_CHUNKS = 2), not a public
option. it costs a little TTFB and is a stopgap for fish's cold-start chunk
pacing; once the inference side streams the opening chunks smoothly we'll drop it
to 1 to start on the first chunk. working with the fish inference team on that now.
verified no crackles on livekit-demo-web.onrender.com; chunk-2 headroom measured
before/after and the negative tail is gone.