Skip to content

Documentation Updates for RTVI version 1.0.0 and corresponding client-js and client-react libraries. #265

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 17 commits into
base: main
Choose a base branch
from

Conversation

mattieruth
Copy link
Contributor

@mattieruth mattieruth commented Jun 19, 2025

This PR updates the client-side documentation to match the new PipecatClient and the underlying, simplified RTVI Message protocol. Corresponding code PRs:

  1. pipecat-client-web
  2. pipecat-client-web-transports
  3. pipecat

This should be merged after the above PRs have been merged and released.

Copilot

This comment was marked as outdated.

@mattieruth mattieruth force-pushed the v2-client-docs branch 2 times, most recently from 4960756 to 7fee2fb Compare June 19, 2025 14:34
@mattieruth mattieruth changed the title A start to updating docs for v2 client changes Documentation Updates for RTVI version 1.0.0 and corresponding client-js and client-react libraries. Jun 24, 2025
@mattieruth mattieruth marked this pull request as ready for review June 24, 2025 17:50
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR updates the client-side documentation to reflect the migration from RTVIClient to the new PipecatClient, updating import names, code examples, and documentation files across the JS and React SDKs. Key changes include:

  • Renaming of client classes, hooks, and components from RTVI* to Pipecat*.
  • Updates to code examples and configuration details in multiple transport and API reference documentations.
  • Removal of outdated documentation pages and streamlining of migration guides.

Reviewed Changes

Copilot reviewed 24 out of 26 changed files in this pull request and generated 1 comment.

