Fixed Schema A2UI
Pre-defined A2UI schema with dynamic data. The fastest approach — no LLM schema generation needed.
In the fixed-schema approach, you design the UI schema once (in a JSON file or using the A2UI Composer) and your agent tool only provides the data. The surface appears instantly when the tool returns.
How it works#
- Schema is loaded from a JSON file at startup
- Agent tool receives data from the LLM (e.g., flight search results)
- Tool returns
a2ui.render()with createSurface + updateComponents + updateDataModel - The A2UI middleware intercepts the tool result and renders the surface
Implementation#
Create the A2UI schema#
Design your schema using the A2UI Composer or write it by hand. Save it as a JSON file:
apps/agent/src/a2ui/schemas/flight_schema.jsonDefine the agent tool (Python)#
from copilotkit import a2ui
from langchain.tools import tool
from pathlib import Path
from typing import TypedDict
class Flight(TypedDict):
id: str
airline: str
airlineLogo: str
flightNumber: str
origin: str
destination: str
date: str
departureTime: str
arrivalTime: str
duration: str
status: str
statusIcon: str
price: str
SURFACE_ID = "flight-search-results"
FLIGHT_SCHEMA = a2ui.load_schema(
Path(__file__).parent / "a2ui" / "schemas" / "flight_schema.json"
)
@tool
def search_flights(flights: list[Flight]) -> str:
"""Search for flights and display results as rich cards."""
return a2ui.render(
operations=[
a2ui.create_surface(SURFACE_ID),
a2ui.update_components(SURFACE_ID, FLIGHT_SCHEMA),
a2ui.update_data_model(SURFACE_ID, {"flights": flights}),
],
)Key points:
- The
FlightTypedDict is essential — LangChain serializes it into the tool's JSON schema, which is what the LLM sees when deciding what data to generate. - The Python SDK's
a2ui.renderdoes not yet support theaction_handlers=keyword, so the example keeps the button schema but does not declare server-side handlers. Button clicks are forwarded to the agent, but this example has no server-side handler for them. "book_flight"is the action name used by the schema button and can be handled with the frontend APIs in the Advanced — Action Handlers guide.
Register the tool#
from deepagents import create_deep_agent
from src.a2ui_fixed_schema import search_flights
agent = create_deep_agent(
tools=[search_flights, ...],
...
)Configure the runtime (TypeScript)#
Enable A2UI in your CopilotRuntime:
const runtime = new CopilotRuntime({
agents: { default: myAgent },
a2ui: {
injectA2UITool: true,
},
});Action handler details#
The current Python SDK does not support the action_handlers= option. The button schema can still define the action context used by frontend handlers. Here's how the schema side looks:
Button with action context#
In your flight_schema.json, buttons declare an action with data-bound context fields. When clicked, the values are resolved from that specific card's data:
{
"id": "book-button",
"component": "Button",
"child": "book-label",
"variant": "primary",
"action": {
"event": {
"name": "book_flight",
"context": {
"flightNumber": { "path": "flightNumber" },
"price": { "path": "price" }
}
}
}
}When this button is clicked on a card showing flight AA100 at $350, frontend action handling receives context: { flightNumber: "AA100", price: "$350" }. The Python action_handlers= path is not yet supported.
For custom frontend handling with createA2UIMessageRenderer and its onAction option, see the Advanced — Action Handlers guide.