pydantic-ai-slim 0.0.55__py3-none-any.whl → 0.1.1__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/result.py CHANGED
@@ -1,43 +1,47 @@
1
1
  from __future__ import annotations as _annotations
2
2
 
3
+ import warnings
3
4
  from collections.abc import AsyncIterable, AsyncIterator, Awaitable, Callable
4
5
  from copy import copy
5
6
  from dataclasses import dataclass, field
6
7
  from datetime import datetime
7
- from typing import Generic, Union, cast
8
+ from typing import TYPE_CHECKING, Generic, Union, cast
8
9
 
9
- from typing_extensions import TypeVar, assert_type
10
+ from typing_extensions import TypeVar, assert_type, deprecated, overload
10
11
 
11
- from . import _result, _utils, exceptions, messages as _messages, models
12
+ from . import _utils, exceptions, messages as _messages, models
12
13
  from .messages import AgentStreamEvent, FinalResultEvent
13
14
  from .tools import AgentDepsT, RunContext
14
15
  from .usage import Usage, UsageLimits
15
16
 
16
- __all__ = 'ResultDataT', 'ResultDataT_inv', 'ResultValidatorFunc'
17
+ if TYPE_CHECKING:
18
+ from . import _output
19
+
20
+ __all__ = 'OutputDataT', 'OutputDataT_inv', 'ToolOutput', 'OutputValidatorFunc'
17
21
 
18
22
 
19
23
  T = TypeVar('T')
20
24
  """An invariant TypeVar."""
21
- ResultDataT_inv = TypeVar('ResultDataT_inv', default=str)
25
+ OutputDataT_inv = TypeVar('OutputDataT_inv', default=str)
22
26
  """
23
27
  An invariant type variable for the result data of a model.
24
28
 
25
- We need to use an invariant typevar for `ResultValidator` and `ResultValidatorFunc` because the result data type is used
26
- in both the input and output of a `ResultValidatorFunc`. This can theoretically lead to some issues assuming that types
27
- possessing ResultValidator's are covariant in the result data type, but in practice this is rarely an issue, and
29
+ We need to use an invariant typevar for `OutputValidator` and `OutputValidatorFunc` because the output data type is used
30
+ in both the input and output of a `OutputValidatorFunc`. This can theoretically lead to some issues assuming that types
31
+ possessing OutputValidator's are covariant in the result data type, but in practice this is rarely an issue, and
28
32
  changing it would have negative consequences for the ergonomics of the library.
29
33
 
30
- At some point, it may make sense to change the input to ResultValidatorFunc to be `Any` or `object` as doing that would
34
+ At some point, it may make sense to change the input to OutputValidatorFunc to be `Any` or `object` as doing that would
31
35
  resolve these potential variance issues.
32
36
  """
33
- ResultDataT = TypeVar('ResultDataT', default=str, covariant=True)
37
+ OutputDataT = TypeVar('OutputDataT', default=str, covariant=True)
34
38
  """Covariant type variable for the result data type of a run."""
35
39
 
