@octavus/docs 0.0.1
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/LICENSE +22 -0
- package/content/01-getting-started/01-introduction.md +93 -0
- package/content/01-getting-started/02-quickstart.md +268 -0
- package/content/01-getting-started/_meta.md +4 -0
- package/content/02-server-sdk/01-overview.md +133 -0
- package/content/02-server-sdk/02-sessions.md +164 -0
- package/content/02-server-sdk/03-tools.md +242 -0
- package/content/02-server-sdk/04-streaming.md +211 -0
- package/content/02-server-sdk/_meta.md +4 -0
- package/content/03-client-sdk/01-overview.md +148 -0
- package/content/03-client-sdk/02-messages.md +207 -0
- package/content/03-client-sdk/03-streaming.md +215 -0
- package/content/03-client-sdk/04-execution-blocks.md +231 -0
- package/content/03-client-sdk/_meta.md +4 -0
- package/content/04-protocol/01-overview.md +142 -0
- package/content/04-protocol/02-input-resources.md +200 -0
- package/content/04-protocol/03-triggers.md +209 -0
- package/content/04-protocol/04-tools.md +255 -0
- package/content/04-protocol/05-handlers.md +251 -0
- package/content/04-protocol/06-agent-config.md +215 -0
- package/content/04-protocol/_meta.md +4 -0
- package/content/05-api-reference/01-overview.md +129 -0
- package/content/05-api-reference/02-sessions.md +232 -0
- package/content/05-api-reference/03-agents.md +222 -0
- package/content/05-api-reference/_meta.md +4 -0
- package/dist/chunk-2YMRODFE.js +421 -0
- package/dist/chunk-2YMRODFE.js.map +1 -0
- package/dist/chunk-6JQ3OMGF.js +421 -0
- package/dist/chunk-6JQ3OMGF.js.map +1 -0
- package/dist/chunk-ESGSYVGK.js +421 -0
- package/dist/chunk-ESGSYVGK.js.map +1 -0
- package/dist/chunk-GDCTM2SV.js +421 -0
- package/dist/chunk-GDCTM2SV.js.map +1 -0
- package/dist/chunk-J26MLMLN.js +421 -0
- package/dist/chunk-J26MLMLN.js.map +1 -0
- package/dist/chunk-LWYMRXBF.js +421 -0
- package/dist/chunk-LWYMRXBF.js.map +1 -0
- package/dist/chunk-NFVJQNDP.js +421 -0
- package/dist/chunk-NFVJQNDP.js.map +1 -0
- package/dist/chunk-SCKIOGKI.js +421 -0
- package/dist/chunk-SCKIOGKI.js.map +1 -0
- package/dist/chunk-VWPQ6ORV.js +421 -0
- package/dist/chunk-VWPQ6ORV.js.map +1 -0
- package/dist/chunk-WUNFFJ32.js +421 -0
- package/dist/chunk-WUNFFJ32.js.map +1 -0
- package/dist/chunk-XVSMRXBJ.js +421 -0
- package/dist/chunk-XVSMRXBJ.js.map +1 -0
- package/dist/content.d.ts +37 -0
- package/dist/content.js +17 -0
- package/dist/content.js.map +1 -0
- package/dist/docs.json +173 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/search-index.json +1 -0
- package/dist/search.d.ts +19 -0
- package/dist/search.js +30 -0
- package/dist/search.js.map +1 -0
- package/dist/sections.json +213 -0
- package/dist/types-BNRNBPDE.d.ts +28 -0
- package/dist/types-BltYGlWI.d.ts +36 -0
- package/package.json +52 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Messages
|
|
3
|
+
description: Working with message state in the Client SDK.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Messages
|
|
7
|
+
|
|
8
|
+
Messages represent the conversation history. The Client SDK tracks messages automatically and provides structured access to their content.
|
|
9
|
+
|
|
10
|
+
## Message Structure
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
interface Message {
|
|
14
|
+
id: string;
|
|
15
|
+
role: 'user' | 'assistant' | 'system';
|
|
16
|
+
content: string;
|
|
17
|
+
parts?: MessagePart[];
|
|
18
|
+
toolCalls?: ToolCallWithDescription[];
|
|
19
|
+
thinking?: string;
|
|
20
|
+
visible?: boolean;
|
|
21
|
+
createdAt: Date;
|
|
22
|
+
}
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### Message Parts
|
|
26
|
+
|
|
27
|
+
For rich content display, use the `parts` array which preserves content ordering:
|
|
28
|
+
|
|
29
|
+
```typescript
|
|
30
|
+
interface MessagePart {
|
|
31
|
+
type: 'text' | 'thinking' | 'tool-call';
|
|
32
|
+
visible: boolean;
|
|
33
|
+
content?: string; // For text and thinking
|
|
34
|
+
toolCall?: ToolCallInfo; // For tool-call
|
|
35
|
+
thread?: string; // For non-main thread content
|
|
36
|
+
}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Adding User Messages
|
|
40
|
+
|
|
41
|
+
```tsx
|
|
42
|
+
const { addUserMessage, triggerAction } = useOctavusChat({...});
|
|
43
|
+
|
|
44
|
+
async function sendMessage(text: string) {
|
|
45
|
+
// 1. Add message to UI immediately
|
|
46
|
+
addUserMessage(text);
|
|
47
|
+
|
|
48
|
+
// 2. Trigger the agent
|
|
49
|
+
await triggerAction('user-message', { USER_MESSAGE: text });
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**Important**: `addUserMessage` only updates UI state. You must call `triggerAction` to actually send the message to the agent.
|
|
54
|
+
|
|
55
|
+
## Rendering Messages
|
|
56
|
+
|
|
57
|
+
### Basic Rendering
|
|
58
|
+
|
|
59
|
+
```tsx
|
|
60
|
+
function MessageList({ messages }: { messages: Message[] }) {
|
|
61
|
+
return (
|
|
62
|
+
<div className="space-y-4">
|
|
63
|
+
{messages.map((msg) => (
|
|
64
|
+
<div
|
|
65
|
+
key={msg.id}
|
|
66
|
+
className={msg.role === 'user' ? 'text-right' : 'text-left'}
|
|
67
|
+
>
|
|
68
|
+
<div className="inline-block p-3 rounded-lg">
|
|
69
|
+
{msg.content}
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
))}
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Rich Rendering with Parts
|
|
79
|
+
|
|
80
|
+
For messages with tool calls and thinking, render parts in order:
|
|
81
|
+
|
|
82
|
+
```tsx
|
|
83
|
+
function MessageContent({ message }: { message: Message }) {
|
|
84
|
+
if (!message.parts) {
|
|
85
|
+
return <p>{message.content}</p>;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<div className="space-y-2">
|
|
90
|
+
{message.parts.map((part, i) => {
|
|
91
|
+
if (!part.visible) return null;
|
|
92
|
+
|
|
93
|
+
switch (part.type) {
|
|
94
|
+
case 'text':
|
|
95
|
+
return <p key={i}>{part.content}</p>;
|
|
96
|
+
|
|
97
|
+
case 'thinking':
|
|
98
|
+
return (
|
|
99
|
+
<details key={i} className="text-gray-500">
|
|
100
|
+
<summary>Thinking...</summary>
|
|
101
|
+
<pre className="text-sm">{part.content}</pre>
|
|
102
|
+
</details>
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
case 'tool-call':
|
|
106
|
+
return (
|
|
107
|
+
<div key={i} className="bg-gray-100 p-2 rounded text-sm">
|
|
108
|
+
🔧 {part.toolCall?.name}
|
|
109
|
+
{part.toolCall?.status === 'completed' && ' ✓'}
|
|
110
|
+
</div>
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
default:
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
})}
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Tool Calls in Messages
|
|
123
|
+
|
|
124
|
+
Tool calls include status, arguments, and results:
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
interface ToolCallInfo {
|
|
128
|
+
id: string;
|
|
129
|
+
name: string;
|
|
130
|
+
description?: string;
|
|
131
|
+
arguments: Record<string, unknown>;
|
|
132
|
+
status: 'pending' | 'in_progress' | 'completed' | 'error';
|
|
133
|
+
result?: unknown;
|
|
134
|
+
error?: string;
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Rendering Tool Calls
|
|
139
|
+
|
|
140
|
+
```tsx
|
|
141
|
+
function ToolCallCard({ toolCall }: { toolCall: ToolCallInfo }) {
|
|
142
|
+
return (
|
|
143
|
+
<div className="border rounded p-3">
|
|
144
|
+
<div className="flex items-center gap-2">
|
|
145
|
+
<span className="text-lg">🔧</span>
|
|
146
|
+
<span className="font-medium">{toolCall.description || toolCall.name}</span>
|
|
147
|
+
<StatusBadge status={toolCall.status} />
|
|
148
|
+
</div>
|
|
149
|
+
|
|
150
|
+
{toolCall.status === 'completed' && toolCall.result && (
|
|
151
|
+
<pre className="mt-2 text-xs bg-gray-50 p-2 rounded">
|
|
152
|
+
{JSON.stringify(toolCall.result, null, 2)}
|
|
153
|
+
</pre>
|
|
154
|
+
)}
|
|
155
|
+
|
|
156
|
+
{toolCall.status === 'error' && (
|
|
157
|
+
<p className="mt-2 text-red-500 text-sm">{toolCall.error}</p>
|
|
158
|
+
)}
|
|
159
|
+
</div>
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Session Restore
|
|
165
|
+
|
|
166
|
+
When restoring a session, pass existing messages:
|
|
167
|
+
|
|
168
|
+
```tsx
|
|
169
|
+
// Fetch session state from your backend
|
|
170
|
+
const sessionState = await fetchSession(sessionId);
|
|
171
|
+
|
|
172
|
+
// Convert ChatMessage[] to Message[]
|
|
173
|
+
const initialMessages = sessionState.messages
|
|
174
|
+
.filter(msg => msg.visible !== false)
|
|
175
|
+
.map(msg => ({
|
|
176
|
+
id: msg.id,
|
|
177
|
+
role: msg.role,
|
|
178
|
+
content: msg.content,
|
|
179
|
+
parts: msg.parts,
|
|
180
|
+
toolCalls: msg.toolCalls,
|
|
181
|
+
thinking: msg.reasoning,
|
|
182
|
+
createdAt: new Date(msg.createdAt),
|
|
183
|
+
}));
|
|
184
|
+
|
|
185
|
+
// Pass to hook
|
|
186
|
+
const { messages } = useOctavusChat({
|
|
187
|
+
initialMessages,
|
|
188
|
+
onTrigger: ...
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Message Callbacks
|
|
193
|
+
|
|
194
|
+
Get notified when messages are added:
|
|
195
|
+
|
|
196
|
+
```tsx
|
|
197
|
+
useOctavusChat({
|
|
198
|
+
onTrigger: ...,
|
|
199
|
+
onMessage: (message) => {
|
|
200
|
+
console.log('New message:', message.role, message.content);
|
|
201
|
+
|
|
202
|
+
// Analytics, logging, etc.
|
|
203
|
+
trackMessage(message);
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Streaming
|
|
3
|
+
description: Building streaming UIs with the Client SDK.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Streaming
|
|
7
|
+
|
|
8
|
+
The Client SDK provides real-time access to streaming content, enabling responsive UIs that update as the agent generates responses.
|
|
9
|
+
|
|
10
|
+
## Streaming State
|
|
11
|
+
|
|
12
|
+
```tsx
|
|
13
|
+
const {
|
|
14
|
+
streamingText, // Current visible text being streamed
|
|
15
|
+
streamingParts, // Structured parts being streamed
|
|
16
|
+
thinkingText, // Current thinking content
|
|
17
|
+
status, // 'idle' | 'loading' | 'streaming' | 'error'
|
|
18
|
+
isLoading, // true during loading or streaming
|
|
19
|
+
} = useOctavusChat({...});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Basic Streaming UI
|
|
23
|
+
|
|
24
|
+
```tsx
|
|
25
|
+
function Chat() {
|
|
26
|
+
const { messages, streamingText, isLoading } = useOctavusChat({...});
|
|
27
|
+
|
|
28
|
+
return (
|
|
29
|
+
<div>
|
|
30
|
+
{/* Completed messages */}
|
|
31
|
+
{messages.map((msg) => (
|
|
32
|
+
<div key={msg.id}>{msg.content}</div>
|
|
33
|
+
))}
|
|
34
|
+
|
|
35
|
+
{/* Currently streaming */}
|
|
36
|
+
{streamingText && (
|
|
37
|
+
<div className="animate-pulse">
|
|
38
|
+
{streamingText}
|
|
39
|
+
<span className="inline-block w-2 h-4 bg-gray-400 ml-1" />
|
|
40
|
+
</div>
|
|
41
|
+
)}
|
|
42
|
+
</div>
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Streaming Parts
|
|
48
|
+
|
|
49
|
+
For rich streaming UIs, use `streamingParts`:
|
|
50
|
+
|
|
51
|
+
```tsx
|
|
52
|
+
function StreamingContent() {
|
|
53
|
+
const { streamingParts } = useOctavusChat({...});
|
|
54
|
+
|
|
55
|
+
return (
|
|
56
|
+
<div>
|
|
57
|
+
{streamingParts.map((part, i) => {
|
|
58
|
+
switch (part.type) {
|
|
59
|
+
case 'text':
|
|
60
|
+
return <span key={i}>{part.content}</span>;
|
|
61
|
+
|
|
62
|
+
case 'thinking':
|
|
63
|
+
return (
|
|
64
|
+
<div key={i} className="text-gray-500 italic">
|
|
65
|
+
💭 {part.content}
|
|
66
|
+
</div>
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
case 'tool-call':
|
|
70
|
+
return (
|
|
71
|
+
<div key={i} className="flex items-center gap-2">
|
|
72
|
+
<Spinner />
|
|
73
|
+
<span>{part.toolCall?.description}</span>
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
77
|
+
})}
|
|
78
|
+
</div>
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Thinking Indicator
|
|
84
|
+
|
|
85
|
+
Show when the model is "thinking" (extended reasoning):
|
|
86
|
+
|
|
87
|
+
```tsx
|
|
88
|
+
function ThinkingIndicator() {
|
|
89
|
+
const { thinkingText, status } = useOctavusChat({...});
|
|
90
|
+
|
|
91
|
+
if (!thinkingText || status !== 'streaming') {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return (
|
|
96
|
+
<div className="bg-purple-50 p-3 rounded-lg">
|
|
97
|
+
<div className="flex items-center gap-2 text-purple-600">
|
|
98
|
+
<Brain className="w-4 h-4 animate-pulse" />
|
|
99
|
+
<span className="font-medium">Thinking...</span>
|
|
100
|
+
</div>
|
|
101
|
+
<p className="mt-2 text-sm text-gray-600 line-clamp-3">
|
|
102
|
+
{thinkingText}
|
|
103
|
+
</p>
|
|
104
|
+
</div>
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Status Indicator
|
|
110
|
+
|
|
111
|
+
```tsx
|
|
112
|
+
function StatusIndicator() {
|
|
113
|
+
const { status } = useOctavusChat({...});
|
|
114
|
+
|
|
115
|
+
switch (status) {
|
|
116
|
+
case 'idle':
|
|
117
|
+
return null;
|
|
118
|
+
case 'loading':
|
|
119
|
+
return <div>Starting...</div>;
|
|
120
|
+
case 'streaming':
|
|
121
|
+
return <div>Agent is responding...</div>;
|
|
122
|
+
case 'error':
|
|
123
|
+
return <div className="text-red-500">Something went wrong</div>;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Handling Stream Completion
|
|
129
|
+
|
|
130
|
+
```tsx
|
|
131
|
+
useOctavusChat({
|
|
132
|
+
onTrigger: ...,
|
|
133
|
+
onDone: () => {
|
|
134
|
+
console.log('Stream completed');
|
|
135
|
+
// Scroll to bottom, play sound, etc.
|
|
136
|
+
},
|
|
137
|
+
onError: (error) => {
|
|
138
|
+
console.error('Stream error:', error);
|
|
139
|
+
toast.error('Failed to get response');
|
|
140
|
+
},
|
|
141
|
+
});
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Streaming with Tool Calls
|
|
145
|
+
|
|
146
|
+
When tools are called, streaming continues after tool execution:
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
1. User sends message
|
|
150
|
+
2. streamingText starts filling: "Let me look that up..."
|
|
151
|
+
3. Tool call starts (visible in streamingParts)
|
|
152
|
+
4. Tool executes (handled by server-sdk)
|
|
153
|
+
5. streamingText continues: "I found your account..."
|
|
154
|
+
6. Stream completes, message finalized
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
```tsx
|
|
158
|
+
function ChatWithTools() {
|
|
159
|
+
const { messages, streamingText, streamingParts } = useOctavusChat({...});
|
|
160
|
+
|
|
161
|
+
// Find active tool call
|
|
162
|
+
const activeToolCall = streamingParts.find(
|
|
163
|
+
p => p.type === 'tool-call' && p.toolCall?.status === 'in_progress'
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
return (
|
|
167
|
+
<div>
|
|
168
|
+
{messages.map(msg => <Message key={msg.id} message={msg} />)}
|
|
169
|
+
|
|
170
|
+
{/* Show streaming text */}
|
|
171
|
+
{streamingText && <p>{streamingText}</p>}
|
|
172
|
+
|
|
173
|
+
{/* Show active tool */}
|
|
174
|
+
{activeToolCall && (
|
|
175
|
+
<div className="flex items-center gap-2 text-blue-600">
|
|
176
|
+
<Spinner />
|
|
177
|
+
{activeToolCall.toolCall?.description}
|
|
178
|
+
</div>
|
|
179
|
+
)}
|
|
180
|
+
</div>
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
## Non-Main Thread Content
|
|
186
|
+
|
|
187
|
+
Named threads (like summarization) stream separately:
|
|
188
|
+
|
|
189
|
+
```tsx
|
|
190
|
+
function StreamingContent() {
|
|
191
|
+
const { streamingParts } = useOctavusChat({...});
|
|
192
|
+
|
|
193
|
+
// Group by thread
|
|
194
|
+
const mainParts = streamingParts.filter(p => !p.thread || p.thread === 'main');
|
|
195
|
+
const otherParts = streamingParts.filter(p => p.thread && p.thread !== 'main');
|
|
196
|
+
|
|
197
|
+
return (
|
|
198
|
+
<div>
|
|
199
|
+
{/* Main conversation */}
|
|
200
|
+
<div>{mainParts.map(renderPart)}</div>
|
|
201
|
+
|
|
202
|
+
{/* Named thread (e.g., summarization) */}
|
|
203
|
+
{otherParts.length > 0 && (
|
|
204
|
+
<div className="bg-orange-50 p-3 rounded mt-4">
|
|
205
|
+
<div className="text-orange-600 font-medium">
|
|
206
|
+
Processing in background...
|
|
207
|
+
</div>
|
|
208
|
+
{otherParts.map(renderPart)}
|
|
209
|
+
</div>
|
|
210
|
+
)}
|
|
211
|
+
</div>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Execution Blocks
|
|
3
|
+
description: Tracking agent execution progress with the Client SDK.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Execution Blocks
|
|
7
|
+
|
|
8
|
+
Execution blocks let you show users what the agent is doing. Each block represents a step in the agent's execution flow.
|
|
9
|
+
|
|
10
|
+
## Block Structure
|
|
11
|
+
|
|
12
|
+
```typescript
|
|
13
|
+
interface ExecutionBlock {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string; // Human-readable name from protocol
|
|
16
|
+
type: string; // Block type (next-message, tool-call, etc.)
|
|
17
|
+
status: BlockStatus; // pending | running | completed | error
|
|
18
|
+
display: DisplayMode; // hidden | name | description | stream
|
|
19
|
+
description?: string;
|
|
20
|
+
outputToChat: boolean; // Whether output goes to main chat
|
|
21
|
+
thread?: string; // For named threads
|
|
22
|
+
streamingText: string; // Current streaming content
|
|
23
|
+
thinking?: string; // Extended reasoning content
|
|
24
|
+
toolCalls: ToolCallWithDescription[];
|
|
25
|
+
startedAt: Date;
|
|
26
|
+
completedAt?: Date;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Accessing Blocks
|
|
31
|
+
|
|
32
|
+
```tsx
|
|
33
|
+
const { executionBlocks, onBlockStart, onBlockEnd } = useOctavusChat({
|
|
34
|
+
onTrigger: ...,
|
|
35
|
+
onBlockStart: (block) => {
|
|
36
|
+
console.log('Block started:', block.name);
|
|
37
|
+
},
|
|
38
|
+
onBlockEnd: (block) => {
|
|
39
|
+
console.log('Block completed:', block.name, block.status);
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Showing Execution Progress
|
|
45
|
+
|
|
46
|
+
```tsx
|
|
47
|
+
function ExecutionProgress() {
|
|
48
|
+
const { executionBlocks, status } = useOctavusChat({...});
|
|
49
|
+
|
|
50
|
+
if (status !== 'streaming' || executionBlocks.length === 0) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div className="space-y-2">
|
|
56
|
+
{executionBlocks.map((block) => (
|
|
57
|
+
<ExecutionBlockCard key={block.id} block={block} />
|
|
58
|
+
))}
|
|
59
|
+
</div>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function ExecutionBlockCard({ block }: { block: ExecutionBlock }) {
|
|
64
|
+
return (
|
|
65
|
+
<div className="border rounded p-3">
|
|
66
|
+
<div className="flex items-center gap-2">
|
|
67
|
+
<BlockStatusIcon status={block.status} />
|
|
68
|
+
<span className="font-medium">{block.name}</span>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
{block.description && (
|
|
72
|
+
<p className="text-sm text-gray-500 mt-1">{block.description}</p>
|
|
73
|
+
)}
|
|
74
|
+
|
|
75
|
+
{/* Show streaming content for stream display mode */}
|
|
76
|
+
{block.display === 'stream' && block.streamingText && (
|
|
77
|
+
<div className="mt-2 text-sm">
|
|
78
|
+
{block.streamingText}
|
|
79
|
+
</div>
|
|
80
|
+
)}
|
|
81
|
+
|
|
82
|
+
{/* Show tool calls */}
|
|
83
|
+
{block.toolCalls.length > 0 && (
|
|
84
|
+
<div className="mt-2 space-y-1">
|
|
85
|
+
{block.toolCalls.map((tc) => (
|
|
86
|
+
<div key={tc.id} className="text-sm flex items-center gap-1">
|
|
87
|
+
🔧 {tc.description || tc.name}
|
|
88
|
+
{tc.status === 'completed' && ' ✓'}
|
|
89
|
+
{tc.status === 'error' && ' ✗'}
|
|
90
|
+
</div>
|
|
91
|
+
))}
|
|
92
|
+
</div>
|
|
93
|
+
)}
|
|
94
|
+
</div>
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Display Modes
|
|
100
|
+
|
|
101
|
+
Blocks have different display modes that control visibility:
|
|
102
|
+
|
|
103
|
+
| Mode | Description | UI Recommendation |
|
|
104
|
+
|------|-------------|-------------------|
|
|
105
|
+
| `hidden` | Not shown to user | Don't render |
|
|
106
|
+
| `name` | Shows block name | Show name only |
|
|
107
|
+
| `description` | Shows description | Show name + description |
|
|
108
|
+
| `stream` | Streams content | Show full streaming content |
|
|
109
|
+
|
|
110
|
+
```tsx
|
|
111
|
+
function ExecutionBlockCard({ block }: { block: ExecutionBlock }) {
|
|
112
|
+
// Hidden blocks aren't sent to client, but check just in case
|
|
113
|
+
if (block.display === 'hidden') {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return (
|
|
118
|
+
<div className="border rounded p-3">
|
|
119
|
+
{/* Always show name */}
|
|
120
|
+
<div className="font-medium">{block.name}</div>
|
|
121
|
+
|
|
122
|
+
{/* Show description if display mode is description or stream */}
|
|
123
|
+
{(block.display === 'description' || block.display === 'stream') &&
|
|
124
|
+
block.description && (
|
|
125
|
+
<p className="text-sm text-gray-500">{block.description}</p>
|
|
126
|
+
)}
|
|
127
|
+
|
|
128
|
+
{/* Show content if display mode is stream */}
|
|
129
|
+
{block.display === 'stream' && (
|
|
130
|
+
<div className="mt-2">
|
|
131
|
+
{block.streamingText}
|
|
132
|
+
</div>
|
|
133
|
+
)}
|
|
134
|
+
</div>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Named Threads
|
|
140
|
+
|
|
141
|
+
Blocks can belong to named threads (like "summary"). Use the `thread` property:
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
function ExecutionProgress() {
|
|
145
|
+
const { executionBlocks } = useOctavusChat({...});
|
|
146
|
+
|
|
147
|
+
// Group by thread
|
|
148
|
+
const mainBlocks = executionBlocks.filter(b => !b.thread || b.thread === 'main');
|
|
149
|
+
const namedThreads = new Map<string, ExecutionBlock[]>();
|
|
150
|
+
|
|
151
|
+
executionBlocks.forEach(b => {
|
|
152
|
+
if (b.thread && b.thread !== 'main') {
|
|
153
|
+
if (!namedThreads.has(b.thread)) {
|
|
154
|
+
namedThreads.set(b.thread, []);
|
|
155
|
+
}
|
|
156
|
+
namedThreads.get(b.thread)!.push(b);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
return (
|
|
161
|
+
<div>
|
|
162
|
+
{/* Main execution */}
|
|
163
|
+
<div className="space-y-2">
|
|
164
|
+
{mainBlocks.map(b => <BlockCard key={b.id} block={b} />)}
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
{/* Named threads */}
|
|
168
|
+
{Array.from(namedThreads.entries()).map(([thread, blocks]) => (
|
|
169
|
+
<div key={thread} className="mt-4 bg-orange-50 p-3 rounded">
|
|
170
|
+
<div className="text-orange-600 font-medium mb-2">
|
|
171
|
+
Thread: {thread}
|
|
172
|
+
</div>
|
|
173
|
+
{blocks.map(b => <BlockCard key={b.id} block={b} />)}
|
|
174
|
+
</div>
|
|
175
|
+
))}
|
|
176
|
+
</div>
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Block Status Icons
|
|
182
|
+
|
|
183
|
+
```tsx
|
|
184
|
+
function BlockStatusIcon({ status }: { status: BlockStatus }) {
|
|
185
|
+
switch (status) {
|
|
186
|
+
case 'pending':
|
|
187
|
+
return <Circle className="w-4 h-4 text-gray-400" />;
|
|
188
|
+
case 'running':
|
|
189
|
+
return <Loader className="w-4 h-4 text-blue-500 animate-spin" />;
|
|
190
|
+
case 'completed':
|
|
191
|
+
return <CheckCircle className="w-4 h-4 text-green-500" />;
|
|
192
|
+
case 'error':
|
|
193
|
+
return <XCircle className="w-4 h-4 text-red-500" />;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
## Example: Support Chat with Escalation
|
|
199
|
+
|
|
200
|
+
When a user clicks "Talk to Human", the agent runs multiple blocks:
|
|
201
|
+
|
|
202
|
+
```tsx
|
|
203
|
+
function SupportChatProgress() {
|
|
204
|
+
const { executionBlocks } = useOctavusChat({...});
|
|
205
|
+
|
|
206
|
+
// Show blocks while streaming
|
|
207
|
+
return (
|
|
208
|
+
<div className="space-y-2">
|
|
209
|
+
{executionBlocks.map((block) => (
|
|
210
|
+
<div key={block.id} className="flex items-center gap-2">
|
|
211
|
+
<BlockStatusIcon status={block.status} />
|
|
212
|
+
<span>{getBlockLabel(block)}</span>
|
|
213
|
+
</div>
|
|
214
|
+
))}
|
|
215
|
+
</div>
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function getBlockLabel(block: ExecutionBlock): string {
|
|
220
|
+
// Use description if available, otherwise name
|
|
221
|
+
return block.description || block.name;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Example output during escalation:
|
|
225
|
+
// ✓ Serialize conversation
|
|
226
|
+
// ✓ Start summary thread
|
|
227
|
+
// ● Summarizing your conversation (streaming...)
|
|
228
|
+
// ○ Creating a support ticket
|
|
229
|
+
// ○ Respond with ticket info
|
|
230
|
+
```
|
|
231
|
+
|