Skip to content

Latest commit

 

History

History
379 lines (309 loc) · 10.1 KB

File metadata and controls

379 lines (309 loc) · 10.1 KB
### Import the hook
First, import `useAgent` from the v2 package:

```tsx title="page.tsx"
import { useAgent } from "@copilotkit/react-core/v2"; // [!code highlight]
```
### 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>
      {/* [!code highlight:4] */}
      <p>Agent ID: {agent.agentId}</p>
      <p>Thread ID: {agent.threadId}</p>
      <p>Status: {agent.isRunning ? "Running" : "Idle"}</p>
      <p>Messages: {agent.messages.length}</p>
    </div>
  );
}
```

<Callout type="info">
  Note: If you are not using CopilotKit Cloud public access/license key, then you need to pass your agentId to useAgent() hook.
  ```ts
  const { agent } = useAgent({agentId: "myAgent"})
  ```
</Callout>

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

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

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

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

  return (
    <div>
      {/* [!code highlight:8] */}
      {agent.isRunning ? (
        <div>
          <div className="spinner" />
          <span>Agent is processing...</span>
        </div>
      ) : (
        <span>Ready</span>
      )}
    </div>
  );
}
```
### 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";

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.

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:

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

  return (
    <div>
      <h3>Agent State</h3>
      {/* [!code highlight:1] */}
      <pre>{JSON.stringify(agent.state, null, 2)}</pre>

      {/* Access specific properties */}
      {/* [!code highlight:2] */}
      {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:

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

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

  return (
    <div>
      {/* [!code highlight:2] */}
      <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

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();
  }, [agent]);

  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:

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:

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

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

Then use useRenderToolCall to render tool calls from agent messages:

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}>
          {/* Display message content */}
          {message.content && <p>{message.content}</p>}

          {/* Render tool calls if present */}
          {/* [!code highlight:9] */}
          {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:

"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">
      {/* Status */}
      <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">
          {/* [!code highlight:6] */}
          <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>

      {/* State */}
      <div className="p-6 bg-white rounded-lg shadow">
        <h2 className="text-xl font-bold mb-4">Agent State</h2>
        {/* [!code highlight:3] */}
        <pre className="bg-gray-50 p-4 rounded text-sm overflow-auto">
          {JSON.stringify(agent.state, null, 2)}
        </pre>
      </div>

      {/* Messages */}
      <div className="p-6 bg-white rounded-lg shadow">
        <h2 className="text-xl font-bold mb-4">Conversation</h2>
        <div className="space-y-3">
          {/* [!code highlight:11] */}
          {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>
  );
}