Skip to content

useAgentChat

useAgentChat is the core hook for building custom chat interfaces. It manages the message list, user input, loading state, streaming, error handling, and communication with the agent backend. This is the same hook used internally by AgentChat.

Import

import { useAgentChat } from "@modernpath/agent-ui-react";

Options

interface UseAgentChatOptions {
  /** Backend instance for communicating with the agent */
  backend: AgentBackend;

  /** Auditing context identifier */
  auditingId: number;

  /** Agent type identifier sent in the execute request */
  agentType: string;

  /**
   * Maximum number of messages included in the chat history.
   * Older messages are trimmed from the beginning.
   * @default 50
   */
  maxHistoryMessages?: number;

  /**
   * Custom function to format the assistant response content.
   * Receives the raw response data and returns the display string.
   */
  formatResponse?: (data: AgentExecuteResponse<any>) => string;

  /**
   * Enable streaming via Server-Sent Events.
   * Requires the backend to provide an `executeStream` method.
   * @default false
   */
  stream?: boolean;
}

Return Value

interface UseAgentChatReturn {
  /** Array of chat messages (user and assistant) */
  messages: AgentChatMessage[];

  /** Whether a request or stream is currently in progress */
  isLoading: boolean;

  /** Error from the last failed request, or null */
  error: Error | null;

  /** Current value of the text input */
  input: string;

  /** Setter for the text input value */
  setInput: (value: string) => void;

  /**
   * Send the current input as a user message.
   * Appends the user message, clears the input,
   * calls the backend, and appends the assistant response.
   */
  send: () => void;

  /** Clear all messages and reset the chat state */
  reset: () => void;

  /**
   * Abort the current request or stream.
   * Has no effect if no request is in progress.
   */
  abort: () => void;
}

AgentChatMessage Type

interface AgentChatMessage {
  /** Message role */
  role: ChatRole; // "user" | "assistant"

  /** Display content (text) */
  content: string;

  /** Timestamp of the message */
  timestamp: number;

  /**
   * Full response data from the backend (assistant messages only).
   * Contains execution details, grounding context, statistics, etc.
   */
  data?: AgentExecuteResponse<any>;
}

Behavior

Message Flow

  1. When send() is called, the hook appends a user message to the messages array and clears input.
  2. If stream is false, the hook calls backend.execute() and waits for the complete response.
  3. If stream is true, the hook calls backend.executeStream() and incrementally updates the assistant message content as delta events arrive.
  4. On completion, the full response data is attached to the assistant message's data field.
  5. On error, the error state is set and isLoading returns to false.

History Trimming

The hook maintains at most maxHistoryMessages messages in the messages array. When the limit is exceeded, the oldest messages are removed. The trimmed history is also what gets sent to the backend as chat context.

Abort

Calling abort() cancels the in-flight HTTP request or closes the SSE stream. The assistant message is preserved with whatever content was received up to that point.

Cleanup

The hook automatically aborts any in-flight request when the component unmounts.

Usage

Basic Custom Chat

import {
  useAgentChat,
  createSseAgentBackend,
} from "@modernpath/agent-ui-react";

const backend = createSseAgentBackend({ baseUrl: "/api" });

function CustomChat() {
  const { messages, isLoading, input, setInput, send, reset } =
    useAgentChat({
      backend,
      auditingId: 1,
      agentType: "qa-chat",
      stream: true,
    });

  return (
    <div>
      <div style={{ maxHeight: 400, overflow: "auto" }}>
        {messages.map((msg, i) => (
          <div key={i}>
            <strong>{msg.role === "user" ? "You" : "Assistant"}:</strong>
            <p>{msg.content}</p>
          </div>
        ))}
      </div>
      <form
        onSubmit={(e) => {
          e.preventDefault();
          send();
        }}
      >
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          disabled={isLoading}
          placeholder="Ask a question..."
        />
        <button type="submit" disabled={isLoading}>
          Send
        </button>
        <button type="button" onClick={reset}>
          Clear
        </button>
      </form>
    </div>
  );
}

With Custom Response Formatting

import { useAgentChat } from "@modernpath/agent-ui-react";

function FormattedChat() {
  const { messages, input, setInput, send, isLoading } = useAgentChat({
    backend,
    auditingId: 1,
    agentType: "incident-resolver",
    stream: false,
    formatResponse: (response) => {
      // Extract just the answer text from a structured response
      return response.data?.answer ?? response.data?.message ?? "No response";
    },
  });

  // ... render messages
}

With Error Handling and Abort

import { useAgentChat } from "@modernpath/agent-ui-react";

function ChatWithControls() {
  const { messages, isLoading, error, input, setInput, send, abort, reset } =
    useAgentChat({
      backend,
      auditingId: 1,
      agentType: "qa-chat",
      stream: true,
      maxHistoryMessages: 20,
    });

  return (
    <div>
      {messages.map((msg, i) => (
        <div key={i}>{msg.content}</div>
      ))}

      {error && (
        <div style={{ color: "red" }}>
          Error: {error.message}
          <button onClick={() => send()}>Retry</button>
        </div>
      )}

      <div style={{ display: "flex", gap: 8 }}>
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          onKeyDown={(e) => e.key === "Enter" && !isLoading && send()}
        />
        {isLoading ? (
          <button onClick={abort}>Stop</button>
        ) : (
          <button onClick={send}>Send</button>
        )}
        <button onClick={reset}>Reset</button>
      </div>
    </div>
  );
}