pydantic-ai-slim 1.2.1__py3-none-any.whl → 1.10.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.
- pydantic_ai/__init__.py +6 -0
- pydantic_ai/_agent_graph.py +67 -20
- pydantic_ai/_cli.py +2 -2
- pydantic_ai/_output.py +20 -12
- pydantic_ai/_run_context.py +6 -2
- pydantic_ai/_utils.py +26 -8
- pydantic_ai/ag_ui.py +50 -696
- pydantic_ai/agent/__init__.py +13 -25
- pydantic_ai/agent/abstract.py +146 -9
- pydantic_ai/builtin_tools.py +106 -4
- pydantic_ai/direct.py +16 -4
- pydantic_ai/durable_exec/dbos/_agent.py +3 -0
- pydantic_ai/durable_exec/prefect/_agent.py +3 -0
- pydantic_ai/durable_exec/temporal/__init__.py +11 -0
- pydantic_ai/durable_exec/temporal/_agent.py +3 -0
- pydantic_ai/durable_exec/temporal/_function_toolset.py +23 -72
- pydantic_ai/durable_exec/temporal/_mcp_server.py +30 -30
- pydantic_ai/durable_exec/temporal/_run_context.py +7 -2
- pydantic_ai/durable_exec/temporal/_toolset.py +67 -3
- pydantic_ai/exceptions.py +6 -1
- pydantic_ai/mcp.py +1 -22
- pydantic_ai/messages.py +46 -8
- pydantic_ai/models/__init__.py +87 -38
- pydantic_ai/models/anthropic.py +132 -11
- pydantic_ai/models/bedrock.py +4 -4
- pydantic_ai/models/cohere.py +0 -7
- pydantic_ai/models/gemini.py +9 -2
- pydantic_ai/models/google.py +26 -23
- pydantic_ai/models/groq.py +13 -5
- pydantic_ai/models/huggingface.py +2 -2
- pydantic_ai/models/openai.py +251 -52
- pydantic_ai/models/outlines.py +563 -0
- pydantic_ai/models/test.py +6 -3
- pydantic_ai/profiles/openai.py +7 -0
- pydantic_ai/providers/__init__.py +25 -12
- pydantic_ai/providers/anthropic.py +2 -2
- pydantic_ai/providers/bedrock.py +60 -16
- pydantic_ai/providers/gateway.py +60 -72
- pydantic_ai/providers/google.py +91 -24
- pydantic_ai/providers/openrouter.py +3 -0
- pydantic_ai/providers/outlines.py +40 -0
- pydantic_ai/providers/ovhcloud.py +95 -0
- pydantic_ai/result.py +173 -8
- pydantic_ai/run.py +40 -24
- pydantic_ai/settings.py +8 -0
- pydantic_ai/tools.py +10 -6
- pydantic_ai/toolsets/fastmcp.py +215 -0
- pydantic_ai/ui/__init__.py +16 -0
- pydantic_ai/ui/_adapter.py +386 -0
- pydantic_ai/ui/_event_stream.py +591 -0
- pydantic_ai/ui/_messages_builder.py +28 -0
- pydantic_ai/ui/ag_ui/__init__.py +9 -0
- pydantic_ai/ui/ag_ui/_adapter.py +187 -0
- pydantic_ai/ui/ag_ui/_event_stream.py +236 -0
- pydantic_ai/ui/ag_ui/app.py +148 -0
- pydantic_ai/ui/vercel_ai/__init__.py +16 -0
- pydantic_ai/ui/vercel_ai/_adapter.py +199 -0
- pydantic_ai/ui/vercel_ai/_event_stream.py +187 -0
- pydantic_ai/ui/vercel_ai/_utils.py +16 -0
- pydantic_ai/ui/vercel_ai/request_types.py +275 -0
- pydantic_ai/ui/vercel_ai/response_types.py +230 -0
- pydantic_ai/usage.py +13 -2
- {pydantic_ai_slim-1.2.1.dist-info → pydantic_ai_slim-1.10.0.dist-info}/METADATA +23 -5
- {pydantic_ai_slim-1.2.1.dist-info → pydantic_ai_slim-1.10.0.dist-info}/RECORD +67 -49
- {pydantic_ai_slim-1.2.1.dist-info → pydantic_ai_slim-1.10.0.dist-info}/WHEEL +0 -0
- {pydantic_ai_slim-1.2.1.dist-info → pydantic_ai_slim-1.10.0.dist-info}/entry_points.txt +0 -0
- {pydantic_ai_slim-1.2.1.dist-info → pydantic_ai_slim-1.10.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,56 +1,22 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from collections.abc import Callable
|
|
4
|
-
from
|
|
5
|
-
from typing import Annotated, Any, Literal, assert_never
|
|
4
|
+
from typing import Any, Literal
|
|
6
5
|
|
|
7
|
-
from pydantic import ConfigDict, Discriminator, with_config
|
|
8
6
|
from temporalio import activity, workflow
|
|
9
7
|
from temporalio.workflow import ActivityConfig
|
|
10
8
|
|
|
11
9
|
from pydantic_ai import FunctionToolset, ToolsetTool
|
|
12
|
-
from pydantic_ai.exceptions import
|
|
10
|
+
from pydantic_ai.exceptions import UserError
|
|
13
11
|
from pydantic_ai.tools import AgentDepsT, RunContext
|
|
14
12
|
from pydantic_ai.toolsets.function import FunctionToolsetTool
|
|
15
13
|
|
|
16
14
|
from ._run_context import TemporalRunContext
|
|
17
|
-
from ._toolset import
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class _CallToolParams:
|
|
23
|
-
name: str
|
|
24
|
-
tool_args: dict[str, Any]
|
|
25
|
-
serialized_run_context: Any
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
@dataclass
|
|
29
|
-
class _ApprovalRequired:
|
|
30
|
-
kind: Literal['approval_required'] = 'approval_required'
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
@dataclass
|
|
34
|
-
class _CallDeferred:
|
|
35
|
-
kind: Literal['call_deferred'] = 'call_deferred'
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@dataclass
|
|
39
|
-
class _ModelRetry:
|
|
40
|
-
message: str
|
|
41
|
-
kind: Literal['model_retry'] = 'model_retry'
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
@dataclass
|
|
45
|
-
class _ToolReturn:
|
|
46
|
-
result: Any
|
|
47
|
-
kind: Literal['tool_return'] = 'tool_return'
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
_CallToolResult = Annotated[
|
|
51
|
-
_ApprovalRequired | _CallDeferred | _ModelRetry | _ToolReturn,
|
|
52
|
-
Discriminator('kind'),
|
|
53
|
-
]
|
|
15
|
+
from ._toolset import (
|
|
16
|
+
CallToolParams,
|
|
17
|
+
CallToolResult,
|
|
18
|
+
TemporalWrapperToolset,
|
|
19
|
+
)
|
|
54
20
|
|
|
55
21
|
|
|
56
22
|
class TemporalFunctionToolset(TemporalWrapperToolset[AgentDepsT]):
|
|
@@ -69,7 +35,7 @@ class TemporalFunctionToolset(TemporalWrapperToolset[AgentDepsT]):
|
|
|
69
35
|
self.tool_activity_config = tool_activity_config
|
|
70
36
|
self.run_context_type = run_context_type
|
|
71
37
|
|
|
72
|
-
async def call_tool_activity(params:
|
|
38
|
+
async def call_tool_activity(params: CallToolParams, deps: AgentDepsT) -> CallToolResult:
|
|
73
39
|
name = params.name
|
|
74
40
|
ctx = self.run_context_type.deserialize_run_context(params.serialized_run_context, deps=deps)
|
|
75
41
|
try:
|
|
@@ -83,15 +49,7 @@ class TemporalFunctionToolset(TemporalWrapperToolset[AgentDepsT]):
|
|
|
83
49
|
# The tool args will already have been validated into their proper types in the `ToolManager`,
|
|
84
50
|
# but `execute_activity` would have turned them into simple Python types again, so we need to re-validate them.
|
|
85
51
|
args_dict = tool.args_validator.validate_python(params.tool_args)
|
|
86
|
-
|
|
87
|
-
result = await self.wrapped.call_tool(name, args_dict, ctx, tool)
|
|
88
|
-
return _ToolReturn(result=result)
|
|
89
|
-
except ApprovalRequired:
|
|
90
|
-
return _ApprovalRequired()
|
|
91
|
-
except CallDeferred:
|
|
92
|
-
return _CallDeferred()
|
|
93
|
-
except ModelRetry as e:
|
|
94
|
-
return _ModelRetry(message=e.message)
|
|
52
|
+
return await self._wrap_call_tool_result(self.wrapped.call_tool(name, args_dict, ctx, tool))
|
|
95
53
|
|
|
96
54
|
# Set type hint explicitly so that Temporal can take care of serialization and deserialization
|
|
97
55
|
call_tool_activity.__annotations__['deps'] = deps_type
|
|
@@ -122,25 +80,18 @@ class TemporalFunctionToolset(TemporalWrapperToolset[AgentDepsT]):
|
|
|
122
80
|
|
|
123
81
|
tool_activity_config = self.activity_config | tool_activity_config
|
|
124
82
|
serialized_run_context = self.run_context_type.serialize_run_context(ctx)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
83
|
+
return self._unwrap_call_tool_result(
|
|
84
|
+
await workflow.execute_activity( # pyright: ignore[reportUnknownMemberType]
|
|
85
|
+
activity=self.call_tool_activity,
|
|
86
|
+
args=[
|
|
87
|
+
CallToolParams(
|
|
88
|
+
name=name,
|
|
89
|
+
tool_args=tool_args,
|
|
90
|
+
serialized_run_context=serialized_run_context,
|
|
91
|
+
tool_def=None,
|
|
92
|
+
),
|
|
93
|
+
ctx.deps,
|
|
94
|
+
],
|
|
95
|
+
**tool_activity_config,
|
|
96
|
+
)
|
|
136
97
|
)
|
|
137
|
-
if isinstance(result, _ApprovalRequired):
|
|
138
|
-
raise ApprovalRequired()
|
|
139
|
-
elif isinstance(result, _CallDeferred):
|
|
140
|
-
raise CallDeferred()
|
|
141
|
-
elif isinstance(result, _ModelRetry):
|
|
142
|
-
raise ModelRetry(result.message)
|
|
143
|
-
elif isinstance(result, _ToolReturn):
|
|
144
|
-
return result.result
|
|
145
|
-
else:
|
|
146
|
-
assert_never(result)
|
|
@@ -11,11 +11,15 @@ from typing_extensions import Self
|
|
|
11
11
|
|
|
12
12
|
from pydantic_ai import ToolsetTool
|
|
13
13
|
from pydantic_ai.exceptions import UserError
|
|
14
|
-
from pydantic_ai.mcp import MCPServer
|
|
14
|
+
from pydantic_ai.mcp import MCPServer
|
|
15
15
|
from pydantic_ai.tools import AgentDepsT, RunContext, ToolDefinition
|
|
16
16
|
|
|
17
17
|
from ._run_context import TemporalRunContext
|
|
18
|
-
from ._toolset import
|
|
18
|
+
from ._toolset import (
|
|
19
|
+
CallToolParams,
|
|
20
|
+
CallToolResult,
|
|
21
|
+
TemporalWrapperToolset,
|
|
22
|
+
)
|
|
19
23
|
|
|
20
24
|
|
|
21
25
|
@dataclass
|
|
@@ -24,15 +28,6 @@ class _GetToolsParams:
|
|
|
24
28
|
serialized_run_context: Any
|
|
25
29
|
|
|
26
30
|
|
|
27
|
-
@dataclass
|
|
28
|
-
@with_config(ConfigDict(arbitrary_types_allowed=True))
|
|
29
|
-
class _CallToolParams:
|
|
30
|
-
name: str
|
|
31
|
-
tool_args: dict[str, Any]
|
|
32
|
-
serialized_run_context: Any
|
|
33
|
-
tool_def: ToolDefinition
|
|
34
|
-
|
|
35
|
-
|
|
36
31
|
class TemporalMCPServer(TemporalWrapperToolset[AgentDepsT]):
|
|
37
32
|
def __init__(
|
|
38
33
|
self,
|
|
@@ -72,13 +67,16 @@ class TemporalMCPServer(TemporalWrapperToolset[AgentDepsT]):
|
|
|
72
67
|
get_tools_activity
|
|
73
68
|
)
|
|
74
69
|
|
|
75
|
-
async def call_tool_activity(params:
|
|
70
|
+
async def call_tool_activity(params: CallToolParams, deps: AgentDepsT) -> CallToolResult:
|
|
76
71
|
run_context = self.run_context_type.deserialize_run_context(params.serialized_run_context, deps=deps)
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
72
|
+
assert isinstance(params.tool_def, ToolDefinition)
|
|
73
|
+
return await self._wrap_call_tool_result(
|
|
74
|
+
self.wrapped.call_tool(
|
|
75
|
+
params.name,
|
|
76
|
+
params.tool_args,
|
|
77
|
+
run_context,
|
|
78
|
+
self.tool_for_tool_def(params.tool_def),
|
|
79
|
+
)
|
|
82
80
|
)
|
|
83
81
|
|
|
84
82
|
# Set type hint explicitly so that Temporal can take care of serialization and deserialization
|
|
@@ -125,22 +123,24 @@ class TemporalMCPServer(TemporalWrapperToolset[AgentDepsT]):
|
|
|
125
123
|
tool_args: dict[str, Any],
|
|
126
124
|
ctx: RunContext[AgentDepsT],
|
|
127
125
|
tool: ToolsetTool[AgentDepsT],
|
|
128
|
-
) ->
|
|
126
|
+
) -> CallToolResult:
|
|
129
127
|
if not workflow.in_workflow():
|
|
130
128
|
return await super().call_tool(name, tool_args, ctx, tool)
|
|
131
129
|
|
|
132
130
|
tool_activity_config = self.activity_config | self.tool_activity_config.get(name, {})
|
|
133
131
|
serialized_run_context = self.run_context_type.serialize_run_context(ctx)
|
|
134
|
-
return
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
132
|
+
return self._unwrap_call_tool_result(
|
|
133
|
+
await workflow.execute_activity( # pyright: ignore[reportUnknownMemberType]
|
|
134
|
+
activity=self.call_tool_activity,
|
|
135
|
+
args=[
|
|
136
|
+
CallToolParams(
|
|
137
|
+
name=name,
|
|
138
|
+
tool_args=tool_args,
|
|
139
|
+
serialized_run_context=serialized_run_context,
|
|
140
|
+
tool_def=tool.tool_def,
|
|
141
|
+
),
|
|
142
|
+
ctx.deps,
|
|
143
|
+
],
|
|
144
|
+
**tool_activity_config,
|
|
145
|
+
)
|
|
146
146
|
)
|
|
@@ -2,8 +2,13 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Any
|
|
4
4
|
|
|
5
|
+
from typing_extensions import TypeVar
|
|
6
|
+
|
|
5
7
|
from pydantic_ai.exceptions import UserError
|
|
6
|
-
from pydantic_ai.tools import
|
|
8
|
+
from pydantic_ai.tools import RunContext
|
|
9
|
+
|
|
10
|
+
AgentDepsT = TypeVar('AgentDepsT', default=None, covariant=True)
|
|
11
|
+
"""Type variable for the agent dependencies in `RunContext`."""
|
|
7
12
|
|
|
8
13
|
|
|
9
14
|
class TemporalRunContext(RunContext[AgentDepsT]):
|
|
@@ -47,6 +52,6 @@ class TemporalRunContext(RunContext[AgentDepsT]):
|
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
@classmethod
|
|
50
|
-
def deserialize_run_context(cls, ctx: dict[str, Any], deps:
|
|
55
|
+
def deserialize_run_context(cls, ctx: dict[str, Any], deps: Any) -> TemporalRunContext[Any]:
|
|
51
56
|
"""Deserialize the run context from a `dict[str, Any]`."""
|
|
52
57
|
return cls(**ctx, deps=deps)
|
|
@@ -1,17 +1,58 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
from abc import ABC, abstractmethod
|
|
4
|
-
from collections.abc import Callable
|
|
5
|
-
from
|
|
4
|
+
from collections.abc import Awaitable, Callable
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from typing import Annotated, Any, Literal
|
|
6
7
|
|
|
8
|
+
from pydantic import ConfigDict, Discriminator, with_config
|
|
7
9
|
from temporalio.workflow import ActivityConfig
|
|
10
|
+
from typing_extensions import assert_never
|
|
8
11
|
|
|
9
12
|
from pydantic_ai import AbstractToolset, FunctionToolset, WrapperToolset
|
|
10
|
-
from pydantic_ai.
|
|
13
|
+
from pydantic_ai.exceptions import ApprovalRequired, CallDeferred, ModelRetry
|
|
14
|
+
from pydantic_ai.tools import AgentDepsT, ToolDefinition
|
|
11
15
|
|
|
12
16
|
from ._run_context import TemporalRunContext
|
|
13
17
|
|
|
14
18
|
|
|
19
|
+
@dataclass
|
|
20
|
+
@with_config(ConfigDict(arbitrary_types_allowed=True))
|
|
21
|
+
class CallToolParams:
|
|
22
|
+
name: str
|
|
23
|
+
tool_args: dict[str, Any]
|
|
24
|
+
serialized_run_context: Any
|
|
25
|
+
tool_def: ToolDefinition | None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass
|
|
29
|
+
class _ApprovalRequired:
|
|
30
|
+
kind: Literal['approval_required'] = 'approval_required'
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class _CallDeferred:
|
|
35
|
+
kind: Literal['call_deferred'] = 'call_deferred'
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass
|
|
39
|
+
class _ModelRetry:
|
|
40
|
+
message: str
|
|
41
|
+
kind: Literal['model_retry'] = 'model_retry'
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@dataclass
|
|
45
|
+
class _ToolReturn:
|
|
46
|
+
result: Any
|
|
47
|
+
kind: Literal['tool_return'] = 'tool_return'
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
CallToolResult = Annotated[
|
|
51
|
+
_ApprovalRequired | _CallDeferred | _ModelRetry | _ToolReturn,
|
|
52
|
+
Discriminator('kind'),
|
|
53
|
+
]
|
|
54
|
+
|
|
55
|
+
|
|
15
56
|
class TemporalWrapperToolset(WrapperToolset[AgentDepsT], ABC):
|
|
16
57
|
@property
|
|
17
58
|
def id(self) -> str:
|
|
@@ -30,6 +71,29 @@ class TemporalWrapperToolset(WrapperToolset[AgentDepsT], ABC):
|
|
|
30
71
|
# Temporalized toolsets cannot be swapped out after the fact.
|
|
31
72
|
return self
|
|
32
73
|
|
|
74
|
+
async def _wrap_call_tool_result(self, coro: Awaitable[Any]) -> CallToolResult:
|
|
75
|
+
try:
|
|
76
|
+
result = await coro
|
|
77
|
+
return _ToolReturn(result=result)
|
|
78
|
+
except ApprovalRequired:
|
|
79
|
+
return _ApprovalRequired()
|
|
80
|
+
except CallDeferred:
|
|
81
|
+
return _CallDeferred()
|
|
82
|
+
except ModelRetry as e:
|
|
83
|
+
return _ModelRetry(message=e.message)
|
|
84
|
+
|
|
85
|
+
def _unwrap_call_tool_result(self, result: CallToolResult) -> Any:
|
|
86
|
+
if isinstance(result, _ToolReturn):
|
|
87
|
+
return result.result
|
|
88
|
+
elif isinstance(result, _ApprovalRequired):
|
|
89
|
+
raise ApprovalRequired()
|
|
90
|
+
elif isinstance(result, _CallDeferred):
|
|
91
|
+
raise CallDeferred()
|
|
92
|
+
elif isinstance(result, _ModelRetry):
|
|
93
|
+
raise ModelRetry(result.message)
|
|
94
|
+
else:
|
|
95
|
+
assert_never(result)
|
|
96
|
+
|
|
33
97
|
|
|
34
98
|
def temporalize_toolset(
|
|
35
99
|
toolset: AbstractToolset[AgentDepsT],
|
pydantic_ai/exceptions.py
CHANGED
|
@@ -23,6 +23,7 @@ __all__ = (
|
|
|
23
23
|
'UnexpectedModelBehavior',
|
|
24
24
|
'UsageLimitExceeded',
|
|
25
25
|
'ModelHTTPError',
|
|
26
|
+
'IncompleteToolCall',
|
|
26
27
|
'FallbackExceptionGroup',
|
|
27
28
|
)
|
|
28
29
|
|
|
@@ -158,7 +159,7 @@ class ModelHTTPError(AgentRunError):
|
|
|
158
159
|
super().__init__(message)
|
|
159
160
|
|
|
160
161
|
|
|
161
|
-
class FallbackExceptionGroup(ExceptionGroup):
|
|
162
|
+
class FallbackExceptionGroup(ExceptionGroup[Any]):
|
|
162
163
|
"""A group of exceptions that can be raised when all fallback models fail."""
|
|
163
164
|
|
|
164
165
|
|
|
@@ -168,3 +169,7 @@ class ToolRetryError(Exception):
|
|
|
168
169
|
def __init__(self, tool_retry: RetryPromptPart):
|
|
169
170
|
self.tool_retry = tool_retry
|
|
170
171
|
super().__init__()
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
class IncompleteToolCall(UnexpectedModelBehavior):
|
|
175
|
+
"""Error raised when a model stops due to token limit while emitting a tool call."""
|
pydantic_ai/mcp.py
CHANGED
|
@@ -441,14 +441,9 @@ class MCPServerStdio(MCPServer):
|
|
|
441
441
|
'uv', args=['run', 'mcp-run-python', 'stdio'], timeout=10
|
|
442
442
|
)
|
|
443
443
|
agent = Agent('openai:gpt-4o', toolsets=[server])
|
|
444
|
-
|
|
445
|
-
async def main():
|
|
446
|
-
async with agent: # (2)!
|
|
447
|
-
...
|
|
448
444
|
```
|
|
449
445
|
|
|
450
446
|
1. See [MCP Run Python](https://github.com/pydantic/mcp-run-python) for more information.
|
|
451
|
-
2. This will start the server as a subprocess and connect to it.
|
|
452
447
|
"""
|
|
453
448
|
|
|
454
449
|
command: str
|
|
@@ -788,13 +783,7 @@ class MCPServerSSE(_MCPServerHTTP):
|
|
|
788
783
|
|
|
789
784
|
server = MCPServerSSE('http://localhost:3001/sse')
|
|
790
785
|
agent = Agent('openai:gpt-4o', toolsets=[server])
|
|
791
|
-
|
|
792
|
-
async def main():
|
|
793
|
-
async with agent: # (1)!
|
|
794
|
-
...
|
|
795
786
|
```
|
|
796
|
-
|
|
797
|
-
1. This will connect to a server running on `localhost:3001`.
|
|
798
787
|
"""
|
|
799
788
|
|
|
800
789
|
@classmethod
|
|
@@ -837,13 +826,7 @@ class MCPServerHTTP(MCPServerSSE):
|
|
|
837
826
|
|
|
838
827
|
server = MCPServerHTTP('http://localhost:3001/sse')
|
|
839
828
|
agent = Agent('openai:gpt-4o', toolsets=[server])
|
|
840
|
-
|
|
841
|
-
async def main():
|
|
842
|
-
async with agent: # (2)!
|
|
843
|
-
...
|
|
844
829
|
```
|
|
845
|
-
|
|
846
|
-
1. This will connect to a server running on `localhost:3001`.
|
|
847
830
|
"""
|
|
848
831
|
|
|
849
832
|
|
|
@@ -862,12 +845,8 @@ class MCPServerStreamableHTTP(_MCPServerHTTP):
|
|
|
862
845
|
from pydantic_ai import Agent
|
|
863
846
|
from pydantic_ai.mcp import MCPServerStreamableHTTP
|
|
864
847
|
|
|
865
|
-
server = MCPServerStreamableHTTP('http://localhost:8000/mcp')
|
|
848
|
+
server = MCPServerStreamableHTTP('http://localhost:8000/mcp')
|
|
866
849
|
agent = Agent('openai:gpt-4o', toolsets=[server])
|
|
867
|
-
|
|
868
|
-
async def main():
|
|
869
|
-
async with agent: # (2)!
|
|
870
|
-
...
|
|
871
850
|
```
|
|
872
851
|
"""
|
|
873
852
|
|
pydantic_ai/messages.py
CHANGED
|
@@ -13,7 +13,7 @@ import pydantic
|
|
|
13
13
|
import pydantic_core
|
|
14
14
|
from genai_prices import calc_price, types as genai_types
|
|
15
15
|
from opentelemetry._events import Event # pyright: ignore[reportPrivateImportUsage]
|
|
16
|
-
from typing_extensions import
|
|
16
|
+
from typing_extensions import deprecated
|
|
17
17
|
|
|
18
18
|
from . import _otel_messages, _utils
|
|
19
19
|
from ._utils import generate_tool_call_id as _generate_tool_call_id, now_utc as _now_utc
|
|
@@ -34,6 +34,7 @@ DocumentMediaType: TypeAlias = Literal[
|
|
|
34
34
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
35
35
|
'text/html',
|
|
36
36
|
'text/markdown',
|
|
37
|
+
'application/msword',
|
|
37
38
|
'application/vnd.ms-excel',
|
|
38
39
|
]
|
|
39
40
|
VideoMediaType: TypeAlias = Literal[
|
|
@@ -434,8 +435,12 @@ class DocumentUrl(FileUrl):
|
|
|
434
435
|
return 'application/pdf'
|
|
435
436
|
elif self.url.endswith('.rtf'):
|
|
436
437
|
return 'application/rtf'
|
|
438
|
+
elif self.url.endswith('.doc'):
|
|
439
|
+
return 'application/msword'
|
|
437
440
|
elif self.url.endswith('.docx'):
|
|
438
441
|
return 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
442
|
+
elif self.url.endswith('.xls'):
|
|
443
|
+
return 'application/vnd.ms-excel'
|
|
439
444
|
elif self.url.endswith('.xlsx'):
|
|
440
445
|
return 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
|
441
446
|
|
|
@@ -480,7 +485,7 @@ class BinaryContent:
|
|
|
480
485
|
"""
|
|
481
486
|
|
|
482
487
|
_identifier: Annotated[str | None, pydantic.Field(alias='identifier', default=None, exclude=True)] = field(
|
|
483
|
-
compare=False, default=None
|
|
488
|
+
compare=False, default=None, repr=False
|
|
484
489
|
)
|
|
485
490
|
|
|
486
491
|
kind: Literal['binary'] = 'binary'
|
|
@@ -514,16 +519,16 @@ class BinaryContent:
|
|
|
514
519
|
vendor_metadata=bc.vendor_metadata,
|
|
515
520
|
)
|
|
516
521
|
else:
|
|
517
|
-
return bc
|
|
522
|
+
return bc
|
|
518
523
|
|
|
519
524
|
@classmethod
|
|
520
|
-
def from_data_uri(cls, data_uri: str) ->
|
|
525
|
+
def from_data_uri(cls, data_uri: str) -> BinaryContent:
|
|
521
526
|
"""Create a `BinaryContent` from a data URI."""
|
|
522
527
|
prefix = 'data:'
|
|
523
528
|
if not data_uri.startswith(prefix):
|
|
524
|
-
raise ValueError('Data URI must start with "data:"')
|
|
529
|
+
raise ValueError('Data URI must start with "data:"')
|
|
525
530
|
media_type, data = data_uri[len(prefix) :].split(';base64,', 1)
|
|
526
|
-
return cls(data=base64.b64decode(data), media_type=media_type)
|
|
531
|
+
return cls.narrow_type(cls(data=base64.b64decode(data), media_type=media_type))
|
|
527
532
|
|
|
528
533
|
@pydantic.computed_field
|
|
529
534
|
@property
|
|
@@ -645,6 +650,7 @@ _document_format_lookup: dict[str, DocumentFormat] = {
|
|
|
645
650
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'xlsx',
|
|
646
651
|
'text/html': 'html',
|
|
647
652
|
'text/markdown': 'md',
|
|
653
|
+
'application/msword': 'doc',
|
|
648
654
|
'application/vnd.ms-excel': 'xls',
|
|
649
655
|
}
|
|
650
656
|
_audio_format_lookup: dict[str, AudioFormat] = {
|
|
@@ -1612,6 +1618,14 @@ class PartStartEvent:
|
|
|
1612
1618
|
part: ModelResponsePart
|
|
1613
1619
|
"""The newly started `ModelResponsePart`."""
|
|
1614
1620
|
|
|
1621
|
+
previous_part_kind: (
|
|
1622
|
+
Literal['text', 'thinking', 'tool-call', 'builtin-tool-call', 'builtin-tool-return', 'file'] | None
|
|
1623
|
+
) = None
|
|
1624
|
+
"""The kind of the previous part, if any.
|
|
1625
|
+
|
|
1626
|
+
This is useful for UI event streams to know whether to group parts of the same kind together when emitting events.
|
|
1627
|
+
"""
|
|
1628
|
+
|
|
1615
1629
|
event_kind: Literal['part_start'] = 'part_start'
|
|
1616
1630
|
"""Event type identifier, used as a discriminator."""
|
|
1617
1631
|
|
|
@@ -1634,6 +1648,30 @@ class PartDeltaEvent:
|
|
|
1634
1648
|
__repr__ = _utils.dataclasses_no_defaults_repr
|
|
1635
1649
|
|
|
1636
1650
|
|
|
1651
|
+
@dataclass(repr=False, kw_only=True)
|
|
1652
|
+
class PartEndEvent:
|
|
1653
|
+
"""An event indicating that a part is complete."""
|
|
1654
|
+
|
|
1655
|
+
index: int
|
|
1656
|
+
"""The index of the part within the overall response parts list."""
|
|
1657
|
+
|
|
1658
|
+
part: ModelResponsePart
|
|
1659
|
+
"""The complete `ModelResponsePart`."""
|
|
1660
|
+
|
|
1661
|
+
next_part_kind: (
|
|
1662
|
+
Literal['text', 'thinking', 'tool-call', 'builtin-tool-call', 'builtin-tool-return', 'file'] | None
|
|
1663
|
+
) = None
|
|
1664
|
+
"""The kind of the next part, if any.
|
|
1665
|
+
|
|
1666
|
+
This is useful for UI event streams to know whether to group parts of the same kind together when emitting events.
|
|
1667
|
+
"""
|
|
1668
|
+
|
|
1669
|
+
event_kind: Literal['part_end'] = 'part_end'
|
|
1670
|
+
"""Event type identifier, used as a discriminator."""
|
|
1671
|
+
|
|
1672
|
+
__repr__ = _utils.dataclasses_no_defaults_repr
|
|
1673
|
+
|
|
1674
|
+
|
|
1637
1675
|
@dataclass(repr=False, kw_only=True)
|
|
1638
1676
|
class FinalResultEvent:
|
|
1639
1677
|
"""An event indicating the response to the current model request matches the output schema and will produce a result."""
|
|
@@ -1649,9 +1687,9 @@ class FinalResultEvent:
|
|
|
1649
1687
|
|
|
1650
1688
|
|
|
1651
1689
|
ModelResponseStreamEvent = Annotated[
|
|
1652
|
-
PartStartEvent | PartDeltaEvent | FinalResultEvent, pydantic.Discriminator('event_kind')
|
|
1690
|
+
PartStartEvent | PartDeltaEvent | PartEndEvent | FinalResultEvent, pydantic.Discriminator('event_kind')
|
|
1653
1691
|
]
|
|
1654
|
-
"""An event in the model response stream, starting a new part, applying a delta to an existing one, or indicating the final result."""
|
|
1692
|
+
"""An event in the model response stream, starting a new part, applying a delta to an existing one, indicating a part is complete, or indicating the final result."""
|
|
1655
1693
|
|
|
1656
1694
|
|
|
1657
1695
|
@dataclass(repr=False)
|