36
- ResultValidatorFunc = Union[
37
- Callable[[RunContext[AgentDepsT], ResultDataT_inv], ResultDataT_inv],
38
- Callable[[RunContext[AgentDepsT], ResultDataT_inv], Awaitable[ResultDataT_inv]],
39
- Callable[[ResultDataT_inv], ResultDataT_inv],
40
- Callable[[ResultDataT_inv], Awaitable[ResultDataT_inv]],
40
+ OutputValidatorFunc = Union[
41
+ Callable[[RunContext[AgentDepsT], OutputDataT_inv], OutputDataT_inv],
42
+ Callable[[RunContext[AgentDepsT], OutputDataT_inv], Awaitable[OutputDataT_inv]],
43
+ Callable[[OutputDataT_inv], OutputDataT_inv],
44
+ Callable[[OutputDataT_inv], Awaitable[OutputDataT_inv]],
41
45
  ]
42
46
  """
43
47
  A function that always takes and returns the same type of data (which is the result type of an agent run), and:
@@ -45,15 +49,56 @@ A function that always takes and returns the same type of data (which is the res
45
49
  * may or may not take [`RunContext`][pydantic_ai.tools.RunContext] as a first argument
46
50
  * may or may not be async
47
51
 
48
- Usage `ResultValidatorFunc[AgentDepsT, T]`.
52
+ Usage `OutputValidatorFunc[AgentDepsT, T]`.
49
53
  """
50
54
 
55
+ DEFAULT_OUTPUT_TOOL_NAME = 'final_result'
56
+
57
+
58
+ @dataclass(init=False)
59
+ class ToolOutput(Generic[OutputDataT]):
60
+ """Marker class to use tools for structured outputs, and customize the tool."""
61
+
62
+ output_type: type[OutputDataT]
63
+ # TODO: Add `output_call` support, for calling a function to get the output
64
+ # output_call: Callable[..., OutputDataT] | None
65
+ name: str
66
+ description: str | None
67
+ max_retries: int | None
68
+ strict: bool | None
69
+
70
+ def __init__(
71
+ self,
72
+ *,
73
+ type_: type[OutputDataT],
74
+ # call: Callable[..., OutputDataT] | None = None,
75
+ name: str = 'final_result',
76
+ description: str | None = None,
77
+ max_retries: int | None = None,
78
+ strict: bool | None = None,
79
+ ):
80
+ self.output_type = type_
81
+ self.name = name
82
+ self.description = description
83
+ self.max_retries = max_retries
84
+ self.strict = strict
85
+
86
+ # TODO: add support for call and make type_ optional, with the following logic:
87
+ # if type_ is None and call is None:
88
+ # raise ValueError('Either type_ or call must be provided')
89
+ # if call is not None:
90
+ # if type_ is None:
91
+ # type_ = get_type_hints(call).get('return')
92
+ # if type_ is None:
93
+ # raise ValueError('Unable to determine type_ from call signature; please provide it explicitly')
94
+ # self.output_call = call
95
+
51
96
 
52
97
  @dataclass
53
- class AgentStream(Generic[AgentDepsT, ResultDataT]):
98
+ class AgentStream(Generic[AgentDepsT, OutputDataT]):
54
99
  _raw_stream_response: models.StreamedResponse
55
- _result_schema: _result.ResultSchema[ResultDataT] | None
56
- _result_validators: list[_result.ResultValidator[AgentDepsT, ResultDataT]]
100
+ _output_schema: _output.OutputSchema[OutputDataT] | None
101
+ _output_validators: list[_output.OutputValidator[AgentDepsT, OutputDataT]]
57
102
  _run_ctx: RunContext[AgentDepsT]
58
103
  _usage_limits: UsageLimits | None
59
104
 
@@ -64,7 +109,7 @@ class AgentStream(Generic[AgentDepsT, ResultDataT]):
64
109
  def __post_init__(self):
65
110
  self._initial_run_ctx_usage = copy(self._run_ctx.usage)
66
111
 
67
- async def stream_output(self, *, debounce_by: float | None = 0.1) -> AsyncIterator[ResultDataT]:
112
+ async def stream_output(self, *, debounce_by: float | None = 0.1) -> AsyncIterator[OutputDataT]:
68
113
  """Asynchronously stream the (validated) agent outputs."""
69
114
  async for response in self.stream_responses(debounce_by=debounce_by):
70
115
  if self._final_result_event is not None:
@@ -96,32 +141,32 @@ class AgentStream(Generic[AgentDepsT, ResultDataT]):
96
141
  return self._initial_run_ctx_usage + self._raw_stream_response.usage()
97
142
 
98
143
  async def _validate_response(
99
- self, message: _messages.ModelResponse, result_tool_name: str | None, *, allow_partial: bool = False
100
- ) -> ResultDataT:
144
+ self, message: _messages.ModelResponse, output_tool_name: str | None, *, allow_partial: bool = False
145
+ ) -> OutputDataT:
101
146
  """Validate a structured result message."""
102
- if self._result_schema is not None and result_tool_name is not None:
103
- match = self._result_schema.find_named_tool(message.parts, result_tool_name)
147
+ if self._output_schema is not None and output_tool_name is not None:
148
+ match = self._output_schema.find_named_tool(message.parts, output_tool_name)
104
149
  if match is None:
105
150
  raise exceptions.UnexpectedModelBehavior(
106
- f'Invalid response, unable to find tool: {self._result_schema.tool_names()}'
151
+ f'Invalid response, unable to find tool: {self._output_schema.tool_names()}'
107
152
  )
108
153
 
109
- call, result_tool = match
110
- result_data = result_tool.validate(call, allow_partial=allow_partial, wrap_validation_errors=False)
154
+ call, output_tool = match
155
+ result_data = output_tool.validate(call, allow_partial=allow_partial, wrap_validation_errors=False)
111
156
 
112
- for validator in self._result_validators:
157
+ for validator in self._output_validators:
113
158
  result_data = await validator.validate(result_data, call, self._run_ctx)
114
159
  return result_data
115
160
  else:
116
161
  text = '\n\n'.join(x.content for x in message.parts if isinstance(x, _messages.TextPart))
117
- for validator in self._result_validators:
162
+ for validator in self._output_validators:
118
163
  text = await validator.validate(
119
164
  text,
120
165
  None,
121
166
  self._run_ctx,
122
167
  )
123
- # Since there is no result tool, we can assume that str is compatible with ResultDataT
124
- return cast(ResultDataT, text)
168
+ # Since there is no output tool, we can assume that str is compatible with OutputDataT
169
+ return cast(OutputDataT, text)
125
170
 
126
171
  def __aiter__(self) -> AsyncIterator[AgentStreamEvent]:
