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.

Tutorial: Research ANA

Step 7: Agentic Generative UI

We're almost done! In this step, we're going to add generative UI to the application so that we can visualize the agent state in the chat UI. The end goal with this is to allow the user to see the progress of the agent's research as it is completed.

We call UI that is rendered from the state of the agent or its tool calls "Generative UI".

For this guide, we're going to start using the CopilotKit SDK to emit the state of the agent manually. This is because in LangGraph, state is only updated when a node change occurs (i.e, an edge is traversed).

As such, in-progress work is not emitted to the user by default. However, we can manually emit the state using the copilotkit_emit_state function. With that emitted state, we'll be using the useCoAgentStateRender hook to render updates in the chat to give the user a sense of progress.

CopilotKit Python SDK

This tutorial already comes with the CopilotKit Python SDK installed. This allows us to utilize various CopilotKit specific features, such as emitting state.

Manually emit state

The research ANA emits state in a variety of places. For the sake of simplicity, we'll be adding the copilotkit_emit_state function to the agent/graph.py file so you can understand how it works. However, state is also emitted in the following files if you'd like to look at them:

  • agent/tools/outline_writer.py
  • agent/tools/section_writer.py
  • agent/tools/tavily_extract.py
  • agent/tools/tavily_search.py

Each of these files will write their progress to the logs field of the agent's state. Directly after that, we call copilotkit_emit_state to emit the state to the frontend.

For example, in the tool_node we update some state based on the tool result and then use copilotkit_emit_state to emit the state to the frontend.

agent/graph.py
# ...
from copilotkit.langchain import copilotkit_emit_state 
#...

async def tool_node(self, state: ResearchState, config: RunnableConfig) -> Command[Literal["process_feedback_node", "call_model_node"]]:
        # ...
        for tool_call in state["messages"][-1].tool_calls:
            # ...

            tool_state = {
                "title": new_state.get("title", ""),
                "outline": new_state.get("outline", {}),
                "sections": new_state.get("sections", []),
                "sources": new_state.get("sources", {}),
                "proposal": new_state.get("proposal", {}),
                "logs": new_state.get("logs", []),
                "tool": new_state.get("tool", {}),
                "messages": msgs
            }
            await copilotkit_emit_state(config, tool_state) 

        return tool_state

As this loop is iterated through, the intermediate state that the tools write will be emitted to the frontend. Basically, any time that you want to emit state to the frontend, you can do so by calling copilotkit_emit_state.

Render the emitted state

Now, our state is being emitted to the frontend. However, we need to render it in the chat. To do this, we'll be using the useCoAgentStateRender hook.

frontend/src/app/layout.tsx
import { useCoAgentStateRender, useLangGraphInterrupt } from "@copilotkit/react-core"; 

export default function HomePage() {
    //...
    const { state: researchState, setResearchState } = useResearch()
    // ...

    useCoAgentStateRender<ResearchState>({
        name: 'agent',
        render: ({ state }) => {
            if (state.logs?.length > 0) {
                return <Progress logs={state.logs} />;
            }
            return null;
        },
    }, [researchState]);

    // ...
}

Recap

Try running the agent again and going through the same steps. You'll now notice that the state is streaming intermediately and the user can see the progress of the agent's research.

Please research dogs!
CopilotCloud API Key

To recap, we did the following:

  • Learned about how to emit state whenever we want with copilotkit_emit_state.
  • Added the useCoAgentStateRender hook to our application to render the intermediate state in the chat.

We're almost done, just one step to go! Now we're going to learn about progressive state updates which will allow us to render the sections as they are written into state. This will complete the agentic experience.

PREV
Step 6: Shared State
Slanted end borderSlanted end border
Slanted start borderSlanted start border
NEXT
Step 8: Progressive State Updates

On this page

CopilotKit Python SDK
Manually emit state
Render the emitted state
Recap