pydantic-ai-slim 0.0.19__tar.gz → 0.0.21__tar.gz

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.

Files changed (31) hide show
  1. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/PKG-INFO +6 -4
  2. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/_parts_manager.py +1 -1
  3. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/_pydantic.py +1 -0
  4. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/_result.py +29 -28
  5. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/_system_prompt.py +4 -4
  6. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/_utils.py +1 -56
  7. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/agent.py +137 -113
  8. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/messages.py +24 -56
  9. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/models/__init__.py +122 -51
  10. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/models/anthropic.py +109 -38
  11. pydantic_ai_slim-0.0.21/pydantic_ai/models/cohere.py +290 -0
  12. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/models/function.py +12 -8
  13. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/models/gemini.py +29 -15
  14. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/models/groq.py +27 -23
  15. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/models/mistral.py +34 -29
  16. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/models/openai.py +45 -23
  17. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/models/test.py +47 -24
  18. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/models/vertexai.py +2 -1
  19. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/result.py +45 -26
  20. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/settings.py +58 -1
  21. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/tools.py +29 -26
  22. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pyproject.toml +5 -4
  23. pydantic_ai_slim-0.0.19/pydantic_ai/models/ollama.py +0 -120
  24. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/.gitignore +0 -0
  25. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/README.md +0 -0
  26. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/__init__.py +0 -0
  27. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/_griffe.py +0 -0
  28. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/exceptions.py +0 -0
  29. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/format_as_xml.py +0 -0
  30. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/py.typed +0 -0
  31. {pydantic_ai_slim-0.0.19 → pydantic_ai_slim-0.0.21}/pydantic_ai/usage.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pydantic-ai-slim
3
- Version: 0.0.19
3
+ Version: 0.0.21
4
4
  Summary: Agent Framework / shim to use Pydantic with LLMs, slim package
5
5
  Author-email: Samuel Colvin <samuel@pydantic.dev>
6
6
  License-Expression: MIT
@@ -26,13 +26,15 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
26
26
  Requires-Python: >=3.9
27
27
  Requires-Dist: eval-type-backport>=0.2.0
28
28
  Requires-Dist: griffe>=1.3.2
29
- Requires-Dist: httpx>=0.27.2
29
+ Requires-Dist: httpx>=0.27
30
30
  Requires-Dist: logfire-api>=1.2.0
31
31
  Requires-Dist: pydantic>=2.10
32
32
  Provides-Extra: anthropic
33
33
  Requires-Dist: anthropic>=0.40.0; extra == 'anthropic'
34
+ Provides-Extra: cohere
35
+ Requires-Dist: cohere>=5.13.11; extra == 'cohere'
34
36
  Provides-Extra: graph
35
- Requires-Dist: pydantic-graph==0.0.19; extra == 'graph'
37
+ Requires-Dist: pydantic-graph==0.0.21; extra == 'graph'
36
38
  Provides-Extra: groq
37
39
  Requires-Dist: groq>=0.12.0; extra == 'groq'
38
40
  Provides-Extra: logfire
@@ -40,7 +42,7 @@ Requires-Dist: logfire>=2.3; extra == 'logfire'
40
42
  Provides-Extra: mistral
41
43
  Requires-Dist: mistralai>=1.2.5; extra == 'mistral'
42
44
  Provides-Extra: openai
43
- Requires-Dist: openai>=1.54.3; extra == 'openai'
45
+ Requires-Dist: openai>=1.59.0; extra == 'openai'
44
46
  Provides-Extra: vertexai
45
47
  Requires-Dist: google-auth>=2.36.0; extra == 'vertexai'
46
48
  Requires-Dist: requests>=2.32.3; extra == 'vertexai'
@@ -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.from_raw_args(tool_name=tool_name, args=args, tool_call_id=tool_call_id)
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)
@@ -115,6 +115,7 @@ def function_schema( # noqa: C901
115
115
  field_name,
116
116
  field_info,
117
117
  decorators,
118
+ required=p.default is Parameter.empty,
118
119
  )
119
120
  # noinspection PyTypeChecker
120
121
  td_schema.setdefault('metadata', {})['is_model_like'] = is_model_like(annotation)
@@ -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 Self, TypeAliasType, TypedDict
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 ResultData, ResultValidatorFunc
16
- from .tools import AgentDeps, RunContext, ToolDefinition
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[AgentDeps, ResultData]):
21
- function: ResultValidatorFunc[AgentDeps, ResultData]
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: ResultData,
34
+ result: T,
32
35
  tool_call: _messages.ToolCallPart | None,
33
- run_context: RunContext[AgentDeps],
34
- ) -> ResultData:
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[ResultData]], self.function)
56
+ function = cast(Callable[[Any], Awaitable[T]], self.function)
54
57
  result_data = await function(*args)
55
58
  else:
56
- function = cast(Callable[[Any], ResultData], self.function)
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[ResultData]):
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[ResultData]]
86
+ tools: dict[str, ResultTool[ResultDataT]]
84
87
  allow_text_result: bool
85
88
 
86
89
  @classmethod
87
- def build(cls, response_type: type[ResultData], name: str, description: str | None) -> Self | None:
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[ResultData]:
99
- return cast(ResultTool[ResultData], ResultTool(a, tool_name_, description, multiple))
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[ResultData]] = {}
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[ResultData]] | None:
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[ResultData]] | None:
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[ResultData]):
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[ResultData], name: str, description: str | None, multiple: bool):
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
- ) -> ResultData:
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, _messages.ArgsJson):
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(
@@ -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 AgentDeps, RunContext, SystemPromptFunc
9
+ from .tools import AgentDepsT, RunContext, SystemPromptFunc
10
10
 
11
11
 
12
12
  @dataclass
13
- class SystemPromptRunner(Generic[AgentDeps]):
14
- function: SystemPromptFunc[AgentDeps]
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[AgentDeps]) -> str:
23
+ async def run(self, run_context: RunContext[AgentDepsT]) -> str:
24
24
  if self._takes_ctx:
25
25
  args = (run_context,)
26
26
  else:
@@ -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, cast, overload
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