Agent Chat UI

repository·main·Indexed 25 days ago

https://github.com/langchain-ai/agent-chat-ui

A Next.js application providing a ready-to-use chat interface for interacting with LangGraph servers that expose a messages key. It features support for human-in-the-loop (HITL) interruptions, artifact rendering in a side panel via the useArtifact hook, and multimodal content block previews. The UI can be configured via environment variables for API URLs and assistant IDs, and supports production deployment using the langgraph-nextjs-api-passthrough package or custom authentication.

Tokens
5.5K
Snippets
11
Records
34
Agent score
81%

What's inside agent-chat-ui

  1. Production Setup: API Passthrough

    main

    The quickest way to productionize is using the langgraph-nextjs-api-passthrough package to proxy requests and handle authentication.

    Set the following environment variables:

    • NEXT_PUBLIC_ASSISTANT_ID: The ID of the assistant (client-side).
    • LANGGRAPH_API_URL: The production deployment URL of your LangGraph server.
    • NEXT_PUBLIC_API_URL: Your website URL + /api (e.g., https://my-website.com/api).
    • LANGSMITH_API_KEY: Your LangSmith API key (server-side secret, do NOT use NEXT_PUBLIC_ prefix).

    Example Configuration:

    NEXT_PUBLIC_ASSISTANT_ID="agent"
    LANGGRAPH_API_URL="https://my-agent.default.us.langgraph.app"
    NEXT_PUBLIC_API_URL="https://my-website.com/api"
    LANGSMITH_API_KEY="lsv2_..."
    NEXT_PUBLIC_ASSISTANT_ID="agent"
    LANGGRAPH_API_URL="https://my-agent.default.us.langgraph.app"
    NEXT_PUBLIC_API_URL="https://my-website.com/api"
    LANGSMITH_API_KEY="lsv2_..."
  2. Production Setup: Custom Authentication

    main

    For advanced setups where you want to use custom access controls without a LangSmith API key, you must modify the useTypedStream hook to pass your authentication token in the headers.

    Implementation: Modify src/providers/Stream.tsx to include your token in the defaultHeaders:

    const streamValue = useTypedStream({
      apiUrl: process.env.NEXT_PUBLIC_API_URL,
      assistantId: process.env.NEXT_PUBLIC_ASSISTANT_ID,
      // ... other fields
      defaultHeaders: {
        Authentication: `Bearer ${addYourTokenHere}`,
      },
    });
    const streamValue = useTypedStream({
      apiUrl: process.env.NEXT_PUBLIC_API_URL,
      assistantId: process.env.NEXT_PUBLIC_ASSISTANT_ID,
      // ... other fields
      defaultHeaders: {
        Authentication: `Bearer ${addYourTokenHere}`,
      },
    });
  3. Install and run Agent Chat UI

    main

    You can quickly start a local instance of the Agent Chat UI using npx or by cloning the repository manually.

    Option 1: Using npx

    npx create-agent-chat-app

    Option 2: Manual Installation

    1. Clone the repository:
      git clone https://github.com/langchain-ai/agent-chat-ui.git
      cd agent-chat-ui
    2. Install dependencies using pnpm:
      pnpm install
    3. Run the development server:
      pnpm dev

    The app will be available at http://localhost:3000.

    npx create-agent-chat-app
  4. Prevent live streaming of messages

    main

    To stop messages from being displayed as they stream from an LLM call, add the langsmith:nostream tag to the chat model's configuration. Note that the message will still appear once the LLM call completes if it is saved to the graph state.

    Python Example:

    from langchain_anthropic import ChatAnthropic
    
    model = ChatAnthropic().with_config(
        config={"tags": ["langsmith:nostream"]}
    )

    TypeScript Example:

    import { ChatAnthropic } from "@langchain/anthropic";
    
    const model = new ChatAnthropic()
      .withConfig({ tags: ["langsmith:nostream"] });
    model = ChatAnthropic().with_config(
        config={"tags": ["langsmith:nostream"]}
    )
  5. Hide messages permanently from the UI

    main

    To ensure a message is never displayed (neither during streaming nor after being saved), you must do two things:

    1. Add the langsmith:do-not-render tag to the chat model's configuration.
    2. Prefix the message's id field with do-not-render- before adding it to the graph's state.

    Python Example:

    result = model.invoke([messages])
    result.id = f"do-not-render-{result.id}"
    return {"messages": [result]}

    TypeScript Example:

    const result = await model.invoke([messages]);
    result.id = `do-not-render-${result.id}`;
    return { messages: [result] };
    result.id = f"do-not-render-{result.id}"
  6. Configure Agent Chat UI via Environment Variables

    main

    To bypass the initial setup form in the browser, you can set the following environment variables in a .env file:

    • NEXT_PUBLIC_API_URL: The URL of your LangGraph server (e.g., http://localhost:2024).
    • NEXT_PUBLIC_ASSISTANT_ID: The ID of the assistant/graph to use.
    • NEXT_PUBLIC_AUTH_SCHEME: Set to langsmith-api-key if connecting to a LangSmith Agent Builder deployment.

    Setup steps:

    1. Copy .env.example to .env.
    2. Fill in the values.
    3. Restart the application.
    NEXT_PUBLIC_API_URL=http://localhost:2024
    NEXT_PUBLIC_ASSISTANT_ID=agent
    NEXT_PUBLIC_AUTH_SCHEME=
  7. Configure the StreamProvider for LangGraph connections

    main

    The StreamProvider component manages the connection to a LangGraph server. It handles authentication (via API keys), deployment URLs, and Assistant/Graph IDs. It supports configuration through three layers of priority:

    1. URL Parameters: apiUrl, assistantId, and authScheme can be passed via URL query strings.
    2. Environment Variables: Use the following variables for default configuration:
      • NEXT_PUBLIC_API_URL
      • NEXT_PUBLIC_ASSISTANT_ID
      • NEXT_PUBLIC_AUTH_SCHEME
    3. UI Form: If no configuration is detected, the provider renders a setup form for the user to input these values manually.

    API keys are stored in the browser's localStorage under the key lg:chat:apiKey for persistence across sessions.

  8. Render artifacts using ArtifactProvider and useArtifact

    main

    To display structured content or files (artifacts) in the chat UI, wrap your component tree in an ArtifactProvider and use the useArtifact hook. The useArtifact hook provides an ArtifactContent component that, when rendered, uses React Portals to inject its title and children into specific slots defined by ArtifactTitle and ArtifactContent components elsewhere in the UI.

    1. Wrap the parent container in <ArtifactProvider />.
    2. Place <ArtifactTitle /> and <ArtifactContent /> in the locations where you want the artifact's header and body to appear.
    3. Use the ArtifactContent component returned by useArtifact() to define the artifact's content.
  9. Render artifacts in the chat side panel

    main

    Agent Chat UI supports rendering artifacts in a side panel using the useArtifact hook. You can obtain the artifact context from the thread.meta.artifact field.

    Usage Pattern:

    1. Use the useArtifact hook to get the Artifact component and its control Bag.
    2. Use the Artifact component to wrap your content. It will render in the side panel when opened.

    Example Component:

    import { useArtifact } from "../utils/use-artifact";
    
    export function Writer(props: { title?: string; content?: string; description?: string; }) {
      const [Artifact, { open, setOpen }] = useArtifact();
    
      return (
        <>
          <div onClick={() => setOpen(!open)} className="cursor-pointer rounded-lg border p-4">
            <p className="font-medium">{props.title}</p>
            <p className="text-sm text-gray-500">{props.description}</p>
          </div>
    
          <Artifact title={props.title}>
            <p className="whitespace-pre-wrap p-4">{props.content}</p>
          </Artifact>
        </>
      );
    }
    const [Artifact, { open, setOpen }] = useArtifact();
  10. Use the useStreamContext hook to access streaming state

    main

    To access the streaming state, messages, and thread information within your chat application, use the useStreamContext hook. This hook must be called within a component that is a child of StreamProvider.

    Note on State Structure: The stream state follows the StateType schema:

    type StateType = { 
      messages: Message[]; 
      ui?: UIMessage[] 
    };

    Error Handling: If useStreamContext is called outside of a StreamProvider, it will throw an error: "useStreamContext must be used within a StreamProvider".

  11. ContentBlocksPreviewProps configuration

    main

    The ContentBlocksPreview component accepts the following props:

    PropTypeDescription
    blocksContentBlock.Multimodal.Data[]An array of multimodal content blocks to preview.
    onRemove(idx: number) => voidCallback function triggered when a user attempts to remove a block. Receives the index of the block.
    size"sm" | "md" | "lg"The visual size of the previews. Defaults to "md".
    classNamestringAdditional CSS classes to apply to the container div.