pydantic_ai.ui.ag_ui
AG-UI protocol integration for Pydantic AI agents.
Bases: UIEventStream[RunAgentInput, BaseEvent, AgentDepsT, OutputDataT]
UI event stream transformer for the Agent-User Interaction (AG-UI) protocol.
@async
def handle_event(event: NativeEvent) -> AsyncIterator[BaseEvent]
Override to set timestamps on all AG-UI events.
AsyncIterator[BaseEvent]
Bases: UIAdapter[RunAgentInput, Message, BaseEvent, AgentDepsT, OutputDataT]
UI adapter for the Agent-User Interaction (AG-UI) protocol.
AG-UI protocol version controlling behavior thresholds.
Accepts any version string (e.g. '0.1.13'). Defaults to the version detected from
the installed ag-ui-protocol package.
Known thresholds:
< 0.1.13: emitsTHINKING_*events during streaming, dropsThinkingPartfromdump_messagesoutput.>= 0.1.13: emitsREASONING_*events with encrypted metadata during streaming, and includesThinkingPartasReasoningMessageindump_messagesoutput for full round-trip fidelity of thinking signatures and provider metadata.>= 0.1.15: emits typed multimodal input content (ImageInputContent,AudioInputContent,VideoInputContent,DocumentInputContent) instead of genericBinaryInputContent.
load_messages always accepts ReasoningMessage and multimodal content types regardless
of this setting.
Type: str Default: DEFAULT_AG_UI_VERSION
Whether to round-trip FilePart and UploadedFile through reserved pydantic_ai_*
activity messages.
Defaults to False. AG-UI has no native representation for agent-generated files
(FilePart) or uploaded-file references
(UploadedFile), so when this is True they are
serialized as sidecar activity messages on dump_messages and reconstructed on
load_messages. A frontend only completes the round-trip if it echoes these activity
messages back on the next request.
This is a representation setting, not a security one: honoring a reconstructed inbound
UploadedFile still requires
allow_uploaded_files, which the shared
sanitize_messages step enforces regardless of this flag. Multimodal tool-return files are
unaffected — they ride inline in ToolMessage.content.
Type: bool Default: False
Pydantic AI messages from the AG-UI run input.
Type: list[ModelMessage]
Toolset representing frontend tools from the AG-UI run input.
Type: AbstractToolset[AgentDepsT] | None
Frontend state from the AG-UI run input.
Conversation ID from the AG-UI RunAgentInput.threadId.
Translate AG-UI RunAgentInput.resume[] into Pydantic AI DeferredToolResults.
See docs.ag-ui.com/concepts/interrupts.
Each ResumeEntry is mapped to an approval keyed by the original tool_call_id.
The mapping is deny-by-default: approval requires an explicit
payload.approved == True. Any other shape is treated as a denial so a malformed
or hostile client cannot accidentally execute a tool that requires human approval.
status == 'cancelled'→ToolDenied('Cancelled by user.')payload.approved is Truewithpayload.editedArgs→ToolApproved(override_args=...)payload.approved is Truewithout edits →ToolApproved()- Anything else (
False, missing,null, non-bool, non-dict payload) →ToolDenied(payload.get('reason'))ifreasonis a non-empty string, elseToolDenied()(which carries the default"The tool call was denied."message).
Returns None when resume is missing or empty, or when the installed
ag-ui-protocol predates the interrupt lifecycle.
Type: DeferredToolResults | None
@classmethod
def build_run_input(cls, body: bytes) -> RunAgentInput
Build an AG-UI run input object from the request body.
RunAgentInput
def build_event_stream(
) -> UIEventStream[RunAgentInput, BaseEvent, AgentDepsT, OutputDataT]
Build an AG-UI event stream transformer.
UIEventStream[RunAgentInput, BaseEvent, AgentDepsT, OutputDataT]
@async
@classmethod
def from_request(
cls,
request: Request,
*,
agent: AbstractAgent[AgentDepsT, OutputDataT],
ag_ui_version: str = DEFAULT_AG_UI_VERSION,
preserve_file_data: bool = False,
manage_system_prompt: Literal['server', 'client'] = 'server',
allowed_file_url_schemes: frozenset[str] = frozenset({'http', 'https'}),
allowed_file_url_force_download: frozenset[ForceDownloadMode] = frozenset(),
allow_uploaded_files: bool = False,
**kwargs: Any,
) -> AGUIAdapter[AgentDepsT, OutputDataT]
Extends from_request with AG-UI-specific parameters.
AGUIAdapter[AgentDepsT, OutputDataT]
@classmethod
def load_messages(
cls,
messages: Sequence[Message],
*,
preserve_file_data: bool = False,
) -> list[ModelMessage]
Transform AG-UI messages into Pydantic AI messages.
@classmethod
def dump_messages(
cls,
messages: Sequence[ModelMessage],
*,
ag_ui_version: str = DEFAULT_AG_UI_VERSION,
preserve_file_data: bool = False,
) -> list[Message]
Transform Pydantic AI messages into AG-UI messages.
Note: The round-trip dump_messages -> load_messages is not fully lossless:
TextPart.id,.provider_name,.provider_detailsare lost.ToolCallPart.id,.provider_name,.provider_detailsare lost.NativeToolCallPart.id,.provider_detailsare lost (only.provider_namesurvives via the prefixed tool call ID).NativeToolReturnPart.provider_detailsis lost.tool_kindis lost whenag_ui_version < '0.1.11'(before itsencrypted_valuecarrier existed), so typed tool parts reload as their base classes.tool_kindis not restored on error/denied tool returns (a typed return implies success to its readers), so those reload as plainToolReturnPart.RetryPromptPartbecomesToolReturnPart(orUserPromptPart) on reload.CachePointandUploadedFilecontent items are dropped (unlesspreserve_file_data=True).FileUrl.force_downloadis dropped whenag_ui_version < '0.1.15'(before typed multimodal content gained a metadata carrier).ThinkingPartis dropped whenag_ui_version='0.1.10'.FilePartis silently dropped unlesspreserve_file_data=True.UploadedFilein a multi-itemUserPromptPartis split into a separate activity message whenpreserve_file_data=True, which reloads as a separateUserPromptPart.MultiModalContentitems inToolReturnPart/NativeToolReturnPart.contentalways round-trip, regardless ofpreserve_file_data: the full content (files as base64/URL dicts) is serialized inline into the JSONToolMessage.contentand rehydrated on reload via theToolReturnContentdiscriminator. The same serialization is used for both history (dump_messages) and the live event stream (ToolCallResultEvent.content), so files survive either round-trip.- Part ordering within a
ModelResponsemay change when text follows tool calls.
list[Message] — A list of AG-UI Message objects.
messages : Sequence[ModelMessage]
A sequence of ModelMessage objects to convert.
ag_ui_version : str Default: DEFAULT_AG_UI_VERSION
AG-UI protocol version controlling ThinkingPart emission.
preserve_file_data : bool Default: False
Whether to include FilePart and UploadedFile items as ActivityMessages.
(Multimodal tool-return files always ride inline in ToolMessage.content and are unaffected.)
The default AG-UI version, auto-detected from the installed ag-ui-protocol package.
Type: str Default: detect_ag_ui_version()