@octavus/docs 0.0.6 → 0.0.8
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/content/01-getting-started/02-quickstart.md +28 -19
- package/content/02-server-sdk/01-overview.md +34 -17
- package/content/02-server-sdk/02-sessions.md +6 -3
- package/content/02-server-sdk/03-tools.md +4 -2
- package/content/02-server-sdk/04-streaming.md +12 -4
- package/content/03-client-sdk/01-overview.md +107 -42
- package/content/03-client-sdk/02-messages.md +71 -19
- package/content/03-client-sdk/03-streaming.md +38 -28
- package/content/03-client-sdk/05-socket-transport.md +414 -0
- package/content/03-client-sdk/06-http-transport.md +280 -0
- package/content/04-protocol/03-triggers.md +48 -21
- package/content/04-protocol/04-tools.md +25 -15
- package/content/05-api-reference/01-overview.md +3 -17
- package/content/06-examples/01-overview.md +27 -0
- package/content/06-examples/02-nextjs-chat.md +343 -0
- package/content/06-examples/03-socket-chat.md +392 -0
- package/content/06-examples/_meta.md +5 -0
- package/dist/chunk-232K4EME.js +439 -0
- package/dist/chunk-232K4EME.js.map +1 -0
- package/dist/chunk-2JDZLMS3.js +439 -0
- package/dist/chunk-2JDZLMS3.js.map +1 -0
- package/dist/chunk-5M7DS4DF.js +519 -0
- package/dist/chunk-5M7DS4DF.js.map +1 -0
- package/dist/chunk-7AS4ST73.js +421 -0
- package/dist/chunk-7AS4ST73.js.map +1 -0
- package/dist/chunk-H6JGSSAJ.js +519 -0
- package/dist/chunk-H6JGSSAJ.js.map +1 -0
- package/dist/chunk-JZRABTHU.js +519 -0
- package/dist/chunk-JZRABTHU.js.map +1 -0
- package/dist/chunk-OECAPVSX.js +439 -0
- package/dist/chunk-OECAPVSX.js.map +1 -0
- package/dist/chunk-OL5QDJ42.js +483 -0
- package/dist/chunk-OL5QDJ42.js.map +1 -0
- package/dist/chunk-PMOVVTHO.js +519 -0
- package/dist/chunk-PMOVVTHO.js.map +1 -0
- package/dist/chunk-R5MTVABN.js +439 -0
- package/dist/chunk-R5MTVABN.js.map +1 -0
- package/dist/chunk-RJ4H4YVA.js +519 -0
- package/dist/chunk-RJ4H4YVA.js.map +1 -0
- package/dist/chunk-S5U4IWCR.js +439 -0
- package/dist/chunk-S5U4IWCR.js.map +1 -0
- package/dist/chunk-UCJE36LL.js +519 -0
- package/dist/chunk-UCJE36LL.js.map +1 -0
- package/dist/chunk-WW7TRC7S.js +519 -0
- package/dist/chunk-WW7TRC7S.js.map +1 -0
- package/dist/content.js +1 -1
- package/dist/docs.json +57 -12
- 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 +65 -12
- package/package.json +2 -2
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Socket Chat
|
|
3
|
+
description: Building a chat interface with SockJS for real-time frameworks.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Socket Chat Example
|
|
7
|
+
|
|
8
|
+
This example builds a chat interface using SockJS for bidirectional communication. Use this pattern for Meteor, Phoenix, or when you need custom real-time events.
|
|
9
|
+
|
|
10
|
+
## What You're Building
|
|
11
|
+
|
|
12
|
+
A chat interface that:
|
|
13
|
+
- Uses SockJS for real-time streaming
|
|
14
|
+
- Manages sessions server-side (client doesn't need sessionId)
|
|
15
|
+
- Supports custom events alongside chat
|
|
16
|
+
- Works with frameworks that use WebSocket-like transports
|
|
17
|
+
|
|
18
|
+
## Architecture
|
|
19
|
+
|
|
20
|
+
```mermaid
|
|
21
|
+
flowchart LR
|
|
22
|
+
Browser["Browser<br/>(React)"] <-->|"SockJS"| Server["Your Server<br/>(Express)"]
|
|
23
|
+
Server --> Platform["Octavus Platform"]
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
**Key difference from HTTP:** The server maintains a persistent socket connection and manages sessions internally. The client never needs to know about `sessionId`.
|
|
27
|
+
|
|
28
|
+
## Prerequisites
|
|
29
|
+
|
|
30
|
+
- Express (or similar Node.js server)
|
|
31
|
+
- React frontend
|
|
32
|
+
- `sockjs` (server) and `sockjs-client` (client)
|
|
33
|
+
- Octavus account with API key
|
|
34
|
+
|
|
35
|
+
## Step 1: Install Dependencies
|
|
36
|
+
|
|
37
|
+
**Server:**
|
|
38
|
+
```bash
|
|
39
|
+
npm install @octavus/server-sdk sockjs express
|
|
40
|
+
npm install -D @types/sockjs @types/express
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Client:**
|
|
44
|
+
```bash
|
|
45
|
+
npm install @octavus/react sockjs-client
|
|
46
|
+
npm install -D @types/sockjs-client
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Step 2: Configure Environment
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# .env
|
|
53
|
+
OCTAVUS_API_URL=https://octavus.ai
|
|
54
|
+
OCTAVUS_API_KEY=your-api-key
|
|
55
|
+
OCTAVUS_AGENT_ID=your-agent-id
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Step 3: Create the Octavus Client (Server)
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
// server/octavus/client.ts
|
|
62
|
+
import { OctavusClient } from '@octavus/server-sdk';
|
|
63
|
+
|
|
64
|
+
export const octavus = new OctavusClient({
|
|
65
|
+
baseUrl: process.env.OCTAVUS_API_URL!,
|
|
66
|
+
apiKey: process.env.OCTAVUS_API_KEY!,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
export const AGENT_ID = process.env.OCTAVUS_AGENT_ID!;
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Step 4: Create the Socket Handler (Server)
|
|
73
|
+
|
|
74
|
+
This is the core of socket integration. Each connection gets its own session:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
// server/octavus/socket-handler.ts
|
|
78
|
+
import type { Connection } from 'sockjs';
|
|
79
|
+
import { OctavusClient, type AgentSession } from '@octavus/server-sdk';
|
|
80
|
+
|
|
81
|
+
const octavus = new OctavusClient({
|
|
82
|
+
baseUrl: process.env.OCTAVUS_API_URL!,
|
|
83
|
+
apiKey: process.env.OCTAVUS_API_KEY!,
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const AGENT_ID = process.env.OCTAVUS_AGENT_ID!;
|
|
87
|
+
|
|
88
|
+
export function createSocketHandler() {
|
|
89
|
+
return (conn: Connection) => {
|
|
90
|
+
let session: AgentSession | null = null;
|
|
91
|
+
let abortController: AbortController | null = null;
|
|
92
|
+
|
|
93
|
+
conn.on('data', (rawData: string) => {
|
|
94
|
+
void handleMessage(rawData);
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
async function handleMessage(rawData: string) {
|
|
98
|
+
const msg = JSON.parse(rawData);
|
|
99
|
+
|
|
100
|
+
// Handle stop request
|
|
101
|
+
if (msg.type === 'stop') {
|
|
102
|
+
abortController?.abort();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Handle trigger
|
|
107
|
+
if (msg.type === 'trigger') {
|
|
108
|
+
// Create session lazily on first trigger
|
|
109
|
+
if (!session) {
|
|
110
|
+
const sessionId = await octavus.agentSessions.create(AGENT_ID, {
|
|
111
|
+
// Initial input variables from your protocol
|
|
112
|
+
COMPANY_NAME: 'Acme Corp',
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
session = octavus.agentSessions.attach(sessionId, {
|
|
116
|
+
tools: {
|
|
117
|
+
'get-user-account': async () => {
|
|
118
|
+
// Fetch from your database
|
|
119
|
+
return { name: 'Demo User', plan: 'pro' };
|
|
120
|
+
},
|
|
121
|
+
'create-support-ticket': async () => {
|
|
122
|
+
return { ticketId: 'TKT-123', estimatedResponse: '24h' };
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
abortController = new AbortController();
|
|
129
|
+
|
|
130
|
+
// trigger() returns parsed events — iterate directly
|
|
131
|
+
const events = session.trigger(msg.triggerName, msg.input);
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
for await (const event of events) {
|
|
135
|
+
if (abortController.signal.aborted) break;
|
|
136
|
+
conn.write(JSON.stringify(event));
|
|
137
|
+
}
|
|
138
|
+
} catch {
|
|
139
|
+
// Handle errors
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
conn.on('close', () => {
|
|
145
|
+
abortController?.abort();
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Step 5: Set Up the Express Server
|
|
152
|
+
|
|
153
|
+
```typescript
|
|
154
|
+
// server/index.ts
|
|
155
|
+
import express from 'express';
|
|
156
|
+
import http from 'http';
|
|
157
|
+
import sockjs from 'sockjs';
|
|
158
|
+
import { createSocketHandler } from './octavus/socket-handler';
|
|
159
|
+
|
|
160
|
+
const app = express();
|
|
161
|
+
const server = http.createServer(app);
|
|
162
|
+
|
|
163
|
+
// Create SockJS server
|
|
164
|
+
const sockServer = sockjs.createServer({
|
|
165
|
+
prefix: '/octavus',
|
|
166
|
+
log: () => {}, // Silence logs
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
// Attach handler
|
|
170
|
+
sockServer.on('connection', createSocketHandler());
|
|
171
|
+
sockServer.installHandlers(server);
|
|
172
|
+
|
|
173
|
+
// Serve your frontend
|
|
174
|
+
app.use(express.static('dist/client'));
|
|
175
|
+
|
|
176
|
+
server.listen(3001, () => {
|
|
177
|
+
console.log('Server running on http://localhost:3001');
|
|
178
|
+
});
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
## Step 6: Create the Socket Hook (Client)
|
|
182
|
+
|
|
183
|
+
```typescript
|
|
184
|
+
// src/hooks/useOctavusSocket.ts
|
|
185
|
+
import { useMemo } from 'react';
|
|
186
|
+
import SockJS from 'sockjs-client';
|
|
187
|
+
import { useOctavusChat, createSocketTransport, type SocketLike } from '@octavus/react';
|
|
188
|
+
|
|
189
|
+
function connectSocket(): Promise<SocketLike> {
|
|
190
|
+
return new Promise((resolve, reject) => {
|
|
191
|
+
const sock = new SockJS('/octavus');
|
|
192
|
+
|
|
193
|
+
sock.onopen = () => resolve(sock);
|
|
194
|
+
sock.onerror = () => reject(new Error('Connection failed'));
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function useOctavusSocket() {
|
|
199
|
+
// Transport is stable - empty deps because server manages sessions
|
|
200
|
+
const transport = useMemo(
|
|
201
|
+
() => createSocketTransport({ connect: connectSocket }),
|
|
202
|
+
[],
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
const { messages, status, error, send, stop } = useOctavusChat({
|
|
206
|
+
transport,
|
|
207
|
+
onError: (err) => console.error('Chat error:', err),
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
const sendMessage = async (message: string) => {
|
|
211
|
+
await send(
|
|
212
|
+
'user-message',
|
|
213
|
+
{ USER_MESSAGE: message },
|
|
214
|
+
{ userMessage: { content: message } },
|
|
215
|
+
);
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
return { messages, status, error, sendMessage, stop };
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## Step 7: Build the Chat Component
|
|
223
|
+
|
|
224
|
+
```tsx
|
|
225
|
+
// src/components/Chat.tsx
|
|
226
|
+
import { useState } from 'react';
|
|
227
|
+
import { useOctavusSocket } from '../hooks/useOctavusSocket';
|
|
228
|
+
|
|
229
|
+
export function Chat() {
|
|
230
|
+
const [inputValue, setInputValue] = useState('');
|
|
231
|
+
const { messages, status, sendMessage, stop } = useOctavusSocket();
|
|
232
|
+
|
|
233
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
234
|
+
e.preventDefault();
|
|
235
|
+
if (!inputValue.trim() || status === 'streaming') return;
|
|
236
|
+
|
|
237
|
+
const message = inputValue.trim();
|
|
238
|
+
setInputValue('');
|
|
239
|
+
await sendMessage(message);
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
return (
|
|
243
|
+
<div className="flex flex-col h-screen">
|
|
244
|
+
{/* Messages */}
|
|
245
|
+
<div className="flex-1 overflow-y-auto p-4 space-y-4">
|
|
246
|
+
{messages.map((msg) => (
|
|
247
|
+
<div
|
|
248
|
+
key={msg.id}
|
|
249
|
+
className={msg.role === 'user' ? 'text-right' : 'text-left'}
|
|
250
|
+
>
|
|
251
|
+
<div
|
|
252
|
+
className={`inline-block p-3 rounded-lg ${
|
|
253
|
+
msg.role === 'user'
|
|
254
|
+
? 'bg-blue-500 text-white'
|
|
255
|
+
: 'bg-gray-100'
|
|
256
|
+
}`}
|
|
257
|
+
>
|
|
258
|
+
{msg.parts.map((part, i) => {
|
|
259
|
+
if (part.type === 'text') return <p key={i}>{part.text}</p>;
|
|
260
|
+
return null;
|
|
261
|
+
})}
|
|
262
|
+
</div>
|
|
263
|
+
</div>
|
|
264
|
+
))}
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
{/* Input */}
|
|
268
|
+
<form onSubmit={handleSubmit} className="p-4 border-t flex gap-2">
|
|
269
|
+
<input
|
|
270
|
+
type="text"
|
|
271
|
+
value={inputValue}
|
|
272
|
+
onChange={(e) => setInputValue(e.target.value)}
|
|
273
|
+
placeholder="Type a message..."
|
|
274
|
+
className="flex-1 px-4 py-2 border rounded-lg"
|
|
275
|
+
disabled={status === 'streaming'}
|
|
276
|
+
/>
|
|
277
|
+
{status === 'streaming' ? (
|
|
278
|
+
<button
|
|
279
|
+
type="button"
|
|
280
|
+
onClick={stop}
|
|
281
|
+
className="px-4 py-2 bg-red-500 text-white rounded-lg"
|
|
282
|
+
>
|
|
283
|
+
Stop
|
|
284
|
+
</button>
|
|
285
|
+
) : (
|
|
286
|
+
<button
|
|
287
|
+
type="submit"
|
|
288
|
+
className="px-4 py-2 bg-blue-500 text-white rounded-lg"
|
|
289
|
+
>
|
|
290
|
+
Send
|
|
291
|
+
</button>
|
|
292
|
+
)}
|
|
293
|
+
</form>
|
|
294
|
+
</div>
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
## Custom Events
|
|
300
|
+
|
|
301
|
+
Socket transport supports custom events alongside Octavus events:
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
// Client - handle custom events
|
|
305
|
+
const transport = useMemo(
|
|
306
|
+
() =>
|
|
307
|
+
createSocketTransport({
|
|
308
|
+
connect: connectSocket,
|
|
309
|
+
onMessage: (data) => {
|
|
310
|
+
const msg = data as { type: string; [key: string]: unknown };
|
|
311
|
+
|
|
312
|
+
if (msg.type === 'typing-indicator') {
|
|
313
|
+
setAgentTyping(msg.isTyping as boolean);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (msg.type === 'custom-notification') {
|
|
317
|
+
showToast(msg.message as string);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Octavus events (text-delta, finish, etc.) are handled automatically
|
|
321
|
+
},
|
|
322
|
+
}),
|
|
323
|
+
[],
|
|
324
|
+
);
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
// Server - send custom events
|
|
329
|
+
conn.write(JSON.stringify({
|
|
330
|
+
type: 'typing-indicator',
|
|
331
|
+
isTyping: true,
|
|
332
|
+
}));
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
## Protocol Integration
|
|
336
|
+
|
|
337
|
+
### Triggers
|
|
338
|
+
|
|
339
|
+
The socket handler receives trigger messages and forwards them to Octavus:
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
// Client sends:
|
|
343
|
+
{ type: 'trigger', triggerName: 'user-message', input: { USER_MESSAGE: 'Hello' } }
|
|
344
|
+
|
|
345
|
+
// Server handles:
|
|
346
|
+
if (msg.type === 'trigger') {
|
|
347
|
+
const events = session.trigger(msg.triggerName, msg.input);
|
|
348
|
+
for await (const event of events) {
|
|
349
|
+
conn.write(JSON.stringify(event));
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### Tools
|
|
355
|
+
|
|
356
|
+
Tools are defined in your agent's protocol and handled server-side:
|
|
357
|
+
|
|
358
|
+
```yaml
|
|
359
|
+
# protocol.yaml
|
|
360
|
+
tools:
|
|
361
|
+
get-user-account:
|
|
362
|
+
description: Fetch user details
|
|
363
|
+
parameters:
|
|
364
|
+
userId:
|
|
365
|
+
type: string
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
```typescript
|
|
369
|
+
// Server tool handler
|
|
370
|
+
tools: {
|
|
371
|
+
'get-user-account': async (args) => {
|
|
372
|
+
const userId = args.userId as string;
|
|
373
|
+
return await db.users.find(userId);
|
|
374
|
+
},
|
|
375
|
+
}
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
## Meteor Integration Note
|
|
379
|
+
|
|
380
|
+
Meteor's bundler may have issues with ES6 imports of `sockjs-client`:
|
|
381
|
+
|
|
382
|
+
```typescript
|
|
383
|
+
// Use require() instead of import
|
|
384
|
+
const SockJS: typeof import('sockjs-client') = require('sockjs-client');
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
## Next Steps
|
|
388
|
+
|
|
389
|
+
- [Socket Transport](/docs/client-sdk/socket-transport) — Advanced socket patterns
|
|
390
|
+
- [Protocol Overview](/docs/protocol/overview) — Define agent behavior
|
|
391
|
+
- [Tools](/docs/protocol/tools) — Building tool handlers
|
|
392
|
+
|