@ably/ai-transport 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -111
- package/dist/ably-ai-transport.js +2401 -1387
- package/dist/ably-ai-transport.js.map +1 -1
- package/dist/ably-ai-transport.umd.cjs +1 -1
- package/dist/ably-ai-transport.umd.cjs.map +1 -1
- package/dist/constants.d.ts +116 -42
- package/dist/core/agent.d.ts +44 -0
- package/dist/core/channel-options.d.ts +57 -0
- package/dist/core/codec/codec-event.d.ts +9 -0
- package/dist/core/codec/decoder.d.ts +24 -24
- package/dist/core/codec/define-codec.d.ts +100 -0
- package/dist/core/codec/encoder.d.ts +10 -12
- package/dist/core/codec/field-bag.d.ts +85 -0
- package/dist/core/codec/fields.d.ts +141 -0
- package/dist/core/codec/index.d.ts +8 -2
- package/dist/core/codec/input-descriptor-decoder.d.ts +19 -0
- package/dist/core/codec/input-descriptor-encoder.d.ts +22 -0
- package/dist/core/codec/input-descriptors.d.ts +281 -0
- package/dist/core/codec/lifecycle-tracker.d.ts +10 -9
- package/dist/core/codec/output-descriptor-decoder.d.ts +29 -0
- package/dist/core/codec/output-descriptor-encoder.d.ts +31 -0
- package/dist/core/codec/output-descriptors.d.ts +237 -0
- package/dist/core/codec/types.d.ts +470 -119
- package/dist/core/codec/well-known-inputs.d.ts +52 -0
- package/dist/core/transport/agent-session.d.ts +10 -0
- package/dist/core/transport/agent-view.d.ts +296 -0
- package/dist/core/transport/client-session.d.ts +13 -0
- package/dist/core/transport/decode-fold.d.ts +55 -0
- package/dist/core/transport/headers.d.ts +121 -14
- package/dist/core/transport/index.d.ts +5 -6
- package/dist/core/transport/internal/bounded-map.d.ts +20 -0
- package/dist/core/transport/invocation.d.ts +74 -0
- package/dist/core/transport/load-history-pages.d.ts +71 -0
- package/dist/core/transport/load-history.d.ts +44 -0
- package/dist/core/transport/pipe-stream.d.ts +9 -9
- package/dist/core/transport/run-manager.d.ts +76 -0
- package/dist/core/transport/session-support.d.ts +55 -0
- package/dist/core/transport/tree.d.ts +523 -109
- package/dist/core/transport/types/agent.d.ts +375 -0
- package/dist/core/transport/types/client.d.ts +201 -0
- package/dist/core/transport/types/shared.d.ts +24 -0
- package/dist/core/transport/types/tree.d.ts +357 -0
- package/dist/core/transport/types/view.d.ts +249 -0
- package/dist/core/transport/types.d.ts +13 -553
- package/dist/core/transport/view.d.ts +390 -84
- package/dist/core/transport/wire-log.d.ts +102 -0
- package/dist/errors.d.ts +27 -10
- package/dist/index.d.ts +8 -9
- package/dist/logger.d.ts +12 -0
- package/dist/react/ably-ai-transport-react.js +1365 -1010
- package/dist/react/ably-ai-transport-react.js.map +1 -1
- package/dist/react/ably-ai-transport-react.umd.cjs +1 -1
- package/dist/react/ably-ai-transport-react.umd.cjs.map +1 -1
- package/dist/react/contexts/client-session-context.d.ts +37 -0
- package/dist/react/contexts/client-session-provider.d.ts +56 -0
- package/dist/react/create-session-hooks.d.ts +116 -0
- package/dist/react/index.d.ts +13 -12
- package/dist/react/internal/skipped-session.d.ts +8 -0
- package/dist/react/internal/use-resolved-session.d.ts +36 -0
- package/dist/react/use-ably-messages.d.ts +17 -14
- package/dist/react/use-client-session.d.ts +81 -0
- package/dist/react/use-create-view.d.ts +14 -13
- package/dist/react/use-tree.d.ts +30 -15
- package/dist/react/use-view.d.ts +81 -50
- package/dist/utils.d.ts +48 -71
- package/dist/vercel/ably-ai-transport-vercel.js +3257 -2499
- package/dist/vercel/ably-ai-transport-vercel.js.map +1 -1
- package/dist/vercel/ably-ai-transport-vercel.umd.cjs +1 -1
- package/dist/vercel/ably-ai-transport-vercel.umd.cjs.map +1 -1
- package/dist/vercel/codec/decode-lifecycle.d.ts +9 -0
- package/dist/vercel/codec/events.d.ts +50 -0
- package/dist/vercel/codec/fields.d.ts +44 -0
- package/dist/vercel/codec/fold-content.d.ts +16 -0
- package/dist/vercel/codec/fold-data.d.ts +16 -0
- package/dist/vercel/codec/fold-input.d.ts +67 -0
- package/dist/vercel/codec/fold-lifecycle.d.ts +16 -0
- package/dist/vercel/codec/fold-text.d.ts +16 -0
- package/dist/vercel/codec/fold-tool-input.d.ts +17 -0
- package/dist/vercel/codec/fold-tool-output.d.ts +16 -0
- package/dist/vercel/codec/index.d.ts +7 -20
- package/dist/vercel/codec/inputs.d.ts +11 -0
- package/dist/vercel/codec/outputs.d.ts +11 -0
- package/dist/vercel/codec/reducer-state.d.ts +121 -0
- package/dist/vercel/codec/reducer.d.ts +62 -0
- package/dist/vercel/codec/tool-transitions.d.ts +2 -8
- package/dist/vercel/codec/wire-data.d.ts +34 -0
- package/dist/vercel/index.d.ts +5 -5
- package/dist/vercel/react/ably-ai-transport-vercel-react.js +2859 -9705
- package/dist/vercel/react/ably-ai-transport-vercel-react.js.map +1 -1
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs +1 -45
- package/dist/vercel/react/ably-ai-transport-vercel-react.umd.cjs.map +1 -1
- package/dist/vercel/react/contexts/chat-transport-context.d.ts +9 -7
- package/dist/vercel/react/contexts/chat-transport-provider.d.ts +53 -41
- package/dist/vercel/react/index.d.ts +1 -2
- package/dist/vercel/react/use-chat-transport.d.ts +30 -26
- package/dist/vercel/react/use-message-sync.d.ts +17 -30
- package/dist/vercel/run-end-reason.d.ts +84 -0
- package/dist/vercel/tool-part.d.ts +21 -0
- package/dist/vercel/transport/chat-transport.d.ts +41 -24
- package/dist/vercel/transport/index.d.ts +24 -20
- package/dist/vercel/transport/run-output-stream.d.ts +54 -0
- package/dist/version.d.ts +2 -0
- package/package.json +31 -24
- package/src/constants.ts +124 -51
- package/src/core/agent.ts +92 -0
- package/src/core/channel-options.ts +89 -0
- package/src/core/codec/codec-event.ts +27 -0
- package/src/core/codec/decoder.ts +202 -105
- package/src/core/codec/define-codec.ts +432 -0
- package/src/core/codec/encoder.ts +114 -107
- package/src/core/codec/field-bag.ts +142 -0
- package/src/core/codec/fields.ts +193 -0
- package/src/core/codec/index.ts +56 -6
- package/src/core/codec/input-descriptor-decoder.ts +97 -0
- package/src/core/codec/input-descriptor-encoder.ts +150 -0
- package/src/core/codec/input-descriptors.ts +373 -0
- package/src/core/codec/lifecycle-tracker.ts +10 -9
- package/src/core/codec/output-descriptor-decoder.ts +139 -0
- package/src/core/codec/output-descriptor-encoder.ts +101 -0
- package/src/core/codec/output-descriptors.ts +307 -0
- package/src/core/codec/types.ts +505 -126
- package/src/core/codec/well-known-inputs.ts +96 -0
- package/src/core/transport/agent-session.ts +1085 -0
- package/src/core/transport/agent-view.ts +738 -0
- package/src/core/transport/client-session.ts +780 -0
- package/src/core/transport/decode-fold.ts +101 -0
- package/src/core/transport/headers.ts +234 -22
- package/src/core/transport/index.ts +27 -27
- package/src/core/transport/internal/bounded-map.ts +27 -0
- package/src/core/transport/invocation.ts +98 -0
- package/src/core/transport/load-history-pages.ts +220 -0
- package/src/core/transport/load-history.ts +271 -0
- package/src/core/transport/pipe-stream.ts +63 -39
- package/src/core/transport/run-manager.ts +243 -0
- package/src/core/transport/session-support.ts +96 -0
- package/src/core/transport/tree.ts +1293 -308
- package/src/core/transport/types/agent.ts +434 -0
- package/src/core/transport/types/client.ts +247 -0
- package/src/core/transport/types/shared.ts +27 -0
- package/src/core/transport/types/tree.ts +393 -0
- package/src/core/transport/types/view.ts +288 -0
- package/src/core/transport/types.ts +13 -706
- package/src/core/transport/view.ts +1229 -450
- package/src/core/transport/wire-log.ts +189 -0
- package/src/errors.ts +29 -9
- package/src/event-emitter.ts +3 -2
- package/src/index.ts +86 -42
- package/src/logger.ts +14 -1
- package/src/react/contexts/client-session-context.ts +41 -0
- package/src/react/contexts/client-session-provider.tsx +222 -0
- package/src/react/create-session-hooks.ts +141 -0
- package/src/react/index.ts +24 -13
- package/src/react/internal/skipped-session.ts +62 -0
- package/src/react/internal/use-resolved-session.ts +63 -0
- package/src/react/use-ably-messages.ts +32 -22
- package/src/react/use-client-session.ts +178 -0
- package/src/react/use-create-view.ts +33 -29
- package/src/react/use-tree.ts +61 -30
- package/src/react/use-view.ts +138 -96
- package/src/utils.ts +83 -131
- package/src/vercel/codec/decode-lifecycle.ts +70 -0
- package/src/vercel/codec/events.ts +85 -0
- package/src/vercel/codec/fields.ts +58 -0
- package/src/vercel/codec/fold-content.ts +54 -0
- package/src/vercel/codec/fold-data.ts +46 -0
- package/src/vercel/codec/fold-input.ts +255 -0
- package/src/vercel/codec/fold-lifecycle.ts +85 -0
- package/src/vercel/codec/fold-text.ts +55 -0
- package/src/vercel/codec/fold-tool-input.ts +86 -0
- package/src/vercel/codec/fold-tool-output.ts +79 -0
- package/src/vercel/codec/index.ts +28 -21
- package/src/vercel/codec/inputs.ts +116 -0
- package/src/vercel/codec/outputs.ts +207 -0
- package/src/vercel/codec/reducer-state.ts +169 -0
- package/src/vercel/codec/reducer.ts +191 -0
- package/src/vercel/codec/tool-transitions.ts +3 -14
- package/src/vercel/codec/wire-data.ts +64 -0
- package/src/vercel/index.ts +7 -19
- package/src/vercel/react/contexts/chat-transport-context.ts +8 -7
- package/src/vercel/react/contexts/chat-transport-provider.tsx +87 -59
- package/src/vercel/react/index.ts +3 -5
- package/src/vercel/react/use-chat-transport.ts +44 -66
- package/src/vercel/react/use-message-sync.ts +75 -39
- package/src/vercel/run-end-reason.ts +157 -0
- package/src/vercel/tool-part.ts +25 -0
- package/src/vercel/transport/chat-transport.ts +380 -98
- package/src/vercel/transport/index.ts +38 -37
- package/src/vercel/transport/run-output-stream.ts +169 -0
- package/src/version.ts +2 -0
- package/dist/core/transport/client-transport.d.ts +0 -10
- package/dist/core/transport/decode-history.d.ts +0 -43
- package/dist/core/transport/server-transport.d.ts +0 -7
- package/dist/core/transport/stream-router.d.ts +0 -29
- package/dist/core/transport/turn-manager.d.ts +0 -37
- package/dist/react/contexts/transport-context.d.ts +0 -31
- package/dist/react/contexts/transport-provider.d.ts +0 -49
- package/dist/react/create-transport-hooks.d.ts +0 -124
- package/dist/react/use-active-turns.d.ts +0 -12
- package/dist/react/use-client-transport.d.ts +0 -80
- package/dist/vercel/codec/accumulator.d.ts +0 -21
- package/dist/vercel/codec/decoder.d.ts +0 -22
- package/dist/vercel/codec/encoder.d.ts +0 -41
- package/dist/vercel/react/use-staged-add-tool-approval-response.d.ts +0 -30
- package/dist/vercel/tool-approvals.d.ts +0 -124
- package/dist/vercel/tool-events.d.ts +0 -26
- package/src/core/transport/client-transport.ts +0 -977
- package/src/core/transport/decode-history.ts +0 -485
- package/src/core/transport/server-transport.ts +0 -612
- package/src/core/transport/stream-router.ts +0 -136
- package/src/core/transport/turn-manager.ts +0 -165
- package/src/react/contexts/transport-context.ts +0 -37
- package/src/react/contexts/transport-provider.tsx +0 -164
- package/src/react/create-transport-hooks.ts +0 -144
- package/src/react/use-active-turns.ts +0 -72
- package/src/react/use-client-transport.ts +0 -197
- package/src/vercel/codec/accumulator.ts +0 -588
- package/src/vercel/codec/decoder.ts +0 -618
- package/src/vercel/codec/encoder.ts +0 -410
- package/src/vercel/react/use-staged-add-tool-approval-response.ts +0 -87
- package/src/vercel/tool-approvals.ts +0 -380
- package/src/vercel/tool-events.ts +0 -53
package/README.md
CHANGED
|
@@ -8,23 +8,23 @@ A durable transport layer between AI agents and users. Streams AI responses over
|
|
|
8
8
|
|
|
9
9
|
Most AI frameworks stream tokens over HTTP response bodies or SSE. That works until it doesn't: connections drop through corporate proxies, responses vanish on page refresh, and sessions are stuck on a single device or tab. Once an agent starts a long-running task, the user has no way to interrupt it, check if it's still running, or continue the conversation from another device. If a human needs to take over from the agent, the session context is lost.
|
|
10
10
|
|
|
11
|
-
Ably AI Transport replaces the HTTP stream with an Ably channel. The server publishes tokens to the channel as they arrive from the LLM; the response accumulates on the channel and persists, so partial responses survive disconnection. Any client can subscribe to the same channel from any device. Cancel signals,
|
|
11
|
+
Ably AI Transport replaces the HTTP stream with an Ably channel. The server publishes tokens to the channel as they arrive from the LLM; the response accumulates on the channel and persists, so partial responses survive disconnection. Any client can subscribe to the same channel from any device. Cancel signals, run lifecycle events, and conversation history all flow through the channel rather than depending on a single HTTP connection.
|
|
12
12
|
|
|
13
13
|
```mermaid
|
|
14
14
|
sequenceDiagram
|
|
15
15
|
participant U as User
|
|
16
|
-
participant
|
|
16
|
+
participant CS as Client Session
|
|
17
17
|
participant AC as Ably Channel
|
|
18
|
-
participant
|
|
18
|
+
participant AS as Agent Session
|
|
19
19
|
participant LLM
|
|
20
20
|
|
|
21
|
-
U->>
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
LLM-->>
|
|
25
|
-
|
|
26
|
-
AC->>
|
|
27
|
-
|
|
21
|
+
U->>CS: type message
|
|
22
|
+
CS->>AS: HTTP POST (messages)
|
|
23
|
+
AS->>LLM: prompt
|
|
24
|
+
LLM-->>AS: token stream
|
|
25
|
+
AS->>AC: publish chunks
|
|
26
|
+
AC->>CS: subscribe (decode)
|
|
27
|
+
CS->>U: render tokens
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
Ably AI Transport SDK is not an agent framework or orchestration layer - it works alongside whatever agent framework/model provider you choose, through a pluggable codec architecture (Vercel AI SDK supported now, more frameworks and models coming soon). It can be used in a serverless architecture (e.g. Next.js), with a durable execution framework (e.g. Temporal, Vercel Workflow DevKit) or in a traditional client-server architecture.
|
|
@@ -34,11 +34,13 @@ Ably AI Transport SDK is not an agent framework or orchestration layer - it work
|
|
|
34
34
|
- **Resumable streaming** - If a connection drops mid-response, client reconnects and picks up where it left off. The response persists on the channel, so nothing is lost.
|
|
35
35
|
- **Session continuity across surfaces** - The session belongs to the channel, not the connection. A user can change tab or device and pick up at the same point.
|
|
36
36
|
- **Multi-client sync** - Multiple users, agents, or operators subscribe to the same channel. Human-AI handover is a channel operation, not a session migration.
|
|
37
|
-
- **Cancellation** - Cancel signals travel over the Ably channel, not the HTTP connection, and the server
|
|
37
|
+
- **Cancellation** - Cancel signals travel over the Ably channel, not the HTTP connection, and the server run's `abortSignal` fires automatically.
|
|
38
38
|
- **Interruption** - Users send new messages while the AI is still responding, with composable primitives for cancel-and-resend or queue-until-complete.
|
|
39
|
-
- **Concurrent
|
|
39
|
+
- **Concurrent runs** - Multiple request-response cycles run in parallel on the same channel. Each run has its own stream and abort signal.
|
|
40
40
|
- **History** - The Ably channel is the conversation record. Clients hydrate from channel history on load - no separate database query needed.
|
|
41
41
|
- **Branching** - Regenerate or edit messages to fork the conversation. The SDK tracks parent/child relationships and exposes a navigable tree.
|
|
42
|
+
- **Presence** - The session channel carries Ably Presence. `session.presence` exposes it directly, and ably-js's presence hooks work inside the React providers - see which clients are connected to a session.
|
|
43
|
+
- **LiveObjects** - The session channel can carry Ably LiveObjects: synchronized shared state (maps, counters) alongside the conversation. `session.object` exposes it directly - opt in with the LiveObjects plugin and `channelModes`.
|
|
42
44
|
- **Framework-agnostic** - A codec interface decouples transport from the AI framework. Ships with a Vercel AI SDK codec; bring your own for any other stack.
|
|
43
45
|
|
|
44
46
|
### When you need this
|
|
@@ -68,7 +70,7 @@ npm install @ably/ai-transport ably ai
|
|
|
68
70
|
|
|
69
71
|
| Platform | Support |
|
|
70
72
|
| ------------- | -------------------------------------------------- |
|
|
71
|
-
| Node.js |
|
|
73
|
+
| Node.js | 22+ |
|
|
72
74
|
| Browsers | All major browsers (Chrome, Firefox, Edge, Safari) |
|
|
73
75
|
| TypeScript | Written in TypeScript, ships with types |
|
|
74
76
|
| React | 18+ and 19+ via dedicated hooks |
|
|
@@ -88,49 +90,35 @@ import { streamText, convertToModelMessages } from 'ai';
|
|
|
88
90
|
import type { UIMessage } from 'ai';
|
|
89
91
|
import { anthropic } from '@ai-sdk/anthropic';
|
|
90
92
|
import Ably from 'ably';
|
|
91
|
-
import {
|
|
92
|
-
import type
|
|
93
|
-
|
|
94
|
-
interface ChatRequestBody {
|
|
95
|
-
turnId: string;
|
|
96
|
-
clientId: string;
|
|
97
|
-
messages: TreeNode<UIMessage>[];
|
|
98
|
-
history?: TreeNode<UIMessage>[];
|
|
99
|
-
chatId: string;
|
|
100
|
-
forkOf?: string;
|
|
101
|
-
parent?: string | null;
|
|
102
|
-
}
|
|
93
|
+
import { createAgentSession } from '@ably/ai-transport/vercel';
|
|
94
|
+
import { Invocation, type InvocationData } from '@ably/ai-transport';
|
|
95
|
+
import type { UIMessageChunk } from 'ai';
|
|
103
96
|
|
|
104
97
|
const ably = new Ably.Realtime({ key: process.env.ABLY_API_KEY });
|
|
105
98
|
|
|
106
99
|
export async function POST(req: Request) {
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
const channel = ably.channels.get(chatId);
|
|
110
|
-
const transport = createServerTransport({ channel });
|
|
111
|
-
const turn = transport.newTurn({ turnId, clientId, parent, forkOf });
|
|
100
|
+
const data = (await req.json()) as InvocationData<UIMessageChunk, UIMessage>;
|
|
101
|
+
const invocation = Invocation.fromJSON(data);
|
|
112
102
|
|
|
113
|
-
|
|
103
|
+
const session = createAgentSession({ client: ably, channelName: invocation.sessionName });
|
|
104
|
+
await session.connect();
|
|
105
|
+
const run = session.createRun(invocation, { signal: req.signal });
|
|
114
106
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const historyMsgs = (history ?? []).map((h) => h.message);
|
|
120
|
-
const newMsgs = messages.map((m) => m.message);
|
|
107
|
+
await run.start();
|
|
108
|
+
await run.loadConversation();
|
|
121
109
|
|
|
122
110
|
const result = streamText({
|
|
123
111
|
model: anthropic('claude-sonnet-4-6'),
|
|
124
112
|
system: 'You are a helpful assistant.',
|
|
125
|
-
messages: await convertToModelMessages(
|
|
126
|
-
abortSignal:
|
|
113
|
+
messages: await convertToModelMessages(run.messages),
|
|
114
|
+
abortSignal: run.abortSignal,
|
|
127
115
|
});
|
|
128
116
|
|
|
129
117
|
// Stream the response over Ably in the background
|
|
130
118
|
after(async () => {
|
|
131
|
-
const { reason } = await
|
|
132
|
-
await
|
|
133
|
-
|
|
119
|
+
const { reason } = await run.pipe(result.toUIMessageStream());
|
|
120
|
+
await run.end({ reason });
|
|
121
|
+
session.close();
|
|
134
122
|
});
|
|
135
123
|
|
|
136
124
|
return new Response(null, { status: 200 });
|
|
@@ -143,25 +131,19 @@ export async function POST(req: Request) {
|
|
|
143
131
|
'use client';
|
|
144
132
|
|
|
145
133
|
import { useChat } from '@ai-sdk/react';
|
|
146
|
-
import {
|
|
147
|
-
ChatTransportProvider,
|
|
148
|
-
useChatTransport,
|
|
149
|
-
useMessageSync,
|
|
150
|
-
useActiveTurns,
|
|
151
|
-
useView,
|
|
152
|
-
} from '@ably/ai-transport/vercel/react';
|
|
134
|
+
import { ChatTransportProvider, useChatTransport, useMessageSync, useView } from '@ably/ai-transport/vercel/react';
|
|
153
135
|
|
|
154
136
|
function ChatInner({ chatId }: { chatId: string }) {
|
|
155
137
|
const { chatTransport } = useChatTransport();
|
|
156
138
|
|
|
157
|
-
const { messages, setMessages, sendMessage, stop } = useChat({
|
|
139
|
+
const { messages, setMessages, sendMessage, stop, status } = useChat({
|
|
158
140
|
id: chatId,
|
|
159
141
|
transport: chatTransport,
|
|
160
142
|
});
|
|
161
143
|
|
|
162
144
|
useMessageSync({ setMessages });
|
|
163
145
|
|
|
164
|
-
const
|
|
146
|
+
const isStreaming = status === 'submitted' || status === 'streaming';
|
|
165
147
|
useView({ limit: 30 });
|
|
166
148
|
|
|
167
149
|
return (
|
|
@@ -175,7 +157,7 @@ function ChatInner({ chatId }: { chatId: string }) {
|
|
|
175
157
|
sendMessage({ text: 'Hello' });
|
|
176
158
|
}}
|
|
177
159
|
>
|
|
178
|
-
{
|
|
160
|
+
{isStreaming ? (
|
|
179
161
|
<button
|
|
180
162
|
type="button"
|
|
181
163
|
onClick={stop}
|
|
@@ -190,12 +172,9 @@ function ChatInner({ chatId }: { chatId: string }) {
|
|
|
190
172
|
);
|
|
191
173
|
}
|
|
192
174
|
|
|
193
|
-
function Chat({ chatId
|
|
175
|
+
function Chat({ chatId }: { chatId: string }) {
|
|
194
176
|
return (
|
|
195
|
-
<ChatTransportProvider
|
|
196
|
-
channelName={chatId}
|
|
197
|
-
clientId={clientId}
|
|
198
|
-
>
|
|
177
|
+
<ChatTransportProvider channelName={chatId}>
|
|
199
178
|
<ChatInner chatId={chatId} />
|
|
200
179
|
</ChatTransportProvider>
|
|
201
180
|
);
|
|
@@ -240,20 +219,20 @@ The core entry point is framework-agnostic. Bring your own `Codec` to map betwee
|
|
|
240
219
|
### Client
|
|
241
220
|
|
|
242
221
|
```typescript
|
|
243
|
-
import {
|
|
222
|
+
import { createClientSession } from '@ably/ai-transport';
|
|
244
223
|
import { myCodec } from './my-codec';
|
|
245
224
|
|
|
246
|
-
const
|
|
247
|
-
|
|
225
|
+
const session = createClientSession({
|
|
226
|
+
client: ably, // Ably.Realtime
|
|
227
|
+
channelName: 'ai:demo',
|
|
248
228
|
codec: myCodec,
|
|
249
|
-
clientId: 'user-123',
|
|
250
|
-
api: '/api/chat',
|
|
251
229
|
});
|
|
230
|
+
await session.connect();
|
|
252
231
|
|
|
253
|
-
const
|
|
232
|
+
const run = await session.view.send(messages);
|
|
254
233
|
|
|
255
234
|
// Read the stream
|
|
256
|
-
const reader =
|
|
235
|
+
const reader = run.stream.getReader();
|
|
257
236
|
while (true) {
|
|
258
237
|
const { done, value } = await reader.read();
|
|
259
238
|
if (done) break;
|
|
@@ -261,21 +240,22 @@ while (true) {
|
|
|
261
240
|
}
|
|
262
241
|
```
|
|
263
242
|
|
|
264
|
-
###
|
|
243
|
+
### Agent (server-side)
|
|
265
244
|
|
|
266
245
|
```typescript
|
|
267
|
-
import {
|
|
246
|
+
import { createAgentSession, Invocation } from '@ably/ai-transport';
|
|
268
247
|
import { myCodec } from './my-codec';
|
|
269
248
|
|
|
270
|
-
const
|
|
271
|
-
|
|
249
|
+
const session = createAgentSession({ client: ably, channelName: 'ai:demo', codec: myCodec });
|
|
250
|
+
await session.connect();
|
|
251
|
+
const run = session.createRun(invocation);
|
|
272
252
|
|
|
273
|
-
await
|
|
274
|
-
await
|
|
253
|
+
await run.start();
|
|
254
|
+
await run.loadConversation();
|
|
275
255
|
|
|
276
|
-
const { reason } = await
|
|
277
|
-
await
|
|
278
|
-
|
|
256
|
+
const { reason } = await run.pipe(aiStream);
|
|
257
|
+
await run.end({ reason });
|
|
258
|
+
session.close();
|
|
279
259
|
```
|
|
280
260
|
|
|
281
261
|
---
|
|
@@ -291,15 +271,14 @@ transport.close();
|
|
|
291
271
|
|
|
292
272
|
### React hooks
|
|
293
273
|
|
|
294
|
-
| Hook
|
|
295
|
-
|
|
|
296
|
-
| `
|
|
297
|
-
| `useView`
|
|
298
|
-
| `
|
|
299
|
-
| `
|
|
300
|
-
| `
|
|
301
|
-
| `
|
|
302
|
-
| `useMessageSync` | `/vercel/react` | Sync transport state with `useChat`'s `setMessages` |
|
|
274
|
+
| Hook | Entry point | Description |
|
|
275
|
+
| ------------------ | --------------- | ------------------------------------------------- |
|
|
276
|
+
| `useClientSession` | `/react` | Read a client session from the nearest provider |
|
|
277
|
+
| `useView` | `/react` | Subscribe to messages with history loading |
|
|
278
|
+
| `useTree` | `/react` | Navigate branches in a forked conversation |
|
|
279
|
+
| `useAblyMessages` | `/react` | Access raw Ably messages |
|
|
280
|
+
| `useChatTransport` | `/vercel/react` | Wrap session for Vercel's `useChat` |
|
|
281
|
+
| `useMessageSync` | `/vercel/react` | Sync session state with `useChat`'s `setMessages` |
|
|
303
282
|
|
|
304
283
|
---
|
|
305
284
|
|
|
@@ -315,17 +294,18 @@ Two mechanisms cover different failure modes:
|
|
|
315
294
|
### Cancellation
|
|
316
295
|
|
|
317
296
|
```typescript
|
|
318
|
-
// Client: cancel
|
|
319
|
-
await
|
|
297
|
+
// Client: cancel a specific run by id
|
|
298
|
+
await session.cancel('run-abc');
|
|
320
299
|
|
|
321
|
-
//
|
|
322
|
-
await
|
|
300
|
+
// Or via the ActiveRun returned by send / regenerate / edit
|
|
301
|
+
const run = await view.send(codec.createUserMessage(userMsg));
|
|
302
|
+
await run.cancel();
|
|
323
303
|
|
|
324
|
-
//
|
|
304
|
+
// Agent: the run's abortSignal fires automatically
|
|
325
305
|
const result = streamText({
|
|
326
306
|
model: anthropic('claude-sonnet-4-6'),
|
|
327
307
|
messages,
|
|
328
|
-
abortSignal:
|
|
308
|
+
abortSignal: run.abortSignal, // Aborted when client cancels
|
|
329
309
|
});
|
|
330
310
|
```
|
|
331
311
|
|
|
@@ -335,15 +315,15 @@ Regenerate or edit messages to create forks in the conversation tree. The SDK tr
|
|
|
335
315
|
|
|
336
316
|
```typescript
|
|
337
317
|
// Regenerate the last assistant message
|
|
338
|
-
const
|
|
318
|
+
const run = await session.view.regenerate(assistantMessageId);
|
|
339
319
|
|
|
340
320
|
// Edit a user message and regenerate from that point
|
|
341
|
-
const
|
|
321
|
+
const run = await session.view.edit(userMessageId, [newMessage]);
|
|
342
322
|
|
|
343
323
|
// Navigate branches
|
|
344
|
-
const tree =
|
|
324
|
+
const tree = session.tree;
|
|
345
325
|
const siblings = tree.getSiblings(messageId);
|
|
346
|
-
|
|
326
|
+
session.view.select(messageId, 1); // Switch to second branch
|
|
347
327
|
```
|
|
348
328
|
|
|
349
329
|
### History and hydration
|
|
@@ -351,9 +331,9 @@ tree.select(messageId, 1); // Switch to second branch
|
|
|
351
331
|
Load previous conversation state when a client joins or returns to a session.
|
|
352
332
|
|
|
353
333
|
```typescript
|
|
354
|
-
const view =
|
|
334
|
+
const view = session.view;
|
|
355
335
|
await view.loadOlder(50);
|
|
356
|
-
// view.
|
|
336
|
+
// view.getMessages() returns the flat message list loaded so far
|
|
357
337
|
|
|
358
338
|
// Load more older messages
|
|
359
339
|
await view.loadOlder(50);
|
|
@@ -362,15 +342,15 @@ await view.loadOlder(50);
|
|
|
362
342
|
### Events
|
|
363
343
|
|
|
364
344
|
```typescript
|
|
365
|
-
|
|
366
|
-
console.log(
|
|
345
|
+
session.view.on('update', () => {
|
|
346
|
+
console.log(session.view.getMessages());
|
|
367
347
|
});
|
|
368
348
|
|
|
369
|
-
|
|
370
|
-
console.log(event.
|
|
349
|
+
session.tree.on('run', (event) => {
|
|
350
|
+
console.log(event.runId, event.type); // 'ai-run-start' | 'ai-run-end'
|
|
371
351
|
});
|
|
372
352
|
|
|
373
|
-
|
|
353
|
+
session.on('error', (error) => {
|
|
374
354
|
console.error(error.code, error.message);
|
|
375
355
|
});
|
|
376
356
|
```
|
|
@@ -381,10 +361,10 @@ transport.on('error', (error) => {
|
|
|
381
361
|
|
|
382
362
|
Detailed documentation lives in the [`docs/`](./docs/) directory:
|
|
383
363
|
|
|
384
|
-
- **[Concepts](./docs/concepts/)** - [
|
|
385
|
-
- **[Get started](./docs/get-started/)** - [Vercel AI SDK with useChat](./docs/get-started/vercel-use-chat.md), [Vercel AI SDK with
|
|
364
|
+
- **[Concepts](./docs/concepts/)** - [Sessions](./docs/concepts/sessions.md), [Runs](./docs/concepts/runs.md)
|
|
365
|
+
- **[Get started](./docs/get-started/)** - [Vercel AI SDK with useChat](./docs/get-started/vercel-use-chat.md), [Vercel AI SDK with useClientSession](./docs/get-started/vercel-use-client-session.md)
|
|
386
366
|
- **[Frameworks](./docs/frameworks/)** - [Vercel AI SDK](./docs/frameworks/vercel-ai-sdk.md)
|
|
387
|
-
- **[Features](./docs/features/)** - [Streaming](./docs/features/streaming.md), [Cancellation](./docs/features/cancel.md), [Interruption](./docs/features/interruption.md), [Optimistic updates](./docs/features/optimistic-updates.md), [History](./docs/features/history.md), [Branching](./docs/features/branching.md), [Multi-client sync](./docs/features/multi-client.md), [Concurrent
|
|
367
|
+
- **[Features](./docs/features/)** - [Streaming](./docs/features/streaming.md), [Cancellation](./docs/features/cancel.md), [Interruption](./docs/features/interruption.md), [Optimistic updates](./docs/features/optimistic-updates.md), [History](./docs/features/history.md), [Branching](./docs/features/branching.md), [Multi-client sync](./docs/features/multi-client.md), [Concurrent runs](./docs/features/concurrent-runs.md), [Presence](./docs/features/presence.md), [LiveObjects](./docs/features/liveobjects.md)
|
|
388
368
|
- **[Reference](./docs/reference/)** - [React hooks](./docs/reference/react-hooks.md), [Error codes](./docs/reference/error-codes.md)
|
|
389
369
|
- **[Internals](./docs/internals/)** - Architecture details for contributors
|
|
390
370
|
|
|
@@ -395,20 +375,22 @@ Detailed documentation lives in the [`docs/`](./docs/) directory:
|
|
|
395
375
|
Working demo applications live in the [`demo/`](./demo/) directory:
|
|
396
376
|
|
|
397
377
|
- **[`demo/vercel/react/use-chat/`](./demo/vercel/react/use-chat/)** - Vercel AI SDK with `useChat` integration
|
|
398
|
-
- **[`demo/vercel/react/use-client-
|
|
378
|
+
- **[`demo/vercel/react/use-client-session/`](./demo/vercel/react/use-client-session/)** - Vercel AI SDK with direct `useClientSession` hooks
|
|
399
379
|
|
|
400
380
|
---
|
|
401
381
|
|
|
402
382
|
## Development
|
|
403
383
|
|
|
384
|
+
This repository uses [pnpm](https://pnpm.io/). Enable Corepack once (`corepack enable`) to pick up the pinned version automatically.
|
|
385
|
+
|
|
404
386
|
```bash
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
387
|
+
pnpm install
|
|
388
|
+
pnpm run build # Build all entry points (ESM + UMD/CJS + .d.ts)
|
|
389
|
+
pnpm run typecheck # Type check
|
|
390
|
+
pnpm run lint # Lint
|
|
391
|
+
pnpm test # Unit tests (mocks only)
|
|
392
|
+
pnpm run test:integration # Integration tests (needs ABLY_API_KEY)
|
|
393
|
+
pnpm run precommit # format:check + lint + typecheck
|
|
412
394
|
```
|
|
413
395
|
|
|
414
396
|
### Project structure
|
|
@@ -417,7 +399,7 @@ npm run precommit # format:check + lint + typecheck
|
|
|
417
399
|
src/
|
|
418
400
|
├── core/ # Generic transport and codec (no framework deps)
|
|
419
401
|
│ ├── codec/ # Codec interfaces and core encoder/decoder
|
|
420
|
-
│ └── transport/ #
|
|
402
|
+
│ └── transport/ # ClientSession, AgentSession, Tree
|
|
421
403
|
├── react/ # React hooks for any codec
|
|
422
404
|
├── vercel/ # Vercel AI SDK codec and transport adapters
|
|
423
405
|
│ ├── codec/ # UIMessageCodec
|