@octavus/docs 0.0.7 → 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 +4 -3
- package/content/02-server-sdk/01-overview.md +11 -5
- 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 +11 -5
- package/content/03-client-sdk/05-socket-transport.md +263 -153
- package/content/03-client-sdk/06-http-transport.md +280 -0
- package/content/04-protocol/03-triggers.md +10 -2
- 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-5M7DS4DF.js +519 -0
- package/dist/chunk-5M7DS4DF.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-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 +46 -10
- 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 +54 -10
- package/package.json +1 -1
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: HTTP Transport
|
|
3
|
+
description: Using HTTP/SSE for streaming with Octavus in Next.js, Express, and other frameworks.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# HTTP Transport
|
|
7
|
+
|
|
8
|
+
The HTTP transport uses standard HTTP requests with Server-Sent Events (SSE) for streaming. This is the simplest and most compatible transport option.
|
|
9
|
+
|
|
10
|
+
## When to Use HTTP Transport
|
|
11
|
+
|
|
12
|
+
| Use Case | Recommendation |
|
|
13
|
+
|----------|----------------|
|
|
14
|
+
| Next.js, Remix, or similar frameworks | ✅ Use HTTP |
|
|
15
|
+
| Standard web apps without special requirements | ✅ Use HTTP |
|
|
16
|
+
| Serverless deployments (Vercel, etc.) | ✅ Use HTTP |
|
|
17
|
+
| Need custom real-time events | Consider [Socket Transport](/docs/client-sdk/socket-transport) |
|
|
18
|
+
|
|
19
|
+
## Basic Setup
|
|
20
|
+
|
|
21
|
+
### Client
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { useMemo } from 'react';
|
|
25
|
+
import { useOctavusChat, createHttpTransport } from '@octavus/react';
|
|
26
|
+
|
|
27
|
+
function Chat({ sessionId }: { sessionId: string }) {
|
|
28
|
+
const transport = useMemo(
|
|
29
|
+
() =>
|
|
30
|
+
createHttpTransport({
|
|
31
|
+
triggerRequest: (triggerName, input) =>
|
|
32
|
+
fetch('/api/trigger', {
|
|
33
|
+
method: 'POST',
|
|
34
|
+
headers: { 'Content-Type': 'application/json' },
|
|
35
|
+
body: JSON.stringify({ sessionId, triggerName, input }),
|
|
36
|
+
}),
|
|
37
|
+
}),
|
|
38
|
+
[sessionId],
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
const { messages, status, error, send, stop } = useOctavusChat({ transport });
|
|
42
|
+
|
|
43
|
+
const sendMessage = async (text: string) => {
|
|
44
|
+
await send(
|
|
45
|
+
'user-message',
|
|
46
|
+
{ USER_MESSAGE: text },
|
|
47
|
+
{ userMessage: { content: text } },
|
|
48
|
+
);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// ... render chat
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Server (Next.js API Route)
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
// app/api/trigger/route.ts
|
|
59
|
+
import { OctavusClient, toSSEStream } from '@octavus/server-sdk';
|
|
60
|
+
|
|
61
|
+
const client = new OctavusClient({
|
|
62
|
+
baseUrl: process.env.OCTAVUS_API_URL!,
|
|
63
|
+
apiKey: process.env.OCTAVUS_API_KEY!,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
export async function POST(request: Request) {
|
|
67
|
+
const { sessionId, triggerName, input } = await request.json();
|
|
68
|
+
|
|
69
|
+
const session = client.agentSessions.attach(sessionId, {
|
|
70
|
+
tools: {
|
|
71
|
+
'get-user-account': async (args) => {
|
|
72
|
+
return { name: 'Demo User', plan: 'pro' };
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// trigger() returns parsed events, toSSEStream() converts to SSE format
|
|
78
|
+
const events = session.trigger(triggerName, input);
|
|
79
|
+
|
|
80
|
+
return new Response(toSSEStream(events), {
|
|
81
|
+
headers: {
|
|
82
|
+
'Content-Type': 'text/event-stream',
|
|
83
|
+
'Cache-Control': 'no-cache',
|
|
84
|
+
Connection: 'keep-alive',
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Session Creation
|
|
91
|
+
|
|
92
|
+
Sessions should be created server-side before rendering the chat. There are two patterns:
|
|
93
|
+
|
|
94
|
+
### Pattern 1: Create Session on Page Load
|
|
95
|
+
|
|
96
|
+
```tsx
|
|
97
|
+
// app/chat/page.tsx
|
|
98
|
+
'use client';
|
|
99
|
+
|
|
100
|
+
import { useEffect, useState } from 'react';
|
|
101
|
+
import { Chat } from '@/components/Chat';
|
|
102
|
+
|
|
103
|
+
export default function ChatPage() {
|
|
104
|
+
const [sessionId, setSessionId] = useState<string | null>(null);
|
|
105
|
+
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
fetch('/api/sessions', {
|
|
108
|
+
method: 'POST',
|
|
109
|
+
headers: { 'Content-Type': 'application/json' },
|
|
110
|
+
body: JSON.stringify({
|
|
111
|
+
agentId: 'your-agent-id',
|
|
112
|
+
input: { COMPANY_NAME: 'Acme Corp' },
|
|
113
|
+
}),
|
|
114
|
+
})
|
|
115
|
+
.then(res => res.json())
|
|
116
|
+
.then(data => setSessionId(data.sessionId));
|
|
117
|
+
}, []);
|
|
118
|
+
|
|
119
|
+
if (!sessionId) {
|
|
120
|
+
return <LoadingSpinner />;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return <Chat sessionId={sessionId} />;
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### Pattern 2: Server-Side Session Creation (App Router)
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
// app/chat/page.tsx
|
|
131
|
+
import { octavus } from '@/lib/octavus';
|
|
132
|
+
import { Chat } from '@/components/Chat';
|
|
133
|
+
|
|
134
|
+
export default async function ChatPage() {
|
|
135
|
+
// Create session server-side
|
|
136
|
+
const sessionId = await octavus.agentSessions.create('your-agent-id', {
|
|
137
|
+
COMPANY_NAME: 'Acme Corp',
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return <Chat sessionId={sessionId} />;
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
This pattern is cleaner as the session is ready before the component renders.
|
|
145
|
+
|
|
146
|
+
## Error Handling
|
|
147
|
+
|
|
148
|
+
Handle errors in both the transport and the hook:
|
|
149
|
+
|
|
150
|
+
```tsx
|
|
151
|
+
const { messages, status, error, send } = useOctavusChat({
|
|
152
|
+
transport,
|
|
153
|
+
onError: (err) => {
|
|
154
|
+
console.error('Stream error:', err);
|
|
155
|
+
// Show toast, update UI, etc.
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
// Also check error state
|
|
160
|
+
if (error) {
|
|
161
|
+
return <ErrorMessage error={error} />;
|
|
162
|
+
}
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Stop Streaming
|
|
166
|
+
|
|
167
|
+
Allow users to cancel ongoing streams:
|
|
168
|
+
|
|
169
|
+
```tsx
|
|
170
|
+
const { send, stop, status } = useOctavusChat({ transport });
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<button
|
|
174
|
+
onClick={status === 'streaming' ? stop : () => sendMessage()}
|
|
175
|
+
disabled={status === 'streaming' && !inputValue}
|
|
176
|
+
>
|
|
177
|
+
{status === 'streaming' ? 'Stop' : 'Send'}
|
|
178
|
+
</button>
|
|
179
|
+
);
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Express Server
|
|
183
|
+
|
|
184
|
+
For non-Next.js backends:
|
|
185
|
+
|
|
186
|
+
```typescript
|
|
187
|
+
import express from 'express';
|
|
188
|
+
import { OctavusClient, toSSEStream } from '@octavus/server-sdk';
|
|
189
|
+
|
|
190
|
+
const app = express();
|
|
191
|
+
const client = new OctavusClient({
|
|
192
|
+
baseUrl: process.env.OCTAVUS_API_URL!,
|
|
193
|
+
apiKey: process.env.OCTAVUS_API_KEY!,
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
app.post('/api/trigger', async (req, res) => {
|
|
197
|
+
const { sessionId, triggerName, input } = req.body;
|
|
198
|
+
|
|
199
|
+
const session = client.agentSessions.attach(sessionId, {
|
|
200
|
+
tools: {
|
|
201
|
+
// Your tool handlers
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
const events = session.trigger(triggerName, input);
|
|
206
|
+
const stream = toSSEStream(events);
|
|
207
|
+
|
|
208
|
+
// Set SSE headers
|
|
209
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
210
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
211
|
+
res.setHeader('Connection', 'keep-alive');
|
|
212
|
+
|
|
213
|
+
// Pipe the stream to the response
|
|
214
|
+
const reader = stream.getReader();
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
while (true) {
|
|
218
|
+
const { done, value } = await reader.read();
|
|
219
|
+
if (done) break;
|
|
220
|
+
res.write(value);
|
|
221
|
+
}
|
|
222
|
+
} finally {
|
|
223
|
+
reader.releaseLock();
|
|
224
|
+
res.end();
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Transport Options
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
interface HttpTransportOptions {
|
|
233
|
+
// Function that makes the HTTP request
|
|
234
|
+
triggerRequest: (
|
|
235
|
+
triggerName: string,
|
|
236
|
+
input?: Record<string, unknown>
|
|
237
|
+
) => Promise<Response>;
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Protocol
|
|
242
|
+
|
|
243
|
+
### Request Format
|
|
244
|
+
|
|
245
|
+
The `triggerRequest` function should send a POST request with:
|
|
246
|
+
|
|
247
|
+
```json
|
|
248
|
+
{
|
|
249
|
+
"sessionId": "sess_abc123",
|
|
250
|
+
"triggerName": "user-message",
|
|
251
|
+
"input": {
|
|
252
|
+
"USER_MESSAGE": "Hello"
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Response Format
|
|
258
|
+
|
|
259
|
+
The server responds with an SSE stream:
|
|
260
|
+
|
|
261
|
+
```
|
|
262
|
+
data: {"type":"start","messageId":"msg_xyz"}
|
|
263
|
+
|
|
264
|
+
data: {"type":"text-delta","id":"msg_xyz","delta":"Hello"}
|
|
265
|
+
|
|
266
|
+
data: {"type":"text-delta","id":"msg_xyz","delta":" there!"}
|
|
267
|
+
|
|
268
|
+
data: {"type":"finish","finishReason":"stop"}
|
|
269
|
+
|
|
270
|
+
data: [DONE]
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
See [Streaming Events](/docs/server-sdk/streaming#event-types) for the full list of event types.
|
|
274
|
+
|
|
275
|
+
## Next Steps
|
|
276
|
+
|
|
277
|
+
- [Quick Start](/docs/getting-started/quickstart) — Complete Next.js integration guide
|
|
278
|
+
- [Messages](/docs/client-sdk/messages) — Working with message state
|
|
279
|
+
- [Streaming](/docs/client-sdk/streaming) — Building streaming UIs
|
|
280
|
+
|
|
@@ -117,11 +117,19 @@ function Chat({ sessionId }: { sessionId: string }) {
|
|
|
117
117
|
### From Server SDK
|
|
118
118
|
|
|
119
119
|
```typescript
|
|
120
|
-
|
|
120
|
+
// trigger() returns an async generator of events
|
|
121
|
+
const events = session.trigger('user-message', {
|
|
121
122
|
USER_MESSAGE: 'Help me with billing',
|
|
122
123
|
});
|
|
123
124
|
|
|
124
|
-
|
|
125
|
+
// Iterate events directly
|
|
126
|
+
for await (const event of events) {
|
|
127
|
+
console.log(event);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Or convert to SSE for HTTP responses
|
|
131
|
+
import { toSSEStream } from '@octavus/server-sdk';
|
|
132
|
+
return new Response(toSSEStream(events), { headers: { 'Content-Type': 'text/event-stream' } });
|
|
125
133
|
```
|
|
126
134
|
|
|
127
135
|
## Handlers
|
|
@@ -22,7 +22,7 @@ curl -H "Authorization: Bearer YOUR_API_KEY" \
|
|
|
22
22
|
https://octavus.ai/api/agents
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
API keys can be created in the Octavus Platform under
|
|
25
|
+
API keys can be created in the Octavus Platform under your project's **API Keys** page.
|
|
26
26
|
|
|
27
27
|
## Response Format
|
|
28
28
|
|
|
@@ -57,23 +57,8 @@ All responses are JSON. Success responses return the data directly (not wrapped
|
|
|
57
57
|
| 401 | Unauthorized - Missing or invalid API key |
|
|
58
58
|
| 403 | Forbidden - Insufficient permissions |
|
|
59
59
|
| 404 | Not Found |
|
|
60
|
-
| 429 | Too Many Requests - Rate limited |
|
|
61
60
|
| 500 | Internal Server Error |
|
|
62
61
|
|
|
63
|
-
## Rate Limits
|
|
64
|
-
|
|
65
|
-
- **Standard tier**: 100 requests/minute
|
|
66
|
-
- **Pro tier**: 1,000 requests/minute
|
|
67
|
-
- **Enterprise**: Custom limits
|
|
68
|
-
|
|
69
|
-
Rate limit headers are included in responses:
|
|
70
|
-
|
|
71
|
-
```
|
|
72
|
-
X-RateLimit-Limit: 100
|
|
73
|
-
X-RateLimit-Remaining: 95
|
|
74
|
-
X-RateLimit-Reset: 1735312800
|
|
75
|
-
```
|
|
76
|
-
|
|
77
62
|
## Endpoints Overview
|
|
78
63
|
|
|
79
64
|
### Agents
|
|
@@ -131,7 +116,8 @@ data: [DONE]
|
|
|
131
116
|
We recommend using our SDKs instead of calling the API directly:
|
|
132
117
|
|
|
133
118
|
- **Server SDK**: `@octavus/server-sdk` - For Node.js backends
|
|
134
|
-
- **
|
|
119
|
+
- **React SDK**: `@octavus/react` - For React applications
|
|
120
|
+
- **Client SDK**: `@octavus/client-sdk` - For other frontend frameworks
|
|
135
121
|
|
|
136
122
|
The SDKs handle authentication, streaming, and tool execution automatically.
|
|
137
123
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Overview
|
|
3
|
+
description: Complete integration examples for different architectures.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Examples
|
|
7
|
+
|
|
8
|
+
This section provides complete integration examples for different architectures. Each example walks through building a functional chat interface from scratch.
|
|
9
|
+
|
|
10
|
+
## Available Examples
|
|
11
|
+
|
|
12
|
+
| Example | Transport | Best For |
|
|
13
|
+
|---------|-----------|----------|
|
|
14
|
+
| [Next.js Chat](/docs/examples/nextjs-chat) | HTTP/SSE | Next.js, Remix, standard web apps |
|
|
15
|
+
| [Socket Chat](/docs/examples/socket-chat) | SockJS | Meteor, Phoenix, real-time apps |
|
|
16
|
+
|
|
17
|
+
## Choosing a Transport
|
|
18
|
+
|
|
19
|
+
**Use HTTP Transport when:**
|
|
20
|
+
- Building with Next.js, Remix, or similar frameworks
|
|
21
|
+
- You want the simplest integration
|
|
22
|
+
- Deploying to serverless (Vercel, Netlify, etc.)
|
|
23
|
+
|
|
24
|
+
**Use Socket Transport when:**
|
|
25
|
+
- Using Meteor, Phoenix, or socket-based frameworks
|
|
26
|
+
- Need custom real-time events (typing indicators, presence)
|
|
27
|
+
- Behind proxies that don't support SSE well
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Next.js Chat
|
|
3
|
+
description: Building a chat interface with Next.js and HTTP transport.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Next.js Chat Example
|
|
7
|
+
|
|
8
|
+
This example builds a support chat interface using Next.js App Router with HTTP/SSE transport. This is the recommended pattern for most web applications.
|
|
9
|
+
|
|
10
|
+
## What You're Building
|
|
11
|
+
|
|
12
|
+
A chat interface that:
|
|
13
|
+
- Creates sessions server-side
|
|
14
|
+
- Streams AI responses in real-time
|
|
15
|
+
- Handles tool calls on your server
|
|
16
|
+
- Shows typing status during streaming
|
|
17
|
+
|
|
18
|
+
## Architecture
|
|
19
|
+
|
|
20
|
+
```mermaid
|
|
21
|
+
flowchart LR
|
|
22
|
+
Browser["Browser<br/>(React)"] -->|"POST /api/chat"| NextJS["Next.js API<br/>Routes"]
|
|
23
|
+
NextJS -->|"SSE"| Browser
|
|
24
|
+
NextJS --> Platform["Octavus Platform"]
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Prerequisites
|
|
28
|
+
|
|
29
|
+
- Next.js 14+ with App Router
|
|
30
|
+
- Octavus account with API key
|
|
31
|
+
- An agent configured in Octavus
|
|
32
|
+
|
|
33
|
+
## Step 1: Install Dependencies
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install @octavus/server-sdk @octavus/react
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Step 2: Configure Environment
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# .env.local
|
|
43
|
+
OCTAVUS_API_URL=https://octavus.ai
|
|
44
|
+
OCTAVUS_API_KEY=your-api-key
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Step 3: Create the Octavus Client
|
|
48
|
+
|
|
49
|
+
```typescript
|
|
50
|
+
// lib/octavus.ts
|
|
51
|
+
import { OctavusClient } from '@octavus/server-sdk';
|
|
52
|
+
|
|
53
|
+
export const octavus = new OctavusClient({
|
|
54
|
+
baseUrl: process.env.OCTAVUS_API_URL!,
|
|
55
|
+
apiKey: process.env.OCTAVUS_API_KEY!,
|
|
56
|
+
});
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Step 4: Create Session Endpoint
|
|
60
|
+
|
|
61
|
+
Sessions hold conversation state. Create one when the user opens the chat:
|
|
62
|
+
|
|
63
|
+
```typescript
|
|
64
|
+
// app/api/sessions/route.ts
|
|
65
|
+
import { NextResponse } from 'next/server';
|
|
66
|
+
import { octavus } from '@/lib/octavus';
|
|
67
|
+
|
|
68
|
+
export async function POST(request: Request) {
|
|
69
|
+
const { agentId, input } = await request.json();
|
|
70
|
+
|
|
71
|
+
// Create a new session with initial input variables
|
|
72
|
+
const sessionId = await octavus.agentSessions.create(agentId, input);
|
|
73
|
+
|
|
74
|
+
return NextResponse.json({ sessionId });
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
**Protocol Note:** The `input` object contains variables defined in your agent's protocol. For example, if your agent has `COMPANY_NAME` as an input variable:
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
const sessionId = await octavus.agentSessions.create(agentId, {
|
|
82
|
+
COMPANY_NAME: 'Acme Corp',
|
|
83
|
+
USER_ID: user.id,
|
|
84
|
+
});
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Step 5: Create Trigger Endpoint
|
|
88
|
+
|
|
89
|
+
Triggers execute agent actions. The `user-message` trigger is the most common:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
// app/api/trigger/route.ts
|
|
93
|
+
import { toSSEStream } from '@octavus/server-sdk';
|
|
94
|
+
import { octavus } from '@/lib/octavus';
|
|
95
|
+
|
|
96
|
+
export async function POST(request: Request) {
|
|
97
|
+
const { sessionId, triggerName, input } = await request.json();
|
|
98
|
+
|
|
99
|
+
// Attach to the session with tool handlers
|
|
100
|
+
const session = octavus.agentSessions.attach(sessionId, {
|
|
101
|
+
tools: {
|
|
102
|
+
// Tool handlers run on YOUR server, not Octavus
|
|
103
|
+
'get-user-account': async (args) => {
|
|
104
|
+
const userId = args.userId as string;
|
|
105
|
+
// Fetch from your database
|
|
106
|
+
const user = await db.users.findUnique({ where: { id: userId } });
|
|
107
|
+
return {
|
|
108
|
+
name: user.name,
|
|
109
|
+
email: user.email,
|
|
110
|
+
plan: user.plan,
|
|
111
|
+
};
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
'create-support-ticket': async (args) => {
|
|
115
|
+
const ticket = await db.tickets.create({
|
|
116
|
+
data: {
|
|
117
|
+
summary: args.summary as string,
|
|
118
|
+
priority: args.priority as string,
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
return {
|
|
122
|
+
ticketId: ticket.id,
|
|
123
|
+
estimatedResponse: '24 hours',
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// trigger() returns parsed events, toSSEStream() converts to SSE format
|
|
130
|
+
const events = session.trigger(triggerName, input);
|
|
131
|
+
|
|
132
|
+
return new Response(toSSEStream(events), {
|
|
133
|
+
headers: {
|
|
134
|
+
'Content-Type': 'text/event-stream',
|
|
135
|
+
'Cache-Control': 'no-cache',
|
|
136
|
+
Connection: 'keep-alive',
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Protocol Note:** Tool names and arguments are defined in your agent's protocol YAML. The tool handlers here must match those definitions.
|
|
143
|
+
|
|
144
|
+
## Step 6: Build the Chat Component
|
|
145
|
+
|
|
146
|
+
```tsx
|
|
147
|
+
// components/Chat.tsx
|
|
148
|
+
'use client';
|
|
149
|
+
|
|
150
|
+
import { useState, useMemo } from 'react';
|
|
151
|
+
import { useOctavusChat, createHttpTransport } from '@octavus/react';
|
|
152
|
+
|
|
153
|
+
interface ChatProps {
|
|
154
|
+
sessionId: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function Chat({ sessionId }: ChatProps) {
|
|
158
|
+
const [inputValue, setInputValue] = useState('');
|
|
159
|
+
|
|
160
|
+
// Create transport - memoized on sessionId
|
|
161
|
+
const transport = useMemo(
|
|
162
|
+
() =>
|
|
163
|
+
createHttpTransport({
|
|
164
|
+
triggerRequest: (triggerName, input) =>
|
|
165
|
+
fetch('/api/trigger', {
|
|
166
|
+
method: 'POST',
|
|
167
|
+
headers: { 'Content-Type': 'application/json' },
|
|
168
|
+
body: JSON.stringify({ sessionId, triggerName, input }),
|
|
169
|
+
}),
|
|
170
|
+
}),
|
|
171
|
+
[sessionId],
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
const { messages, status, send } = useOctavusChat({ transport });
|
|
175
|
+
|
|
176
|
+
const handleSubmit = async (e: React.FormEvent) => {
|
|
177
|
+
e.preventDefault();
|
|
178
|
+
if (!inputValue.trim() || status === 'streaming') return;
|
|
179
|
+
|
|
180
|
+
const message = inputValue.trim();
|
|
181
|
+
setInputValue('');
|
|
182
|
+
|
|
183
|
+
// Send triggers the 'user-message' action
|
|
184
|
+
// The third argument adds the user message to the UI
|
|
185
|
+
await send(
|
|
186
|
+
'user-message',
|
|
187
|
+
{ USER_MESSAGE: message },
|
|
188
|
+
{ userMessage: { content: message } },
|
|
189
|
+
);
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
return (
|
|
193
|
+
<div className="flex flex-col h-screen">
|
|
194
|
+
{/* Messages */}
|
|
195
|
+
<div className="flex-1 overflow-y-auto p-4 space-y-4">
|
|
196
|
+
{messages.map((msg) => (
|
|
197
|
+
<div
|
|
198
|
+
key={msg.id}
|
|
199
|
+
className={msg.role === 'user' ? 'text-right' : 'text-left'}
|
|
200
|
+
>
|
|
201
|
+
<div
|
|
202
|
+
className={`inline-block p-3 rounded-lg ${
|
|
203
|
+
msg.role === 'user'
|
|
204
|
+
? 'bg-blue-500 text-white'
|
|
205
|
+
: 'bg-gray-100'
|
|
206
|
+
}`}
|
|
207
|
+
>
|
|
208
|
+
{msg.parts.map((part, i) => {
|
|
209
|
+
if (part.type === 'text') {
|
|
210
|
+
return <p key={i}>{part.text}</p>;
|
|
211
|
+
}
|
|
212
|
+
if (part.type === 'tool-call') {
|
|
213
|
+
return (
|
|
214
|
+
<div key={i} className="text-sm opacity-70">
|
|
215
|
+
Using {part.toolName}...
|
|
216
|
+
</div>
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
return null;
|
|
220
|
+
})}
|
|
221
|
+
</div>
|
|
222
|
+
</div>
|
|
223
|
+
))}
|
|
224
|
+
</div>
|
|
225
|
+
|
|
226
|
+
{/* Input */}
|
|
227
|
+
<form onSubmit={handleSubmit} className="p-4 border-t">
|
|
228
|
+
<div className="flex gap-2">
|
|
229
|
+
<input
|
|
230
|
+
type="text"
|
|
231
|
+
value={inputValue}
|
|
232
|
+
onChange={(e) => setInputValue(e.target.value)}
|
|
233
|
+
placeholder="Type a message..."
|
|
234
|
+
className="flex-1 px-4 py-2 border rounded-lg"
|
|
235
|
+
disabled={status === 'streaming'}
|
|
236
|
+
/>
|
|
237
|
+
<button
|
|
238
|
+
type="submit"
|
|
239
|
+
disabled={status === 'streaming'}
|
|
240
|
+
className="px-4 py-2 bg-blue-500 text-white rounded-lg"
|
|
241
|
+
>
|
|
242
|
+
{status === 'streaming' ? 'Sending...' : 'Send'}
|
|
243
|
+
</button>
|
|
244
|
+
</div>
|
|
245
|
+
</form>
|
|
246
|
+
</div>
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
## Step 7: Create the Page
|
|
252
|
+
|
|
253
|
+
```tsx
|
|
254
|
+
// app/chat/page.tsx
|
|
255
|
+
'use client';
|
|
256
|
+
|
|
257
|
+
import { useEffect, useState } from 'react';
|
|
258
|
+
import { Chat } from '@/components/Chat';
|
|
259
|
+
|
|
260
|
+
const AGENT_ID = 'your-agent-id'; // From Octavus dashboard
|
|
261
|
+
|
|
262
|
+
export default function ChatPage() {
|
|
263
|
+
const [sessionId, setSessionId] = useState<string | null>(null);
|
|
264
|
+
|
|
265
|
+
useEffect(() => {
|
|
266
|
+
// Create session on mount
|
|
267
|
+
fetch('/api/sessions', {
|
|
268
|
+
method: 'POST',
|
|
269
|
+
headers: { 'Content-Type': 'application/json' },
|
|
270
|
+
body: JSON.stringify({
|
|
271
|
+
agentId: AGENT_ID,
|
|
272
|
+
input: {
|
|
273
|
+
COMPANY_NAME: 'Acme Corp',
|
|
274
|
+
},
|
|
275
|
+
}),
|
|
276
|
+
})
|
|
277
|
+
.then((res) => res.json())
|
|
278
|
+
.then((data) => setSessionId(data.sessionId));
|
|
279
|
+
}, []);
|
|
280
|
+
|
|
281
|
+
if (!sessionId) {
|
|
282
|
+
return <div className="p-8">Loading...</div>;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return <Chat sessionId={sessionId} />;
|
|
286
|
+
}
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Protocol Integration
|
|
290
|
+
|
|
291
|
+
Your agent's protocol defines the triggers and tools. Here's how the code maps to protocol:
|
|
292
|
+
|
|
293
|
+
### Triggers
|
|
294
|
+
|
|
295
|
+
```yaml
|
|
296
|
+
# In your agent's protocol.yaml
|
|
297
|
+
triggers:
|
|
298
|
+
user-message:
|
|
299
|
+
description: User sends a chat message
|
|
300
|
+
input:
|
|
301
|
+
USER_MESSAGE:
|
|
302
|
+
type: string
|
|
303
|
+
description: The user's message
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
The `send()` call maps directly:
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
await send(
|
|
310
|
+
'user-message', // trigger name
|
|
311
|
+
{ USER_MESSAGE: message }, // trigger inputs
|
|
312
|
+
{ userMessage: { content: message } },
|
|
313
|
+
);
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Tools
|
|
317
|
+
|
|
318
|
+
```yaml
|
|
319
|
+
# In your agent's protocol.yaml
|
|
320
|
+
tools:
|
|
321
|
+
get-user-account:
|
|
322
|
+
description: Fetch user account details
|
|
323
|
+
parameters:
|
|
324
|
+
userId:
|
|
325
|
+
type: string
|
|
326
|
+
description: The user ID to look up
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
Tool handlers receive the parameters as `args`:
|
|
330
|
+
|
|
331
|
+
```typescript
|
|
332
|
+
'get-user-account': async (args) => {
|
|
333
|
+
const userId = args.userId as string;
|
|
334
|
+
// ...
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
## Next Steps
|
|
339
|
+
|
|
340
|
+
- [Protocol Overview](/docs/protocol/overview) — Define agent behavior
|
|
341
|
+
- [Messages](/docs/client-sdk/messages) — Rich message rendering
|
|
342
|
+
- [Streaming](/docs/client-sdk/streaming) — Advanced streaming UI
|
|
343
|
+
|