Talk to your agents

How to connect Vercel AI SDK useChat to your Agent Graph

Copy page

Use the Vercel AI SDK's useChat hook to build React chat interfaces that stream responses from your agent graph.

Overview

The Vercel AI SDK v5's useChat hook provides a React-friendly way to build chat interfaces that stream responses from your agent graph. This guide shows you how to configure useChat to connect to the agent framework's chat endpoint.

Tip
Tip

The agent framework's /api/chat endpoint is compatible with Vercel AI SDK's streaming protocol. See Via API for low-level HTTP details.

Installation

Install the Vercel AI SDK in your React application:

npm install ai @ai-sdk/react

Basic Configuration

Here's a minimal example of using useChat with your agent graph:

"use client";

import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport } from "ai";
import { useState } from "react";

export default function Page() {
  const { messages, sendMessage } = useChat({
    transport: new DefaultChatTransport({
      api: "http://localhost:3003/api/chat",
      headers: {
        "x-inkeep-tenant-id": "default",
        "x-inkeep-project-id": "weather-project",
        "x-inkeep-graph-id": "weather-graph",
      },
    }),
  });
  const [input, setInput] = useState("");

  return (
    <div className="max-w-2xl mx-auto p-4">
      <div className="space-y-4 mb-4">
        {messages.map((message) => (
          <div key={message.id} className="border rounded p-3">
            <div className="font-semibold mb-2">
              {message.role === "user" ? "👤 User" : "🤖 Assistant"}
            </div>

            <div className="space-y-2">
              {message.parts.map((part, partIndex) => {
                const partKey = `${message.id}-${part.type}-${partIndex}`;

                if (part.type === "text") {
                  return (
                    <div key={partKey} className="whitespace-pre-wrap">
                      {part.text}
                    </div>
                  );
                }
                
                return null;
              })}
            </div>
          </div>
        ))}
      </div>

      <form
        onSubmit={(e) => {
          e.preventDefault();
          if (input.trim()) {
            sendMessage({ text: input });
            setInput("");
          }
        }}
        className="flex gap-2"
      >
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="Ask about weather, request confirmation, or ask for location..."
          className="flex-1 p-2 border rounded"
        />
        <button
          type="submit"
          className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
        >
          Send
        </button>
      </form>

      <div className="mt-4 text-sm text-gray-600">
        <p>Try asking:</p>
        <ul className="list-disc list-inside space-y-1">
          <li>"What's the weather in New York?"</li>
          <li>"Can you get my location?"</li>
          <li>"Ask me to confirm something"</li>
        </ul>
      </div>
    </div>
  );
}

Configuration Options

API Endpoint

The api parameter should point to your Run API's chat endpoint:

  • Local development: http://localhost:3003/api/chat
  • Production: https://your-run-api-domain.com/api/chat

Authentication

Choose the authentication method:

See Authentication → Run API for more details.

Optional Configuration

const { messages, sendMessage, status } = useChat({
  api: 'http://localhost:3003/api/chat',
  headers: { /* ... */ },

  // Handle completion
  onFinish: (message) => {
    console.log('Agent finished responding:', message);
  },

  // Handle errors
  onError: (error) => {
    console.error('Chat error:', error);
  },
});

Request Context

To pass request context (validated against your Context Config), include it as custom headers:

const { messages, sendMessage } = useChat({
  api: 'http://localhost:3003/api/chat',
  headers: {
    'Authorization': `Bearer ${apiKey}`,
    // Custom context headers
    'x-user-id': 'user-123',
    'x-organization-id': 'org-456',
  },
});
Note
Note

Request context must be passed via headers, not in the request body. See Request Context for details.

Complete Example

Here's a more complete example with conversation management and styling:

"use client";

import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport } from "ai";
import { useState } from "react";

export default function Page() {
  const { messages, sendMessage } = useChat({
    transport: new DefaultChatTransport({
      api: "http://localhost:3003/api/chat",
      headers: {
        "x-inkeep-tenant-id": "default",
        "x-inkeep-project-id": "weather-project",
        "x-inkeep-graph-id": "weather-intermediate-graph",
        tz: "US/Pacific",
      },
    }),
  });
  const [input, setInput] = useState("");

  return (
    <div className="max-w-2xl mx-auto p-4">
      <div className="space-y-4 mb-4">
        {messages.map((message) => (
          <div key={message.id} className="border rounded p-3">
            <div className="font-semibold mb-2">
              {message.role === "user" ? "👤 User" : "🤖 Assistant"}
            </div>

            <div className="space-y-2">
              {message.parts.map((part, partIndex) => {
                const partKey = `${message.id}-${part.type}-${partIndex}`;

                if (part.type === "text") {
                  return (
                    <div key={partKey} className="whitespace-pre-wrap">
                      {part.text}
                    </div>
                  );
                }

                if (part.type.startsWith("data-")) {
                  return (
                    <div
                      key={partKey}
                      className="bg-blue-50 p-3 rounded border-l-4 border-blue-400"
                    >
                      <div className="font-semibold text-blue-700">
                        Data: {JSON.stringify(part, null, 2)}
                      </div>
                    </div>
                  );
                }
                return null;
              })}
            </div>
          </div>
        ))}
      </div>

      <form
        onSubmit={(e) => {
          e.preventDefault();
          if (input.trim()) {
            sendMessage({ text: input });
            setInput("");
          }
        }}
        className="flex gap-2"
      >
        <input
          value={input}
          onChange={(e) => setInput(e.target.value)}
          placeholder="Ask about weather, request confirmation, or ask for location..."
          className="flex-1 p-2 border rounded"
        />
        <button
          type="submit"
          className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
        >
          Send
        </button>
      </form>

      <div className="mt-4 text-sm text-gray-600">
        <p>Try asking:</p>
        <ul className="list-disc list-inside space-y-1">
          <li>"What's the weather in New York?"</li>
          <li>"Can you get my location?"</li>
          <li>"Ask me to confirm something"</li>
        </ul>
      </div>
    </div>
  );
}

Environment Variables

For production deployments, store configuration in environment variables:

NEXT_PUBLIC_AGENTS_RUN_API_URL=http://localhost:3003
NEXT_PUBLIC_INKEEP_API_KEY=your-api-key
NEXT_PUBLIC_TENANT_ID=your-tenant-id
NEXT_PUBLIC_PROJECT_ID=your-project-id
NEXT_PUBLIC_GRAPH_ID=your-graph-id
Note
Note

In local development, the Run API runs at http://localhost:3003. For production, update this to your deployed Run API URL.

Next Steps