@rodrigocoliveira/agno-react 1.0.1 → 1.0.2

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @rodrigocoliveira/agno-react
2
2
 
3
- React hooks for Agno client with full TypeScript support.
3
+ React hooks and pre-built UI components for Agno client with full TypeScript support.
4
4
 
5
5
  ## Installation
6
6
 
@@ -12,11 +12,14 @@ This package includes `@rodrigocoliveira/agno-client` and `@rodrigocoliveira/agn
12
12
 
13
13
  ## Features
14
14
 
15
- - **Easy Integration** - Drop-in React hooks for Agno agents
16
- - **Context Provider** - Manages client lifecycle automatically
17
- - **Real-time Updates** - React state synced with streaming updates
18
- - **Type-Safe** - Full TypeScript support
19
- - **Familiar API** - Matches the original Agno React hooks design
15
+ - **Easy Integration** Drop-in React hooks for Agno agents
16
+ - **Context Provider** Manages client lifecycle automatically
17
+ - **Real-time Updates** React state synced with streaming updates
18
+ - **Pre-built UI Components** Compound components and primitives via `/ui` sub-path
19
+ - **Audio Recording & Transcription** Record audio to send or transcribe to text
20
+ - **Frontend Tool Execution (HITL)** — Execute agent tools in the browser
21
+ - **Type-Safe** — Full TypeScript support
22
+ - **Familiar API** — Matches the original Agno React hooks design
20
23
 
21
24
  ## Quick Start
22
25
 
@@ -32,13 +35,9 @@ function App() {
32
35
  endpoint: 'http://localhost:7777',
33
36
  mode: 'agent',
34
37
  agentId: 'your-agent-id',
35
- userId: 'user-123', // Optional: Link sessions to a user
36
- headers: { // Optional: Global headers for all requests
37
- 'X-API-Version': 'v2'
38
- },
39
- params: { // Optional: Global query params for all requests
40
- locale: 'en-US'
41
- }
38
+ userId: 'user-123',
39
+ headers: { 'X-API-Version': 'v2' },
40
+ params: { locale: 'en-US' }
42
41
  }}
43
42
  >
44
43
  <YourComponents />
@@ -80,6 +79,36 @@ function ChatComponent() {
80
79
  }
81
80
  ```
82
81
 
82
+ ### 3. Or Use Pre-built UI Components
83
+
84
+ For a full-featured chat interface with minimal code, use the compound components from the `/ui` sub-path:
85
+
86
+ ```tsx
87
+ import { AgnoChat } from '@rodrigocoliveira/agno-react/ui';
88
+
89
+ function ChatPage() {
90
+ return (
91
+ <AgnoChat>
92
+ <AgnoChat.Messages>
93
+ <AgnoChat.EmptyState>
94
+ <h3>Welcome!</h3>
95
+ <p>Start a conversation with the agent.</p>
96
+ <AgnoChat.SuggestedPrompts
97
+ prompts={[
98
+ { text: 'What can you help me with?' },
99
+ { text: 'Show me a code example' },
100
+ ]}
101
+ />
102
+ </AgnoChat.EmptyState>
103
+ </AgnoChat.Messages>
104
+ <AgnoChat.ToolStatus />
105
+ <AgnoChat.ErrorBar />
106
+ <AgnoChat.Input placeholder="Ask me anything..." />
107
+ </AgnoChat>
108
+ );
109
+ }
110
+ ```
111
+
83
112
  ## API Reference
84
113
 
85
114
  ### AgnoProvider