127
172
  """Stream [`AgentStreamEvent`][pydantic_ai.messages.AgentStreamEvent]s.
@@ -134,20 +179,20 @@ class AgentStream(Generic[AgentDepsT, ResultDataT]):
134
179
  return self._agent_stream_iterator
135
180
 
136
181
  async def aiter():
137
- result_schema = self._result_schema
138
- allow_text_result = result_schema is None or result_schema.allow_text_result
182
+ output_schema = self._output_schema
183
+ allow_text_output = output_schema is None or output_schema.allow_text_output
139
184
 
140
185
  def _get_final_result_event(e: _messages.ModelResponseStreamEvent) -> _messages.FinalResultEvent | None:
141
186
  """Return an appropriate FinalResultEvent if `e` corresponds to a part that will produce a final result."""
142
187
  if isinstance(e, _messages.PartStartEvent):
143
188
  new_part = e.part
144
189
  if isinstance(new_part, _messages.ToolCallPart):
145
- if result_schema:
146
- for call, _ in result_schema.find_tool([new_part]):
190
+ if output_schema:
191
+ for call, _ in output_schema.find_tool([new_part]):
147
192
  return _messages.FinalResultEvent(
148
193
  tool_name=call.tool_name, tool_call_id=call.tool_call_id
149
194
  )
150
- elif allow_text_result:
195
+ elif allow_text_output:
151
196
  assert_type(e, _messages.PartStartEvent)
152
197
  return _messages.FinalResultEvent(tool_name=None, tool_call_id=None)
153
198
 
@@ -171,7 +216,7 @@ class AgentStream(Generic[AgentDepsT, ResultDataT]):
171
216
 
172
217
 
173
218
  @dataclass
174
- class StreamedRunResult(Generic[AgentDepsT, ResultDataT]):
219
+ class StreamedRunResult(Generic[AgentDepsT, OutputDataT]):
175
220
  """Result of a streamed run that returns structured data via a tool call."""
176
221
 
177
222
  _all_messages: list[_messages.ModelMessage]
@@ -179,10 +224,10 @@ class StreamedRunResult(Generic[AgentDepsT, ResultDataT]):
179
224
 
180
225
  _usage_limits: UsageLimits | None
181
226
  _stream_response: models.StreamedResponse
182
- _result_schema: _result.ResultSchema[ResultDataT] | None
227
+ _output_schema: _output.OutputSchema[OutputDataT] | None
183
228
  _run_ctx: RunContext[AgentDepsT]
184
- _result_validators: list[_result.ResultValidator[AgentDepsT, ResultDataT]]
185
- _result_tool_name: str | None
229
+ _output_validators: list[_output.OutputValidator[AgentDepsT, OutputDataT]]
230
+ _output_tool_name: str | None
186
231
  _on_complete: Callable[[], Awaitable[None]]
187
232
 
188
233
  _initial_run_ctx_usage: Usage = field(init=False)
@@ -193,78 +238,118 @@ class StreamedRunResult(Generic[AgentDepsT, ResultDataT]):
193
238
  [`stream`][pydantic_ai.result.StreamedRunResult.stream],
194
239
  [`stream_text`][pydantic_ai.result.StreamedRunResult.stream_text],
195
240
  [`stream_structured`][pydantic_ai.result.StreamedRunResult.stream_structured] or
196
- [`get_data`][pydantic_ai.result.StreamedRunResult.get_data] completes.
241
+ [`get_output`][pydantic_ai.result.StreamedRunResult.get_output] completes.
197
242
  """
198
243
 
199
244
  def __post_init__(self):
200
245
  self._initial_run_ctx_usage = copy(self._run_ctx.usage)
201
246
 
202
- def all_messages(self, *, result_tool_return_content: str | None = None) -> list[_messages.ModelMessage]:
247
+ @overload
248
+ def all_messages(self, *, output_tool_return_content: str | None = None) -> list[_messages.ModelMessage]: ...
249
+
250
+ @overload
251
+ @deprecated('`result_tool_return_content` is deprecated, use `output_tool_return_content` instead.')
252
+ def all_messages(self, *, result_tool_return_content: str | None = None) -> list[_messages.ModelMessage]: ...
253
+
254
+ def all_messages(
255
+ self, *, output_tool_return_content: str | None = None, result_tool_return_content: str | None = None
256
+ ) -> list[_messages.ModelMessage]:
203
257
  """Return the history of _messages.
204
258
 
205
259
  Args:
206
- result_tool_return_content: The return content of the tool call to set in the last message.
207
- This provides a convenient way to modify the content of the result tool call if you want to continue
208
- the conversation and want to set the response to the result tool call. If `None`, the last message will
260
+ output_tool_return_content: The return content of the tool call to set in the last message.
261
+ This provides a convenient way to modify the content of the output tool call if you want to continue
262
+ the conversation and want to set the response to the output tool call. If `None`, the last message will
209
263
  not be modified.
264
+ result_tool_return_content: deprecated, use `output_tool_return_content` instead.
210
265
 
211
266
  Returns:
212
267
  List of messages.
213
268
  """
214
269
  # this is a method to be consistent with the other methods
215
- if result_tool_return_content is not None:
216
- raise NotImplementedError('Setting result tool return content is not supported for this result type.')
270
+ content = coalesce_deprecated_return_content(output_tool_return_content, result_tool_return_content)
271
+ if content is not None:
272
+ raise NotImplementedError('Setting output tool return content is not supported for this result type.')
217
273
  return self._all_messages
218
274
 
