Back to Copilotkit

Use Agent

showcase/shell-docs/src/content/snippets/use-agent.mdx

1.57.09.4 KB
Original Source
<Steps> <Step> ### Import the hook
First, import `useAgent` from the v2 package:

```tsx title="page.tsx"
import { useAgent } from "@copilotkit/react-core/v2"; // [!code highlight]
```
</Step> <Step> ### Access your agent
Call the hook to get a reference to your agent:

```tsx title="page.tsx"
export function AgentInfo() {
  const { agent } = useAgent(); // [!code highlight]

  return (
    <div>
      <p>Agent ID: {agent.id}</p>
      <p>Thread ID: {agent.threadId}</p>
      <p>Status: {agent.isRunning ? "Running" : "Idle"}</p>
      <p>Messages: {agent.messages.length}</p>
    </div>
  );
}
```

The hook will throw an error if no agent is configured, so you can safely use `agent` without null checks.
</Step> <Step> ### Display messages
Access the agent's conversation history:

```tsx title="page.tsx"
export function MessageList() {
  const { agent } = useAgent();

  return (
    <div>
      {agent.messages.map((msg) => (
        <div key={msg.id}>
          <strong>{msg.role}:</strong>
          <span>{msg.content}</span>
        </div>
      ))}
    </div>
  );
}
```
</Step> <Step> ### Show running status
Add a loading indicator when the agent is processing:

```tsx title="page.tsx"
export function AgentStatus() {
  const { agent } = useAgent();

  return (
    <div>
      {agent.isRunning ? (
        <div>
          <div className="spinner" />
          <span>Agent is processing...</span>
        </div>
      ) : (
        <span>Ready</span>
      )}
    </div>
  );
}
```
</Step> <Step> ### Run the agent
Use `copilotkit.runAgent()` to trigger your agent programmatically:

```tsx title="page.tsx"
import { useAgent } from "@copilotkit/react-core/v2";
import { useCopilotKit } from "@copilotkit/react-core/v2";
import { randomUUID } from "@copilotkit/shared/v2";

export function RunAgent() {
  const { agent } = useAgent();
  // [!code highlight:1]
  const { copilotkit } = useCopilotKit();

  const handleRun = async () => {
    agent.addMessage({
      id: randomUUID(),
      role: "user",
      content: "Hello, agent!",
    });

    // [!code highlight:1]
    await copilotkit.runAgent({ agent });
  };

  return <button onClick={handleRun}>Send</button>;
}
```

`copilotkit.runAgent()` orchestrates the full agent lifecycle — executing frontend tools, handling follow-up runs, and streaming results. This is the same method `<CopilotChat />` uses internally.
</Step> </Steps>

Working with State

Agents expose their state through the agent.state property. This state is shared between your application and the agent - both can read and modify it.

Reading State

Access your agent's current state:

tsx
export function StateDisplay() {
  const { agent } = useAgent();

  return (
    <div>
      <h3>Agent State</h3>
      <pre>{JSON.stringify(agent.state, null, 2)}</pre>
      {agent.state.user_name && <p>User: {agent.state.user_name}</p>}
      {agent.state.preferences && (
        <p>Preferences: {JSON.stringify(agent.state.preferences)}</p>
      )}
    </div>
  );
}

Your component automatically re-renders when the agent's state changes.

Updating State

Update state that your agent can access:

tsx
export function ThemeSelector() {
  const { agent } = useAgent();

  const updateTheme = (theme: string) => {
    // [!code highlight:4]
    agent.setState({
      ...agent.state,
      user_theme: theme,
    });
  };

  return (
    <div>
      <button onClick={() => updateTheme("dark")}>Dark Mode</button>
      <button onClick={() => updateTheme("light")}>Light Mode</button>
      <p>Current: {agent.state.user_theme || "default"}</p>
    </div>
  );
}

State updates are immediately available to your agent in its next execution.

Subscribing to Agent Events

You can subscribe to agent events using the subscribe() method. This is useful for logging, monitoring, or responding to specific agent behaviors.

Basic Event Subscription

tsx
import { useEffect } from "react";
import { useAgent } from "@copilotkit/react-core/v2";
import type { AgentSubscriber } from "@ag-ui/client";

