UI Integration with @ulifeai/agentb-ui
The @ulifeai/agentb-ui
package provides React components and hooks to quickly build a rich, interactive chat interface for your AgentB agents. It handles the complexities of Server-Sent Event (SSE) stream consumption, message state management, and rendering of different event types.
This guide focuses on using the primary components and the useChat
hook.
Key Components & Hooks
<AgentBChat />
Component:The main, all-in-one chat component.
Renders a complete chat interface including a message list, message input field, and status indicators.
Internally uses the
useChat
hook to manage state and communication.
useChat
Hook (import { useChat } from '@ulifeai/agentb-ui';
):The core React hook for managing agent interactions.
Responsibilities:
Initiating requests to your AgentB backend's streaming SSE endpoint.
Consuming and parsing the SSE stream of
AgentEvent
s usingparseSSEStream
.Managing the list of
ChatMessage
objects displayed in the UI.Handling message accumulation for streaming text responses.
Tracking loading states, streaming status, and errors.
Providing a
sendMessage
function to send user input to the backend.
Returns: An object containing
messages
,sendMessage
,isLoading
,isStreaming
,error
,threadId
,setMessages
, andcurrentRunId
.
Supporting Components (Often used internally by
<AgentBChat />
):<MessageList />
: Renders the list ofChatMessage
s.<MessageItem />
: Renders a single chat message, styled according to its sender and content type (text, tool thought, tool result, error, etc.).<MessageInput />
: The text input field for the user.
Types (
ChatMessage
,AgentEvent
, etc.):The package re-exports
AgentEvent
types (mirrored from the backend) and defines UI-specific types likeChatMessage
which adaptsAgentEvent
data for display.
Using <AgentBChat />
<AgentBChat />
This is the simplest way to add a full chat UI.
1. Installation (Reminder):
npm install @ulifeai/agentb-ui react-icons uuid tailwind-merge clsx
# or
yarn add @ulifeai/agentb-ui react-icons uuid tailwind-merge clsx
2. Import and Use:
import React from 'react';
import { AgentBChat, UseChatOptions } from '@ulifeai/agentb-ui';
// You might need to import default styles or set up Tailwind
// import '@ulifeai/agentb-ui/dist/style.css'; // Path depends on package structure
function MyChatPage() {
const chatOptions: UseChatOptions = {
backendUrl: 'http://localhost:3001/agent/stream', // Your AgentB SSE endpoint
// Optional:
// initialThreadId: 'existing-thread-id',
// initialMessages: [
// { id: '1', text: 'Hello from initial props!', sender: 'user', status: 'sent', timestamp: new Date().toISOString() }
// ],
};
return (
<div style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}>
{/* Optional: Add your app's header/navbar here */}
<header style={{ padding: '1rem', backgroundColor: '#eee' }}>My AgentB Chat App</header>
<div style={{ flexGrow: 1, position: 'relative' /* For potential absolute positioning inside AgentBChat */ }}>
<AgentBChat
options={chatOptions}
// --- Optional Customization Props ---
// title="My Virtual Assistant"
// inputPlaceholder="Type your message..."
// showSuggestions={false} // Example: disable default suggestions
// See package documentation for all available props
/>
</div>
</div>
);
}
export default MyChatPage;
3. Backend Server:
Ensure your AgentB backend (e.g., using AgentB.getExpressStreamingHttpHandler()
) is running and accessible at the backendUrl
. Crucially, it must have CORS enabled if your React app and backend are on different origins/ports.
The <AgentBChat />
component will then render a complete chat interface.
Using the useChat
Hook (For Custom UIs)
useChat
Hook (For Custom UIs)If you need to build a completely custom chat UI but want to leverage AgentB's robust state management and SSE handling, you can use the useChat
hook directly.
UseChatOptions
(passed to the hook):
interface UseChatOptions {
backendUrl: string;
initialThreadId?: string;
initialMessages?: ChatMessage[]; // UI's ChatMessage type
}
UseChatReturn
(what the hook returns):
interface UseChatReturn {
messages: ChatMessage[]; // Array of messages to display
sendMessage: (text: string) => Promise<void>; // Function to send a user message
isLoading: boolean; // True if waiting for the initial part of agent's response (before streaming starts)
isStreaming: boolean; // True if the agent is currently streaming text
error: string | null; // Any error message from the interaction
threadId: string | null; // The current conversation thread ID
setMessages: React.Dispatch<React.SetStateAction<ChatMessage[]>>; // To directly manipulate messages if needed
currentRunId: string | null; // ID of the current agent run
}
Example: Basic Custom UI with useChat
import React, { useState, useEffect, useRef } from 'react';
import { useChat, ChatMessage } from '@ulifeai/agentb-ui'; // Assuming ChatMessage is exported
function MyCustomChat() {
const {
messages,
sendMessage,
isLoading,
isStreaming,
error,
threadId
} = useChat({
backendUrl: 'http://localhost:3001/agent/stream',
});
const [inputText, setInputText] = useState('');
const messagesEndRef = useRef<null | HTMLDivElement>(null);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
};
useEffect(scrollToBottom, [messages]);
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!inputText.trim()) return;
sendMessage(inputText);
setInputText('');
};
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '400px', border: '1px solid #ccc' }}>
<div style={{ flexGrow: 1, overflowY: 'auto', padding: '10px' }}>
{messages.map((msg) => (
<div key={msg.id} style={{ marginBottom: '10px', textAlign: msg.sender === 'user' ? 'right' : 'left' }}>
<span style={{
backgroundColor: msg.sender === 'user' ? '#dcf8c6' : (msg.sender === 'system' || msg.metadata?.isError) ? '#fdecea' : '#fff',
padding: '5px 10px',
borderRadius: '7px',
boxShadow: '0 1px 0.5px rgba(0,0,0,0.13)',
display: 'inline-block',
maxWidth: '70%',
}}>
<strong>{msg.sender}: </strong>{msg.text}
{msg.status === 'failed' && <em style={{color: 'red'}}> (failed)</em>}
{msg.metadata?.toolName && <em style={{fontSize: '0.8em', display: 'block', color: '#555'}}>Tool: {msg.metadata.toolName}</em>}
</span>
</div>
))}
<div ref={messagesEndRef} />
</div>
{isLoading && <p>Agent is thinking...</p>}
{isStreaming && <p>Agent is typing...</p>}
{error && <p style={{ color: 'red' }}>Error: {error}</p>}
<form onSubmit={handleSubmit} style={{ display: 'flex', padding: '10px' }}>
<input
type="text"
value={inputText}
onChange={(e) => setInputText(e.target.value)}
placeholder="Type your message..."
style={{ flexGrow: 1, padding: '10px', marginRight: '10px' }}
disabled={isLoading || isStreaming}
/>
<button type="submit" disabled={isLoading || isStreaming} style={{ padding: '10px' }}>
Send
</button>
</form>
{threadId && <p style={{fontSize: '0.8em', textAlign: 'center'}}>Thread ID: {threadId}</p>}
</div>
);
}
export default MyCustomChat;
This example is basic but shows how useChat
provides the necessary state and functions to build your own interface.
ChatMessage
Interface (UI-Specific)
ChatMessage
Interface (UI-Specific)The messages
array returned by useChat
(and used by <AgentBChat />
) contains objects of type ChatMessage
. This interface is defined in @ulifeai/agentb-ui
(in ui/src/components/types.ts
) and adapts backend AgentEvent
data for display.
Key ChatMessage
properties:
export interface ChatMessage {
id: string; // Unique message ID (can be original message ID or generated for UI thoughts)
text: string; // Main content or description of the event for display
sender: 'user' | 'ai' | 'system' | 'tool_thought' | 'tool_executing' | 'tool_result';
timestamp: string; // ISO string
status: 'sending' | 'sent' | 'failed' | 'streaming' | 'in_progress' | 'completed' | 'error';
metadata?: {
eventType?: AgentEvent['type']; // Original backend event type
toolName?: string;
toolInput?: Record<string, any> | string;
toolOutput?: any; // Success data or error string/object
isError?: boolean;
stepId?: string;
originalEvent?: AgentEvent; // The full backend event for debugging/advanced rendering
};
}
The useChat
hook intelligently processes backend AgentEvent
s and creates/updates these ChatMessage
objects. For example:
thread.message.delta
withcontentChunk
appends to an 'ai' sender message withstatus: 'streaming'
.agent.tool.execution.started
might create a message withsender: 'tool_executing'
and text like "Executing tool X...".agent.tool.execution.completed
updates that message tosender: 'tool_result'
with success/failure details.
Styling
The <AgentBChat />
component and its sub-components are designed with Tailwind CSS in mind but include default functional styling. You can:
Use Tailwind: If your project uses Tailwind CSS, the components should pick up your theme and allow utility class overrides.
CSS Overrides: Target the components' CSS classes with your own stylesheets. Inspect the rendered HTML to find the class names used (they are typically prefixed, e.g.,
agentb-chat-container
,agentb-message-item
).Style Props (Limited): Some components might accept
style
orclassName
props for direct styling. Check the specific component's documentation/props.
Summary
@ulifeai/agentb-ui
significantly accelerates the development of chat interfaces for your AgentB agents.
For a quick, full-featured UI, use
<AgentBChat />
.For maximum control over UI presentation while still benefiting from AgentB's client-side logic, use the
useChat
hook.
Always ensure your AgentB backend server is running, correctly configured (especially CORS), and that the backendUrl
in your UI points to the correct SSE endpoint.
Last updated