219
- def all_messages_json(self, *, result_tool_return_content: str | None = None) -> bytes:
275
+ @overload
276
+ def all_messages_json(self, *, output_tool_return_content: str | None = None) -> bytes: ...
277
+
278
+ @overload
279
+ @deprecated('`result_tool_return_content` is deprecated, use `output_tool_return_content` instead.')
280
+ def all_messages_json(self, *, result_tool_return_content: str | None = None) -> bytes: ...
281
+
282
+ def all_messages_json(
283
+ self, *, output_tool_return_content: str | None = None, result_tool_return_content: str | None = None
284
+ ) -> bytes: # pragma: no cover
220
285
  """Return all messages from [`all_messages`][pydantic_ai.result.StreamedRunResult.all_messages] as JSON bytes.
221
286
 
222
287
  Args:
223
- result_tool_return_content: The return content of the tool call to set in the last message.
224
- This provides a convenient way to modify the content of the result tool call if you want to continue
225
- the conversation and want to set the response to the result tool call. If `None`, the last message will
288
+ output_tool_return_content: The return content of the tool call to set in the last message.
289
+ This provides a convenient way to modify the content of the output tool call if you want to continue
290
+ the conversation and want to set the response to the output tool call. If `None`, the last message will
226
291
  not be modified.
292
+ result_tool_return_content: deprecated, use `output_tool_return_content` instead.
227
293
 
228
294
  Returns:
229
295
  JSON bytes representing the messages.
230
296
  """
231
- return _messages.ModelMessagesTypeAdapter.dump_json(
232
- self.all_messages(result_tool_return_content=result_tool_return_content)
233
- )
297
+ content = coalesce_deprecated_return_content(output_tool_return_content, result_tool_return_content)
298
+ return _messages.ModelMessagesTypeAdapter.dump_json(self.all_messages(output_tool_return_content=content))
299
+
300
+ @overload
301
+ def new_messages(self, *, output_tool_return_content: str | None = None) -> list[_messages.ModelMessage]: ...
234
302
 
235
- def new_messages(self, *, result_tool_return_content: str | None = None) -> list[_messages.ModelMessage]:
303
+ @overload
304
+ @deprecated('`result_tool_return_content` is deprecated, use `output_tool_return_content` instead.')
305
+ def new_messages(self, *, output_tool_return_content: str | None = None) -> list[_messages.ModelMessage]: ...
306
+
307
+ def new_messages(
308
+ self, *, output_tool_return_content: str | None = None, result_tool_return_content: str | None = None
309
+ ) -> list[_messages.ModelMessage]: # pragma: no cover
236
310
  """Return new messages associated with this run.
237
311
 
238
312
  Messages from older runs are excluded.
239
313
 
240
314
  Args:
241
- result_tool_return_content: The return content of the tool call to set in the last message.
242
- This provides a convenient way to modify the content of the result tool call if you want to continue
243
- the conversation and want to set the response to the result tool call. If `None`, the last message will
315
+ output_tool_return_content: The return content of the tool call to set in the last message.
316
+ This provides a convenient way to modify the content of the output tool call if you want to continue
317
+ the conversation and want to set the response to the output tool call. If `None`, the last message will
244
318
  not be modified.
319
+ result_tool_return_content: deprecated, use `output_tool_return_content` instead.
245
320
 
246
321
  Returns:
247
322
  List of new messages.
248
323
  """
249
- return self.all_messages(result_tool_return_content=result_tool_return_content)[self._new_message_index :]
324
+ content = coalesce_deprecated_return_content(output_tool_return_content, result_tool_return_content)
325
+ return self.all_messages(output_tool_return_content=content)[self._new_message_index :]
326
+
327
+ @overload
328
+ def new_messages_json(self, *, output_tool_return_content: str | None = None) -> bytes: ...
250
329
 
251
- def new_messages_json(self, *, result_tool_return_content: str | None = None) -> bytes:
330
+ @overload
331
+ @deprecated('`result_tool_return_content` is deprecated, use `output_tool_return_content` instead.')
332
+ def new_messages_json(self, *, result_tool_return_content: str | None = None) -> bytes: ...
333
+
334
+ def new_messages_json(
335
+ self, *, output_tool_return_content: str | None = None, result_tool_return_content: str | None = None
336
+ ) -> bytes: # pragma: no cover
252
337
  """Return new messages from [`new_messages`][pydantic_ai.result.StreamedRunResult.new_messages] as JSON bytes.
253
338
 
254
339
  Args:
255
- result_tool_return_content: The return content of the tool call to set in the last message.
256
- This provides a convenient way to modify the content of the result tool call if you want to continue
257
- the conversation and want to set the response to the result tool call. If `None`, the last message will
340
+ output_tool_return_content: The return content of the tool call to set in the last message.
341
+ This provides a convenient way to modify the content of the output tool call if you want to continue
342
+ the conversation and want to set the response to the output tool call. If `None`, the last message will
258
343
  not be modified.
344
+ result_tool_return_content: deprecated, use `output_tool_return_content` instead.
259
345
 
260
346
  Returns:
261
347
  JSON bytes representing the new messages.
262
348
  """
263
- return _messages.ModelMessagesTypeAdapter.dump_json(
264
- self.new_messages(result_tool_return_content=result_tool_return_content)
265
- )
349
+ content = coalesce_deprecated_return_content(output_tool_return_content, result_tool_return_content)
350
+ return _messages.ModelMessagesTypeAdapter.dump_json(self.new_messages(output_tool_return_content=content))
266
351
 
