Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 17 additions & 5 deletions src/data/nav/aitransport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,37 @@ export default {
link: '/docs/ai-transport/why',
},
{
name: 'How it works',
name: 'Concepts',
pages: [
{
name: 'Sessions and turns',
link: '/docs/ai-transport/how-it-works/sessions-and-turns',
name: 'Sessions',
link: '/docs/ai-transport/concepts/sessions',
},
{
name: 'Messages and conversation tree',
link: '/docs/ai-transport/concepts/messages-and-conversation-tree',
},
{
name: 'Turns',
link: '/docs/ai-transport/concepts/turns',
},
{
name: 'Transport',
link: '/docs/ai-transport/how-it-works/transport',
link: '/docs/ai-transport/concepts/transport',
},
{
name: 'Authentication',
link: '/docs/ai-transport/how-it-works/authentication',
link: '/docs/ai-transport/concepts/authentication',
},
],
},
{
name: 'Getting started',
pages: [
{
name: 'Core SDK',
link: '/docs/ai-transport/getting-started/core-sdk',
},
{
name: 'Vercel AI SDK',
link: '/docs/ai-transport/getting-started/vercel-ai-sdk',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,7 @@ A handle to an active client-side turn, returned by `send()`, `regenerate()`, `e
| stream | `ReadableStream<TEvent>` | The decoded event stream for this turn. |
| turnId | `string` | The unique identifier for this turn. |
| cancel | `() => Promise<void>` | Cancel this specific turn. Publishes a cancel message and closes the local stream. |
| optimisticMsgIds | `string[]` | The msg-ids of optimistically inserted user messages, in order. Present when the send included user messages (edit); empty for regeneration. |

## SendOptions <a id="send-options"/>

Expand Down Expand Up @@ -351,7 +352,7 @@ A structured event describing a turn starting or ending.
<Code>
```javascript
type TurnLifecycleEvent =
| { type: 'x-ably-turn-start'; turnId: string; clientId: string }
| { type: 'x-ably-turn-start'; turnId: string; clientId: string; parent?: string; forkOf?: string }
| { type: 'x-ably-turn-end'; turnId: string; clientId: string; reason: TurnEndReason }
```
</Code>
Expand Down
73 changes: 42 additions & 31 deletions src/pages/docs/ai-transport/api-reference/react-hooks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -32,36 +32,45 @@ import {

### useClientTransport <a id="use-client-transport"/>

Create and memoize a `ClientTransport` instance. The transport is created on the first render and the same instance is returned on subsequent renders. The hook does not auto-close the transport on unmount; channel lifecycle is managed by the Ably provider. Call `transport.close()` explicitly if you need to tear down the transport independently of the channel lifecycle.
Access a `ClientTransport` from the nearest `TransportProvider`. The transport is created by the provider, not by this hook. When `channelName` is omitted, the innermost `TransportProvider` in the tree is used.

<Code>
```javascript
function useClientTransport(options: ClientTransportOptions): ClientTransport
function useClientTransport(options?: {
channelName?: string,
skip?: boolean
}): ClientTransport
```
</Code>

See [ClientTransportOptions](/docs/ai-transport/api-reference/client-transport#client-transport-options) for available options.
| Property | Type | Description |
| --- | --- | --- |
| channelName | `string` | The channel name passed to the enclosing `TransportProvider`. Omit to use the nearest provider. |
| skip | `boolean` | When `true`, return a stub transport that throws on any access. Useful when auth is not yet resolved. |

See [TransportProvider](/docs/ai-transport/api-reference/react-hooks#transport-provider) for setting up the provider.

### useView <a id="use-view"/>

Subscribe to a view and return the visible node list with pagination, navigation, and write operations. Accepts either a `ClientTransport` (uses its default view), a `View` directly, or `null`/`undefined`. Returns a `ViewHandle` that re-renders the component when the view updates.
Subscribe to a view and return the visible node list with pagination, navigation, and write operations. Pass `transport` to use a transport's default view, or `view` to subscribe to a specific view directly. When both are omitted, defaults to the nearest `TransportProvider`'s transport. Returns a `ViewHandle` that re-renders the component when the view updates.

<Code>
```javascript
function useView(
source: ClientTransport | View | null | undefined,
options?: UseViewOptions
): ViewHandle
function useView(options?: {
transport?: ClientTransport | null,
view?: View | null,
limit?: number,
skip?: boolean
}): ViewHandle
```
</Code>

#### UseViewOptions <a id="use-view-options"/>

| Property | Type | Description |
| --- | --- | --- |
| limit | `number` | Maximum number of older messages to load per page. Defaults to 100. |

When `options` are provided, the hook auto-loads the first page on mount.
| transport | `ClientTransport \| null` | Client transport whose default view to subscribe to. Defaults to the nearest provider when omitted. |
| view | `View \| null` | A specific view to subscribe to directly. Takes priority over `transport`. |
| limit | `number` | Maximum number of older messages to load per page. When provided, auto-loads the first page on mount. |
| skip | `boolean` | When `true`, skip all subscriptions and return an empty handle. |

#### ViewHandle <a id="view-handle"/>

Expand All @@ -88,7 +97,11 @@ Create an additional view of the conversation tree. Returns a `ViewHandle` with

<Code>
```javascript
function useCreateView(transport: ClientTransport): ViewHandle
function useCreateView(options?: {
transport?: ClientTransport | null,
limit?: number,
skip?: boolean
}): ViewHandle
```
</Code>

Expand Down Expand Up @@ -124,11 +137,13 @@ function useRegenerate(view: View): (messageId: string, options?: SendOptions) =

### useTree <a id="use-tree"/>

Provide stable structural query callbacks backed by the transport's conversation tree. These are thin `useCallback` wrappers around the tree -- no local state or subscriptions. The hook does not re-render on tree changes; use `useView` for reactive updates.
Provide stable structural query callbacks backed by the transport's conversation tree. These are thin `useCallback` wrappers around the tree. The hook does not re-render on tree changes; use `useView` for reactive updates.

<Code>
```javascript
function useTree(transport: ClientTransport): TreeHandle
function useTree(options?: {
transport?: ClientTransport
}): TreeHandle
```
</Code>

Expand All @@ -146,7 +161,9 @@ Subscribe to active turns on the channel. Returns a reactive `Map` keyed by clie

<Code>
```javascript
function useActiveTurns(transport: ClientTransport): Map<string, Set<string>>
function useActiveTurns(options?: {
transport?: ClientTransport | null
}): Map<string, Set<string>>
```
</Code>

Expand All @@ -158,7 +175,10 @@ Subscribe to raw Ably messages on the transport's channel. Useful for building c

<Code>
```javascript
function useAblyMessages(transport: ClientTransport): Ably.Message[]
function useAblyMessages(options?: {
transport?: ClientTransport,
skip?: boolean
}): Ably.InboundMessage[]
```
</Code>

Expand All @@ -185,26 +205,17 @@ function useChatTransport(transport: ClientTransport, chatOptions?: ChatTranspor

When you pass `VercelClientTransportOptions`, the hook creates a new `ClientTransport` internally with the codec pre-bound to `UIMessageCodec`. When you pass an existing `ClientTransport`, it wraps that instance as a `ChatTransport` without creating a new transport.

**Using options (creates transport internally):**

<Code>
```javascript
const transport = useChatTransport({ channel })
const { messages } = useChat({ transport })
```
</Code>

**Using an existing ClientTransport:**
**Using an existing ClientTransport (recommended when you also need `useView`, `useActiveTurns`, or `useMessageSync`):**

<Code>
```javascript
const transport = useClientTransport({ channel, codec: UIMessageCodec, clientId })
const transport = useClientTransport({ channelName: chatId })
const chatTransport = useChatTransport(transport)
const { messages } = useChat({ transport: chatTransport })
```
</Code>

Use the second form when you need direct access to the `ClientTransport` for features like active turn tracking or cancellation. See [ChatTransportOptions](/docs/ai-transport/api-reference/vercel#chat-transport-options) for available options.
See [ChatTransportOptions](/docs/ai-transport/api-reference/vercel#chat-transport-options) for available options.

### useMessageSync <a id="use-message-sync"/>

Expand All @@ -221,7 +232,7 @@ function useMessageSync(

<Code>
```javascript
const transport = useClientTransport({ channel, codec: UIMessageCodec, clientId })
const transport = useClientTransport({ channelName: chatId })
const chatTransport = useChatTransport(transport)
const { messages, setMessages } = useChat({ transport: chatTransport })
useMessageSync(transport, setMessages)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ function close(): void
| onAbort | optional | `(write: (event: TEvent) => Promise<void>) => void \| Promise<void>` | Callback fired when the turn is aborted. Receives a `write` function to publish final events before the abort finalizes. |
| onMessage | optional | `(message: Ably.Message) => void` | Hook called before each Ably message is published in this turn. Mutate the message in place to add custom headers. |
| onError | optional | `(error: ErrorInfo) => void` | Callback for errors during this turn. |
| signal | optional | `AbortSignal` | An external abort signal (typically the HTTP request's `req.signal`) that aborts this turn when fired. This allows platform-level cancellation (request cancellation, serverless function timeout) to stop LLM generation gracefully. |

## Turn <a id="turn"/>

Expand Down
19 changes: 5 additions & 14 deletions src/pages/docs/ai-transport/api-reference/vercel.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -95,34 +95,25 @@ In most cases you will use the `useChatTransport` hook instead of calling this f

## useChatTransport <a id="use-chat-transport"/>

React hook that creates a `ChatTransport` for use with Vercel's `useChat`. Accepts either a `ChatTransportOptions` object or an existing `ClientTransport` instance.
React hook that creates a `ChatTransport` for use with Vercel's `useChat`. Accepts either a `VercelClientTransportOptions` object (creates a transport internally) or an existing `ClientTransport` instance. Both forms accept an optional second argument for `ChatTransportOptions`.

<Code>
```javascript
import { useChatTransport } from '@ably/ai-transport/vercel/react'

function useChatTransport(options: ChatTransportOptions): ChatTransport
function useChatTransport(transport: ClientTransport): ChatTransport
function useChatTransport(options: VercelClientTransportOptions, chatOptions?: ChatTransportOptions): ChatTransport
function useChatTransport(transport: ClientTransport, chatOptions?: ChatTransportOptions): ChatTransport
```
</Code>

When you pass `ChatTransportOptions`, the hook creates a `ClientTransport` internally with the codec pre-bound to `UIMessageCodec`. When you pass an existing `ClientTransport`, it wraps that instance as a `ChatTransport` without creating a new transport. Use the second form when you need direct access to the `ClientTransport` for features like active turn tracking or cancellation.
When you pass `VercelClientTransportOptions`, the hook creates a `ClientTransport` internally with the codec pre-bound to `UIMessageCodec`. When you pass an existing `ClientTransport`, it wraps that instance as a `ChatTransport` without creating a new transport. Use the second form when you need direct access to the `ClientTransport` for features like active turn tracking, `useMessageSync`, or cancellation.

See the [React hooks reference](/docs/ai-transport/api-reference/react-hooks#use-chat-transport) for full usage examples.

## ChatTransportOptions <a id="chat-transport-options"/>

Options for `useChatTransport` (from `@ably/ai-transport/vercel/react`).
Optional second argument to `useChatTransport` and `createChatTransport` for customizing request behavior.

| Property | Required | Type | Description |
| --- | --- | --- | --- |
| channel | required | `Ably.RealtimeChannel` | The Ably channel for the session. |
| clientId | optional | `string` | The client ID for this transport instance. |
| api | optional | `string` | URL of the API endpoint. |
| headers | optional | `Record<string, string>` | Additional HTTP headers. |
| body | optional | `Record<string, unknown>` | Additional request body fields. |
| credentials | optional | `RequestCredentials` | Credentials mode for fetch. |
| fetch | optional | `typeof fetch` | Custom fetch implementation. |
| messages | optional | `UIMessage[]` | Pre-loaded messages for history. |
| logger | optional | `Logger` | Logger instance. |
| prepareSendMessagesRequest | optional | `(context: SendMessagesRequestContext) => { body?: Record<string, unknown>; headers?: Record<string, string> }` | Hook to customize the HTTP POST body and headers before sending to the API. Receives conversation context including history, new messages, and trigger type. |
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ title: "Authentication"
meta_description: "Understand how authentication works in Ably AI Transport: Ably token auth for channel access, HTTP headers for server endpoints, and cancel authorization."
meta_keywords: "AI Transport, authentication, token auth, cancel authorization, HTTP headers, credentials"
redirect_from:
- /docs/ai-transport/how-it-works/authentication
- /docs/ai-transport/sessions-identity/identifying-users-and-agents
---

Expand Down Expand Up @@ -71,8 +72,9 @@ const channel = realtimeClient.channels.get('your-conversation');

import { useEffect, useState } from 'react';
import * as Ably from 'ably';
import { AblyProvider, useChannel } from 'ably/react';
import { useClientTransport } from '@ably/ai-transport/react';
import { AblyProvider } from 'ably/react';
import { TransportProvider, useClientTransport } from '@ably/ai-transport/react';
import { UIMessageCodec } from '@ably/ai-transport/vercel';

// 1. Create an authenticated Ably client and wrap your app
export function Providers({ children }) {
Expand All @@ -97,10 +99,17 @@ export function Providers({ children }) {
return <AblyProvider client={client}>{children}</AblyProvider>;
}

// 2. Inside the provider, get a channel and pass it to the transport
// 2. Wrap with TransportProvider, then access the transport inside
function App({ conversationId }) {
return (
<TransportProvider channelName={conversationId} codec={UIMessageCodec}>
<Chat conversationId={conversationId} />
</TransportProvider>
);
}

function Chat({ conversationId }) {
const { channel } = useChannel({ channelName: conversationId });
const transport = useClientTransport({ channel, codec: yourCodec });
const transport = useClientTransport({ channelName: conversationId });
// ...
}
```
Expand Down Expand Up @@ -202,31 +211,35 @@ await realtimeClient.auth.authorize();

## Server endpoint authentication <a id="server-auth"/>

When the client sends user messages, it makes an HTTP POST to the server endpoint. The `ClientTransportOptions` provide two mechanisms for authenticating these requests:
When the client sends user messages, it makes an HTTP POST to the server endpoint. `TransportProvider` accepts two props for authenticating these requests:

**headers**: Static or dynamic HTTP headers sent with every POST.

<Code>
```javascript
const transport = useClientTransport({
channel,
codec: UIMessageCodec,
headers: () => ({
<TransportProvider
channelName={conversationId}
codec={UIMessageCodec}
headers={() => ({
'Authorization': `Bearer ${getAuthToken()}`,
}),
})
})}
>
<Chat />
</TransportProvider>
```
</Code>

**credentials**: Controls whether cookies are sent with the POST request. This maps directly to the Fetch API `credentials` option. Set to `'include'` if your server uses cookie-based session authentication (for example, NextAuth or `express-session`) and the endpoint is cross-origin. For same-origin requests, the browser sends cookies by default and this option is not needed.

<Code>
```javascript
const transport = useClientTransport({
channel,
codec: UIMessageCodec,
credentials: 'include',
})
<TransportProvider
channelName={conversationId}
codec={UIMessageCodec}
credentials='include'
>
<Chat />
</TransportProvider>
```
</Code>

Expand All @@ -252,7 +265,8 @@ const turn = transport.newTurn({

If `onCancel` returns `false`, the cancellation is rejected. If `onCancel` is not provided, all cancel requests are accepted by default.

## What to read next <a id="next"/>
## Read next <a id="next"/>

- [Getting started](/docs/ai-transport/getting-started/vercel-ai-sdk): Set up authentication in a working app.
- [Cancellation](/docs/ai-transport/features/cancellation): How cancel signals and authorization work in detail.
- [Getting started with Vercel AI SDK](/docs/ai-transport/getting-started/vercel-ai-sdk): set up authentication in a working app.
- [Getting started with the Core SDK](/docs/ai-transport/getting-started/core-sdk): set up authentication without a framework wrapper.
- [Cancellation](/docs/ai-transport/features/cancellation): how cancel signals and authorization work in detail.
Loading
Loading