feat: add livekit transport - #86
Conversation
5eca560 to
38c49ee
Compare
|
Thanks for your PR — I tested your transport and have a few simple pieces of feedback. 1. Support
|
|
I guess I fond the solution for feedback 2 Read waitUntilActive's docs for more information |
|
Thanks for the feedback. I will incorporate these changes and update the PR soon. |
0ffcf00 to
31de936
Compare
e276dfb to
32a55f2
Compare
|
Hey @AbaoFromCUG the PR has been updated. |
1acfc17 to
d562ed5
Compare
|
Hey @markbackman @AbaoFromCUG , this PR has been open for a while. Could you please take a look when you get a chance? If there's anything needed from my side, let me know. Once everything looks good, I'd appreciate it if you could merge it. |
b759856 to
9cd9cdb
Compare
|
@monster-anshu reviewing this is on our to do list. |
mattieruth
left a comment
There was a problem hiding this comment.
This is fantastic work. In some cases pointing out things i need to update for Daily :). Thanks for this contribution and apologies it took so long to get to. I have a bunch of comments, not too many of which are beyond a nit. I still need to test, though. Do you have plans for adding an example? or have one you've been using? If not, I'll just modify one of our existing ones locally.
|
|
||
| ``` | ||
|
|
||
| <<<<<<< HEAD |
| This transport enables a purely WebSocket based connection between clients and your Pipecat application. It implements bidirectional audio and video streaming using a WebSocket for real-time communication. | ||
|
|
||
| It is intended for lightweight implementations, particularly for local development and testing. It expects your Pipecat server to include the corresponding server-side [WebSocketTransport](https://docs.pipecat.ai/api-reference/server/services/transport/websocket-server) implementation. | ||
| # It is intended for lightweight implementations, particularly for local development and testing. It expects your Pipecat server to include the corresponding server-side [WebSocketTransport](https://docs.pipecat.ai/api-reference/server/services/transport/websocket-server) implementation. |
There was a problem hiding this comment.
this should not be a # heading
|
|
||
| ``` | ||
|
|
||
| > > > > > > > e6ded27 (feat: add livekit transport) |
| [](/transports/livekit-transport/README.md) | ||
|  | ||
|
|
||
| This Transport uses the [LiveKit](https://livekit.io) real-time communication platform to connect to a bot and stream media over a WebRTC connection. LiveKit provides a scalable, distributed WebRTC infrastructure with features like edge routing, session observability, and recording capabilities. |
There was a problem hiding this comment.
The second sentence here reads too much like marketing. The Daily Transport provides the exact same set of features but does not include that in the description. We try to be as vendor neutral as possible in the Pipecat ecosystem, so this sentence should either be removed or updated to be more informative of what it enables and that same verbage should be used for Daily as well. These two transports will essentially be equivalent as to what they provide. It just comes down to client preference on vendor.
| @@ -0,0 +1,41 @@ | |||
| { | |||
| "name": "@pipecat-ai/livekit-transport", | |||
| "version": "1.8.1", | |||
There was a problem hiding this comment.
let's start with 1.0.0. The transport versions are not synchronized, so it's a bit odd to start at 1.8.1.
|
|
||
| async sendReadyMessage() { | ||
| this.state = "ready"; | ||
| await this._room.localParticipant.waitUntilActive(); |
There was a problem hiding this comment.
i could use some LiveKit guidance here. In general, we want to wait to send the ready message until we know that our webRTC track connections are ready to send/receive audio. The point being that we don't want the bot to start talking to us until we know we will hear the audio. If we send clientReady() too early, then the start of the bot's audio will be clipped. Will waitUntilActive() give us this assurance? The docs say that it's ready to receive data messages, but does that include audio?
| const decoder = new TextDecoder(); | ||
| const str = decoder.decode(payload); | ||
| const msg = JSON.parse(str); | ||
| if (msg && typeof msg === "object" && "type" in msg) { |
There was a problem hiding this comment.
add a check for label === 'rtvi-ai'
| this.toParticipant(participant) | ||
| ); | ||
| } | ||
| if (publication.source === Track.Source.Microphone) { |
There was a problem hiding this comment.
again, why is the microphone handled differently than cam/screen here?
| participant: LocalParticipant | ||
| ) { | ||
| if (publication.track?.mediaStreamTrack) { | ||
| this._callbacks.onTrackStopped?.( |
There was a problem hiding this comment.
here and in handleLocalTrackPublished we should call onScreenTrackStopped/Started if the track is a screenshare track. (I assume these hit this handler as well)
|
|
||
| private handleMediaDevicesError(e: Error) { | ||
| this._callbacks.onDeviceError?.( | ||
| new DeviceError(["cam", "mic"], "unknown", e.message) |
There was a problem hiding this comment.
it looks like there is support in LiveKit for determining which device failed and how: https://docs.livekit.io/reference/client-sdk-js/types/ParticipantEventCallbacks.html#mediadeviceserror
we should take advantage of this to be consistent with other transports.
waitUntilActive() resolves on ParticipantInfo_State.ACTIVE (server-side participant state). The SDK documents it as "ready to receive data messages." Audio from the bot only starts after it receives clientReady, so there's no clipping risk — bot audio is a consequence of this message, not concurrent with it. |
mattieruth
left a comment
There was a problem hiding this comment.
Thanks for the quick turn-around! This is looking great. I think the only major thing is to figure out the initDevices() logic. I also need to get myself a LiveKit account and test it :)
| let { url, token } = params; | ||
|
|
||
| this.state = "connecting"; | ||
| if (params.authUrl) { |
There was a problem hiding this comment.
I'm guessing you removed this version of connect to encourage the API request to live server-side as part of startBot?
There was a problem hiding this comment.
I want users to generate the LiveKit token using their own setup, either through the LiveKit SDK or any other token generation API, and pass the generated token in the connect parameters. This keeps token generation outside the transport layer, allowing the transport to remain responsible only for establishing the connection.
There was a problem hiding this comment.
great! and i agree. this keeps the API much cleaner and consistent with other transports.
initDevices() previously stopped tracks after enumeration, breaking lobby/pre-join UX. Now uses createLocalAudioTrack/createLocalVideoTrack so tracks persist and are published via publishTrack() at connect time. Also restores abort signal check post-connect to prevent state landing in "connected" after an aborted connection.
Abort check sat before the track-publish awaits, so a disconnect() landing during publishing would resume _connect() past the guard and set state to "connected" after _disconnect() already set it to "disconnected". Move the guard after the publishes, immediately before the state assignment, so it covers every await in the connect path. Also disconnect the room in the abort branch — room.connect() has already succeeded by then, so a bare return leaks a live room.
Part of the transport contract is to fire onBotConnected/onBotDisconnected whenever the bot join/leaves the connection. As we don't currently support multiple remote bots out of the box, the current standard approach is to simply treat the first remote participant as the bot. Track the bot's participant identity (_botId) the same way Daily tracs its _botId, reuse it in tracks() instead of blindly indexing remoteParticipants[0], and clear it on room disconnect/leave so a stale id doesn't linger across reconnects.
|
Hey @mattieruth , the test pipeline is consistently failing for this PR. Based on the error logs, this doesn't appear to be an issue with the test cases themselves. It looks like a GitHub Actions/environment issue where the "@rolldown/binding-linux-x64-gnu" native dependency isn't being installed, causing Vitest to fail before the tests even start. |
i'll look into. i'm also working on a batch of changes to help get it to the finish line and align with the other transports. |
…it-client's native APIs Replaces raw getUserMedia device handling with LiveKit's own track lifecycle (mute/unmute, restartTrack, setMicrophoneEnabled/ setCameraEnabled) for smoother device switching, sticky "default"-device reselection across system default changes, and no track leaks across connect/disconnect. Also fixes bot-connected/disconnected parity, device- error reporting, screen-share vs. generic track callback routing, speaker selection on init, and gates readiness on the bot's media actually being subscribed rather than just being connected. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fixes duplicate behavior and ensures cleanup happens even on unexpected disconnects
|
I have created this PR for your branch that gets us 98% of the way there (i just need to test video, but everything else is working). let me know what you think. this brings parity to the livekit transport, connecting/disconnecting correctly and fixing all the various device state to work both before and after connection to a livekit room. This work also revealed gaps in the server-side livekit transport component. I have a corresponding PR to fix these here: pipecat-ai/pipecat#5297 |
|
Here's a list of all corresponding PRs for getting LiveKit completely wired up in the pipecat ecosystem: New Livekit client-side Transport PRs: Corresponding Server fixes for LiveKit Transport: Voice-Ui Kit support: Pipecat Prebuilt: |
Sure seem right to me I will also test the video channel as well and will attach the results. |
great! would you mind going ahead and merging those changes to your branch so they are pulled in here? |
86 livekit fixes
I have merged the monster-anshu#7 in my branch |
Adds a new @pipecat-ai/livekit-transport package to the monorepo — a WebRTC transport for @pipecat-ai/client-js built on LiveKit's infrastructure. Unlike the peer-to-peer small-webrtc-transport, this routes media through LiveKit's SFU, enabling multi-participant rooms, edge routing, and production-scale deployments.
What's included
transport lifecycle:
directly to connect(). Constructor accepts LiveKit RoomOptions.
switchActiveDevice; hot-swapping via a devicechange listener; selected-device state synced to callbacks.
onTrackStarted/onTrackStopped.