@@ -93,8 +122,8 @@ Provider component that creates and manages an `AgnoClient` instance.
93
122
  ```
94
123
 
95
124
  **Props:**
96
- - `config` (AgnoClientConfig) - Client configuration
97
- - `children` (ReactNode) - Child components
125
+ - `config` (AgnoClientConfig) Client configuration
126
+ - `children` (ReactNode) Child components
98
127
 
99
128
  ### useAgnoClient()
100
129
 
@@ -145,12 +174,6 @@ await sendMessage('Hello!', {
145
174
  await sendMessage('Hello!', {
146
175
  params: { temperature: '0.7', max_tokens: '500' }
147
176
  });
148
-
149
- // Send with both headers and params
150
- await sendMessage('Hello!', {
151
- headers: { 'X-Request-ID': '12345' },
152
- params: { debug: 'true' }
153
- });
154
177
  ```
155
178
 
156
179
  #### `clearMessages()`
@@ -167,96 +190,353 @@ Hook for session management.
167
190
  const {
168
191
  sessions, // SessionEntry[] - Available sessions
169
192
  currentSessionId, // string | undefined - Current session ID
170
- loadSession, // (sessionId) => Promise<ChatMessage[]>
171
- fetchSessions, // () => Promise<SessionEntry[]>
193
+ loadSession, // (sessionId, options?) => Promise<ChatMessage[]>
194
+ fetchSessions, // (options?) => Promise<SessionEntry[]>
172
195
  isLoading, // boolean - Is loading session
173
196
  error, // string | undefined - Current error
174
197
  } = useAgnoSession();
175
198
  ```
176
199
 
177
- **Example:**
200
+ ### useAgnoActions()
201
+
202
+ Hook for common actions and initialization.
178
203
 
179
204
  ```tsx
180
- function SessionList() {
181
- const { sessions, loadSession, fetchSessions } = useAgnoSession();
205
+ const {
206
+ initialize, // (options?) => Promise<{ agents, teams }>
207
+ checkStatus, // (options?) => Promise<boolean>
208
+ fetchAgents, // (options?) => Promise<AgentDetails[]>
209
+ fetchTeams, // (options?) => Promise<TeamDetails[]>
210
+ updateConfig, // (updates) => void
211
+ isInitializing, // boolean
212
+ error, // string | undefined
213
+ } = useAgnoActions();
214
+ ```
182
215
 
183
- useEffect(() => {
184
- // Fetch sessions with optional query params
185
- fetchSessions({ params: { limit: '50', status: 'active' } });
186
- }, [fetchSessions]);
216
+ ### useAgnoToolExecution()
187
217
 
188
- const handleLoadSession = (sessionId: string) => {
189
- // Load session with optional params
190
- loadSession(sessionId, { params: { include_metadata: 'true' } });
191
- };
218
+ Hook for frontend tool execution (Human-in-the-Loop).
192
219
 
193
- return (
194
- <ul>
195
- {sessions.map((session) => (
196
- <li key={session.session_id}>
197
- <button onClick={() => handleLoadSession(session.session_id)}>
198
- {session.session_name}
199
- </button>
200
- </li>
201
- ))}
202
- </ul>
203
- );
204
- }
220
+ ```tsx
221
+ const toolHandlers = {
222
+ show_alert: async (args) => {
223
+ alert(args.content);
224
+ return { success: true };
225
+ },
226
+ };
227
+
228
+ // Auto-execute tools immediately
229
+ useAgnoToolExecution(toolHandlers);
230
+
231
+ // Or require manual confirmation
232
+ useAgnoToolExecution(toolHandlers, false);
205
233
  ```
206
234
 
207
- ### useAgnoActions()
235
+ ### useAgnoMemory()
208
236
 
209
- Hook for common actions and initialization.
237
+ Hook for memory management.
210
238
 
211
239
  ```tsx
212
240
  const {
213
- initialize, // () => Promise<{ agents, teams }>
214
- checkStatus, // () => Promise<boolean>
215
- fetchAgents, // () => Promise<AgentDetails[]>
216
- fetchTeams, // () => Promise<TeamDetails[]>
217
- updateConfig, // (updates) => void
218
- isInitializing, // boolean
219
- error, // string | undefined
220
- } = useAgnoActions();
241
+ memories,
242
+ topics,
243
+ isLoading,
244
+ fetchMemories,
245
+ createMemory,
246
+ updateMemory,
247
+ deleteMemory,
248
+ } = useAgnoMemory();
221
249
  ```
222
250
 
223
- **Example:**
251
+ ### useAgnoCustomEvents()
252
+
253
+ Hook for listening to custom events yielded by the backend.
224
254
 
225
255
  ```tsx
