@octavus/docs 0.0.8 → 1.0.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 +127 -0
- package/content/01-getting-started/01-introduction.md +3 -3
- package/content/01-getting-started/02-quickstart.md +40 -17
- package/content/02-server-sdk/01-overview.md +54 -11
- package/content/02-server-sdk/02-sessions.md +166 -15
- package/content/02-server-sdk/03-tools.md +21 -21
- package/content/02-server-sdk/04-streaming.md +50 -20
- package/content/02-server-sdk/05-cli.md +247 -0
- package/content/03-client-sdk/01-overview.md +65 -35
- package/content/03-client-sdk/02-messages.md +116 -8
- package/content/03-client-sdk/03-streaming.md +36 -17
- package/content/03-client-sdk/04-execution-blocks.md +8 -12
- package/content/03-client-sdk/05-socket-transport.md +161 -45
- package/content/03-client-sdk/06-http-transport.md +48 -24
- package/content/03-client-sdk/07-structured-output.md +412 -0
- package/content/03-client-sdk/08-file-uploads.md +473 -0
- package/content/03-client-sdk/09-error-handling.md +274 -0
- package/content/04-protocol/01-overview.md +25 -14
- package/content/04-protocol/02-input-resources.md +35 -35
- package/content/04-protocol/03-triggers.md +9 -11
- package/content/04-protocol/04-tools.md +72 -29
- package/content/04-protocol/05-skills.md +304 -0
- package/content/04-protocol/06-handlers.md +304 -0
- package/content/04-protocol/07-agent-config.md +334 -0
- package/content/04-protocol/08-provider-options.md +294 -0
- package/content/04-protocol/09-skills-advanced.md +439 -0
- package/content/04-protocol/10-types.md +719 -0
- package/content/05-api-reference/01-overview.md +20 -21
- package/content/05-api-reference/02-sessions.md +192 -37
- package/content/05-api-reference/03-agents.md +25 -37
- package/content/06-examples/01-overview.md +6 -4
- package/content/06-examples/02-nextjs-chat.md +28 -18
- package/content/06-examples/03-socket-chat.md +53 -30
- package/content/06-examples/_meta.md +0 -1
- package/dist/chunk-WJ2W3DUC.js +663 -0
- package/dist/chunk-WJ2W3DUC.js.map +1 -0
- package/dist/content.js +1 -1
- package/dist/docs.json +106 -34
- package/dist/index.js +1 -1
- package/dist/search-index.json +1 -1
- package/dist/search.js +1 -1
- package/dist/search.js.map +1 -1
- package/dist/sections.json +106 -34
- package/package.json +12 -2
- package/content/04-protocol/05-handlers.md +0 -251
- package/content/04-protocol/06-agent-config.md +0 -209
- package/dist/chunk-232K4EME.js +0 -439
- package/dist/chunk-232K4EME.js.map +0 -1
- package/dist/chunk-2JDZLMS3.js +0 -439
- package/dist/chunk-2JDZLMS3.js.map +0 -1
- package/dist/chunk-2YMRODFE.js +0 -421
- package/dist/chunk-2YMRODFE.js.map +0 -1
- package/dist/chunk-2ZBPX5QB.js +0 -421
- package/dist/chunk-2ZBPX5QB.js.map +0 -1
- package/dist/chunk-3PIIST4D.js +0 -421
- package/dist/chunk-3PIIST4D.js.map +0 -1
- package/dist/chunk-42JETGDO.js +0 -421
- package/dist/chunk-42JETGDO.js.map +0 -1
- package/dist/chunk-4WWUKU4V.js +0 -421
- package/dist/chunk-4WWUKU4V.js.map +0 -1
- package/dist/chunk-5M7DS4DF.js +0 -519
- package/dist/chunk-5M7DS4DF.js.map +0 -1
- package/dist/chunk-6JQ3OMGF.js +0 -421
- package/dist/chunk-6JQ3OMGF.js.map +0 -1
- package/dist/chunk-7AOWCJHW.js +0 -421
- package/dist/chunk-7AOWCJHW.js.map +0 -1
- package/dist/chunk-7AS4ST73.js +0 -421
- package/dist/chunk-7AS4ST73.js.map +0 -1
- package/dist/chunk-7F5WOCIL.js +0 -421
- package/dist/chunk-7F5WOCIL.js.map +0 -1
- package/dist/chunk-7FPUAWSX.js +0 -421
- package/dist/chunk-7FPUAWSX.js.map +0 -1
- package/dist/chunk-APASMJBS.js +0 -421
- package/dist/chunk-APASMJBS.js.map +0 -1
- package/dist/chunk-BCEV3WV2.js +0 -421
- package/dist/chunk-BCEV3WV2.js.map +0 -1
- package/dist/chunk-CHGY4G27.js +0 -421
- package/dist/chunk-CHGY4G27.js.map +0 -1
- package/dist/chunk-CI7JDWKU.js +0 -421
- package/dist/chunk-CI7JDWKU.js.map +0 -1
- package/dist/chunk-CVFWWRL7.js +0 -421
- package/dist/chunk-CVFWWRL7.js.map +0 -1
- package/dist/chunk-EPDM2NIJ.js +0 -421
- package/dist/chunk-EPDM2NIJ.js.map +0 -1
- package/dist/chunk-ESGSYVGK.js +0 -421
- package/dist/chunk-ESGSYVGK.js.map +0 -1
- package/dist/chunk-GDCTM2SV.js +0 -421
- package/dist/chunk-GDCTM2SV.js.map +0 -1
- package/dist/chunk-GJ6FMIPD.js +0 -421
- package/dist/chunk-GJ6FMIPD.js.map +0 -1
- package/dist/chunk-H6JGSSAJ.js +0 -519
- package/dist/chunk-H6JGSSAJ.js.map +0 -1
- package/dist/chunk-IKQHGGUZ.js +0 -421
- package/dist/chunk-IKQHGGUZ.js.map +0 -1
- package/dist/chunk-IUKE3XDN.js +0 -421
- package/dist/chunk-IUKE3XDN.js.map +0 -1
- package/dist/chunk-J26MLMLN.js +0 -421
- package/dist/chunk-J26MLMLN.js.map +0 -1
- package/dist/chunk-J7BMB3ZW.js +0 -421
- package/dist/chunk-J7BMB3ZW.js.map +0 -1
- package/dist/chunk-JCBQRD5N.js +0 -421
- package/dist/chunk-JCBQRD5N.js.map +0 -1
- package/dist/chunk-JOB6YWEF.js +0 -421
- package/dist/chunk-JOB6YWEF.js.map +0 -1
- package/dist/chunk-JZRABTHU.js +0 -519
- package/dist/chunk-JZRABTHU.js.map +0 -1
- package/dist/chunk-K3GFQUMC.js +0 -421
- package/dist/chunk-K3GFQUMC.js.map +0 -1
- package/dist/chunk-LWYMRXBF.js +0 -421
- package/dist/chunk-LWYMRXBF.js.map +0 -1
- package/dist/chunk-M2R2NDPR.js +0 -421
- package/dist/chunk-M2R2NDPR.js.map +0 -1
- package/dist/chunk-MA3P7WCA.js +0 -421
- package/dist/chunk-MA3P7WCA.js.map +0 -1
- package/dist/chunk-MDMRCS4W.mjs +0 -421
- package/dist/chunk-MDMRCS4W.mjs.map +0 -1
- package/dist/chunk-MJXTA2R6.js +0 -421
- package/dist/chunk-MJXTA2R6.js.map +0 -1
- package/dist/chunk-NFVJQNDP.js +0 -421
- package/dist/chunk-NFVJQNDP.js.map +0 -1
- package/dist/chunk-O5TLYMQP.js +0 -421
- package/dist/chunk-O5TLYMQP.js.map +0 -1
- package/dist/chunk-OECAPVSX.js +0 -439
- package/dist/chunk-OECAPVSX.js.map +0 -1
- package/dist/chunk-OL5QDJ42.js +0 -483
- package/dist/chunk-OL5QDJ42.js.map +0 -1
- package/dist/chunk-PMOVVTHO.js +0 -519
- package/dist/chunk-PMOVVTHO.js.map +0 -1
- package/dist/chunk-QCHDPR2D.js +0 -421
- package/dist/chunk-QCHDPR2D.js.map +0 -1
- package/dist/chunk-R5MTVABN.js +0 -439
- package/dist/chunk-R5MTVABN.js.map +0 -1
- package/dist/chunk-RJ4H4YVA.js +0 -519
- package/dist/chunk-RJ4H4YVA.js.map +0 -1
- package/dist/chunk-S5U4IWCR.js +0 -439
- package/dist/chunk-S5U4IWCR.js.map +0 -1
- package/dist/chunk-SCKIOGKI.js +0 -421
- package/dist/chunk-SCKIOGKI.js.map +0 -1
- package/dist/chunk-TGJSIJYP.js +0 -421
- package/dist/chunk-TGJSIJYP.js.map +0 -1
- package/dist/chunk-TQZRBMU7.js +0 -421
- package/dist/chunk-TQZRBMU7.js.map +0 -1
- package/dist/chunk-TRL4RSEO.js +0 -421
- package/dist/chunk-TRL4RSEO.js.map +0 -1
- package/dist/chunk-TWUMRHQ7.js +0 -421
- package/dist/chunk-TWUMRHQ7.js.map +0 -1
- package/dist/chunk-UCJE36LL.js +0 -519
- package/dist/chunk-UCJE36LL.js.map +0 -1
- package/dist/chunk-VCASA6KL.js +0 -421
- package/dist/chunk-VCASA6KL.js.map +0 -1
- package/dist/chunk-VWPQ6ORV.js +0 -421
- package/dist/chunk-VWPQ6ORV.js.map +0 -1
- package/dist/chunk-WPXKIHLT.js +0 -421
- package/dist/chunk-WPXKIHLT.js.map +0 -1
- package/dist/chunk-WUNFFJ32.js +0 -421
- package/dist/chunk-WUNFFJ32.js.map +0 -1
- package/dist/chunk-WW7TRC7S.js +0 -519
- package/dist/chunk-WW7TRC7S.js.map +0 -1
- package/dist/chunk-XVSMRXBJ.js +0 -421
- package/dist/chunk-XVSMRXBJ.js.map +0 -1
- package/dist/chunk-YPPXXV3I.js +0 -421
- package/dist/chunk-YPPXXV3I.js.map +0 -1
- package/dist/chunk-ZKZVV4OQ.js +0 -421
- package/dist/chunk-ZKZVV4OQ.js.map +0 -1
- package/dist/chunk-ZOFEX73I.js +0 -421
- package/dist/chunk-ZOFEX73I.js.map +0 -1
- package/dist/content.mjs +0 -17
- package/dist/content.mjs.map +0 -1
- package/dist/index.mjs +0 -11
- package/dist/index.mjs.map +0 -1
- package/dist/search.mjs +0 -30
- package/dist/search.mjs.map +0 -1
- package/dist/types-BltYGlWI.d.ts +0 -36
|
@@ -7,10 +7,10 @@ description: Introduction to the Octavus Client SDKs for building chat interface
|
|
|
7
7
|
|
|
8
8
|
Octavus provides two packages for frontend integration:
|
|
9
9
|
|
|
10
|
-
| Package
|
|
11
|
-
|
|
12
|
-
| `@octavus/react`
|
|
13
|
-
| `@octavus/client-sdk` | Framework-agnostic core
|
|
10
|
+
| Package | Purpose | Use When |
|
|
11
|
+
| --------------------- | ------------------------ | ----------------------------------------------------- |
|
|
12
|
+
| `@octavus/react` | React hooks and bindings | Building React applications |
|
|
13
|
+
| `@octavus/client-sdk` | Framework-agnostic core | Using Vue, Svelte, vanilla JS, or custom integrations |
|
|
14
14
|
|
|
15
15
|
**Most users should install `@octavus/react`** — it includes everything from `@octavus/client-sdk` plus React-specific hooks.
|
|
16
16
|
|
|
@@ -36,9 +36,9 @@ npm install @octavus/client-sdk
|
|
|
36
36
|
|
|
37
37
|
The Client SDK uses a **transport abstraction** to handle communication with your backend. This gives you flexibility in how events are delivered:
|
|
38
38
|
|
|
39
|
-
| Transport
|
|
40
|
-
|
|
41
|
-
| `createHttpTransport`
|
|
39
|
+
| Transport | Use Case | Docs |
|
|
40
|
+
| ----------------------- | -------------------------------------------- | ----------------------------------------------------- |
|
|
41
|
+
| `createHttpTransport` | HTTP/SSE (Next.js, Express, etc.) | [HTTP Transport](/docs/client-sdk/http-transport) |
|
|
42
42
|
| `createSocketTransport` | WebSocket, SockJS, or other socket protocols | [Socket Transport](/docs/client-sdk/socket-transport) |
|
|
43
43
|
|
|
44
44
|
When the transport changes (e.g., when `sessionId` changes), the `useOctavusChat` hook automatically reinitializes with the new transport.
|
|
@@ -71,11 +71,7 @@ function Chat({ sessionId }: { sessionId: string }) {
|
|
|
71
71
|
const { messages, status, send } = useOctavusChat({ transport });
|
|
72
72
|
|
|
73
73
|
const sendMessage = async (text: string) => {
|
|
74
|
-
await send(
|
|
75
|
-
'user-message',
|
|
76
|
-
{ USER_MESSAGE: text },
|
|
77
|
-
{ userMessage: { content: text } },
|
|
78
|
-
);
|
|
74
|
+
await send('user-message', { USER_MESSAGE: text }, { userMessage: { content: text } });
|
|
79
75
|
};
|
|
80
76
|
|
|
81
77
|
return (
|
|
@@ -127,11 +123,7 @@ const unsubscribe = chat.subscribe(() => {
|
|
|
127
123
|
});
|
|
128
124
|
|
|
129
125
|
// Send a message
|
|
130
|
-
await chat.send(
|
|
131
|
-
'user-message',
|
|
132
|
-
{ USER_MESSAGE: 'Hello' },
|
|
133
|
-
{ userMessage: { content: 'Hello' } },
|
|
134
|
-
);
|
|
126
|
+
await chat.send('user-message', { USER_MESSAGE: 'Hello' }, { userMessage: { content: 'Hello' } });
|
|
135
127
|
|
|
136
128
|
// Cleanup when done
|
|
137
129
|
unsubscribe();
|
|
@@ -147,11 +139,7 @@ The `send` function handles both user message display and agent triggering in on
|
|
|
147
139
|
const { send } = useOctavusChat({ transport });
|
|
148
140
|
|
|
149
141
|
// Add user message to UI and trigger agent
|
|
150
|
-
await send(
|
|
151
|
-
'user-message',
|
|
152
|
-
{ USER_MESSAGE: text },
|
|
153
|
-
{ userMessage: { content: text } },
|
|
154
|
-
);
|
|
142
|
+
await send('user-message', { USER_MESSAGE: text }, { userMessage: { content: text } });
|
|
155
143
|
|
|
156
144
|
// Trigger without adding a user message (e.g., button click)
|
|
157
145
|
await send('request-human');
|
|
@@ -167,10 +155,10 @@ const { messages } = useOctavusChat({ transport });
|
|
|
167
155
|
// Each message has typed parts
|
|
168
156
|
message.parts.map((part) => {
|
|
169
157
|
switch (part.type) {
|
|
170
|
-
case 'text':
|
|
171
|
-
case 'reasoning':
|
|
172
|
-
case 'tool-call':
|
|
173
|
-
case 'operation':
|
|
158
|
+
case 'text': // Text content
|
|
159
|
+
case 'reasoning': // Extended reasoning/thinking
|
|
160
|
+
case 'tool-call': // Tool execution
|
|
161
|
+
case 'operation': // Internal operations (set-resource, etc.)
|
|
174
162
|
}
|
|
175
163
|
});
|
|
176
164
|
```
|
|
@@ -203,32 +191,53 @@ interface OctavusChatOptions {
|
|
|
203
191
|
// Required: Transport for streaming events
|
|
204
192
|
transport: Transport;
|
|
205
193
|
|
|
194
|
+
// Optional: Function to request upload URLs for file uploads
|
|
195
|
+
requestUploadUrls?: (
|
|
196
|
+
files: { filename: string; mediaType: string; size: number }[],
|
|
197
|
+
) => Promise<UploadUrlsResponse>;
|
|
198
|
+
|
|
206
199
|
// Optional: Pre-populate with existing messages (session restore)
|
|
207
200
|
initialMessages?: UIMessage[];
|
|
208
201
|
|
|
209
202
|
// Optional: Callbacks
|
|
210
|
-
onError?: (error:
|
|
203
|
+
onError?: (error: OctavusError) => void; // Structured error with type, source, retryable
|
|
211
204
|
onFinish?: () => void;
|
|
205
|
+
onStop?: () => void; // Called when user stops generation
|
|
212
206
|
onResourceUpdate?: (name: string, value: unknown) => void;
|
|
213
207
|
}
|
|
214
208
|
|
|
215
209
|
interface UseOctavusChatReturn {
|
|
216
210
|
// State
|
|
217
211
|
messages: UIMessage[];
|
|
218
|
-
status: ChatStatus;
|
|
219
|
-
error:
|
|
212
|
+
status: ChatStatus; // 'idle' | 'streaming' | 'error'
|
|
213
|
+
error: OctavusError | null; // Structured error with type, source, retryable
|
|
214
|
+
|
|
215
|
+
// Connection (socket transport only - undefined for HTTP)
|
|
216
|
+
connectionState: ConnectionState | undefined; // 'disconnected' | 'connecting' | 'connected' | 'error'
|
|
217
|
+
connectionError: Error | undefined;
|
|
220
218
|
|
|
221
219
|
// Actions
|
|
222
220
|
send: (
|
|
223
221
|
triggerName: string,
|
|
224
222
|
input?: Record<string, unknown>,
|
|
225
|
-
options?: { userMessage?: UserMessageInput }
|
|
223
|
+
options?: { userMessage?: UserMessageInput },
|
|
226
224
|
) => Promise<void>;
|
|
227
225
|
stop: () => void;
|
|
226
|
+
|
|
227
|
+
// Connection management (socket transport only - undefined for HTTP)
|
|
228
|
+
connect: (() => Promise<void>) | undefined;
|
|
229
|
+
disconnect: (() => void) | undefined;
|
|
230
|
+
|
|
231
|
+
// File uploads (requires requestUploadUrls)
|
|
232
|
+
uploadFiles: (
|
|
233
|
+
files: FileList | File[],
|
|
234
|
+
onProgress?: (fileIndex: number, progress: number) => void,
|
|
235
|
+
) => Promise<FileReference[]>;
|
|
228
236
|
}
|
|
229
237
|
|
|
230
238
|
interface UserMessageInput {
|
|
231
|
-
content
|
|
239
|
+
content?: string;
|
|
240
|
+
files?: FileList | File[] | FileReference[];
|
|
232
241
|
}
|
|
233
242
|
```
|
|
234
243
|
|
|
@@ -242,11 +251,12 @@ Creates an HTTP/SSE transport using native `fetch()`:
|
|
|
242
251
|
import { createHttpTransport } from '@octavus/react';
|
|
243
252
|
|
|
244
253
|
const transport = createHttpTransport({
|
|
245
|
-
triggerRequest: (triggerName, input) =>
|
|
254
|
+
triggerRequest: (triggerName, input, options) =>
|
|
246
255
|
fetch('/api/trigger', {
|
|
247
256
|
method: 'POST',
|
|
248
257
|
headers: { 'Content-Type': 'application/json' },
|
|
249
258
|
body: JSON.stringify({ sessionId, triggerName, input }),
|
|
259
|
+
signal: options?.signal,
|
|
250
260
|
}),
|
|
251
261
|
});
|
|
252
262
|
```
|
|
@@ -268,6 +278,24 @@ const transport = createSocketTransport({
|
|
|
268
278
|
});
|
|
269
279
|
```
|
|
270
280
|
|
|
281
|
+
Socket transport provides additional connection management:
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
// Access connection state directly
|
|
285
|
+
transport.connectionState; // 'disconnected' | 'connecting' | 'connected' | 'error'
|
|
286
|
+
|
|
287
|
+
// Subscribe to state changes
|
|
288
|
+
transport.onConnectionStateChange((state, error) => {
|
|
289
|
+
/* ... */
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
// Eager connection (instead of lazy on first send)
|
|
293
|
+
await transport.connect();
|
|
294
|
+
|
|
295
|
+
// Manual disconnect
|
|
296
|
+
transport.disconnect();
|
|
297
|
+
```
|
|
298
|
+
|
|
271
299
|
For detailed WebSocket/SockJS usage including custom events, reconnection patterns, and server-side implementation, see [Socket Transport](/docs/client-sdk/socket-transport).
|
|
272
300
|
|
|
273
301
|
## Class Reference (Framework-Agnostic)
|
|
@@ -281,18 +309,18 @@ class OctavusChat {
|
|
|
281
309
|
// State (read-only)
|
|
282
310
|
readonly messages: UIMessage[];
|
|
283
311
|
readonly status: ChatStatus;
|
|
284
|
-
readonly error:
|
|
312
|
+
readonly error: OctavusError | null; // Structured error
|
|
285
313
|
|
|
286
314
|
// Actions
|
|
287
315
|
send(
|
|
288
316
|
triggerName: string,
|
|
289
317
|
input?: Record<string, unknown>,
|
|
290
|
-
options?: { userMessage?: UserMessageInput }
|
|
318
|
+
options?: { userMessage?: UserMessageInput },
|
|
291
319
|
): Promise<void>;
|
|
292
320
|
stop(): void;
|
|
293
321
|
|
|
294
322
|
// Subscription
|
|
295
|
-
subscribe(callback: () => void): () => void;
|
|
323
|
+
subscribe(callback: () => void): () => void; // Returns unsubscribe function
|
|
296
324
|
}
|
|
297
325
|
```
|
|
298
326
|
|
|
@@ -303,4 +331,6 @@ class OctavusChat {
|
|
|
303
331
|
- [Messages](/docs/client-sdk/messages) — Working with message state
|
|
304
332
|
- [Streaming](/docs/client-sdk/streaming) — Building streaming UIs
|
|
305
333
|
- [Operations](/docs/client-sdk/execution-blocks) — Showing agent progress
|
|
334
|
+
- [Error Handling](/docs/client-sdk/error-handling) — Handling errors with type guards
|
|
335
|
+
- [File Uploads](/docs/client-sdk/file-uploads) — Uploading images and documents
|
|
306
336
|
- [Examples](/docs/examples/overview) — Complete working examples
|
|
@@ -28,14 +28,17 @@ type UIMessagePart =
|
|
|
28
28
|
| UITextPart
|
|
29
29
|
| UIReasoningPart
|
|
30
30
|
| UIToolCallPart
|
|
31
|
-
| UIOperationPart
|
|
31
|
+
| UIOperationPart
|
|
32
|
+
| UISourcePart
|
|
33
|
+
| UIFilePart
|
|
34
|
+
| UIObjectPart;
|
|
32
35
|
|
|
33
36
|
// Text content
|
|
34
37
|
interface UITextPart {
|
|
35
38
|
type: 'text';
|
|
36
39
|
text: string;
|
|
37
40
|
status: 'streaming' | 'done';
|
|
38
|
-
thread?: string;
|
|
41
|
+
thread?: string; // For named threads (e.g., "summary")
|
|
39
42
|
}
|
|
40
43
|
|
|
41
44
|
// Extended reasoning/thinking
|
|
@@ -51,7 +54,7 @@ interface UIToolCallPart {
|
|
|
51
54
|
type: 'tool-call';
|
|
52
55
|
toolCallId: string;
|
|
53
56
|
toolName: string;
|
|
54
|
-
displayName?: string;
|
|
57
|
+
displayName?: string; // Human-readable name
|
|
55
58
|
args: Record<string, unknown>;
|
|
56
59
|
result?: unknown;
|
|
57
60
|
error?: string;
|
|
@@ -68,6 +71,42 @@ interface UIOperationPart {
|
|
|
68
71
|
status: 'running' | 'done';
|
|
69
72
|
thread?: string;
|
|
70
73
|
}
|
|
74
|
+
|
|
75
|
+
// Source references (from web search, document processing)
|
|
76
|
+
interface UISourcePart {
|
|
77
|
+
type: 'source';
|
|
78
|
+
sourceType: 'url' | 'document';
|
|
79
|
+
id: string;
|
|
80
|
+
url?: string; // For URL sources
|
|
81
|
+
title?: string;
|
|
82
|
+
mediaType?: string; // For document sources
|
|
83
|
+
filename?: string;
|
|
84
|
+
thread?: string;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Generated files (from image generation, skills, code execution)
|
|
88
|
+
interface UIFilePart {
|
|
89
|
+
type: 'file';
|
|
90
|
+
id: string;
|
|
91
|
+
mediaType: string; // MIME type (e.g., 'image/png', 'image/webp')
|
|
92
|
+
url: string; // Download/display URL (presigned S3 URL)
|
|
93
|
+
filename?: string;
|
|
94
|
+
size?: number;
|
|
95
|
+
toolCallId?: string; // Present if from a tool call
|
|
96
|
+
thread?: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Structured output (when responseType is used)
|
|
100
|
+
interface UIObjectPart {
|
|
101
|
+
type: 'object';
|
|
102
|
+
id: string;
|
|
103
|
+
typeName: string; // Type name from protocol (e.g., "ChatResponse")
|
|
104
|
+
partial?: unknown; // Partial object while streaming
|
|
105
|
+
object?: unknown; // Final object when done
|
|
106
|
+
status: 'streaming' | 'done' | 'error';
|
|
107
|
+
error?: string;
|
|
108
|
+
thread?: string;
|
|
109
|
+
}
|
|
71
110
|
```
|
|
72
111
|
|
|
73
112
|
## Sending Messages
|
|
@@ -94,11 +133,7 @@ function Chat({ sessionId }: { sessionId: string }) {
|
|
|
94
133
|
|
|
95
134
|
async function handleSend(text: string) {
|
|
96
135
|
// Add user message to UI and trigger agent
|
|
97
|
-
await send(
|
|
98
|
-
'user-message',
|
|
99
|
-
{ USER_MESSAGE: text },
|
|
100
|
-
{ userMessage: { content: text } },
|
|
101
|
-
);
|
|
136
|
+
await send('user-message', { USER_MESSAGE: text }, { userMessage: { content: text } });
|
|
102
137
|
}
|
|
103
138
|
|
|
104
139
|
// ...
|
|
@@ -106,10 +141,56 @@ function Chat({ sessionId }: { sessionId: string }) {
|
|
|
106
141
|
```
|
|
107
142
|
|
|
108
143
|
The `send` function:
|
|
144
|
+
|
|
109
145
|
1. Adds the user message to the UI immediately (if `userMessage` is provided)
|
|
110
146
|
2. Triggers the agent with the specified trigger name and input
|
|
111
147
|
3. Streams the assistant's response back
|
|
112
148
|
|
|
149
|
+
### Message Content Types
|
|
150
|
+
|
|
151
|
+
The `content` field in `userMessage` accepts both strings and objects:
|
|
152
|
+
|
|
153
|
+
```tsx
|
|
154
|
+
// Text content → creates a text part
|
|
155
|
+
await send('user-message', { USER_MESSAGE: text }, { userMessage: { content: text } });
|
|
156
|
+
|
|
157
|
+
// Object content → creates an object part (uses `type` field as typeName)
|
|
158
|
+
const selection = { type: 'product_selection', productId: 'abc123', action: 'select' };
|
|
159
|
+
await send('user-message', { USER_INPUT: selection }, { userMessage: { content: selection } });
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
When passing an object as `content`:
|
|
163
|
+
|
|
164
|
+
- The SDK creates a `UIObjectPart` instead of a `UITextPart`
|
|
165
|
+
- The object's `type` field is used as the `typeName` (defaults to `'object'` if not present)
|
|
166
|
+
- This is useful for rich UI interactions like product selections, quick replies, etc.
|
|
167
|
+
|
|
168
|
+
### Sending with Files
|
|
169
|
+
|
|
170
|
+
Include file attachments with messages:
|
|
171
|
+
|
|
172
|
+
```tsx
|
|
173
|
+
import type { FileReference } from '@octavus/react';
|
|
174
|
+
|
|
175
|
+
async function handleSend(text: string, files?: FileReference[]) {
|
|
176
|
+
await send(
|
|
177
|
+
'user-message',
|
|
178
|
+
{
|
|
179
|
+
USER_MESSAGE: text,
|
|
180
|
+
FILES: files, // Array of FileReference
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
userMessage: {
|
|
184
|
+
content: text,
|
|
185
|
+
files: files, // Shows files in user message bubble
|
|
186
|
+
},
|
|
187
|
+
},
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
See [File Uploads](/docs/client-sdk/file-uploads) for complete upload flow.
|
|
193
|
+
|
|
113
194
|
## Rendering Messages
|
|
114
195
|
|
|
115
196
|
### Basic Rendering
|
|
@@ -180,6 +261,33 @@ function PartRenderer({ part }: { part: UIMessagePart }) {
|
|
|
180
261
|
</div>
|
|
181
262
|
);
|
|
182
263
|
|
|
264
|
+
case 'source':
|
|
265
|
+
return (
|
|
266
|
+
<div className="text-blue-500 text-sm">📎 {part.title || part.url || part.filename}</div>
|
|
267
|
+
);
|
|
268
|
+
|
|
269
|
+
case 'file':
|
|
270
|
+
// Render images inline, other files as download links
|
|
271
|
+
if (part.mediaType.startsWith('image/')) {
|
|
272
|
+
return (
|
|
273
|
+
<img
|
|
274
|
+
src={part.url}
|
|
275
|
+
alt={part.filename || 'Generated image'}
|
|
276
|
+
className="max-w-full rounded-lg"
|
|
277
|
+
/>
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
return (
|
|
281
|
+
<a href={part.url} className="text-blue-500 text-sm underline">
|
|
282
|
+
📄 {part.filename || 'Download file'}
|
|
283
|
+
</a>
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
case 'object':
|
|
287
|
+
// For structured output, render custom UI based on typeName
|
|
288
|
+
// See Structured Output guide for more details
|
|
289
|
+
return <ObjectPartRenderer part={part} />;
|
|
290
|
+
|
|
183
291
|
default:
|
|
184
292
|
return null;
|
|
185
293
|
}
|
|
@@ -85,9 +85,7 @@ function ReasoningPart({ part }: { part: UIReasoningPart }) {
|
|
|
85
85
|
{expanded ? '▼' : '▶'}
|
|
86
86
|
</button>
|
|
87
87
|
|
|
88
|
-
{expanded &&
|
|
89
|
-
<pre className="mt-2 text-sm text-gray-600">{part.text}</pre>
|
|
90
|
-
)}
|
|
88
|
+
{expanded && <pre className="mt-2 text-sm text-gray-600">{part.text}</pre>}
|
|
91
89
|
</div>
|
|
92
90
|
);
|
|
93
91
|
}
|
|
@@ -117,9 +115,10 @@ function ToolCallPart({ part }: { part: UIToolCallPart }) {
|
|
|
117
115
|
)}
|
|
118
116
|
|
|
119
117
|
{/* Show error if failed */}
|
|
120
|
-
{part.status === 'error' &&
|
|
121
|
-
|
|
122
|
-
|
|
118
|
+
{part.status === 'error' && <p className="mt-2 text-red-500 text-sm">{part.error}</p>}
|
|
119
|
+
|
|
120
|
+
{/* Show cancelled state */}
|
|
121
|
+
{part.status === 'cancelled' && <p className="mt-2 text-amber-500 text-sm">Cancelled</p>}
|
|
123
122
|
</div>
|
|
124
123
|
);
|
|
125
124
|
}
|
|
@@ -134,6 +133,8 @@ function StatusBadge({ status }: { status: UIToolCallPart['status'] }) {
|
|
|
134
133
|
return <span className="text-green-500">✓</span>;
|
|
135
134
|
case 'error':
|
|
136
135
|
return <span className="text-red-500">✗</span>;
|
|
136
|
+
case 'cancelled':
|
|
137
|
+
return <span className="text-amber-500">◼</span>;
|
|
137
138
|
}
|
|
138
139
|
}
|
|
139
140
|
```
|
|
@@ -156,19 +157,34 @@ function StatusIndicator({ status }: { status: ChatStatus }) {
|
|
|
156
157
|
## Handling Completion
|
|
157
158
|
|
|
158
159
|
```tsx
|
|
160
|
+
import { isRateLimitError, type OctavusError } from '@octavus/react';
|
|
161
|
+
|
|
159
162
|
useOctavusChat({
|
|
160
163
|
transport,
|
|
161
164
|
onFinish: () => {
|
|
162
|
-
console.log('Stream completed');
|
|
165
|
+
console.log('Stream completed successfully');
|
|
163
166
|
// Scroll to bottom, play sound, etc.
|
|
164
167
|
},
|
|
165
|
-
|
|
166
|
-
console.
|
|
167
|
-
|
|
168
|
+
onStop: () => {
|
|
169
|
+
console.log('User stopped generation');
|
|
170
|
+
// Handle stop - content is preserved
|
|
171
|
+
},
|
|
172
|
+
onError: (error: OctavusError) => {
|
|
173
|
+
console.error('Stream error:', error.errorType, error.message);
|
|
174
|
+
|
|
175
|
+
if (isRateLimitError(error)) {
|
|
176
|
+
toast.error(`Rate limited. Retry in ${error.retryAfter}s`);
|
|
177
|
+
} else {
|
|
178
|
+
toast.error('Failed to get response');
|
|
179
|
+
}
|
|
168
180
|
},
|
|
169
181
|
});
|
|
170
182
|
```
|
|
171
183
|
|
|
184
|
+
See [Error Handling](/docs/client-sdk/error-handling) for comprehensive error handling patterns.
|
|
185
|
+
|
|
186
|
+
````
|
|
187
|
+
|
|
172
188
|
## Stop Function
|
|
173
189
|
|
|
174
190
|
Stop the current stream and finalize any partial message:
|
|
@@ -182,12 +198,17 @@ const { status, stop } = useOctavusChat({ transport });
|
|
|
182
198
|
Stop generating
|
|
183
199
|
</button>
|
|
184
200
|
)}
|
|
185
|
-
|
|
201
|
+
````
|
|
186
202
|
|
|
187
203
|
When `stop()` is called:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
204
|
+
|
|
205
|
+
1. The HTTP request is aborted (requires `signal` in transport)
|
|
206
|
+
2. Any partial text/reasoning is finalized with `done` status
|
|
207
|
+
3. In-progress tool calls are marked as `cancelled`
|
|
208
|
+
4. The `onStop` callback is invoked
|
|
209
|
+
5. Status changes to `idle`
|
|
210
|
+
|
|
211
|
+
Partial content is preserved in the message, so users don't lose what was already generated.
|
|
191
212
|
|
|
192
213
|
## Named Thread Content
|
|
193
214
|
|
|
@@ -211,9 +232,7 @@ function MessageBubble({ message }: { message: UIMessage }) {
|
|
|
211
232
|
{/* Named thread content (e.g., summarization) */}
|
|
212
233
|
{otherParts.length > 0 && (
|
|
213
234
|
<div className="bg-amber-50 p-3 rounded mt-4 border border-amber-200">
|
|
214
|
-
<div className="text-amber-600 font-medium mb-2">
|
|
215
|
-
Background processing
|
|
216
|
-
</div>
|
|
235
|
+
<div className="text-amber-600 font-medium mb-2">Background processing</div>
|
|
217
236
|
{otherParts.map((part, i) => (
|
|
218
237
|
<PartRenderer key={i} part={part} />
|
|
219
238
|
))}
|
|
@@ -13,10 +13,10 @@ Operations represent internal agent activities like setting resources or seriali
|
|
|
13
13
|
interface UIOperationPart {
|
|
14
14
|
type: 'operation';
|
|
15
15
|
operationId: string;
|
|
16
|
-
name: string;
|
|
17
|
-
operationType: string;
|
|
16
|
+
name: string; // Human-readable name
|
|
17
|
+
operationType: string; // e.g., 'set-resource', 'serialize-thread'
|
|
18
18
|
status: 'running' | 'done';
|
|
19
|
-
thread?: string;
|
|
19
|
+
thread?: string; // For named threads
|
|
20
20
|
}
|
|
21
21
|
```
|
|
22
22
|
|
|
@@ -76,9 +76,9 @@ function PartRenderer({ part }: { part: UIMessagePart }) {
|
|
|
76
76
|
|
|
77
77
|
## Common Operation Types
|
|
78
78
|
|
|
79
|
-
| Type
|
|
80
|
-
|
|
81
|
-
| `set-resource`
|
|
79
|
+
| Type | Description |
|
|
80
|
+
| ------------------ | ---------------------------------- |
|
|
81
|
+
| `set-resource` | Updating a resource value |
|
|
82
82
|
| `serialize-thread` | Converting thread messages to text |
|
|
83
83
|
|
|
84
84
|
## Example: Progress During Escalation
|
|
@@ -87,9 +87,7 @@ When a user clicks "Talk to Human", multiple operations may occur:
|
|
|
87
87
|
|
|
88
88
|
```tsx
|
|
89
89
|
function EscalationProgress({ message }: { message: UIMessage }) {
|
|
90
|
-
const operations = message.parts.filter(
|
|
91
|
-
(p): p is UIOperationPart => p.type === 'operation'
|
|
92
|
-
);
|
|
90
|
+
const operations = message.parts.filter((p): p is UIOperationPart => p.type === 'operation');
|
|
93
91
|
|
|
94
92
|
return (
|
|
95
93
|
<div className="space-y-2">
|
|
@@ -123,9 +121,7 @@ Operations can belong to named threads. Use the `thread` property to identify th
|
|
|
123
121
|
function OperationCard({ operation }: { operation: UIOperationPart }) {
|
|
124
122
|
return (
|
|
125
123
|
<div className="flex items-center gap-2 text-sm">
|
|
126
|
-
{operation.thread &&
|
|
127
|
-
<span className="text-amber-500">[{operation.thread}]</span>
|
|
128
|
-
)}
|
|
124
|
+
{operation.thread && <span className="text-amber-500">[{operation.thread}]</span>}
|
|
129
125
|
<span>{operation.name}</span>
|
|
130
126
|
{operation.status === 'done' && <span className="text-green-500">✓</span>}
|
|
131
127
|
</div>
|