267
- async def stream(self, *, debounce_by: float | None = 0.1) -> AsyncIterator[ResultDataT]:
352
+ async def stream(self, *, debounce_by: float | None = 0.1) -> AsyncIterator[OutputDataT]:
268
353
  """Stream the response as an async iterable.
269
354
 
270
355
  The pydantic validator for structured data will be called in
@@ -280,8 +365,7 @@ class StreamedRunResult(Generic[AgentDepsT, ResultDataT]):
280
365
  An async iterable of the response data.
281
366
  """
282
367
  async for structured_message, is_last in self.stream_structured(debounce_by=debounce_by):
283
- result = await self.validate_structured_result(structured_message, allow_partial=not is_last)
284
- yield result
368
+ yield await self.validate_structured_output(structured_message, allow_partial=not is_last)
285
369
 
286
370
  async def stream_text(self, *, delta: bool = False, debounce_by: float | None = 0.1) -> AsyncIterator[str]:
287
371
  """Stream the text result as an async iterable.
@@ -296,7 +380,7 @@ class StreamedRunResult(Generic[AgentDepsT, ResultDataT]):
296
380
  Debouncing is particularly important for long structured responses to reduce the overhead of
297
381
  performing validation as each token is received.
298
382
  """
299
- if self._result_schema and not self._result_schema.allow_text_result:
383
+ if self._output_schema and not self._output_schema.allow_text_output:
300
384
  raise exceptions.UserError('stream_text() can only be used with text responses')
301
385
 
302
386
  if delta:
@@ -304,7 +388,7 @@ class StreamedRunResult(Generic[AgentDepsT, ResultDataT]):
304
388
  yield text
305
389
  else:
306
390
  async for text in self._stream_response_text(delta=delta, debounce_by=debounce_by):
307
- combined_validated_text = await self._validate_text_result(text)
391
+ combined_validated_text = await self._validate_text_output(text)
308
392
  yield combined_validated_text
309
393
  await self._marked_completed(self._stream_response.get())
310
394
 
@@ -336,7 +420,7 @@ class StreamedRunResult(Generic[AgentDepsT, ResultDataT]):
336
420
 
337
421
  await self._marked_completed(msg)
338
422
 
339
- async def get_data(self) -> ResultDataT:
423
+ async def get_output(self) -> OutputDataT:
340
424
  """Stream the whole response, validate and return it."""