File Description
mint.json Updated navigation pages to include new client documentation and migration guides.
client/react/*.md Updated migration guides, introductions, hooks, and components to replace RTVI references with Pipecat references.
client/js/*.md Revised API references, transport guides, and examples to use the new PipecatClient naming.
Comments suppressed due to low confidence (1)

client/js/api-reference/client-constructor.mdx:68

  • [nitpick] Consider renaming the instance variable (e.g., to 'pcClient') rather than reusing the class name 'PipecatClient' to improve code clarity and avoid confusion.
const PipecatClient = new PipecatClient({

@@ -3,6 +3,10 @@ title: "Client SDKs"
description: "Client libraries for building real-time AI applications with Pipecat"
---

<Warning>
The Client SDKs are currently in transition to a new, simpler API design. The js and react libraries have already been deployed with these changes. Their corresponding documentation along with this top-level documentation has been updated to reflect the latest changes, but some specific API references may still be in the old format. For transitioning to the new API, please refer to the [migration guide](/client/migration-guide).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which API references would still be in the old format?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, do you mean the API references for the other libraries (iOS, Android, React Native)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no, i think this was the first paragraph i wrote and figured i'd miss something, so betting on failure, but i feel pretty confident in my updates now 😂 -- i'll remove that language. maybe also specifically point out that iOS, Android, ReactNative have not been ported and therefore their documentation has also not been updated.

} catch (e) {
console.error(e.message);
}

// Events
rtviClient.on(RTVIEvent.Connected, () => {
// Alternative Event Handling Setup
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this pattern an alternative to? Specifying handlers in the callbacks constructor arg?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

correct.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like i just updated what was here in this case and noticed that it was setting up callbacks two different ways, so added the "alternative" language. not sure what's best here really...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems reasonable to show both patterns, as you do here! Maybe a comment tweak would've made it clearer to me, like // Events (alternative to constructor-provided callbacks)

@@ -111,66 +118,71 @@ All Pipecat client SDKs provide:
});

try {
await rtviClient.connect();
// you can provide your Daily connection credentials directly as
Copy link
Contributor

@kompfner kompfner Jun 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah took me a moment to remember that the connect endpoint (shown below) is the way to get the connection credentials. This comment didn't quite get me there quickly—wondering if it could be clearer, like:

Below, we use a REST endpoint to fetch connection credentials for our Daily Transport.
Alternatively, you could provide those credentials directly to `connect()`.

console.log("[EVENT] User disconnected");
});
```

```react react
// Example: Using PipecatClient in a React component
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this missing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whoops

```

```react react
// Example: Using PipecatClient in a React component
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this missing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup.

console.error(e.message);
}
```
await pcClient.connect({url: "https://your-daily-room-url", token: "your-daily-token"});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Like that you found a way to also illustrate the alternative method of connecting 👍

content: "tell me a joke",
},
],
<Tab title="Custom Messaging">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Philosophical question: if we're no longer selling Pipecat Client as a tool for sending "non-custom" bot configuration messages (we're recommending folks do their bot configuration server-side), then can we just call this "messaging" as opposed to "custom messaging"?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think i was considering anything about configuration when i labeled this. I believe i was thinking that RTVI is a bunch of messages, this looks at sending custom ones. But also, that seems nuanced and agree it could just be "messaging" 🤷‍♀️

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think i had also planned on maybe adding more tabs for any other most-common patterns, but only did messaging for now as a replacement for what was here, which was an "actions" example.

@mattieruth mattieruth requested a review from Regaddi June 25, 2025 14:59
`@pipecat-ai/daily-transport`) exports multiple transports classes, you can
specify which to use here.
<Note>
For best practice, we recommend you pass in a transport using its factory function as access to the transport is typically unnecessary. For advanced use cases that do require access to the transport, we recommend doing so via the `PipecatClient.transport` property.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure I follow—how is creating a transport via a factory function v a constructor related to whether or not you then hold a reference to the transport?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

um... uh... good point. you know, i think i initially introduced factory functions thinking we would pass the function to the constructor for the constructor to call, but ended up realizing you need to provide constructor opts to the transport, so backed into using the factory function but calling it to get the transport when just doing a new myTransport(opts) would work just the same.

The real note here is that the best practice is to construct the transport inline in the client constructor so that you are forced to get the transport (if needed) from the client, and not tempted to just hold on to the one you made and passed in, since client.transport !== rawTransport (remember that client.transport returns a wrapped version of the transport with a safety harness).

i.e.

const pcClient = new PipecatClient({transport: new DailyTransport(dailyOpts)});
// forces me to do
(pcClient.transport as DailyClient).preAuth({url});

vs.

const transport = new DailyTransport(dailyOpts);
const pcClient = new PipecatClient({transport});
// this works but now i'm more likely to do:
transport.preAuth({url}); 

GIVEN THAT BREATHY RESPONSE, should i get rid of the create() factory methods? you're right, they don't really accomplish anything. it's more about documenting best practice.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should i get rid of the create() factory methods

Maybe! I suppose they don't really seem necessary.

The real note here is that the best practice is to construct the transport inline in the client constructor so that you are forced to get the transport (if needed) from the client, and not tempted to just hold on to the one you made and passed in

👍 . Maybe the action item is to simplify this note to just focus on this recommendation, then? Like:

"As a best practice, we recommend you construct the transport inline in the client constructor, as opposed to holding a reference to it. Access to the transport is typically unnecessary. For advanced use cases that do require access to the transport, we recommend doing so via the PipecatClient.transport property, which provides some additional safeguards."

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done and factory functions are now removed from code and docs :)

@@ -1,45 +1,288 @@
---
title: "Messages"
title: "Custom Messaging"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See earlier note about the need for "custom"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same response. I'm happy either way, but let me know your thoughts after reading why I used "Custom messaging" here.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No strong feelings about this! Keeping it as "custom messaging" sounds fine to me.


Messages are used by a client for passing an instruction to a bot that typically results in a [callback or event](./callbacks).
Oftentimes clients need to provide configuration data to the server when connecting. This can include things like preferred language, user preferences, initial messages, or any other data that the server needs to know about the client. This must occur before the bot is started and therefore not part of the RTVI standard, but rather a custom implementation. That said, the PipecatClient makes it easy to send this data as part of the `connect()` method, by passing an object with the `requestData` property. Your server endpoint can then handle this data as needed. In the example below, we demonstrate sending an initial prompt and preferred language to the server when connecting.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I'm noticing that sometimes we say PipecatClient and sometimes PipecatClient.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah... that's probably laziness or copilot'ing (or i guess those are one and the same 😂 ) -- What should I try to be consistent towards? i'm guessing PipecatClient?


Examples of messages include:
<CodeGroup>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great example!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks ☺️

# Kick off the conversation
await task.queue_frames([context_aggregator.user().get_context_frame()])

@daily_transport.event_handler("on_participant_left")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

stepping back: would it be better if this were an rtvi event?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe. other than supporting the new behaviors and deprecating the ones going away, though, i didn't do a ton of work on the pipecat side of all this. maybe we do a better sweep of things like this in post?


```python bot
# Listen for a RTVIClientMessageFrame and push a STTUpdateSettingsFrame
class CustomFrameProcessor(FrameProcessor):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm sure we talked about this on a call and I don't remember, but: this is the recommended way for a bot to handle a message? Creating a custom frame processor feels like rather a lot of verbose boilerplate for a customer.

It also feels a bit unintuitive to me...a client message feels conceptually like something that is coming from outside the pipeline and controlling it, not like something that automatically originates within it. I would've expected something more like an event handler in the bot's code file.

Copy link
Contributor

@kompfner kompfner Jun 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh wait, I also see an example below of using an "on_client_message" event handler. That feels much more natural to me.

When are we recommending using one approach v the other? Why are we only showing the custom processor approach in this example, and showing both approaches in the other example?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same question re rtvi.send_server_response v pushing a RTVIServerResponseFrame

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah! GREAT question. walking away from that discussion, my understanding was that handlers are definitely the preferred and simpler approach, so long as you do not need to mess with the pipeline. In this example, I'm updating the STT language and thought that action required a FrameProcessor, but I could be wrong.

...

I just checked with mark and he agrees, this sort of action is best done as a frame processor.

Copy link
Contributor

@kompfner kompfner Jun 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so long as you do not need to mess with the pipeline

Not sure I understand—what's the downside to calling task.queue_frame(frame) inside an event handler, if event handlers are otherwise the preferred and simpler approach?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Also, just realized that using the event handler makes it so you don't have to be so careful about where in your pipeline you put your custom frame processor for it to have the desired effect—in the event handler you'll just shove frames into the top of the pipeline)

class CustomFrameProcessor(FrameProcessor):
async def process_frame(self, frame: Frame, direction: FrameDirection):
await super().process_frame(frame, direction)
if isinstance(frame, STTUpdateSettingsFrame):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The presence of this frame tells us that the language has been updated only if this CustomFrameProcessor is downstream from the STT service, right?

Is it worth showing how the CustomFrameProcessor is inserted into the pipeline? (And maybe also showing how the CustomObserver is wired up, for that matter?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe? though, that feels more a documentation on how observers and processors work and therefore the job of the documentation on those, rather than client-side documentation on sending rtvi messages. you have to draw the line somewhere.

});
```

5. Pipeline Configuration Initialization
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doh. yes, i forgot to come back to this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added

@@ -3,23 +3,27 @@ title: "Transport Overview"
sidebarTitle: "Overview"
---

Transports are the means by which Pipecat clients communicate with their bot services. They handle both message exchange between client and server and real-time media transport. Pipecat implements the RTVI standard for these communications.
Transports are the means by which Pipecat clients communicate with their bot services. They handle both message exchange between client and server and real-time media transport.
Copy link
Contributor

@kompfner kompfner Jun 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There was list of additional transport functionality (like device management) somewhere else in these docs. Should it go here too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea.

description: "A Guide to migrating from RTVIClient to PipecatClient"
---

This guide will cover the high-level changes between the old `RTVIClient` and the new `PipecatClient`. For specific code updates, refer to the platform-specific migration guides.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we also have a warning somewhere in this guide—like we have in the introduction page—that not all client libraries have yet been updated to the new patterns?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea.


#### client-ready

Indicates that the client is ready to receive messages and interact with the server. Typically send after the transport media channels have connected.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit:

Suggested change
Indicates that the client is ready to receive messages and interact with the server. Typically send after the transport media channels have connected.
Indicates that the client is ready to receive messages and interact with the server. Typically sent after the transport media channels have connected.


#### bot-transcription

Transcription of the bot's speech. Note: This protocol currently does not the user transcription format to support real-time timestamping for bot transcriptions. The event is typically sent for each sentence of the bot's response. In the future, this may be updated to include the user with real-time transcription capabilities. Currently, most TTS services do not support (or support well), accurate timing information to make this possible. For now, if you want to attempt real-time transcription to match your bot's speaking, you can try using the `bot-tts-text` message type.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

missing a word here? This protocol currently does not the user transcription

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was i drunk? pushing an update that's hopefully much better...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

was i drunk?

that would've made writing this huge amount of documentation more fun! (i'm trying to give a compliment: very impressive overhaul of the docs)

The payload of the message, which can be any data structure relevant to the message type.
</ParamField>

## RTVI Message Types
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder if there should be a structured way of denoting who (the bot or client) is expected to send each of the messages. Having them mixed together with no labels could be a bit confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good idea... 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:) emojis :)

🤖 🏄


### Client-Server Messaging

#### server-message
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apparently 🤦‍♀️

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

Copy link
Contributor

@kompfner kompfner left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-approving, as all comments (except one new question) have been acknowledged. Nice work!

@mattieruth
Copy link
Contributor Author

I have made all the suggested updates and filled in the missing sections!

),
)
return
else:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like something weird's happened with the indentation here -- I think this is using tabs for the else and spaces for the if

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch. not sure where those tabs came from 🤔

## From inside an Observer, call `send_server_message` directly on your rtvi instance
class CustomObserver(BaseObserver):
async def on_push_frame(self, data: FramePushed):
if isinstance(frame, STTUpdateSettingsFrame):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again it looks like the indentation is weird here (tabs vs spaces mixed)

**New**
```javascript
const client = new PipecatClient({
transport: new DailyTransport(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From the discussions I thought we were going to pass in a factory rather than the transport directly?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the topic of an earlier comment discussion, in which @mattieruth explains:

"i think i initially introduced factory functions thinking we would pass the function to the constructor for the constructor to call, but ended up realizing you need to provide constructor opts to the transport"

So she made the call to just ditch the factory pattern since it wasn't adding anything.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

exactly what kompfner said i said. I had to back away because you need to be able to pass args to the transport constructor/factory function. If you know of a way around this, i'm all ears. The only thing i came up with was to pass the args as a separate param, but that felt ugly. So i'm just trying to document best practice and update all the examples to show inline construction.

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.

3 participants