A2UI Launched: Full CopilotKit support at launch!

A2UI Launched: CopilotKit has partnered with Google to deliver full support in both CopilotKit and AG-UI!

Check it out
LogoLogo
  • Overview
  • Integrations
  • API Reference
  • Copilot Cloud
Slanted end borderSlanted end border
Slanted start borderSlanted start border
Select integration...

Please select an integration to view the sidebar content.

Generative UI

Agent State

Render the state of your agent with custom UI components.

What is this?

All Pydantic AI Agents are stateful. This means that as your agent progresses through nodes, a state object is passed between them perserving the overall state of a session. CopilotKit allows you to render this state in your application with custom UI components, which we call Agentic Generative UI.

When should I use this?

Rendering the state of your agent in the UI is useful when you want to provide the user with feedback about the overall state of a session. A great example of this is a situation where a user and an agent are working together to solve a problem. The agent can store a draft in its state which is then rendered in the UI.

Implementation

Run and connect your agent

You'll need to run your agent and connect it to CopilotKit before proceeding.

If you don't already have CopilotKit and your agent connected, choose one of the following options:

Set up your agent with state

Create your Pydantic AI agent with a stateful structure. Here's a complete example that tracks searches:

agent.py
import asyncio
from textwrap import dedent
from pydantic import BaseModel, Field
from pydantic_ai import Agent, RunContext
from pydantic_ai.ag_ui import StateDeps
from ag_ui.core import StateSnapshotEvent, EventType


class Search(BaseModel):
    query: str
    done: bool


class AgentState(BaseModel):
    searches: list[Search] = Field(default_factory=list)


agent = Agent("openai:gpt-4o-mini", deps_type=StateDeps[AgentState])


@agent.tool
async def add_search(
    ctx: RunContext[StateDeps[AgentState]], new_query: str
) -> StateSnapshotEvent:
    """Add a search to the agent's list of searches."""
    new_search = Search(query=new_query, done=False)
    searches = ctx.deps.state.searches
    searches.append(new_search)
    agent_state = AgentState(searches=searches)
    ctx.deps.state = agent_state

    return StateSnapshotEvent(type=EventType.STATE_SNAPSHOT, snapshot=agent_state)


@agent.tool
async def run_searches(ctx: RunContext[StateDeps[AgentState]]) -> StateSnapshotEvent:
    """Run the searches in the agent's state."""
    searches = ctx.deps.state.searches

    for search in searches:
        await asyncio.sleep(1)
        search.done = True

    agent_state = AgentState(searches=searches)
    ctx.deps.state = agent_state

    return StateSnapshotEvent(type=EventType.STATE_SNAPSHOT, snapshot=agent_state)


@agent.instructions()
async def search_instructions(ctx: RunContext[StateDeps[AgentState]]) -> str:
    """Instructions for the search agent."""
    return dedent(
        f"""
        You are a helpful assistant for storing searches.

        IMPORTANT:
        - Use the `add_search` tool to add a search to the agent's state
        - After using the `add_search` tool, YOU MUST ALWAYS use the `run_searches` tool to run the searches
        - ONLY USE THE `add_search` TOOL ONCE FOR A GIVEN QUERY

        Current searches:
        {ctx.deps.state.model_dump_json(indent=2)}
        """
    )


app = agent.to_ag_ui(deps=StateDeps(AgentState()))

if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=8000)

Render state of the agent in the chat

Now we can utilize useCoAgentStateRender to render the state of our agent in the chat.

app/page.tsx
// ...
import { useCoAgentStateRender } from "@copilotkit/react-core";
// ...

// Define the state of the agent, should match the state of your Pydantic AI Agent.
type AgentState = {
  searches: {
    query: string;
    done: boolean;
  }[];
};

function YourMainContent() {
  // ...

  // styles omitted for brevity
  useCoAgentStateRender<AgentState>({
    name: "my_agent", // MUST match the agent name in CopilotRuntime
    render: ({ state }) => (
      <div>
        {state.searches?.map((search, index) => (
          <div key={index}>
            {search.done ? "✅" : "❌"} {search.query}{search.done ? "" : "..."}
          </div>
        ))}
      </div>
    ),
  });

  // ...

  return <div>...</div>;
}

Important

The name parameter must exactly match the agent name you defined in your CopilotRuntime configuration (e.g., my_agent from the quickstart).

Render state outside of the chat

You can also render the state of your agent outside of the chat. This is useful when you want to render the state of your agent anywhere other than the chat.

app/page.tsx
import { useCoAgent } from "@copilotkit/react-core"; 
// ...

// Define the state of the agent, should match the state of your Pydantic AI Agent.
type AgentState = {
  searches: {
    query: string;
    done: boolean;
  }[];
};

function YourMainContent() {
  // ...

  const { state } = useCoAgent<AgentState>({
    name: "my_agent", // MUST match the agent name in CopilotRuntime
  })

  // ...

  return (
    <div>
      {/* ... */}
      <div className="flex flex-col gap-2 mt-4">
        {state.searches?.map((search, index) => (
          <div key={index} className="flex flex-row">
            {search.done ? "✅" : "❌"} {search.query}
          </div>
        ))}
      </div>
    </div>
  )
}

Important

The name parameter must exactly match the agent name you defined in your CopilotRuntime configuration (e.g., my_agent from the quickstart).

Give it a try!

You've now created a component that will render the agent's state in the chat.

PREV
Frontend Tools
Slanted end borderSlanted end border
Slanted start borderSlanted start border
NEXT
Human-in-the-Loop

On this page

What is this?
When should I use this?
Implementation
Run and connect your agent
Set up your agent with state
Render state of the agent in the chat
Render state outside of the chat
Give it a try!