226
- function InitComponent() {
227
- const { initialize, fetchAgents, updateConfig, isInitializing } = useAgnoActions();
228
- const { state } = useAgnoChat();
256
+ useAgnoCustomEvents((event) => {
257
+ console.log('Custom event:', event);
258
+ });
259
+ ```
229
260
 
230
- useEffect(() => {
231
- // Initialize with optional params
232
- initialize({ params: { filter: 'active' } });
233
- }, [initialize]);
261
+ ---
234
262
 
235
- const loadMoreAgents = () => {
236
- // Fetch agents with custom params
237
- fetchAgents({ params: { page: '2', limit: '20' } });
238
- };
263
+ ## Pre-built UI Components (`/ui` sub-path)
239
264
 
240
- const switchAgent = (agentId: string) => {
241
- updateConfig({ agentId, mode: 'agent' });
242
- };
265
+ The library ships with a complete set of pre-built UI components accessible via `@rodrigocoliveira/agno-react/ui`. These components provide a production-ready chat interface with full customization support.
266
+
267
+ ### Peer Dependencies
243
268
 
244
- if (isInitializing) return <div>Loading...</div>;
269
+ UI components rely on optional peer dependencies. Install only what you need:
245
270
 
271
+ ```bash
272
+ # Core UI dependencies
273
+ npm install @radix-ui/react-slot class-variance-authority clsx tailwind-merge lucide-react
274
+
275
+ # For markdown rendering
276
+ npm install shiki streamdown
277
+
278
+ # For auto-scroll behavior
279
+ npm install use-stick-to-bottom
280
+
281
+ # For additional primitives (as needed)
282
+ npm install @radix-ui/react-accordion @radix-ui/react-avatar @radix-ui/react-collapsible \
283
+ @radix-ui/react-tooltip @radix-ui/react-dropdown-menu @radix-ui/react-hover-card \
284
+ @radix-ui/react-select cmdk
285
+ ```
286
+
287
+ ### Import Path
288
+
289
+ All UI components are imported from the `/ui` sub-path:
290
+
291
+ ```tsx
292
+ import { AgnoChat, AgnoChatInterface, AgnoChatInput, Button, Response } from '@rodrigocoliveira/agno-react/ui';
293
+ ```
294
+
295
+ ---
296
+
297
+ ### AgnoChat (Compound Component)
298
+
299
+ The primary way to build a full-featured chat interface. Uses a compound component pattern — compose only the pieces you need.
300
+
301
+ ```tsx
302
+ import { AgnoChat } from '@rodrigocoliveira/agno-react/ui';
303
+ import type { ToolHandler } from '@rodrigocoliveira/agno-react';
304
+
305
+ const toolHandlers: Record<string, ToolHandler> = {
306
+ show_alert: async (args) => {
307
+ alert(args.content);
308
+ return { success: true };
309
+ },
310
+ };
311
+
312
+ function ChatPage() {
246
313
  return (
247
- <div>
248
- <h3>Agents</h3>
249
- {state.agents.map((agent) => (
250
- <button key={agent.id} onClick={() => switchAgent(agent.id)}>
251
- {agent.name}
252
- </button>
253
- ))}
254
- <button onClick={loadMoreAgents}>Load More</button>
255
- </div>
314
+ <AgnoChat toolHandlers={toolHandlers} autoExecuteTools={true}>
315
+ <AgnoChat.Messages
316
+ userAvatar={<img src="/user.png" className="h-8 w-8 rounded-full" />}
317
+ assistantAvatar={<img src="/bot.png" className="h-8 w-8 rounded-full" />}
318
+ messageItemProps={{
319
+ showToolCalls: false,
320
+ showReasoning: false,
321
+ renderActions: (message) => (
322
+ <button onClick={() => navigator.clipboard.writeText(message.content || '')}>
323
+ Copy
324
+ </button>
325
+ ),
326
+ }}
327
+ >
328
+ <AgnoChat.EmptyState>
329
+ <h3>Welcome!</h3>
330
+ <p>How can I help you today?</p>
331
+ <AgnoChat.SuggestedPrompts
332
+ prompts={[
333
+ { icon: <span>⚡</span>, text: 'What can you help me with?' },
334
+ { icon: <span>💡</span>, text: 'Show me a code example' },
335
+ ]}
336
+ />
337
+ </AgnoChat.EmptyState>
338
+ </AgnoChat.Messages>
339
+
340
+ <AgnoChat.ToolStatus className="bg-violet-500/5 border-violet-500/20" />
341
+ <AgnoChat.ErrorBar className="bg-red-500/5" />
342
+ <AgnoChat.Input
343
+ placeholder="Ask me anything..."
344
+ showAudioRecorder={true}
345
+ audioMode="transcribe"
346
+ transcriptionEndpoint="http://localhost:8000/transcribe"
347
+ />
348
+ </AgnoChat>
349
+ );
350
+ }
351
+ ```
352
+
353
+ **Sub-components:**
354
+
355
+ | Component | Description |
356
+ |-----------|-------------|
357
+ | `AgnoChat` | Root wrapper. Accepts `toolHandlers` and `autoExecuteTools` props. |
358
+ | `AgnoChat.Messages` | Message list with auto-scroll. Accepts `userAvatar`, `assistantAvatar`, `messageItemProps`. |
359
+ | `AgnoChat.EmptyState` | Shown when there are no messages. Place inside `Messages`. |
360
+ | `AgnoChat.SuggestedPrompts` | Clickable prompt suggestions. Place inside `EmptyState`. |
361
+ | `AgnoChat.ToolStatus` | Status bar shown when tools are executing. |
362
+ | `AgnoChat.ErrorBar` | Error display bar. |
363
+ | `AgnoChat.Input` | Chat input with file uploads and optional audio recorder. |
364
+
365
+ ---
366
+
367
+ ### AgnoChatInterface
368
+
369
+ A single-component shortcut that renders a complete chat interface. Less flexible than `AgnoChat` but requires zero composition.
370
+
371
+ ```tsx
372
+ import { AgnoChatInterface } from '@rodrigocoliveira/agno-react/ui';
373
+
374
+ function ChatPage() {
375
+ return (
376
+ <AgnoChatInterface
377
+ placeholder="Type a message..."
378
+ suggestedPrompts={[
379
+ { text: 'What can you help me with?' },
380
+ { text: 'Explain how you work' },
381
+ ]}
382
+ toolHandlers={toolHandlers}
383
+ showAudioRecorder={true}
384
+ userAvatar={<img src="/user.png" />}
385
+ assistantAvatar={<img src="/bot.png" />}
386
+ emptyState={<div>Start a conversation!</div>}
387
+ classNames={{
388
+ root: 'h-full',
389
+ messagesArea: 'px-4',
390
+ inputArea: 'border-t',
391
+ }}
392
+ />
256
393
  );
257
394
  }
258
395
  ```
259
396
 
397
+ **Key props:** `className`, `classNames`, `renderMessage`, `renderInput`, `emptyState`, `headerSlot`, `inputToolbarSlot`, `suggestedPrompts`, `toolHandlers`, `autoExecuteTools`, `placeholder`, `userAvatar`, `assistantAvatar`, `fileUpload`, `showAudioRecorder`, `messageItemProps`, `chatInputProps`.
398
+
399
+ ---
400
+
401
+ ### AgnoChatInput
402
+
403
+ Standalone chat input component with file uploads, audio recording, and transcription support.
404
+
405
+ ```tsx
406
+ import { AgnoChatInput } from '@rodrigocoliveira/agno-react/ui';
407
+
408
+ <AgnoChatInput
409
+ onSend={(message) => { /* handle message */ }}
410
+ placeholder="Type a message..."
411
+ showAudioRecorder={true}
412
+ showAttachments={true}
413
+ audioMode="transcribe"
414
+ transcriptionEndpoint="http://localhost:8000/transcribe"
415
+ parseTranscriptionResponse={(data) => data.text}
416
+ onRequestPermission={async () => {
417
+ // WebView: request mic permission from native bridge
418
+ return await NativeBridge.requestMicPermission();
419
+ }}
420
+ fileUpload={{
421
+ accept: 'image/*,.pdf',
422
+ multiple: true,
423
+ maxFiles: 5,
424
+ maxFileSize: 10 * 1024 * 1024,
425
+ }}
426
+ />
427
+ ```
428
+
429
+ **Props:**
430
+
431
+ | Prop | Type | Default | Description |
432
+ |------|------|---------|-------------|
433
+ | `onSend` | `(message: string \| FormData) => void` | required | Called when the user sends a message |
434
+ | `disabled` | `boolean` | `false` | Disable the input |
435
+ | `placeholder` | `string` | — | Input placeholder text |
436
+ | `showAudioRecorder` | `boolean` | `false` | Show the audio recorder button |
437
+ | `showAttachments` | `boolean` | `true` | Show the file attachment button |
438
+ | `audioMode` | `'send' \| 'transcribe'` | `'send'` | Audio recording behavior |
439
+ | `transcriptionEndpoint` | `string` | — | URL to POST audio for transcription (required when `audioMode='transcribe'`) |
440
+ | `transcriptionHeaders` | `Record<string, string>` | — | Extra headers for transcription requests |
441
+ | `parseTranscriptionResponse` | `(data: unknown) => string` | — | Custom parser for transcription API response |
442
+ | `onRequestPermission` | `() => Promise<boolean>` | — | WebView mic permission bridge callback |
443
+ | `fileUpload` | `FileUploadConfig` | — | File upload configuration |
444
+ | `status` | `ChatStatus` | — | Input status (`'idle'`, `'submitted'`, `'streaming'`, `'error'`) |
445
+ | `extraTools` | `ReactNode` | — | Additional toolbar buttons |
446
+
447
+ ---
448
+
449
+ ### Audio Recorder & Transcription
450
+
451
+ The library includes an `AudioRecorder` component that supports two modes:
452
+
453
+ #### Send mode (default)
454
+
455
+ Records audio, encodes to WAV, and sends the blob directly as a file attachment:
456
+
457
+ ```tsx
458
+ <AgnoChatInput
459
+ onSend={handleSend}
460
+ showAudioRecorder={true}
461
+ audioMode="send"
462
+ />
463
+ ```
464
+
465
+ The audio blob is wrapped in a `FormData` with `message="Audio message"` and the WAV file.
466
+
467
+ #### Transcribe mode
468
+
469
+ Records audio, sends it to a transcription endpoint, and inserts the resulting text into the input:
470
+
471
+ ```tsx
472
+ <AgnoChatInput
473
+ onSend={handleSend}
474
+ showAudioRecorder={true}
475
+ audioMode="transcribe"
476
+ transcriptionEndpoint="http://localhost:8000/transcribe"
477
+ parseTranscriptionResponse={(data) => data.text}
478
+ />
479
+ ```
480
+
481
+ The component POSTs the WAV file to the endpoint and expects a JSON response. The default parser checks `data.text`, `data.transcript`, and `data.transcription` fields. Provide `parseTranscriptionResponse` to handle custom response shapes.
482
+
483
+ #### WebView Permission Bridging
484
+
485
+ For WebView environments where microphone access requires a native bridge:
486
+
487
+ ```tsx
488
+ <AgnoChatInput
489
+ onSend={handleSend}
490
+ showAudioRecorder={true}
491
+ onRequestPermission={async () => {
492
+ // Ask the native app for mic permission before getUserMedia
493
+ return await NativeBridge.requestMicPermission();
494
+ }}
495
+ />
496
+ ```
497
+
498
+ The `onRequestPermission` callback is called before the browser's `getUserMedia`. Return `true` to proceed or `false` to cancel.
499
+
500
+ ---
501
+
502
+ ### Primitive Components
503
+
504
+ Thin wrappers over Radix UI primitives with Tailwind styling via `class-variance-authority`:
505
+
506
+ | Component | Description |
507
+ |-----------|-------------|
508
+ | `Button` | Button with variants (`default`, `outline`, `ghost`, `destructive`, etc.) |
509
+ | `Badge` | Status badge with variants |
510
+ | `Avatar`, `AvatarImage`, `AvatarFallback` | User/assistant avatar |
511
+ | `InputGroup`, `InputGroupAddon`, `InputGroupButton`, `InputGroupInput`, `InputGroupTextarea` | Composable input groups |
512
+ | `Collapsible`, `CollapsibleTrigger`, `CollapsibleContent` | Expandable sections |
513
+ | `Tooltip`, `TooltipTrigger`, `TooltipContent`, `TooltipProvider` | Hover tooltips |
514
+ | `Accordion`, `AccordionItem`, `AccordionTrigger`, `AccordionContent` | Collapsible accordion |
515
+ | `DropdownMenu` + sub-parts | Dropdown menu |
516
+ | `HoverCard`, `HoverCardTrigger`, `HoverCardContent` | Hover card |
517
+ | `Select` + sub-parts | Select dropdown |
518
+ | `Command`, `CommandInput`, `CommandList`, `CommandItem`, ... | Command palette (cmdk) |
519
+
520
+ ---
521
+
522
+ ### Base Components
523
+
524
+ Higher-level components for building chat interfaces:
525
+
526
+ | Component | Description |
527
+ |-----------|-------------|
528
+ | `Message`, `MessageContent`, `MessageAvatar` | Low-level message layout shell |
529
+ | `Conversation`, `ConversationContent`, `ConversationEmptyState`, `ConversationScrollButton` | Scrollable conversation container with auto-scroll |
530
+ | `Response` | Markdown renderer with syntax highlighting (shiki + streamdown) |
531
+ | `Tool`, `ToolHeader`, `ToolContent`, `ToolInput`, `ToolOutput` | Collapsible tool call display |
532
+ | `CodeBlock`, `CodeBlockCopyButton` | Syntax-highlighted code block with copy button |
533
+ | `Artifact`, `ArtifactHeader`, `ArtifactContent`, ... | Artifact panel layout |
534
+ | `StreamingIndicator` | Animated typing/loading indicator |
535
+ | `AudioRecorder` | Audio recording with WAV encoding via AudioWorklet |
536
+ | `PromptInput` + sub-parts | Fully composable input system with attachments, speech, model select |
537
+
538
+ ---
539
+
260
540
  ## Complete Example
261
541
 
262
542
  ```tsx
package/dist/index.js CHANGED
@@ -28,8 +28,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
 
30
30
  // src/index.ts
31
- var index_exports = {};
32
- __export(index_exports, {
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
33
  AgnoProvider: () => AgnoProvider,
34
34
  ComponentRegistry: () => ComponentRegistry,
35
35
  GenerativeUIRenderer: () => GenerativeUIRenderer,
@@ -67,7 +67,7 @@ __export(index_exports, {
67
67
  useAgnoTraces: () => useAgnoTraces,
68
68
  useToolHandlers: () => useToolHandlers
69
69
  });
70
- module.exports = __toCommonJS(index_exports);
70
+ module.exports = __toCommonJS(src_exports);
71
71
 
72
72
  // src/context/AgnoContext.tsx
73
73
  var import_react = require("react");