pydantic-ai-slim 0.0.19__py3-none-any.whl → 0.0.21__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.
Potentially problematic release.
This version of pydantic-ai-slim might be problematic. Click here for more details.
- pydantic_ai/_parts_manager.py +1 -1
- pydantic_ai/_pydantic.py +1 -0
- pydantic_ai/_result.py +29 -28
- pydantic_ai/_system_prompt.py +4 -4
- pydantic_ai/_utils.py +1 -56
- pydantic_ai/agent.py +137 -113
- pydantic_ai/messages.py +24 -56
- pydantic_ai/models/__init__.py +122 -51
- pydantic_ai/models/anthropic.py +109 -38
- pydantic_ai/models/cohere.py +290 -0
- pydantic_ai/models/function.py +12 -8
- pydantic_ai/models/gemini.py +29 -15
- pydantic_ai/models/groq.py +27 -23
- pydantic_ai/models/mistral.py +34 -29
- pydantic_ai/models/openai.py +45 -23
- pydantic_ai/models/test.py +47 -24
- pydantic_ai/models/vertexai.py +2 -1
- pydantic_ai/result.py +45 -26
- pydantic_ai/settings.py +58 -1
- pydantic_ai/tools.py +29 -26
- {pydantic_ai_slim-0.0.19.dist-info → pydantic_ai_slim-0.0.21.dist-info}/METADATA +6 -4
- pydantic_ai_slim-0.0.21.dist-info/RECORD +29 -0
- pydantic_ai/models/ollama.py +0 -120
- pydantic_ai_slim-0.0.19.dist-info/RECORD +0 -29
- {pydantic_ai_slim-0.0.19.dist-info → pydantic_ai_slim-0.0.21.dist-info}/WHEEL +0 -0
pydantic_ai/_parts_manager.py
CHANGED
|
@@ -221,7 +221,7 @@ class ModelResponsePartsManager:
|
|
|
221
221
|
ModelResponseStreamEvent: A `PartStartEvent` indicating that a new tool call part
|
|
222
222
|
has been added to the manager, or replaced an existing part.
|
|
223
223
|
"""
|
|
224
|
-
new_part = ToolCallPart
|
|
224
|
+
new_part = ToolCallPart(tool_name=tool_name, args=args, tool_call_id=tool_call_id)
|
|
225
225
|
if vendor_part_id is None:
|
|
226
226
|
# vendor_part_id is None, so we unconditionally append a new ToolCallPart to the end of the list
|
|
227
227
|
new_part_index = len(self._parts)
|
pydantic_ai/_pydantic.py
CHANGED
pydantic_ai/_result.py
CHANGED
|
@@ -8,17 +8,20 @@ from dataclasses import dataclass, field
|
|
|
8
8
|
from typing import Any, Callable, Generic, Literal, Union, cast, get_args, get_origin
|
|
9
9
|
|
|
10
10
|
from pydantic import TypeAdapter, ValidationError
|
|
11
|
-
from typing_extensions import
|
|
11
|
+
from typing_extensions import TypeAliasType, TypedDict, TypeVar
|
|
12
12
|
|
|
13
13
|
from . import _utils, messages as _messages
|
|
14
14
|
from .exceptions import ModelRetry
|
|
15
|
-
from .result import
|
|
16
|
-
from .tools import
|
|
15
|
+
from .result import ResultDataT, ResultDataT_inv, ResultValidatorFunc
|
|
16
|
+
from .tools import AgentDepsT, RunContext, ToolDefinition
|
|
17
|
+
|
|
18
|
+
T = TypeVar('T')
|
|
19
|
+
"""An invariant TypeVar."""
|
|
17
20
|
|
|
18
21
|
|
|
19
22
|
@dataclass
|
|
20
|
-
class ResultValidator(Generic[
|
|
21
|
-
function: ResultValidatorFunc[
|
|
23
|
+
class ResultValidator(Generic[AgentDepsT, ResultDataT_inv]):
|
|
24
|
+
function: ResultValidatorFunc[AgentDepsT, ResultDataT_inv]
|
|
22
25
|
_takes_ctx: bool = field(init=False)
|
|
23
26
|
_is_async: bool = field(init=False)
|
|
24
27
|
|
|
@@ -28,10 +31,10 @@ class ResultValidator(Generic[AgentDeps, ResultData]):
|
|
|
28
31
|
|
|
29
32
|
async def validate(
|
|
30
33
|
self,
|
|
31
|
-
result:
|
|
34
|
+
result: T,
|
|
32
35
|
tool_call: _messages.ToolCallPart | None,
|
|
33
|
-
run_context: RunContext[
|
|
34
|
-
) ->
|
|
36
|
+
run_context: RunContext[AgentDepsT],
|
|
37
|
+
) -> T:
|
|
35
38
|
"""Validate a result but calling the function.
|
|
36
39
|
|
|
37
40
|
Args:
|
|
@@ -50,10 +53,10 @@ class ResultValidator(Generic[AgentDeps, ResultData]):
|
|
|
50
53
|
|
|
51
54
|
try:
|
|
52
55
|
if self._is_async:
|
|
53
|
-
function = cast(Callable[[Any], Awaitable[
|
|
56
|
+
function = cast(Callable[[Any], Awaitable[T]], self.function)
|
|
54
57
|
result_data = await function(*args)
|
|
55
58
|
else:
|
|
56
|
-
function = cast(Callable[[Any],
|
|
59
|
+
function = cast(Callable[[Any], T], self.function)
|
|
57
60
|
result_data = await _utils.run_in_executor(function, *args)
|
|
58
61
|
except ModelRetry as r:
|
|
59
62
|
m = _messages.RetryPromptPart(content=r.message)
|
|
@@ -74,17 +77,19 @@ class ToolRetryError(Exception):
|
|
|
74
77
|
|
|
75
78
|
|
|
76
79
|
@dataclass
|
|
77
|
-
class ResultSchema(Generic[
|
|
80
|
+
class ResultSchema(Generic[ResultDataT]):
|
|
78
81
|
"""Model the final response from an agent run.
|
|
79
82
|
|
|
80
83
|
Similar to `Tool` but for the final result of running an agent.
|
|
81
84
|
"""
|
|
82
85
|
|
|
83
|
-
tools: dict[str, ResultTool[
|
|
86
|
+
tools: dict[str, ResultTool[ResultDataT]]
|
|
84
87
|
allow_text_result: bool
|
|
85
88
|
|
|
86
89
|
@classmethod
|
|
87
|
-
def build(
|
|
90
|
+
def build(
|
|
91
|
+
cls: type[ResultSchema[T]], response_type: type[T], name: str, description: str | None
|
|
92
|
+
) -> ResultSchema[T] | None:
|
|
88
93
|
"""Build a ResultSchema dataclass from a response type."""
|
|
89
94
|
if response_type is str:
|
|
90
95
|
return None
|
|
@@ -95,10 +100,10 @@ class ResultSchema(Generic[ResultData]):
|
|
|
95
100
|
else:
|
|
96
101
|
allow_text_result = False
|
|
97
102
|
|
|
98
|
-
def _build_tool(a: Any, tool_name_: str, multiple: bool) -> ResultTool[
|
|
99
|
-
return cast(ResultTool[
|
|
103
|
+
def _build_tool(a: Any, tool_name_: str, multiple: bool) -> ResultTool[T]:
|
|
104
|
+
return cast(ResultTool[T], ResultTool(a, tool_name_, description, multiple))
|
|
100
105
|
|
|
101
|
-
tools: dict[str, ResultTool[
|
|
106
|
+
tools: dict[str, ResultTool[T]] = {}
|
|
102
107
|
if args := get_union_args(response_type):
|
|
103
108
|
for i, arg in enumerate(args, start=1):
|
|
104
109
|
tool_name = union_tool_name(name, arg)
|
|
@@ -112,7 +117,7 @@ class ResultSchema(Generic[ResultData]):
|
|
|
112
117
|
|
|
113
118
|
def find_named_tool(
|
|
114
119
|
self, parts: Iterable[_messages.ModelResponsePart], tool_name: str
|
|
115
|
-
) -> tuple[_messages.ToolCallPart, ResultTool[
|
|
120
|
+
) -> tuple[_messages.ToolCallPart, ResultTool[ResultDataT]] | None:
|
|
116
121
|
"""Find a tool that matches one of the calls, with a specific name."""
|
|
117
122
|
for part in parts:
|
|
118
123
|
if isinstance(part, _messages.ToolCallPart):
|
|
@@ -122,7 +127,7 @@ class ResultSchema(Generic[ResultData]):
|
|
|
122
127
|
def find_tool(
|
|
123
128
|
self,
|
|
124
129
|
parts: Iterable[_messages.ModelResponsePart],
|
|
125
|
-
) -> tuple[_messages.ToolCallPart, ResultTool[
|
|
130
|
+
) -> tuple[_messages.ToolCallPart, ResultTool[ResultDataT]] | None:
|
|
126
131
|
"""Find a tool that matches one of the calls."""
|
|
127
132
|
for part in parts:
|
|
128
133
|
if isinstance(part, _messages.ToolCallPart):
|
|
@@ -142,11 +147,11 @@ DEFAULT_DESCRIPTION = 'The final response which ends this conversation'
|
|
|
142
147
|
|
|
143
148
|
|
|
144
149
|
@dataclass(init=False)
|
|
145
|
-
class ResultTool(Generic[
|
|
150
|
+
class ResultTool(Generic[ResultDataT]):
|
|
146
151
|
tool_def: ToolDefinition
|
|
147
152
|
type_adapter: TypeAdapter[Any]
|
|
148
153
|
|
|
149
|
-
def __init__(self, response_type: type[
|
|
154
|
+
def __init__(self, response_type: type[ResultDataT], name: str, description: str | None, multiple: bool):
|
|
150
155
|
"""Build a ResultTool dataclass from a response type."""
|
|
151
156
|
assert response_type is not str, 'ResultTool does not support str as a response type'
|
|
152
157
|
|
|
@@ -183,7 +188,7 @@ class ResultTool(Generic[ResultData]):
|
|
|
183
188
|
|
|
184
189
|
def validate(
|
|
185
190
|
self, tool_call: _messages.ToolCallPart, allow_partial: bool = False, wrap_validation_errors: bool = True
|
|
186
|
-
) ->
|
|
191
|
+
) -> ResultDataT:
|
|
187
192
|
"""Validate a result message.
|
|
188
193
|
|
|
189
194
|
Args:
|
|
@@ -196,14 +201,10 @@ class ResultTool(Generic[ResultData]):
|
|
|
196
201
|
"""
|
|
197
202
|
try:
|
|
198
203
|
pyd_allow_partial: Literal['off', 'trailing-strings'] = 'trailing-strings' if allow_partial else 'off'
|
|
199
|
-
if isinstance(tool_call.args,
|
|
200
|
-
result = self.type_adapter.validate_json(
|
|
201
|
-
tool_call.args.args_json or '', experimental_allow_partial=pyd_allow_partial
|
|
202
|
-
)
|
|
204
|
+
if isinstance(tool_call.args, str):
|
|
205
|
+
result = self.type_adapter.validate_json(tool_call.args, experimental_allow_partial=pyd_allow_partial)
|
|
203
206
|
else:
|
|
204
|
-
result = self.type_adapter.validate_python(
|
|
205
|
-
tool_call.args.args_dict, experimental_allow_partial=pyd_allow_partial
|
|
206
|
-
)
|
|
207
|
+
result = self.type_adapter.validate_python(tool_call.args, experimental_allow_partial=pyd_allow_partial)
|
|
207
208
|
except ValidationError as e:
|
|
208
209
|
if wrap_validation_errors:
|
|
209
210
|
m = _messages.RetryPromptPart(
|
pydantic_ai/_system_prompt.py
CHANGED
|
@@ -6,12 +6,12 @@ from dataclasses import dataclass, field
|
|
|
6
6
|
from typing import Any, Callable, Generic, cast
|
|
7
7
|
|
|
8
8
|
from . import _utils
|
|
9
|
-
from .tools import
|
|
9
|
+
from .tools import AgentDepsT, RunContext, SystemPromptFunc
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
@dataclass
|
|
13
|
-
class SystemPromptRunner(Generic[
|
|
14
|
-
function: SystemPromptFunc[
|
|
13
|
+
class SystemPromptRunner(Generic[AgentDepsT]):
|
|
14
|
+
function: SystemPromptFunc[AgentDepsT]
|
|
15
15
|
dynamic: bool = False
|
|
16
16
|
_takes_ctx: bool = field(init=False)
|
|
17
17
|
_is_async: bool = field(init=False)
|
|
@@ -20,7 +20,7 @@ class SystemPromptRunner(Generic[AgentDeps]):
|
|
|
20
20
|
self._takes_ctx = len(inspect.signature(self.function).parameters) > 0
|
|
21
21
|
self._is_async = inspect.iscoroutinefunction(self.function)
|
|
22
22
|
|
|
23
|
-
async def run(self, run_context: RunContext[
|
|
23
|
+
async def run(self, run_context: RunContext[AgentDepsT]) -> str:
|
|
24
24
|
if self._takes_ctx:
|
|
25
25
|
args = (run_context,)
|
|
26
26
|
else:
|
pydantic_ai/_utils.py
CHANGED
|
@@ -8,7 +8,7 @@ from dataclasses import dataclass, is_dataclass
|
|
|
8
8
|
from datetime import datetime, timezone
|
|
9
9
|
from functools import partial
|
|
10
10
|
from types import GenericAlias
|
|
11
|
-
from typing import TYPE_CHECKING, Any, Callable, Generic, TypeVar, Union
|
|
11
|
+
from typing import TYPE_CHECKING, Any, Callable, Generic, TypeVar, Union
|
|
12
12
|
|
|
13
13
|
from pydantic import BaseModel
|
|
14
14
|
from pydantic.json_schema import JsonSchemaValue
|
|
@@ -79,61 +79,6 @@ def is_set(t_or_unset: T | Unset) -> TypeGuard[T]:
|
|
|
79
79
|
return t_or_unset is not UNSET
|
|
80
80
|
|
|
81
81
|
|
|
82
|
-
Left = TypeVar('Left')
|
|
83
|
-
Right = TypeVar('Right')
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
class Either(Generic[Left, Right]):
|
|
87
|
-
"""Two member Union that records which member was set, this is analogous to Rust enums with two variants.
|
|
88
|
-
|
|
89
|
-
Usage:
|
|
90
|
-
|
|
91
|
-
```python
|
|
92
|
-
if left_thing := either.left:
|
|
93
|
-
use_left(left_thing.value)
|
|
94
|
-
else:
|
|
95
|
-
use_right(either.right)
|
|
96
|
-
```
|
|
97
|
-
"""
|
|
98
|
-
|
|
99
|
-
__slots__ = '_left', '_right'
|
|
100
|
-
|
|
101
|
-
@overload
|
|
102
|
-
def __init__(self, *, left: Left) -> None: ...
|
|
103
|
-
|
|
104
|
-
@overload
|
|
105
|
-
def __init__(self, *, right: Right) -> None: ...
|
|
106
|
-
|
|
107
|
-
def __init__(self, left: Left | Unset = UNSET, right: Right | Unset = UNSET) -> None:
|
|
108
|
-
if left is not UNSET:
|
|
109
|
-
assert right is UNSET, '`Either` must receive exactly one argument - `left` or `right`'
|
|
110
|
-
self._left: Option[Left] = Some(cast(Left, left))
|
|
111
|
-
else:
|
|
112
|
-
assert right is not UNSET, '`Either` must receive exactly one argument - `left` or `right`'
|
|
113
|
-
self._left = None
|
|
114
|
-
self._right = cast(Right, right)
|
|
115
|
-
|
|
116
|
-
@property
|
|
117
|
-
def left(self) -> Option[Left]:
|
|
118
|
-
return self._left
|
|
119
|
-
|
|
120
|
-
@property
|
|
121
|
-
def right(self) -> Right:
|
|
122
|
-
return self._right
|
|
123
|
-
|
|
124
|
-
def is_left(self) -> bool:
|
|
125
|
-
return self._left is not None
|
|
126
|
-
|
|
127
|
-
def whichever(self) -> Left | Right:
|
|
128
|
-
return self._left.value if self._left is not None else self.right
|
|
129
|
-
|
|
130
|
-
def __repr__(self):
|
|
131
|
-
if left := self._left:
|
|
132
|
-
return f'Either(left={left.value!r})'
|
|
133
|
-
else:
|
|
134
|
-
return f'Either(right={self.right!r})'
|
|
135
|
-
|
|
136
|
-
|
|
137
82
|
@asynccontextmanager
|
|
138
83
|
async def group_by_temporal(
|
|
139
84
|
aiterable: AsyncIterable[T], soft_max_interval: float | None
|