Agentic Generative UI
Render the state of your agent with custom UI components.
This video demonstrates the implementation section applied to out coagents starter project.
What is this?
All LangGraph 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 LangGraph to CopilotKit
First, you'll need to make sure you have a running LangGraph. If you haven't already done this, you can follow the getting started guide
This guide uses the CoAgents starter repo as its starting point.
Define your agent state
If you're not familiar with LangGraph, your graphs are stateful. As you progress through nodes, a state object is passed between them. CopilotKit allows you to easily render this state in your application.
For the sake of this guide, let's say our state looks like this in our agent.
# ...
from copilotkit import CopilotKitState # extends MessagesState
# ...
# This is the state of the agent.
# It inherits from the CopilotKitState properties from CopilotKit.
class AgentState(CopilotKitState):
searches: list[dict]
Simulate state updates
Next, let's write some logic into our agent that will simulate state updates occurring.
import asyncio
from typing import TypedDict
from langchain_core.runnables import RunnableConfig
from langchain_openai import ChatOpenAI
from langchain_core.messages import SystemMessage
from copilotkit import CopilotKitState
from copilotkit.langgraph import copilotkit_emit_state
class Searches(TypedDict):
query: str
done: bool
class AgentState(CopilotKitState):
searches: list[Searches] = []
async def chat_node(state: AgentState, config: RunnableConfig):
state["searches"] = [
{"query": "Initial research", "done": False},
{"query": "Retrieving sources", "done": False},
{"query": "Forming an answer", "done": False},
]
await copilotkit_emit_state(config, state)
# Simulate state updates
for search in state["searches"]:
await asyncio.sleep(1)
search["done"] = True
await copilotkit_emit_state(config, state)
# Run the model to generate a response
response = await ChatOpenAI(model="gpt-4o").ainvoke([
SystemMessage(content="You are a helpful assistant."),
*state["messages"],
], config)
Render state of the agent in the chat
Now we can utilize useCoAgentStateRender
to render the state of our agent in the chat.
// ...
import { useCoAgentStateRender } from "@copilotkit/react-core";
// ...
// Define the state of the agent, should match the state of the agent in your LangGraph.
type AgentState = {
searches: {
query: string;
done: boolean;
}[];
};
function YourMainContent() {
// ...
// styles omitted for brevity
useCoAgentStateRender<AgentState>({
name: "sample_agent", // the name the agent is served as
render: ({ state }) => (
<div>
{state.searches?.map((search, index) => (
<div key={index}>
{search.done ? "✅" : "❌"} {search.query}{search.done ? "" : "..."}
</div>
))}
</div>
),
});
// ...
return <div>...</div>;
}
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.
import { useCoAgent } from "@copilotkit/react-core";
// ...
// Define the state of the agent, should match the state of the agent in your LangGraph.
type AgentState = {
searches: {
query: string;
done: boolean;
}[];
};
function YourMainContent() {
// ...
const { state } = useCoAgent<AgentState>({
name: "sample_agent", // the name the agent is served as
})
// ...
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>
)
}
Give it a try!
You've now created a component that will render the agent's state in the chat.