pydantic-ai-slim 1.8.0__py3-none-any.whl → 1.9.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. pydantic_ai/__init__.py +2 -0
  2. pydantic_ai/_agent_graph.py +3 -0
  3. pydantic_ai/ag_ui.py +50 -696
  4. pydantic_ai/agent/abstract.py +13 -3
  5. pydantic_ai/direct.py +12 -0
  6. pydantic_ai/durable_exec/dbos/_agent.py +3 -0
  7. pydantic_ai/durable_exec/prefect/_agent.py +3 -0
  8. pydantic_ai/durable_exec/temporal/_agent.py +3 -0
  9. pydantic_ai/messages.py +39 -7
  10. pydantic_ai/models/__init__.py +42 -1
  11. pydantic_ai/models/groq.py +9 -1
  12. pydantic_ai/result.py +19 -7
  13. pydantic_ai/ui/__init__.py +16 -0
  14. pydantic_ai/ui/_adapter.py +386 -0
  15. pydantic_ai/ui/_event_stream.py +591 -0
  16. pydantic_ai/ui/_messages_builder.py +28 -0
  17. pydantic_ai/ui/ag_ui/__init__.py +9 -0
  18. pydantic_ai/ui/ag_ui/_adapter.py +187 -0
  19. pydantic_ai/ui/ag_ui/_event_stream.py +227 -0
  20. pydantic_ai/ui/ag_ui/app.py +141 -0
  21. pydantic_ai/ui/vercel_ai/__init__.py +16 -0
  22. pydantic_ai/ui/vercel_ai/_adapter.py +199 -0
  23. pydantic_ai/ui/vercel_ai/_event_stream.py +187 -0
  24. pydantic_ai/ui/vercel_ai/_utils.py +16 -0
  25. pydantic_ai/ui/vercel_ai/request_types.py +275 -0
  26. pydantic_ai/ui/vercel_ai/response_types.py +230 -0
  27. {pydantic_ai_slim-1.8.0.dist-info → pydantic_ai_slim-1.9.0.dist-info}/METADATA +5 -3
  28. {pydantic_ai_slim-1.8.0.dist-info → pydantic_ai_slim-1.9.0.dist-info}/RECORD +31 -17
  29. {pydantic_ai_slim-1.8.0.dist-info → pydantic_ai_slim-1.9.0.dist-info}/WHEEL +0 -0
  30. {pydantic_ai_slim-1.8.0.dist-info → pydantic_ai_slim-1.9.0.dist-info}/entry_points.txt +0 -0
  31. {pydantic_ai_slim-1.8.0.dist-info → pydantic_ai_slim-1.9.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,187 @@
1
+ """AG-UI adapter for handling requests."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Sequence
6
+ from functools import cached_property
7
+ from typing import (
8
+ TYPE_CHECKING,
9
+ Any,
10
+ )
11
+
12
+ from ... import ExternalToolset, ToolDefinition
13
+ from ...messages import (
14
+ BuiltinToolCallPart,
15
+ BuiltinToolReturnPart,
16
+ ModelMessage,
17
+ SystemPromptPart,
18
+ TextPart,
19
+ ToolCallPart,
20
+ ToolReturnPart,
21
+ UserPromptPart,
22
+ )
23
+ from ...output import OutputDataT
24
+ from ...tools import AgentDepsT
25
+ from ...toolsets import AbstractToolset
26
+
27
+ try:
28
+ from ag_ui.core import (
29
+ AssistantMessage,
30
+ BaseEvent,
31
+ DeveloperMessage,
32
+ Message,
33
+ RunAgentInput,
34
+ SystemMessage,
35
+ Tool as AGUITool,
36
+ ToolMessage,
37
+ UserMessage,
38
+ )
39
+
40
+ from .. import MessagesBuilder, UIAdapter, UIEventStream
41
+ from ._event_stream import BUILTIN_TOOL_CALL_ID_PREFIX, AGUIEventStream
42
+ except ImportError as e: # pragma: no cover
43
+ raise ImportError(
44
+ 'Please install the `ag-ui-protocol` package to use AG-UI integration, '
45
+ 'you can use the `ag-ui` optional group — `pip install "pydantic-ai-slim[ag-ui]"`'
46
+ ) from e
47
+
48
+ if TYPE_CHECKING:
49
+ pass
50
+
51
+ __all__ = ['AGUIAdapter']
52
+
53
+
54
+ # Frontend toolset
55
+
56
+
57
+ class _AGUIFrontendToolset(ExternalToolset[AgentDepsT]):
58
+ """Toolset for AG-UI frontend tools."""
59
+
60
+ def __init__(self, tools: list[AGUITool]):
61
+ """Initialize the toolset with AG-UI tools.
62
+
63
+ Args:
64
+ tools: List of AG-UI tool definitions.
65
+ """
66
+ super().__init__(
67
+ [
68
+ ToolDefinition(
69
+ name=tool.name,
70
+ description=tool.description,
71
+ parameters_json_schema=tool.parameters,
72
+ )
73
+ for tool in tools
74
+ ]
75
+ )
76
+
77
+ @property
78
+ def label(self) -> str:
79
+ """Return the label for this toolset."""
80
+ return 'the AG-UI frontend tools' # pragma: no cover
81
+
82
+
83
+ class AGUIAdapter(UIAdapter[RunAgentInput, Message, BaseEvent, AgentDepsT, OutputDataT]):
84
+ """UI adapter for the Agent-User Interaction (AG-UI) protocol."""
85
+
86
+ @classmethod
87
+ def build_run_input(cls, body: bytes) -> RunAgentInput:
88
+ """Build an AG-UI run input object from the request body."""
89
+ return RunAgentInput.model_validate_json(body)
90
+
91
+ def build_event_stream(self) -> UIEventStream[RunAgentInput, BaseEvent, AgentDepsT, OutputDataT]:
92
+ """Build an AG-UI event stream transformer."""
93
+ return AGUIEventStream(self.run_input, accept=self.accept)
94
+
95
+ @cached_property
96
+ def messages(self) -> list[ModelMessage]:
97
+ """Pydantic AI messages from the AG-UI run input."""
98
+ return self.load_messages(self.run_input.messages)
99
+
100
+ @cached_property
101
+ def toolset(self) -> AbstractToolset[AgentDepsT] | None:
102
+ """Toolset representing frontend tools from the AG-UI run input."""
103
+ if self.run_input.tools:
104
+ return _AGUIFrontendToolset[AgentDepsT](self.run_input.tools)
105
+ return None
106
+
107
+ @cached_property
108
+ def state(self) -> dict[str, Any] | None:
109
+ """Frontend state from the AG-UI run input."""
110
+ return self.run_input.state
111
+
112
+ @classmethod
113
+ def load_messages(cls, messages: Sequence[Message]) -> list[ModelMessage]:
114
+ """Transform AG-UI messages into Pydantic AI messages."""
115
+ builder = MessagesBuilder()
116
+ tool_calls: dict[str, str] = {} # Tool call ID to tool name mapping.
117
+
118
+ for msg in messages:
119
+ if isinstance(msg, UserMessage | SystemMessage | DeveloperMessage) or (
120
+ isinstance(msg, ToolMessage) and not msg.tool_call_id.startswith(BUILTIN_TOOL_CALL_ID_PREFIX)
121
+ ):
122
+ if isinstance(msg, UserMessage):
123
+ builder.add(UserPromptPart(content=msg.content))
124
+ elif isinstance(msg, SystemMessage | DeveloperMessage):
125
+ builder.add(SystemPromptPart(content=msg.content))
126
+ else:
127
+ tool_call_id = msg.tool_call_id
128
+ tool_name = tool_calls.get(tool_call_id)
129
+ if tool_name is None: # pragma: no cover
130
+ raise ValueError(f'Tool call with ID {tool_call_id} not found in the history.')
131
+
132
+ builder.add(
133
+ ToolReturnPart(
134
+ tool_name=tool_name,
135
+ content=msg.content,
136
+ tool_call_id=tool_call_id,
137
+ )
138
+ )
139
+
140
+ elif isinstance(msg, AssistantMessage) or ( # pragma: no branch
141
+ isinstance(msg, ToolMessage) and msg.tool_call_id.startswith(BUILTIN_TOOL_CALL_ID_PREFIX)
142
+ ):
143
+ if isinstance(msg, AssistantMessage):
144
+ if msg.content:
145
+ builder.add(TextPart(content=msg.content))
146
+
147
+ if msg.tool_calls:
148
+ for tool_call in msg.tool_calls:
149
+ tool_call_id = tool_call.id
150
+ tool_name = tool_call.function.name
151
+ tool_calls[tool_call_id] = tool_name
152
+
153
+ if tool_call_id.startswith(BUILTIN_TOOL_CALL_ID_PREFIX):
154
+ _, provider_name, tool_call_id = tool_call_id.split('|', 2)
155
+ builder.add(
156
+ BuiltinToolCallPart(
157
+ tool_name=tool_name,
158
+ args=tool_call.function.arguments,
159
+ tool_call_id=tool_call_id,
160
+ provider_name=provider_name,
161
+ )
162
+ )
163
+ else:
164
+ builder.add(
165
+ ToolCallPart(
166
+ tool_name=tool_name,
167
+ tool_call_id=tool_call_id,
168
+ args=tool_call.function.arguments,
169
+ )
170
+ )
171
+ else:
172
+ tool_call_id = msg.tool_call_id
173
+ tool_name = tool_calls.get(tool_call_id)
174
+ if tool_name is None: # pragma: no cover
175
+ raise ValueError(f'Tool call with ID {tool_call_id} not found in the history.')
176
+ _, provider_name, tool_call_id = tool_call_id.split('|', 2)
177
+
178
+ builder.add(
179
+ BuiltinToolReturnPart(
180
+ tool_name=tool_name,
181
+ content=msg.content,
182
+ tool_call_id=tool_call_id,
183
+ provider_name=provider_name,
184
+ )
185
+ )
186
+
187
+ return builder.messages
@@ -0,0 +1,227 @@
1
+ """AG-UI protocol adapter for Pydantic AI agents.
2
+
3
+ This module provides classes for integrating Pydantic AI agents with the AG-UI protocol,
4
+ enabling streaming event-based communication for interactive AI applications.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ from collections.abc import AsyncIterator, Iterable
11
+ from dataclasses import dataclass, field
12
+ from typing import Final
13
+
14
+ from ...messages import (
15
+ BuiltinToolCallPart,
16
+ BuiltinToolReturnPart,
17
+ FunctionToolResultEvent,
18
+ RetryPromptPart,
19
+ TextPart,
20
+ TextPartDelta,
21
+ ThinkingPart,
22
+ ThinkingPartDelta,
23
+ ToolCallPart,
24
+ ToolCallPartDelta,
25
+ ToolReturnPart,
26
+ )
27
+ from ...output import OutputDataT
28
+ from ...tools import AgentDepsT
29
+ from .. import SSE_CONTENT_TYPE, UIEventStream
30
+
31
+ try:
32
+ from ag_ui.core import (
33
+ BaseEvent,
34
+ EventType,
35
+ RunAgentInput,
36
+ RunErrorEvent,
37
+ RunFinishedEvent,
38
+ RunStartedEvent,
39
+ TextMessageContentEvent,
40
+ TextMessageEndEvent,
41
+ TextMessageStartEvent,
42
+ ThinkingEndEvent,
43
+ ThinkingStartEvent,
44
+ ThinkingTextMessageContentEvent,
45
+ ThinkingTextMessageEndEvent,
46
+ ThinkingTextMessageStartEvent,
47
+ ToolCallArgsEvent,
48
+ ToolCallEndEvent,
49
+ ToolCallResultEvent,
50
+ ToolCallStartEvent,
51
+ )
52
+ from ag_ui.encoder import EventEncoder
53
+
54
+ except ImportError as e: # pragma: no cover
55
+ raise ImportError(
56
+ 'Please install the `ag-ui-protocol` package to use AG-UI integration, '
57
+ 'you can use the `ag-ui` optional group — `pip install "pydantic-ai-slim[ag-ui]"`'
58
+ ) from e
59
+
60
+ __all__ = [
61
+ 'AGUIEventStream',
62
+ 'RunAgentInput',
63
+ 'RunStartedEvent',
64
+ 'RunFinishedEvent',
65
+ ]
66
+
67
+ BUILTIN_TOOL_CALL_ID_PREFIX: Final[str] = 'pyd_ai_builtin'
68
+
69
+
70
+ @dataclass
71
+ class AGUIEventStream(UIEventStream[RunAgentInput, BaseEvent, AgentDepsT, OutputDataT]):
72
+ """UI event stream transformer for the Agent-User Interaction (AG-UI) protocol."""
73
+
74
+ _thinking_text: bool = False
75
+ _builtin_tool_call_ids: dict[str, str] = field(default_factory=dict)
76
+ _error: bool = False
77
+
78
+ @property
79
+ def _event_encoder(self) -> EventEncoder:
80
+ return EventEncoder(accept=self.accept or SSE_CONTENT_TYPE)
81
+
82
+ @property
83
+ def content_type(self) -> str:
84
+ return self._event_encoder.get_content_type()
85
+
86
+ def encode_event(self, event: BaseEvent) -> str:
87
+ return self._event_encoder.encode(event)
88
+
89
+ async def before_stream(self) -> AsyncIterator[BaseEvent]:
90
+ yield RunStartedEvent(
91
+ thread_id=self.run_input.thread_id,
92
+ run_id=self.run_input.run_id,
93
+ )
94
+
95
+ async def after_stream(self) -> AsyncIterator[BaseEvent]:
96
+ if not self._error:
97
+ yield RunFinishedEvent(
98
+ thread_id=self.run_input.thread_id,
99
+ run_id=self.run_input.run_id,
100
+ )
101
+
102
+ async def on_error(self, error: Exception) -> AsyncIterator[BaseEvent]:
103
+ self._error = True
104
+ yield RunErrorEvent(message=str(error))
105
+
106
+ async def handle_text_start(self, part: TextPart, follows_text: bool = False) -> AsyncIterator[BaseEvent]:
107
+ if follows_text:
108
+ message_id = self.message_id
109
+ else:
110
+ message_id = self.new_message_id()
111
+ yield TextMessageStartEvent(message_id=message_id)
112
+
113
+ if part.content: # pragma: no branch
114
+ yield TextMessageContentEvent(message_id=message_id, delta=part.content)
115
+
116
+ async def handle_text_delta(self, delta: TextPartDelta) -> AsyncIterator[BaseEvent]:
117
+ if delta.content_delta: # pragma: no branch
118
+ yield TextMessageContentEvent(message_id=self.message_id, delta=delta.content_delta)
119
+
120
+ async def handle_text_end(self, part: TextPart, followed_by_text: bool = False) -> AsyncIterator[BaseEvent]:
121
+ if not followed_by_text:
122
+ yield TextMessageEndEvent(message_id=self.message_id)
123
+
124
+ async def handle_thinking_start(
125
+ self, part: ThinkingPart, follows_thinking: bool = False
126
+ ) -> AsyncIterator[BaseEvent]:
127
+ if not follows_thinking:
128
+ yield ThinkingStartEvent(type=EventType.THINKING_START)
129
+
130
+ if part.content:
131
+ yield ThinkingTextMessageStartEvent(type=EventType.THINKING_TEXT_MESSAGE_START)
132
+ yield ThinkingTextMessageContentEvent(type=EventType.THINKING_TEXT_MESSAGE_CONTENT, delta=part.content)
133
+ self._thinking_text = True
134
+
135
+ async def handle_thinking_delta(self, delta: ThinkingPartDelta) -> AsyncIterator[BaseEvent]:
136
+ if not delta.content_delta:
137
+ return # pragma: no cover
138
+
139
+ if not self._thinking_text:
140
+ yield ThinkingTextMessageStartEvent(type=EventType.THINKING_TEXT_MESSAGE_START)
141
+ self._thinking_text = True
142
+
143
+ yield ThinkingTextMessageContentEvent(type=EventType.THINKING_TEXT_MESSAGE_CONTENT, delta=delta.content_delta)
144
+
145
+ async def handle_thinking_end(
146
+ self, part: ThinkingPart, followed_by_thinking: bool = False
147
+ ) -> AsyncIterator[BaseEvent]:
148
+ if self._thinking_text:
149
+ yield ThinkingTextMessageEndEvent(type=EventType.THINKING_TEXT_MESSAGE_END)
150
+ self._thinking_text = False
151
+
152
+ if not followed_by_thinking:
153
+ yield ThinkingEndEvent(type=EventType.THINKING_END)
154
+
155
+ def handle_tool_call_start(self, part: ToolCallPart | BuiltinToolCallPart) -> AsyncIterator[BaseEvent]:
156
+ return self._handle_tool_call_start(part)
157
+
158
+ def handle_builtin_tool_call_start(self, part: BuiltinToolCallPart) -> AsyncIterator[BaseEvent]:
159
+ tool_call_id = part.tool_call_id
160
+ builtin_tool_call_id = '|'.join([BUILTIN_TOOL_CALL_ID_PREFIX, part.provider_name or '', tool_call_id])
161
+ self._builtin_tool_call_ids[tool_call_id] = builtin_tool_call_id
162
+ tool_call_id = builtin_tool_call_id
163
+
164
+ return self._handle_tool_call_start(part, tool_call_id)
165
+
166
+ async def _handle_tool_call_start(
167
+ self, part: ToolCallPart | BuiltinToolCallPart, tool_call_id: str | None = None
168
+ ) -> AsyncIterator[BaseEvent]:
169
+ tool_call_id = tool_call_id or part.tool_call_id
170
+ message_id = self.message_id or self.new_message_id()
171
+
172
+ yield ToolCallStartEvent(tool_call_id=tool_call_id, tool_call_name=part.tool_name, parent_message_id=message_id)
173
+ if part.args:
174
+ yield ToolCallArgsEvent(tool_call_id=tool_call_id, delta=part.args_as_json_str())
175
+
176
+ async def handle_tool_call_delta(self, delta: ToolCallPartDelta) -> AsyncIterator[BaseEvent]:
177
+ tool_call_id = delta.tool_call_id
178
+ assert tool_call_id, '`ToolCallPartDelta.tool_call_id` must be set'
179
+ if tool_call_id in self._builtin_tool_call_ids:
180
+ tool_call_id = self._builtin_tool_call_ids[tool_call_id]
181
+ yield ToolCallArgsEvent(
182
+ tool_call_id=tool_call_id,
183
+ delta=delta.args_delta if isinstance(delta.args_delta, str) else json.dumps(delta.args_delta),
184
+ )
185
+
186
+ async def handle_tool_call_end(self, part: ToolCallPart) -> AsyncIterator[BaseEvent]:
187
+ yield ToolCallEndEvent(tool_call_id=part.tool_call_id)
188
+
189
+ async def handle_builtin_tool_call_end(self, part: BuiltinToolCallPart) -> AsyncIterator[BaseEvent]:
190
+ yield ToolCallEndEvent(tool_call_id=self._builtin_tool_call_ids[part.tool_call_id])
191
+
192
+ async def handle_builtin_tool_return(self, part: BuiltinToolReturnPart) -> AsyncIterator[BaseEvent]:
193
+ tool_call_id = self._builtin_tool_call_ids[part.tool_call_id]
194
+ yield ToolCallResultEvent(
195
+ message_id=self.new_message_id(),
196
+ type=EventType.TOOL_CALL_RESULT,
197
+ role='tool',
198
+ tool_call_id=tool_call_id,
199
+ content=part.model_response_str(),
200
+ )
201
+
202
+ async def handle_function_tool_result(self, event: FunctionToolResultEvent) -> AsyncIterator[BaseEvent]:
203
+ result = event.result
204
+ output = result.model_response() if isinstance(result, RetryPromptPart) else result.model_response_str()
205
+
206
+ yield ToolCallResultEvent(
207
+ message_id=self.new_message_id(),
208
+ type=EventType.TOOL_CALL_RESULT,
209
+ role='tool',
210
+ tool_call_id=result.tool_call_id,
211
+ content=output,
212
+ )
213
+
214
+ # ToolCallResultEvent.content may hold user parts (e.g. text, images) that AG-UI does not currently have events for
215
+
216
+ if isinstance(result, ToolReturnPart):
217
+ # Check for AG-UI events returned by tool calls.
218
+ possible_event = result.metadata or result.content
219
+ if isinstance(possible_event, BaseEvent):
220
+ yield possible_event
221
+ elif isinstance(possible_event, str | bytes): # pragma: no branch
222
+ # Avoid iterable check for strings and bytes.
223
+ pass
224
+ elif isinstance(possible_event, Iterable): # pragma: no branch
225
+ for item in possible_event: # type: ignore[reportUnknownMemberType]
226
+ if isinstance(item, BaseEvent): # pragma: no branch
227
+ yield item
@@ -0,0 +1,141 @@
1
+ """AG-UI protocol integration for Pydantic AI agents."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from collections.abc import Callable, Mapping, Sequence
6
+ from typing import Any, Generic
7
+
8
+ from typing_extensions import Self
9
+
10
+ from pydantic_ai import DeferredToolResults
11
+ from pydantic_ai.agent import AbstractAgent
12
+ from pydantic_ai.builtin_tools import AbstractBuiltinTool
13
+ from pydantic_ai.messages import ModelMessage
14
+ from pydantic_ai.models import KnownModelName, Model
15
+ from pydantic_ai.output import OutputDataT, OutputSpec
16
+ from pydantic_ai.settings import ModelSettings
17
+ from pydantic_ai.tools import AgentDepsT
18
+ from pydantic_ai.toolsets import AbstractToolset
19
+ from pydantic_ai.usage import RunUsage, UsageLimits
20
+
21
+ from .. import OnCompleteFunc
22
+ from ._adapter import AGUIAdapter
23
+
24
+ try:
25
+ from starlette.applications import Starlette
26
+ from starlette.middleware import Middleware
27
+ from starlette.requests import Request
28
+ from starlette.responses import Response
29
+ from starlette.routing import BaseRoute
30
+ from starlette.types import ExceptionHandler, Lifespan
31
+ except ImportError as e: # pragma: no cover
32
+ raise ImportError(
33
+ 'Please install the `starlette` package to use `AGUIApp`, '
34
+ 'you can use the `ag-ui` optional group — `pip install "pydantic-ai-slim[ag-ui]"`'
35
+ ) from e
36
+
37
+
38
+ class AGUIApp(Generic[AgentDepsT, OutputDataT], Starlette):
39
+ """ASGI application for running Pydantic AI agents with AG-UI protocol support."""
40
+
41
+ def __init__(
42
+ self,
43
+ agent: AbstractAgent[AgentDepsT, OutputDataT],
44
+ *,
45
+ # AGUIAdapter.dispatch_request parameters
46
+ output_type: OutputSpec[Any] | None = None,
47
+ message_history: Sequence[ModelMessage] | None = None,
48
+ deferred_tool_results: DeferredToolResults | None = None,
49
+ model: Model | KnownModelName | str | None = None,
50
+ deps: AgentDepsT = None,
51
+ model_settings: ModelSettings | None = None,
52
+ usage_limits: UsageLimits | None = None,
53
+ usage: RunUsage | None = None,
54
+ infer_name: bool = True,
55
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
56
+ builtin_tools: Sequence[AbstractBuiltinTool] | None = None,
57
+ on_complete: OnCompleteFunc[Any] | None = None,
58
+ # Starlette parameters
59
+ debug: bool = False,
60
+ routes: Sequence[BaseRoute] | None = None,
61
+ middleware: Sequence[Middleware] | None = None,
62
+ exception_handlers: Mapping[Any, ExceptionHandler] | None = None,
63
+ on_startup: Sequence[Callable[[], Any]] | None = None,
64
+ on_shutdown: Sequence[Callable[[], Any]] | None = None,
65
+ lifespan: Lifespan[Self] | None = None,
66
+ ) -> None:
67
+ """An ASGI application that handles every request by running the agent and streaming the response.
68
+
69
+ Note that the `deps` will be the same for each request, with the exception of the frontend state that's
70
+ injected into the `state` field of a `deps` object that implements the [`StateHandler`][pydantic_ai.ui.StateHandler] protocol.
71
+ To provide different `deps` for each request (e.g. based on the authenticated user),
72
+ use [`AGUIAdapter.run_stream()`][pydantic_ai.ui.ag_ui.AGUIAdapter.run_stream] or
73
+ [`AGUIAdapter.dispatch_request()`][pydantic_ai.ui.ag_ui.AGUIAdapter.dispatch_request] instead.
74
+
75
+ Args:
76
+ agent: The agent to run.
77
+
78
+ output_type: Custom output type to use for this run, `output_type` may only be used if the agent has
79
+ no output validators since output validators would expect an argument that matches the agent's
80
+ output type.
81
+ message_history: History of the conversation so far.
82
+ deferred_tool_results: Optional results for deferred tool calls in the message history.
83
+ model: Optional model to use for this run, required if `model` was not set when creating the agent.
84
+ deps: Optional dependencies to use for this run.
85
+ model_settings: Optional settings to use for this model's request.
86
+ usage_limits: Optional limits on model request count or token usage.
87
+ usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
88
+ infer_name: Whether to try to infer the agent name from the call frame if it's not set.
89
+ toolsets: Optional additional toolsets for this run.
90
+ builtin_tools: Optional additional builtin tools for this run.
91
+ on_complete: Optional callback function called when the agent run completes successfully.
92
+ The callback receives the completed [`AgentRunResult`][pydantic_ai.agent.AgentRunResult] and can access `all_messages()` and other result data.
93
+
94
+ debug: Boolean indicating if debug tracebacks should be returned on errors.
95
+ routes: A list of routes to serve incoming HTTP and WebSocket requests.
96
+ middleware: A list of middleware to run for every request. A starlette application will always
97
+ automatically include two middleware classes. `ServerErrorMiddleware` is added as the very
98
+ outermost middleware, to handle any uncaught errors occurring anywhere in the entire stack.
99
+ `ExceptionMiddleware` is added as the very innermost middleware, to deal with handled
100
+ exception cases occurring in the routing or endpoints.
101
+ exception_handlers: A mapping of either integer status codes, or exception class types onto
102
+ callables which handle the exceptions. Exception handler callables should be of the form
103
+ `handler(request, exc) -> response` and may be either standard functions, or async functions.
104
+ on_startup: A list of callables to run on application startup. Startup handler callables do not
105
+ take any arguments, and may be either standard functions, or async functions.
106
+ on_shutdown: A list of callables to run on application shutdown. Shutdown handler callables do
107
+ not take any arguments, and may be either standard functions, or async functions.
108
+ lifespan: A lifespan context function, which can be used to perform startup and shutdown tasks.
109
+ This is a newer style that replaces the `on_startup` and `on_shutdown` handlers. Use one or
110
+ the other, not both.
111
+ """
112
+ super().__init__(
113
+ debug=debug,
114
+ routes=routes,
115
+ middleware=middleware,
116
+ exception_handlers=exception_handlers,
117
+ on_startup=on_startup,
118
+ on_shutdown=on_shutdown,
119
+ lifespan=lifespan,
120
+ )
121
+
122
+ async def run_agent(request: Request) -> Response:
123
+ """Endpoint to run the agent with the provided input data."""
124
+ return await AGUIAdapter[AgentDepsT, OutputDataT].dispatch_request(
125
+ request,
126
+ agent=agent,
127
+ output_type=output_type,
128
+ message_history=message_history,
129
+ deferred_tool_results=deferred_tool_results,
130
+ model=model,
131
+ deps=deps,
132
+ model_settings=model_settings,
133
+ usage_limits=usage_limits,
134
+ usage=usage,
135
+ infer_name=infer_name,
136
+ toolsets=toolsets,
137
+ builtin_tools=builtin_tools,
138
+ on_complete=on_complete,
139
+ )
140
+
141
+ self.router.add_route('/', run_agent, methods=['POST'])
@@ -0,0 +1,16 @@
1
+ """Vercel AI protocol adapter for Pydantic AI agents.
2
+
3
+ This module provides classes for integrating Pydantic AI agents with the Vercel AI protocol,
4
+ enabling streaming event-based communication for interactive AI applications.
5
+
6
+ Converted to Python from:
7
+ https://github.com/vercel/ai/blob/ai%405.0.34/packages/ai/src/ui/ui-messages.ts
8
+ """
9
+
10
+ from ._adapter import VercelAIAdapter
11
+ from ._event_stream import VercelAIEventStream
12
+
13
+ __all__ = [
14
+ 'VercelAIEventStream',
15
+ 'VercelAIAdapter',
16
+ ]