pydantic_ai.ui.vercel_ai
Vercel AI protocol adapter for Pydantic AI agents.
This module provides classes for integrating Pydantic AI agents with the Vercel AI protocol, enabling streaming event-based communication for interactive AI applications.
Converted to Python from: https://github.com/vercel/ai/blob/ai%405.0.34/packages/ai/src/ui/ui-messages.ts
Bases: UIAdapter[RequestData, UIMessage, BaseChunk, AgentDepsT, OutputDataT]
UI adapter for the Vercel AI protocol.
Conversation ID from the top-level id field of the Vercel AI request body (the chat ID).
Extract deferred tool results from Vercel AI messages with approval responses.
Type: DeferredToolResults | None
Pydantic AI messages from the Vercel AI run input.
Type: list[ModelMessage]
Vercel AI SDK version to target. Default is 5 for backwards compatibility.
Setting sdk_version=6 enables tool approval streaming for human-in-the-loop workflows.
sdk_version=7 emits the same wire as 6 (v7’s data-stream protocol equals v6’s); it is
accepted so the value reflects the client’s real SDK major and reserves it for future
v7-only chunks.
Type: Literal[5, 6, 7] Default: 5
Optional server-generated message ID to include in the StartChunk.
Type: str | None Default: None
def build_event_stream(
) -> UIEventStream[RequestData, BaseChunk, AgentDepsT, OutputDataT]
Build a Vercel AI event stream transformer.
UIEventStream[RequestData, BaseChunk, AgentDepsT, OutputDataT]
@classmethod
def build_run_input(cls, body: bytes) -> RequestData
Build a Vercel AI run input object from the request body.
RequestData
@async
@classmethod
def dispatch_request(
cls,
request: Request,
*,
agent: AbstractAgent[DispatchDepsT, DispatchOutputDataT],
sdk_version: Literal[5, 6, 7] = 5,
server_message_id: str | None = None,
message_history: Sequence[ModelMessage] | None = None,
deferred_tool_results: DeferredToolResults | None = None,
conversation_id: str | None = None,
run_id: str | None = None,
model: Model | KnownModelName | str | None = None,
instructions: _instructions.AgentInstructions[DispatchDepsT] = None,
deps: DispatchDepsT = None,
output_type: OutputSpec[Any] | None = None,
model_settings: ModelSettings | None = None,
usage_limits: UsageLimits | None = None,
usage: RunUsage | None = None,
metadata: AgentMetadata[DispatchDepsT] | None = None,
infer_name: bool = True,
toolsets: Sequence[AbstractToolset[DispatchDepsT]] | None = None,
capabilities: Sequence[AbstractCapability[DispatchDepsT]] | None = None,
on_complete: OnCompleteFunc[BaseChunk] | None = None,
on_cancel: OnCancelFunc[BaseChunk] | None = None,
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,
preserve_file_data: bool | None = None,
**kwargs: Any,
) -> Response
Extends dispatch_request with Vercel AI-specific parameters.
preserve_file_data is a deprecated alias for allow_uploaded_files.
Response
@classmethod
def dump_messages(
cls,
messages: Sequence[ModelMessage],
*,
generate_message_id: Callable[[ModelRequest | ModelResponse, Literal['system', 'user', 'assistant'], int], str] | None = None,
sdk_version: Literal[5, 6, 7] = 5,
) -> list[UIMessage]
Transform Pydantic AI messages into Vercel AI messages.
Note: The round-trip dump_messages -> load_messages is not fully lossless for tool
results. Successful, failed, and denied results each round-trip via their own part type
(ToolOutputAvailablePart / ToolOutputErrorPart / ToolOutputDeniedPart), but a
RetryPromptPart becomes a ToolReturnPart with outcome='failed' on reload (or a user
text part when it has no tool_name), since the protocol has no separate retry concept —
both a retry prompt and a ToolFailed result map to ToolOutputErrorPart. A reloaded retry
is therefore presented to the model as a definitive failure rather than a request to correct
and retry; keep the conversation in-process rather than persisting through the Vercel AI wire
format if you need retry semantics to survive a round-trip.
Tool calls lose one thing too: ToolCallPart.args that don’t parse as a JSON object are
rewritten to {'INVALID_JSON': '<raw args>'} (see
args_as_dict), so the raw string is
no longer recoverable as args on reload.
When sdk_version=6, tool calls that have no corresponding result in the message history
are automatically detected as deferred and emitted with state='approval-requested', so the
frontend can render approve/reject buttons on reload. On v5, such tool calls are emitted
with state='input-available' (approval states are v6-only).
list[UIMessage] — A list of UIMessage objects in Vercel AI format
messages : Sequence[ModelMessage]
A sequence of ModelMessage objects to convert
generate_message_id : Callable[[ModelRequest | ModelResponse, Literal[‘system’, ‘user’, ‘assistant’], int], str] | None Default: None
Optional custom function to generate message IDs. If provided,
it receives the message, the role (‘system’, ‘user’, or ‘assistant’), and the
message index (incremented per UIMessage appended), and should return a unique
string ID. If not provided, uses provider_response_id for responses,
run_id-based IDs for messages with run_id, or a deterministic UUID5 fallback.
sdk_version : Literal[5, 6, 7] Default: 5
Vercel AI SDK version to target: 5, 6, or 7. Defaults to 5 for backwards compatibility. Set to 6 to emit tool approval parts for deferred tool calls; 7 emits identically to 6 (v7’s data-stream protocol equals v6’s).
@async
@classmethod
def from_request(
cls,
request: Request,
*,
agent: AbstractAgent[AgentDepsT, OutputDataT],
sdk_version: Literal[5, 6, 7] = 5,
server_message_id: str | None = None,
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,
preserve_file_data: bool | None = None,
**kwargs: Any,
) -> VercelAIAdapter[AgentDepsT, OutputDataT]
Extends from_request with Vercel AI-specific parameters.
preserve_file_data is a deprecated alias for allow_uploaded_files.
VercelAIAdapter[AgentDepsT, OutputDataT]
@classmethod
def load_messages(cls, messages: Sequence[UIMessage]) -> list[ModelMessage]
Transform Vercel AI messages into Pydantic AI messages.
Bases: UIEventStream[RequestData, BaseChunk, AgentDepsT, OutputDataT]
UI event stream transformer for the Vercel AI protocol.
Vercel AI SDK version to target. Setting to 6 enables tool approval streaming; 7 emits the same wire as 6 (v7’s data-stream protocol equals v6’s).
Type: Literal[5, 6, 7] Default: 5
Optional server-generated message ID to include in the StartChunk.
Type: str | None Default: None
Vercel AI request types (UI messages).
Converted to Python from: https://github.com/vercel/ai/blob/ai%406.0.57/packages/ai/src/ui/ui-messages.ts
Tool approval types (ToolApprovalRequested, ToolApprovalResponded) require AI SDK v6 or later.
Bases: CamelBaseModel, ABC
Abstract base class for all UI parts.
Bases: BaseUIPart
Data part with dynamic type based on data name.
Bases: BaseUIPart
Dynamic tool part in approval-requested state (awaiting user decision).
Bases: BaseUIPart
Dynamic tool part in approval-responded state (user approved/denied, execution pending).
Bases: BaseUIPart
Dynamic tool part in input-available state.
Bases: BaseUIPart
Dynamic tool part in input-streaming state.
Bases: BaseUIPart
Dynamic tool part in output-available state.
Bases: BaseUIPart
Dynamic tool part in output-denied state (tool was denied, terminal state).
Bases: BaseUIPart
Dynamic tool part in output-error state.
Bases: BaseUIPart
A file part of a message.
Optional filename of the file.
Type: str | None Default: None
IANA media type of the file. @see https://www.iana.org/assignments/media-types/media-types.xhtml
Type: str
The provider metadata.
Type: ProviderMetadata | None Default: None
The URL of the file. It can either be a URL to a hosted file or a Data URL.
Type: str
Bases: BaseUIPart
A reasoning part of a message.
The provider metadata.
Type: ProviderMetadata | None Default: None
The state of the reasoning part.
Type: Literal[‘streaming’, ‘done’] | None Default: None
The reasoning text.
Type: str
Bases: CamelBaseModel
Ask the agent to regenerate a message.
Bases: BaseUIPart
A document source part of a message.
Bases: BaseUIPart
A source part of a message.
Bases: BaseUIPart
A step boundary part of a message.
Bases: CamelBaseModel
Submit message request.
Bases: BaseUIPart
A text part of a message.
The provider metadata.
Type: ProviderMetadata | None Default: None
The state of the text part.
Type: Literal[‘streaming’, ‘done’] | None Default: None
The text content.
Type: str
Bases: CamelBaseModel
Tool approval in requested state (awaiting user response).
The approval request ID.
Type: str
Bases: BaseUIPart
Tool part in approval-requested state (awaiting user decision).
Bases: CamelBaseModel
Tool approval in responded state (user has approved or denied).
Whether the user approved the tool call.
Deliberately strict: in Pydantic’s default lax mode {'approved': 1} or {'approved': 'true'}
would coerce to an approval, and this field is the client-controlled gate on tools declared
requires_approval=True. A non-boolean value fails validation — and so rejects the whole
request — instead of silently executing the call
(#6922).
ToolApproval is an undiscriminated union, so rejecting here only denies because
CamelBaseModel’s extra='forbid' stops the part re-matching ToolApprovalRequested
(which upstream declares approved?: never). Relaxing either would reopen the gate.
Type: StrictBool
The approval request ID.
Type: str
Optional reason for the approval or denial.
Type: str | None Default: None
Bases: BaseUIPart
Tool part in approval-responded state (user approved/denied, execution pending).
Bases: BaseUIPart
Tool part in input-available state.
Bases: BaseUIPart
Tool part in input-streaming state.
Bases: BaseUIPart
Tool part in output-available state.
Bases: BaseUIPart
Tool part in output-denied state (tool was denied, terminal state).
Bases: BaseUIPart
Tool part in output-error state.
Bases: CamelBaseModel
A message as displayed in the UI by Vercel AI Elements.
A unique identifier for the message.
Type: str
The metadata of the message.
Type: Any | None Default: None
The parts of the message. Use this for rendering the message in the UI. System messages should be avoided (set the system prompt on the server instead). They can have text parts. User messages can have text parts and file parts. Assistant messages can have text, reasoning, tool invocation, and file parts.
Type: list[UIMessagePart]
The role of the message.
Type: Literal[‘system’, ‘user’, ‘assistant’]
Union of all dynamic tool part types.
Default: DynamicToolInputStreamingPart | DynamicToolInputAvailablePart | DynamicToolOutputAvailablePart | DynamicToolOutputErrorPart | DynamicToolApprovalRequestedPart | DynamicToolApprovalRespondedPart | DynamicToolOutputDeniedPart
Provider metadata.
Default: dict[str, dict[str, JSONValue]]
Union of all request data types.
Default: Annotated[SubmitMessage | RegenerateMessage, Discriminator('trigger')]
Union of tool approval states.
Default: ToolApprovalRequested | ToolApprovalResponded
Union of all tool part types.
Default: ToolInputStreamingPart | ToolInputAvailablePart | ToolOutputAvailablePart | ToolOutputErrorPart | ToolApprovalRequestedPart | ToolApprovalRespondedPart | ToolOutputDeniedPart
Union of all message part types.
Default: TextUIPart | ReasoningUIPart | ToolUIPart | DynamicToolUIPart | SourceUrlUIPart | SourceDocumentUIPart | FileUIPart | DataUIPart | StepStartUIPart
Vercel AI response types (SSE chunks).
Converted to Python from: https://github.com/vercel/ai/blob/ai%406.0.57/packages/ai/src/ui-message-stream/ui-message-chunks.ts
Tool approval types (ToolApprovalRequestChunk, ToolOutputDeniedChunk) require AI SDK UI v6 or later.
Bases: BaseChunk
Abort chunk.
Bases: CamelBaseModel, ABC
Abstract base class for response SSE events.
Bases: BaseChunk
Data chunk with dynamic type.
Bases: BaseChunk
Done chunk.
Bases: BaseChunk
Error chunk.
Bases: BaseChunk
File chunk.
Bases: BaseChunk
Finish chunk.
Bases: BaseChunk
Finish step chunk.
Bases: BaseChunk
Message metadata chunk.
Bases: BaseChunk
Reasoning delta chunk.
Bases: BaseChunk
Reasoning end chunk.
Bases: BaseChunk
Reasoning start chunk.
Bases: BaseChunk
Source document chunk.
Bases: BaseChunk
Source URL chunk.
Bases: BaseChunk
Start chunk.
Bases: BaseChunk
Start step chunk.
Bases: BaseChunk
Text delta chunk.
Bases: BaseChunk
Text end chunk.
Bases: BaseChunk
Text start chunk.
Bases: BaseChunk
Tool approval request chunk for human-in-the-loop approval.
Requires AI SDK UI v6 or later.
Bases: BaseChunk
Tool input available chunk.
Bases: BaseChunk
Tool input delta chunk.
Bases: BaseChunk
Tool input error chunk.
Requires AI SDK UI v6 or later.
Bases: BaseChunk
Tool input start chunk.
Bases: BaseChunk
Tool output available chunk.
Bases: BaseChunk
Tool output denied chunk when user denies tool execution.
Requires AI SDK UI v6 or later.
Bases: BaseChunk
Tool output error chunk.
Reason why the model finished generating.
Default: Literal['stop', 'length', 'content-filter', 'tool-calls', 'error', 'other'] | None
Provider metadata.
Default: dict[str, dict[str, JSONValue]]