export function EventLogger() {
  const { agent } = useAgent();

  useEffect(() => {
    // [!code highlight:15]
    const subscriber: AgentSubscriber = {
      onCustomEvent: ({ event }) => {
        console.log("Custom event:", event.name, event.value);
      },
      onRunStartedEvent: () => {
        console.log("Agent started running");
      },
      onRunFinalized: () => {
        console.log("Agent finished running");
      },
      onStateChanged: (state) => {
        console.log("State changed:", state);
      },
    };

    // [!code highlight:2]
    const { unsubscribe } = agent.subscribe(subscriber);
    return () => unsubscribe();
  }, []);

  return null;
}

Available Events

The AgentSubscriber interface provides:

  • onCustomEvent - Custom events emitted by the agent
  • onRunStartedEvent - Agent starts executing
  • onRunFinalized - Agent completes execution
  • onStateChanged - Agent's state changes
  • onMessagesChanged - Messages are added or modified

Rendering Tool Calls

You can customize how agent tool calls are displayed in your UI. First, define your tool renderers:

tsx
import { defineToolCallRenderer } from "@copilotkit/react-core/v2";

// [!code highlight:6]
export const weatherToolRender = defineToolCallRenderer({
  name: "get_weather",
  render: ({ args, status }) => {
    return <WeatherCard location={args.location} status={status} />;
  },
});

function WeatherCard({
  location,
  status,
}: {
  location?: string;
  status: string;
}) {
  return (
    <div className="rounded-lg border p-6 shadow-sm">
      <h3 className="text-xl font-semibold">Weather in {location}</h3>
      <div className="mt-4">
        <span className="text-5xl font-light">70°F</span>
      </div>
      {status === "executing" && <div className="spinner">Loading...</div>}
    </div>
  );
}

Register your tool renderers with CopilotKit:

tsx
import { CopilotKit } from "@copilotkit/react-core";
import { weatherToolRender } from "./components/weather-tool";

export default function RootLayout({ children }) {
  return (
    <CopilotKit
      runtimeUrl="/api/copilotkit"
      renderToolCalls={[weatherToolRender]}
    >
      {children}
    </CopilotKit>
  );
}

Then use useRenderToolCall to render tool calls from agent messages:

tsx
import { useAgent, useRenderToolCall } from "@copilotkit/react-core/v2";

export function MessageList() {
  const { agent } = useAgent();
  const renderToolCall = useRenderToolCall();

  return (
    <div className="messages">
      {agent.messages.map((message) => (
        <div key={message.id}>
          {message.content && <p>{message.content}</p>}
          {message.role === "assistant" &&
            message.toolCalls?.map((toolCall) => {
              const toolMessage = agent.messages.find(
                (m) => m.role === "tool" && m.toolCallId === toolCall.id,
              );
              return (
                <div key={toolCall.id}>
                  {renderToolCall({ toolCall, toolMessage })}
                </div>
              );
            })}
        </div>
      ))}
    </div>
  );
}

Building a Complete Dashboard

Here's a full example combining all concepts into an interactive agent dashboard:

tsx
"use client";

import { useAgent } from "@copilotkit/react-core/v2";

export default function AgentDashboard() {
  const { agent } = useAgent();

  return (
    <div className="p-8 max-w-4xl mx-auto space-y-6">
      <div className="p-6 bg-white rounded-lg shadow">
        <h2 className="text-xl font-bold mb-4">Agent Status</h2>
        <div className="space-y-2">
          <div className="flex items-center gap-2">
            <div
              className={`w-3 h-3 rounded-full ${
                agent.isRunning ? "bg-yellow-500 animate-pulse" : "bg-green-500"
              }`}
            />
            <span>{agent.isRunning ? "Running" : "Idle"}</span>
          </div>
          <div>Thread: {agent.threadId}</div>
          <div>Messages: {agent.messages.length}</div>
        </div>
      </div>
      <div className="p-6 bg-white rounded-lg shadow">
        <h2 className="text-xl font-bold mb-4">Agent State</h2>
        <pre className="bg-gray-50 p-4 rounded text-sm overflow-auto">
          {JSON.stringify(agent.state, null, 2)}
        </pre>
      </div>
      <div className="p-6 bg-white rounded-lg shadow">
        <h2 className="text-xl font-bold mb-4">Conversation</h2>
        <div className="space-y-3">
          {agent.messages.map((msg) => (
            <div
              key={msg.id}
              className={`p-3 rounded-lg ${
                msg.role === "user" ? "bg-blue-50 ml-8" : "bg-gray-50 mr-8"
              }`}
            >
              <div className="font-semibold text-sm mb-1">
                {msg.role === "user" ? "You" : "Agent"}
              </div>
              <div>{msg.content}</div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}