State Rendering
Render the state of your agent with custom UI components.
Open your coding agent in your project's folder, or in an empty folder for a new app.This runs in a coding agent on your computer.
What is this?#
AG2 agents can maintain state across a session through conversation variables (Context.variables). CopilotKit can render this state in your application with custom UI components, which we call Agentic Generative UI.
CopilotKit consumes AG-UI protocol events streamed by AG2 over /chat. See the AG2 AG-UI integration docs.
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#
Start your AG2 backend with AG-UI streaming enabled on /chat.
Set up your agent with state#
Store the state in the agent's conversation variables. Changes made by tools are streamed back as STATE_SNAPSHOT events automatically at the end of the run; for per-step updates during a long-running tool, emit intermediate snapshots with context.send:
from typing import Annotated
from ag_ui.core import StateSnapshotEvent
from fastapi import FastAPI, Header
from fastapi.responses import StreamingResponse
from pydantic import BaseModel, Field
from ag2 import Agent, Context, tool
from ag2.ag_ui import AGUIEvent, AGUIStream, RunAgentInput
from ag2.config import OpenAIResponsesConfig
class Search(BaseModel):
query: str
done: bool
class AgentState(BaseModel):
searches: list[Search] = Field(default_factory=list)
def read_state(context: Context) -> AgentState:
return AgentState.model_validate({"searches": context.variables.get("searches", [])})
def write_state(context: Context, state: AgentState) -> None:
context.variables["searches"] = state.model_dump()["searches"]
@tool
def add_search(
new_query: Annotated[str, Field(description="The query to add to state")],
context: Context,
) -> str:
"""Add a search to the state."""
state = read_state(context)
state.searches.append(Search(query=new_query, done=False))
write_state(context, state)
return f"Queued search: {new_query}"
@tool
async def run_searches(context: Context) -> str:
"""Run the queued searches and mark them done."""
state = read_state(context)
for search in state.searches:
search.done = True
write_state(context, state)
# Stream an intermediate snapshot so the UI updates per search
await context.send(
AGUIEvent(StateSnapshotEvent(snapshot=dict(context.variables)))
)
return "All searches done."
agent = Agent(
name="assistant",
prompt=(
"You are a helpful assistant for storing searches. "
"Use `add_search` once per query, then call `run_searches`."
),
config=OpenAIResponsesConfig(model="gpt-5.5"),
tools=[add_search, run_searches],
)
stream = AGUIStream(agent)
app = FastAPI()
@app.post("/chat")
async def run_agent(
message: RunAgentInput,
accept: str | None = Header(None),
) -> StreamingResponse:
return StreamingResponse(
stream.dispatch(message, accept=accept),
media_type=accept or "text/event-stream",
)Render state in the UI#
Use the useAgent hook to access agent state anywhere in your app. You can render it in the chat, in dashboards, sidebars, or custom layouts.
import { useAgent } from "@copilotkit/react-core/v2";
// ...
// Define the state of the agent, should match the state streamed by your AG2 backend.
type AgentState = {
searches: {
query: string;
done: boolean;
}[];
};
function YourMainContent() {
// ...
const { agent } = useAgent({
agentId: "my_agent", // MUST match the agent name in CopilotRuntime
})
const state = (agent.state ?? {}) as Partial<AgentState>;
// ...
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 agentId 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 renders the agent's state in your UI.