useChat Hook

The useChat hook is the core logic provider in @ulifeai/agentb-ui for building chat interfaces. It manages communication with the AgentB backend, processes Server-Sent Events (SSE), and maintains the state of the conversation. The <AgentBChat /> component uses this hook internally.

Import:

import { useChat, UseChatOptions, UseChatReturn, ChatMessage } from '@ulifeai/agentb-ui';
// Or from '@ulifeai/agentb-ui/hooks/useChat' depending on package structure

useChat(options: UseChatOptions): UseChatReturn

UseChatOptions (Input)

An object to configure the useChat hook:

  • backendUrl: string (Required):

    • The full URL of your AgentB backend's streaming SSE endpoint (e.g., http://localhost:3001/agent/stream).

  • initialThreadId?: string:

    • Optional. If provided, the hook will attempt to use this thread ID for the conversation. Useful for resuming existing conversations.

    • If not provided, a new threadId might be generated by the backend (if the backend's getThreadId callback creates one) or a temporary UI-generated one might be used initially, which gets confirmed/updated by the first event from the backend that contains a threadId.

  • initialMessages?: ChatMessage[]:

    • Optional. An array of ChatMessage objects to pre-populate the chat interface. Useful for welcome messages or loading conversation history.

ChatMessage Interface (for initialMessages and messages return value):

interface ChatMessage {
  id: string; // Unique message ID
  text: string; // Main display text
  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'];
    toolName?: string;
    toolInput?: Record<string, any> | string;
    toolOutput?: any;
    isError?: boolean;
    stepId?: string;
    originalEvent?: AgentEvent; // Full backend event
  };
}

UseChatReturn (Output)

An object containing state and functions to manage the chat:

  • messages: ChatMessage[]:

    • An array of ChatMessage objects representing the current state of the conversation. This array is reactive and updates as new events are processed from the backend.

  • sendMessage: (text: string) => Promise<void>:

    • An asynchronous function to send a new user message to the backend.

    • text: string: The content of the user's message.

    • It handles creating the user message in the UI, sending the request to backendUrl, and then processing the subsequent SSE stream of AgentEvents.

  • isLoading: boolean:

    • true when a user message has been sent and the hook is waiting for the initial response or start of the stream from the backend (before any thread.message.delta for the assistant has arrived).

    • false otherwise.

  • isStreaming: boolean:

    • true if the assistant is currently streaming a text response (i.e., thread.message.delta events with contentChunk are being received for an active assistant message).

    • false otherwise.

  • error: string | null:

    • Contains an error message string if an error occurred during the sendMessage process or while handling the stream.

    • null if there are no errors.

  • threadId: string | null:

    • The current conversation thread ID.

    • It's initialized with initialThreadId if provided, or updated by the backend from the first event containing a threadId. Can be null initially if no initialThreadId is given and no backend events have been received yet.

  • setMessages: React.Dispatch<React.SetStateAction<ChatMessage[]>>:

    • The React state setter function for the messages array.

    • Allows direct manipulation of the displayed messages, though this should be used with caution as the hook normally manages this state based on backend events. Useful for scenarios like optimistically adding messages or clearing the chat.

  • currentRunId: string | null:

    • The runId of the currently active agent execution on the backend. null if no run is active.

How useChat Works Internally

  1. Initialization:

    • Sets up internal state for messages, loading status, thread ID, etc.

    • Uses initialMessages and initialThreadId if provided.

  2. sendMessage(text) Call:

    • Adds the user's message to the messages array with status: 'sending'.

    • Sets isLoading: true.

    • Resets internal accumulators for assistant responses and tool call message tracking.

    • Makes a POST request to the backendUrl with the user's message (content: text) and the current threadId. The request headers typically include Accept: text/event-stream.

    • The backend is expected to respond with an SSE stream.

    • Updates the user message status to sent once the request is successfully initiated.

    • Obtains a ReadableStreamDefaultReader from the response body.

  3. SSE Stream Processing:

    • Uses the parseSSEStream utility (from ui/src/api.ts) to consume and parse AgentEvents from the stream reader.

    • For each AgentEvent:

      • Updates threadId and currentRunId: If the event contains these IDs and they are new or different, the hook's state is updated.

      • Transforms AgentEvent to ChatMessage: Logic within the hook (specifically upsertMessage and addChatMessage callbacks) processes different AgentEvent.types:

        • thread.message.created (assistant, inProgress: true): Creates a new assistant ChatMessage with status: 'streaming'. An activeAssistantMessageIdRef is set to track this message.

        • thread.message.delta (contentChunk): Appends the contentChunk to the text of the active assistant message. Sets isStreaming: true.

        • thread.message.delta (toolCallsChunk): May update metadata for the active assistant message or create/update separate ChatMessages of type tool_thought if the UI is designed to show tool calls as they are being formed by the LLM (though more commonly, full tool call info comes from thread.message.completed or dedicated tool events).

        • thread.message.completed (assistant): Finalizes the text of the active assistant message, sets its status: 'sent', and potentially updates its timestamp and originalEvent. Sets isStreaming: false.

        • thread.run.step.tool_call.created: Creates a ChatMessage with sender: 'tool_thought', text like "Agent plans to use tool X...", and status: 'in_progress'. Maps toolCall.id to this ChatMessage.id for later updates.

        • agent.tool.execution.started: Updates the corresponding tool_thought message to sender: 'tool_executing' with text like "Executing tool X...".

        • agent.tool.execution.completed: Updates the corresponding message to sender: 'tool_result', text reflecting success/failure, and status 'completed' or 'error'. Stores toolOutput and isError in metadata.

        • agent.sub_agent.invocation.completed: Similar to agent.tool.execution.completed, but updates the message related to the DelegateToSpecialistTool call, often including details about the sub-agent.

        • thread.run.failed: Sets the global error state, updates any active assistant message to status: 'failed', and sets isLoading: false, isStreaming: false.

        • thread.run.completed: Sets isLoading: false, isStreaming: false. Finalizes any active assistant message.

        • Other events might create 'system' messages or update metadata.

      • Updates the messages state array, triggering UI re-renders.

  4. Stream End / Error:

    • When the stream ends (either normally or due to an error), isLoading and isStreaming are set to false.

    • If an error occurred during fetching or parsing the stream, the error state is set.

    • The ReadableStreamDefaultReader is released.

Example Usage

Refer to the UI Integration Guide for a practical example of using useChat to build a custom chat interface.

The useChat hook is the powerhouse behind @ulifeai/agentb-ui, providing all the necessary client-side logic for a fully interactive and streaming chat experience with your AgentB backend.

Last updated