341
425
  usage_checking_stream = _get_usage_checking_stream_response(
342
426
  self._stream_response, self._usage_limits, self.usage
@@ -346,7 +430,11 @@ class StreamedRunResult(Generic[AgentDepsT, ResultDataT]):
346
430
  pass
347
431
  message = self._stream_response.get()
348
432
  await self._marked_completed(message)
349
- return await self.validate_structured_result(message)
433
+ return await self.validate_structured_output(message)
434
+
435
+ @deprecated('`get_data` is deprecated, use `get_output` instead.')
436
+ async def get_data(self) -> OutputDataT:
437
+ return await self.get_output()
350
438
 
351
439
  def usage(self) -> Usage:
352
440
  """Return the usage of the whole run.
@@ -360,36 +448,42 @@ class StreamedRunResult(Generic[AgentDepsT, ResultDataT]):
360
448
  """Get the timestamp of the response."""
361
449
  return self._stream_response.timestamp
362
450
 
451
+ @deprecated('`validate_structured_result` is deprecated, use `validate_structured_output` instead.')
363
452
  async def validate_structured_result(
364
453
  self, message: _messages.ModelResponse, *, allow_partial: bool = False
365
- ) -> ResultDataT:
454
+ ) -> OutputDataT:
455
+ return await self.validate_structured_output(message, allow_partial=allow_partial)
456
+
457
+ async def validate_structured_output(
458
+ self, message: _messages.ModelResponse, *, allow_partial: bool = False
459
+ ) -> OutputDataT:
366
460
  """Validate a structured result message."""
367
- if self._result_schema is not None and self._result_tool_name is not None:
368
- match = self._result_schema.find_named_tool(message.parts, self._result_tool_name)
461
+ if self._output_schema is not None and self._output_tool_name is not None:
462
+ match = self._output_schema.find_named_tool(message.parts, self._output_tool_name)
369
463
  if match is None:
370
464
  raise exceptions.UnexpectedModelBehavior(
371
- f'Invalid response, unable to find tool: {self._result_schema.tool_names()}'
465
+ f'Invalid response, unable to find tool: {self._output_schema.tool_names()}'
372
466
  )
373
467
 
374
- call, result_tool = match
375
- result_data = result_tool.validate(call, allow_partial=allow_partial, wrap_validation_errors=False)
468
+ call, output_tool = match
469
+ result_data = output_tool.validate(call, allow_partial=allow_partial, wrap_validation_errors=False)
376
470
 
377
- for validator in self._result_validators:
471
+ for validator in self._output_validators:
378
472
  result_data = await validator.validate(result_data, call, self._run_ctx)
379
473
  return result_data
380
474
  else:
381
475
  text = '\n\n'.join(x.content for x in message.parts if isinstance(x, _messages.TextPart))
382
- for validator in self._result_validators:
476
+ for validator in self._output_validators:
383
477
  text = await validator.validate(
384
478
  text,
385
479
  None,
386
480
  self._run_ctx,
387
481
  )
388
- # Since there is no result tool, we can assume that str is compatible with ResultDataT
389
- return cast(ResultDataT, text)
482
+ # Since there is no output tool, we can assume that str is compatible with OutputDataT
483
+ return cast(OutputDataT, text)
390
484
 
391
- async def _validate_text_result(self, text: str) -> str:
392
- for validator in self._result_validators:
485
+ async def _validate_text_output(self, text: str) -> str:
486
+ for validator in self._output_validators:
393
487
  text = await validator.validate(
394
488
  text,
395
489
  None,
@@ -459,15 +553,20 @@ class StreamedRunResult(Generic[AgentDepsT, ResultDataT]):
459
553
 
460
554
 
461
555
  @dataclass
462
- class FinalResult(Generic[ResultDataT]):
463
- """Marker class storing the final result of an agent run and associated metadata."""
556
+ class FinalResult(Generic[OutputDataT]):
557
+ """Marker class storing the final output of an agent run and associated metadata."""
464
558
 
465
- data: ResultDataT
559
+ output: OutputDataT
466
560
  """The final result data."""
467
561
  tool_name: str | None
468
- """Name of the final result tool; `None` if the result came from unstructured text content."""
562
+ """Name of the final output tool; `None` if the output came from unstructured text content."""
469
563
  tool_call_id: str | None
470
- """ID of the tool call that produced the final result; `None` if the result came from unstructured text content."""
564
+ """ID of the tool call that produced the final output; `None` if the output came from unstructured text content."""
565
+
566
+ @property
567
+ @deprecated('`data` is deprecated, use `output` instead.')
568
+ def data(self) -> OutputDataT:
569
+ return self.output
471
570
 
472
571
 
473
572
  def _get_usage_checking_stream_response(
@@ -485,3 +584,17 @@ def _get_usage_checking_stream_response(
485
584
  return _usage_checking_iterator()
486
585
  else:
487
586
  return stream_response
587
+
588
+
589
+ def coalesce_deprecated_return_content(
590
+ output_tool_return_content: T | None, result_tool_return_content: T | None
591
+ ) -> T | None:
592
+ """Return the first non-None value."""
593
+ if output_tool_return_content is None:
594
+ if result_tool_return_content is not None: # pragma: no cover
595
+ warnings.warn(
596
+ '`result_tool_return_content` is deprecated, use `output_tool_return_content` instead.',
597
+ DeprecationWarning,
598
+ )
599
+ return result_tool_return_content
600
+ return output_tool_return_content
pydantic_ai/tools.py CHANGED
@@ -333,7 +333,7 @@ class Tool(Generic[AgentDepsT]):
333
333
  ) -> _messages.ToolReturnPart | _messages.RetryPromptPart:
334
334
  try:
335
335
  if isinstance(message.args, str):
336
- args_dict = self._validator.validate_json(message.args)
336
+ args_dict = self._validator.validate_json(message.args or '{}')
337
337
  else:
338
338
  args_dict = self._validator.validate_python(message.args)
339
339
  except ValidationError as e:
@@ -411,7 +411,7 @@ With PEP-728 this should be a TypedDict with `type: Literal['object']`, and `ext
411
411
  class ToolDefinition:
412
412
  """Definition of a tool passed to a model.
413
413
 
414
- This is used for both function tools and result tools.
414
+ This is used for both function tools and output tools.
415
415
  """
416
416
 
417
417
  name: str
@@ -424,9 +424,9 @@ class ToolDefinition:
424
424
  """The JSON schema for the tool's parameters."""
425
425
 
426
426
  outer_typed_dict_key: str | None = None
427
- """The key in the outer [TypedDict] that wraps a result tool.
427
+ """The key in the outer [TypedDict] that wraps an output tool.
428
428
 
429
- This will only be set for result tools which don't have an `object` JSON schema.
429
+ This will only be set for output tools which don't have an `object` JSON schema.
430
430
  """
431
431
 
432
432
  strict: bool | None = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pydantic-ai-slim
3
- Version: 0.0.55
3
+ Version: 0.1.1
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
@@ -29,13 +29,13 @@ Requires-Dist: exceptiongroup; python_version < '3.11'
29
29
  Requires-Dist: griffe>=1.3.2
30
30
  Requires-Dist: httpx>=0.27
31
31
  Requires-Dist: opentelemetry-api>=1.28.0
32
- Requires-Dist: pydantic-graph==0.0.55
32
+ Requires-Dist: pydantic-graph==0.1.1
33
33
  Requires-Dist: pydantic>=2.10
34
34
  Requires-Dist: typing-inspection>=0.4.0
35
35
  Provides-Extra: anthropic
36
36
  Requires-Dist: anthropic>=0.49.0; extra == 'anthropic'
37
37
  Provides-Extra: bedrock
38
- Requires-Dist: boto3>=1.34.116; extra == 'bedrock'
38
+ Requires-Dist: boto3>=1.35.74; extra == 'bedrock'
39
39
  Provides-Extra: cli
40
40
  Requires-Dist: argcomplete>=3.5.0; extra == 'cli'
41
41
  Requires-Dist: prompt-toolkit>=3; extra == 'cli'
@@ -45,7 +45,7 @@ Requires-Dist: cohere>=5.13.11; (platform_system != 'Emscripten') and extra == '
45
45
  Provides-Extra: duckduckgo
46
46
  Requires-Dist: duckduckgo-search>=7.0.0; extra == 'duckduckgo'
47
47
  Provides-Extra: evals
48
- Requires-Dist: pydantic-evals==0.0.55; extra == 'evals'
48
+ Requires-Dist: pydantic-evals==0.1.1; extra == 'evals'
49
49
  Provides-Extra: groq
50
50
  Requires-Dist: groq>=0.15.0; extra == 'groq'
51
51
  Provides-Extra: logfire
@@ -55,7 +55,7 @@ Requires-Dist: mcp>=1.4.1; (python_version >= '3.10') and extra == 'mcp'
55
55
  Provides-Extra: mistral
56
56
  Requires-Dist: mistralai>=1.2.5; extra == 'mistral'
57
57
  Provides-Extra: openai
58
- Requires-Dist: openai>=1.67.0; extra == 'openai'
58
+ Requires-Dist: openai>=1.74.0; extra == 'openai'
59
59
  Provides-Extra: tavily
60
60
  Requires-Dist: tavily-python>=0.5.0; extra == 'tavily'
61
61
  Provides-Extra: vertexai
@@ -0,0 +1,53 @@
1
+ pydantic_ai/__init__.py,sha256=5flxyMQJVrHRMQ3MYaZf1el2ctNs0JmPClKbw2Q-Lsk,1160
2
+ pydantic_ai/__main__.py,sha256=AW8FzscUWPFtIrQBG0QExLxTQehKtt5FnFVnOT200OE,122
3
+ pydantic_ai/_agent_graph.py,sha256=YNBaxws6CXQ5D_G19wIwf8E4Jkf5mN6aFS2q2Nyxk-w,33318
4
+ pydantic_ai/_cli.py,sha256=j3uSu5lZMNKb876HHMHwVZc1nzJPn6NER5ysrDMFrvo,10730
5
+ pydantic_ai/_griffe.py,sha256=Sf_DisE9k2TA0VFeVIK2nf1oOct5MygW86PBCACJkFA,5244
6
+ pydantic_ai/_output.py,sha256=w_kBc5Lx5AmI0APbohxxYgpFd5VAwh6K0IjP7QIOu9U,11209
7
+ pydantic_ai/_parts_manager.py,sha256=HIi6eth7z2g0tOn6iQYc633xMqy4d_xZ8vwka8J8150,12016
8
+ pydantic_ai/_pydantic.py,sha256=12hX5hON88meO1QxbWrEPXSvr6RTNgr6ubKY6KRwab4,8890
9
+ pydantic_ai/_system_prompt.py,sha256=602c2jyle2R_SesOrITBDETZqsLk4BZ8Cbo8yEhmx04,1120
10
+ pydantic_ai/_utils.py,sha256=t0P9DFX3MulMyoqyZDRinmLCOB1q6rvRnMRqQP2MUpI,10585
11
+ pydantic_ai/agent.py,sha256=IjFjHqt-cCkYorCn_Ry6PKAaI_yRpW7tb0lOpozeMGo,86826
12
+ pydantic_ai/exceptions.py,sha256=gvbFsFkAzSXOo_d1nfjy09kDHUGv1j5q70Uk-wKYGi8,3167
13
+ pydantic_ai/format_as_xml.py,sha256=IINfh1evWDphGahqHNLBArB5dQ4NIqS3S-kru35ztGg,372
14
+ pydantic_ai/format_prompt.py,sha256=qdKep95Sjlr7u1-qag4JwPbjoURbG0GbeU_l5ODTNw4,4466
15
+ pydantic_ai/mcp.py,sha256=wlu3GCdcjgsag75cyuTron1kdTQOWkM0j5hNJxf0JkE,8242
16
+ pydantic_ai/messages.py,sha256=TUfs1AsogZu1YkoJjKwjbKCcaaDg-0gcbIj_wVLUW0E,29748
17
+ pydantic_ai/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ pydantic_ai/result.py,sha256=DgoUd0LqNd9DWPab6iwculKYvZ5JZHuGvToj0kkibvs,27625
19
+ pydantic_ai/settings.py,sha256=RMQxjsJqyiExxtxCr_L3avojAiANdFE0ME1eitLrjhk,3242
20
+ pydantic_ai/tools.py,sha256=Tq-Ba5_7Cx3N3iBExFy06JkelZAz_mOi-K8zLXgCBDU,16923
21
+ pydantic_ai/usage.py,sha256=9sqoIv_RVVUhKXQScTDqUJc074gifsuSzc9_NOt7C3g,5394
22
+ pydantic_ai/common_tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
+ pydantic_ai/common_tools/duckduckgo.py,sha256=Iw8Dl2YQ28S483mzfa8CXs-dc-ujS8un085R2O6oOEw,2241
24
+ pydantic_ai/common_tools/tavily.py,sha256=h8deBDrpG-8BGzydM_zXs7z1ASrhdVvUxL4-CAbncBo,2589
25
+ pydantic_ai/models/__init__.py,sha256=R1YV2gE8l_EDlnmLhfxhdojF6gjGWMC14ocRrxiz5XM,19699
26
+ pydantic_ai/models/_json_schema.py,sha256=GXcELRirUtqfpHlNKlRzCR_p6_-Xl7YgIWYCxnshsLA,6414
27
+ pydantic_ai/models/anthropic.py,sha256=gkeeIEqI0dk72EIL9zYRtvBd9ipDvT0v1-AcdMF_l20,20272
28
+ pydantic_ai/models/bedrock.py,sha256=KSmukt2jeKb6uABVR5BRz5Sm7YGYSh56NF0Ftzk11UI,24977
29
+ pydantic_ai/models/cohere.py,sha256=0clDIXPETo0pjfNgtI-sWqjjZWPkHqFalag_nN3HSNI,11685
30
+ pydantic_ai/models/fallback.py,sha256=sKHyQ1P6zjWORjWgbuhaxyntOfQyDCS8km8FMrlNy3U,4998
31
+ pydantic_ai/models/function.py,sha256=FJvTMwT7p8I_h14ZrudLAI5mmbXxF8AX-Nhz8LXy5U0,11373
32
+ pydantic_ai/models/gemini.py,sha256=zoYbwDusTX4vYeS1OhD6-ACgJyj8-cHluhL7lxQTDXk,35363
33
+ pydantic_ai/models/groq.py,sha256=UNyZPqmHRZ9LgtMhV5f9QI1txZ7TCMyu8Abn1ojk5lE,17034
34
+ pydantic_ai/models/instrumented.py,sha256=_5bV2kkwCix5C25cCAblbhZy1wiKaYBUStmHso4T22g,11163
35
+ pydantic_ai/models/mistral.py,sha256=sNujNLxpPFZA_vrh-74rDRl2syE1QTgILm6hMZNR4SI,28226
36
+ pydantic_ai/models/openai.py,sha256=tfOOtShuPVrxgwUIg4p59Sak6gC-TEpHyEd2idrvDuc,45759
37
+ pydantic_ai/models/test.py,sha256=_Fd7oKNA2p_1zXBMvQStnizGlx-ii-zisJx1nIEZn7c,16973
38
+ pydantic_ai/models/wrapper.py,sha256=8wm4RF-MRZOxRVLefwMsxToopCX5Y4Xq2-Ugs5MtCK4,1710
39
+ pydantic_ai/providers/__init__.py,sha256=lLlHq6B8qmu6Ag5biaZmJGDKELO46KjwP7-CDrz_T4Y,2592
40
+ pydantic_ai/providers/anthropic.py,sha256=0WzWEDseBaJ5eyEatvnDXBtDZKA9-od4BuPZn9NoTPw,2812
41
+ pydantic_ai/providers/azure.py,sha256=2tAE-bLjXY-DvVrVc4ilQe15HhoHP9neAbvwCaCx_uo,4225
42
+ pydantic_ai/providers/bedrock.py,sha256=BV1Zi4asU4Bmcv4t7VRIy2U44Tk_Jrf26x8_mPJiYHQ,3216
43
+ pydantic_ai/providers/cohere.py,sha256=WOFZCllgVbWciF4nNkG3pCqw4poy57VEGyux2mVntbQ,2667
44
+ pydantic_ai/providers/deepseek.py,sha256=_5JPzDGWsyVyTBX-yYYdy5aZwUOWNCVgoWI-UoBamms,2193
45
+ pydantic_ai/providers/google_gla.py,sha256=MJM7aRZRdP4kFlNg0ZHgC95O0wH02OQgbNiDQeK9fZo,1600
46
+ pydantic_ai/providers/google_vertex.py,sha256=WAwPxKTARVzs8DFs2veEUOJSur0krDOo9-JWRHvfHew,9135
47
+ pydantic_ai/providers/groq.py,sha256=DoY6qkfhuemuKB5JXhUkqG-3t1HQkxwSXoE_kHQIAK0,2788
48
+ pydantic_ai/providers/mistral.py,sha256=fcR1uSwORo0jtevX7-wOjvcfT8ojMAaKY81uN5uYymM,2661
49
+ pydantic_ai/providers/openai.py,sha256=ePF-QWwLkGkSE5w245gTTDVR3VoTIUqFoIhQ0TAoUiA,2866
50
+ pydantic_ai_slim-0.1.1.dist-info/METADATA,sha256=Aas5s1kUmLhvIoySwub-x_J7xE4okIR2z13C_ZXvPno,3551
51
+ pydantic_ai_slim-0.1.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
52
+ pydantic_ai_slim-0.1.1.dist-info/entry_points.txt,sha256=KxQSmlMS8GMTkwTsl4_q9a5nJvBjj3HWeXx688wLrKg,45
53
+ pydantic_ai_slim-0.1.1.dist-info/RECORD,,