pydantic-ai-slim 0.7.6__py3-none-any.whl → 0.8.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.

Potentially problematic release.


This version of pydantic-ai-slim might be problematic. Click here for more details.

pydantic_ai/messages.py CHANGED
@@ -110,7 +110,9 @@ class FileUrl(ABC):
110
110
  - `GoogleModel`: `VideoUrl.vendor_metadata` is used as `video_metadata`: https://ai.google.dev/gemini-api/docs/video-understanding#customize-video-processing
111
111
  """
112
112
 
113
- _media_type: str | None = field(init=False, repr=False, compare=False)
113
+ _media_type: Annotated[str | None, pydantic.Field(alias='media_type', default=None, exclude=True)] = field(
114
+ compare=False, default=None
115
+ )
114
116
 
115
117
  def __init__(
116
118
  self,
@@ -124,6 +126,7 @@ class FileUrl(ABC):
124
126
  self.force_download = force_download
125
127
  self._media_type = media_type
126
128
 
129
+ @pydantic.computed_field
127
130
  @property
128
131
  def media_type(self) -> str:
129
132
  """Return the media type of the file, based on the URL or the provided `media_type`."""
@@ -160,8 +163,16 @@ class VideoUrl(FileUrl):
160
163
  vendor_metadata: dict[str, Any] | None = None,
161
164
  media_type: str | None = None,
162
165
  kind: Literal['video-url'] = 'video-url',
166
+ *,
167
+ # Required for inline-snapshot which expects all dataclass `__init__` methods to take all field names as kwargs.
168
+ _media_type: str | None = None,
163
169
  ) -> None:
164
- super().__init__(url=url, force_download=force_download, vendor_metadata=vendor_metadata, media_type=media_type)
170
+ super().__init__(
171
+ url=url,
172
+ force_download=force_download,
173
+ vendor_metadata=vendor_metadata,
174
+ media_type=media_type or _media_type,
175
+ )
165
176
  self.kind = kind
166
177
 
167
178
  def _infer_media_type(self) -> VideoMediaType:
@@ -223,8 +234,16 @@ class AudioUrl(FileUrl):
223
234
  vendor_metadata: dict[str, Any] | None = None,
224
235
  media_type: str | None = None,
225
236
  kind: Literal['audio-url'] = 'audio-url',
237
+ *,
238
+ # Required for inline-snapshot which expects all dataclass `__init__` methods to take all field names as kwargs.
239
+ _media_type: str | None = None,
226
240
  ) -> None:
227
- super().__init__(url=url, force_download=force_download, vendor_metadata=vendor_metadata, media_type=media_type)
241
+ super().__init__(
242
+ url=url,
243
+ force_download=force_download,
244
+ vendor_metadata=vendor_metadata,
245
+ media_type=media_type or _media_type,
246
+ )
228
247
  self.kind = kind
229
248
 
230
249
  def _infer_media_type(self) -> AudioMediaType:
@@ -273,8 +292,16 @@ class ImageUrl(FileUrl):
273
292
  vendor_metadata: dict[str, Any] | None = None,
274
293
  media_type: str | None = None,
275
294
  kind: Literal['image-url'] = 'image-url',
295
+ *,
296
+ # Required for inline-snapshot which expects all dataclass `__init__` methods to take all field names as kwargs.
297
+ _media_type: str | None = None,
276
298
  ) -> None:
277
- super().__init__(url=url, force_download=force_download, vendor_metadata=vendor_metadata, media_type=media_type)
299
+ super().__init__(
300
+ url=url,
301
+ force_download=force_download,
302
+ vendor_metadata=vendor_metadata,
303
+ media_type=media_type or _media_type,
304
+ )
278
305
  self.kind = kind
279
306
 
280
307
  def _infer_media_type(self) -> ImageMediaType:
@@ -318,8 +345,16 @@ class DocumentUrl(FileUrl):
318
345
  vendor_metadata: dict[str, Any] | None = None,
319
346
  media_type: str | None = None,
320
347
  kind: Literal['document-url'] = 'document-url',
348
+ *,
349
+ # Required for inline-snapshot which expects all dataclass `__init__` methods to take all field names as kwargs.
350
+ _media_type: str | None = None,
321
351
  ) -> None:
322
- super().__init__(url=url, force_download=force_download, vendor_metadata=vendor_metadata, media_type=media_type)
352
+ super().__init__(
353
+ url=url,
354
+ force_download=force_download,
355
+ vendor_metadata=vendor_metadata,
356
+ media_type=media_type or _media_type,
357
+ )
323
358
  self.kind = kind
324
359
 
325
360
  def _infer_media_type(self) -> str:
@@ -903,7 +938,7 @@ class ModelResponse:
903
938
  For OpenAI models, this may include 'logprobs', 'finish_reason', etc.
904
939
  """
905
940
 
906
- provider_request_id: str | None = None
941
+ provider_response_id: str | None = None
907
942
  """request ID as specified by the model provider. This can be used to track the specific request to the model."""
908
943
 
909
944
  def price(self) -> genai_types.PriceCalculation:
@@ -991,9 +1026,14 @@ class ModelResponse:
991
1026
  return self.provider_details
992
1027
 
993
1028
  @property
994
- @deprecated('`vendor_id` is deprecated, use `provider_request_id` instead')
1029
+ @deprecated('`vendor_id` is deprecated, use `provider_response_id` instead')
995
1030
  def vendor_id(self) -> str | None:
996
- return self.provider_request_id
1031
+ return self.provider_response_id
1032
+
1033
+ @property
1034
+ @deprecated('`provider_request_id` is deprecated, use `provider_response_id` instead')
1035
+ def provider_request_id(self) -> str | None:
1036
+ return self.provider_response_id
997
1037
 
998
1038
  __repr__ = _utils.dataclasses_no_defaults_repr
999
1039
 
@@ -1263,13 +1303,10 @@ class FinalResultEvent:
1263
1303
  __repr__ = _utils.dataclasses_no_defaults_repr
1264
1304
 
1265
1305
 
1266
- ModelResponseStreamEvent = Annotated[Union[PartStartEvent, PartDeltaEvent], pydantic.Discriminator('event_kind')]
1267
- """An event in the model response stream, either starting a new part or applying a delta to an existing one."""
1268
-
1269
- AgentStreamEvent = Annotated[
1306
+ ModelResponseStreamEvent = Annotated[
1270
1307
  Union[PartStartEvent, PartDeltaEvent, FinalResultEvent], pydantic.Discriminator('event_kind')
1271
1308
  ]
1272
- """An event in the agent stream."""
1309
+ """An event in the model response stream, starting a new part, applying a delta to an existing one, or indicating the final result."""
1273
1310
 
1274
1311
 
1275
1312
  @dataclass(repr=False)
@@ -1338,3 +1375,7 @@ HandleResponseEvent = Annotated[
1338
1375
  Union[FunctionToolCallEvent, FunctionToolResultEvent, BuiltinToolCallEvent, BuiltinToolResultEvent],
1339
1376
  pydantic.Discriminator('event_kind'),
1340
1377
  ]
1378
+ """An event yielded when handling a model response, indicating tool calls and results."""
1379
+
1380
+ AgentStreamEvent = Annotated[Union[ModelResponseStreamEvent, HandleResponseEvent], pydantic.Discriminator('event_kind')]
1381
+ """An event in the agent stream: model response stream events and response-handling events."""
@@ -7,6 +7,7 @@ specific LLM being used.
7
7
  from __future__ import annotations as _annotations
8
8
 
9
9
  import base64
10
+ import warnings
10
11
  from abc import ABC, abstractmethod
11
12
  from collections.abc import AsyncIterator, Iterator
12
13
  from contextlib import asynccontextmanager, contextmanager
@@ -25,7 +26,6 @@ from .._run_context import RunContext
25
26
  from ..builtin_tools import AbstractBuiltinTool
26
27
  from ..exceptions import UserError
27
28
  from ..messages import (
28
- AgentStreamEvent,
29
29
  FileUrl,
30
30
  FinalResultEvent,
31
31
  ModelMessage,
@@ -555,11 +555,11 @@ class StreamedResponse(ABC):
555
555
  final_result_event: FinalResultEvent | None = field(default=None, init=False)
556
556
 
557
557
  _parts_manager: ModelResponsePartsManager = field(default_factory=ModelResponsePartsManager, init=False)
558
- _event_iterator: AsyncIterator[AgentStreamEvent] | None = field(default=None, init=False)
558
+ _event_iterator: AsyncIterator[ModelResponseStreamEvent] | None = field(default=None, init=False)
559
559
  _usage: RequestUsage = field(default_factory=RequestUsage, init=False)
560
560
 
561
- def __aiter__(self) -> AsyncIterator[AgentStreamEvent]:
562
- """Stream the response as an async iterable of [`AgentStreamEvent`][pydantic_ai.messages.AgentStreamEvent]s.
561
+ def __aiter__(self) -> AsyncIterator[ModelResponseStreamEvent]:
562
+ """Stream the response as an async iterable of [`ModelResponseStreamEvent`][pydantic_ai.messages.ModelResponseStreamEvent]s.
563
563
 
564
564
  This proxies the `_event_iterator()` and emits all events, while also checking for matches
565
565
  on the result schema and emitting a [`FinalResultEvent`][pydantic_ai.messages.FinalResultEvent] if/when the
@@ -569,7 +569,7 @@ class StreamedResponse(ABC):
569
569
 
570
570
  async def iterator_with_final_event(
571
571
  iterator: AsyncIterator[ModelResponseStreamEvent],
572
- ) -> AsyncIterator[AgentStreamEvent]:
572
+ ) -> AsyncIterator[ModelResponseStreamEvent]:
573
573
  async for event in iterator:
574
574
  yield event
575
575
  if (
@@ -685,19 +685,29 @@ def infer_model(model: Model | KnownModelName | str) -> Model: # noqa: C901
685
685
  try:
686
686
  provider, model_name = model.split(':', maxsplit=1)
687
687
  except ValueError:
688
+ provider = None
688
689
  model_name = model
689
- # TODO(Marcelo): We should deprecate this way.
690
690
  if model_name.startswith(('gpt', 'o1', 'o3')):
691
691
  provider = 'openai'
692
692
  elif model_name.startswith('claude'):
693
693
  provider = 'anthropic'
694
694
  elif model_name.startswith('gemini'):
695
695
  provider = 'google-gla'
696
+
697
+ if provider is not None:
698
+ warnings.warn(
699
+ f"Specifying a model name without a provider prefix is deprecated. Instead of {model_name!r}, use '{provider}:{model_name}'.",
700
+ DeprecationWarning,
701
+ )
696
702
  else:
697
703
  raise UserError(f'Unknown model: {model}')
698
704
 
699
- if provider == 'vertexai':
700
- provider = 'google-vertex' # pragma: no cover
705
+ if provider == 'vertexai': # pragma: no cover
706
+ warnings.warn(
707
+ "The 'vertexai' provider name is deprecated. Use 'google-vertex' instead.",
708
+ DeprecationWarning,
709
+ )
710
+ provider = 'google-vertex'
701
711
 
702
712
  if provider == 'cohere':
703
713
  from .cohere import CohereModel
@@ -330,7 +330,7 @@ class AnthropicModel(Model):
330
330
  items,
331
331
  usage=_map_usage(response),
332
332
  model_name=response.model,
333
- provider_request_id=response.id,
333
+ provider_response_id=response.id,
334
334
  provider_name=self._provider.name,
335
335
  )
336
336
 
@@ -301,9 +301,13 @@ class BedrockConverseModel(Model):
301
301
  input_tokens=response['usage']['inputTokens'],
302
302
  output_tokens=response['usage']['outputTokens'],
303
303
  )
304
- vendor_id = response.get('ResponseMetadata', {}).get('RequestId', None)
304
+ response_id = response.get('ResponseMetadata', {}).get('RequestId', None)
305
305
  return ModelResponse(
306
- items, usage=u, model_name=self.model_name, provider_request_id=vendor_id, provider_name=self._provider.name
306
+ items,
307
+ usage=u,
308
+ model_name=self.model_name,
309
+ provider_response_id=response_id,
310
+ provider_name=self._provider.name,
307
311
  )
308
312
 
309
313
  @overload
@@ -690,7 +690,7 @@ def _process_response_from_parts(
690
690
  f'Unsupported response from Gemini, expected all parts to be function calls or text, got: {part!r}'
691
691
  )
692
692
  return ModelResponse(
693
- parts=items, usage=usage, model_name=model_name, provider_request_id=vendor_id, provider_details=vendor_details
693
+ parts=items, usage=usage, model_name=model_name, provider_response_id=vendor_id, provider_details=vendor_details
694
694
  )
695
695
 
696
696
 
@@ -648,7 +648,7 @@ def _process_response_from_parts(
648
648
  parts=items,
649
649
  model_name=model_name,
650
650
  usage=usage,
651
- provider_request_id=vendor_id,
651
+ provider_response_id=vendor_id,
652
652
  provider_details=vendor_details,
653
653
  provider_name=provider_name,
654
654
  )
@@ -289,7 +289,7 @@ class GroqModel(Model):
289
289
  usage=_map_usage(response),
290
290
  model_name=response.model,
291
291
  timestamp=timestamp,
292
- provider_request_id=response.id,
292
+ provider_response_id=response.id,
293
293
  provider_name=self._provider.name,
294
294
  )
295
295
 
@@ -271,7 +271,7 @@ class HuggingFaceModel(Model):
271
271
  usage=_map_usage(response),
272
272
  model_name=response.model,
273
273
  timestamp=timestamp,
274
- provider_request_id=response.id,
274
+ provider_response_id=response.id,
275
275
  provider_name=self._provider.name,
276
276
  )
277
277
 
@@ -236,27 +236,36 @@ class InstrumentationSettings:
236
236
  if response.provider_details and 'finish_reason' in response.provider_details:
237
237
  output_message['finish_reason'] = response.provider_details['finish_reason']
238
238
  instructions = InstrumentedModel._get_instructions(input_messages) # pyright: ignore [reportPrivateUsage]
239
+ system_instructions_attributes = self.system_instructions_attributes(instructions)
239
240
  attributes = {
240
241
  'gen_ai.input.messages': json.dumps(self.messages_to_otel_messages(input_messages)),
241
242
  'gen_ai.output.messages': json.dumps([output_message]),
243
+ **system_instructions_attributes,
242
244
  'logfire.json_schema': json.dumps(
243
245
  {
244
246
  'type': 'object',
245
247
  'properties': {
246
248
  'gen_ai.input.messages': {'type': 'array'},
247
249
  'gen_ai.output.messages': {'type': 'array'},
248
- **({'gen_ai.system_instructions': {'type': 'array'}} if instructions else {}),
250
+ **(
251
+ {'gen_ai.system_instructions': {'type': 'array'}}
252
+ if system_instructions_attributes
253
+ else {}
254
+ ),
249
255
  'model_request_parameters': {'type': 'object'},
250
256
  },
251
257
  }
252
258
  ),
253
259
  }
254
- if instructions is not None:
255
- attributes['gen_ai.system_instructions'] = json.dumps(
256
- [_otel_messages.TextPart(type='text', content=instructions)]
257
- )
258
260
  span.set_attributes(attributes)
259
261
 
262
+ def system_instructions_attributes(self, instructions: str | None) -> dict[str, str]:
263
+ if instructions and self.include_content:
264
+ return {
265
+ 'gen_ai.system_instructions': json.dumps([_otel_messages.TextPart(type='text', content=instructions)]),
266
+ }
267
+ return {}
268
+
260
269
  def _emit_events(self, span: Span, events: list[Event]) -> None:
261
270
  if self.event_mode == 'logs':
262
271
  for event in events:
@@ -79,7 +79,7 @@ try:
79
79
  from mistralai.models.usermessage import UserMessage as MistralUserMessage
80
80
  from mistralai.types.basemodel import Unset as MistralUnset
81
81
  from mistralai.utils.eventstreaming import EventStreamAsync as MistralEventStreamAsync
82
- except ImportError as e: # pragma: no cover
82
+ except ImportError as e:
83
83
  raise ImportError(
84
84
  'Please install `mistral` to use the Mistral model, '
85
85
  'you can use the `mistral` optional group — `pip install "pydantic-ai-slim[mistral]"`'
@@ -352,7 +352,7 @@ class MistralModel(Model):
352
352
  usage=_map_usage(response),
353
353
  model_name=response.model,
354
354
  timestamp=timestamp,
355
- provider_request_id=response.id,
355
+ provider_response_id=response.id,
356
356
  provider_name=self._provider.name,
357
357
  )
358
358
 
@@ -517,7 +517,7 @@ class OpenAIChatModel(Model):
517
517
  model_name=response.model,
518
518
  timestamp=timestamp,
519
519
  provider_details=vendor_details,
520
- provider_request_id=response.id,
520
+ provider_response_id=response.id,
521
521
  provider_name=self._provider.name,
522
522
  )
523
523
 
@@ -831,7 +831,7 @@ class OpenAIResponsesModel(Model):
831
831
  items,
832
832
  usage=_map_usage(response),
833
833
  model_name=response.model,
834
- provider_request_id=response.id,
834
+ provider_response_id=response.id,
835
835
  timestamp=timestamp,
836
836
  provider_name=self._provider.name,
837
837
  )
@@ -1375,11 +1375,21 @@ def _map_usage(response: chat.ChatCompletion | ChatCompletionChunk | responses.R
1375
1375
  ).items()
1376
1376
  if isinstance(value, int)
1377
1377
  }
1378
- details['reasoning_tokens'] = response_usage.output_tokens_details.reasoning_tokens
1378
+ # Handle vLLM compatibility - some providers don't include token details
1379
+ if getattr(response_usage, 'input_tokens_details', None) is not None:
1380
+ cache_read_tokens = response_usage.input_tokens_details.cached_tokens
1381
+ else:
1382
+ cache_read_tokens = 0
1383
+
1384
+ if getattr(response_usage, 'output_tokens_details', None) is not None:
1385
+ details['reasoning_tokens'] = response_usage.output_tokens_details.reasoning_tokens
1386
+ else:
1387
+ details['reasoning_tokens'] = 0
1388
+
1379
1389
  return usage.RequestUsage(
1380
1390
  input_tokens=response_usage.input_tokens,
1381
1391
  output_tokens=response_usage.output_tokens,
1382
- cache_read_tokens=response_usage.input_tokens_details.cached_tokens,
1392
+ cache_read_tokens=cache_read_tokens,
1383
1393
  details=details,
1384
1394
  )
1385
1395
  else:
pydantic_ai/result.py CHANGED
@@ -7,7 +7,7 @@ from datetime import datetime
7
7
  from typing import Generic, cast
8
8
 
9
9
  from pydantic import ValidationError
10
- from typing_extensions import TypeVar
10
+ from typing_extensions import TypeVar, deprecated
11
11
 
12
12
  from pydantic_ai._tool_manager import ToolManager
13
13
 
@@ -22,7 +22,7 @@ from ._output import (
22
22
  ToolOutputSchema,
23
23
  )
24
24
  from ._run_context import AgentDepsT, RunContext
25
- from .messages import AgentStreamEvent
25
+ from .messages import ModelResponseStreamEvent
26
26
  from .output import (
27
27
  OutputDataT,
28
28
  ToolOutput,
@@ -51,7 +51,7 @@ class AgentStream(Generic[AgentDepsT, OutputDataT]):
51
51
  _usage_limits: UsageLimits | None
52
52
  _tool_manager: ToolManager[AgentDepsT]
53
53
 
54
- _agent_stream_iterator: AsyncIterator[AgentStreamEvent] | None = field(default=None, init=False)
54
+ _agent_stream_iterator: AsyncIterator[ModelResponseStreamEvent] | None = field(default=None, init=False)
55
55
  _initial_run_ctx_usage: RunUsage = field(init=False)
56
56
 
57
57
  def __post_init__(self):
@@ -62,11 +62,11 @@ class AgentStream(Generic[AgentDepsT, OutputDataT]):
62
62
  async for response in self.stream_responses(debounce_by=debounce_by):
63
63
  if self._raw_stream_response.final_result_event is not None:
64
64
  try:
65
- yield await self._validate_response(response, allow_partial=True)
65
+ yield await self.validate_response_output(response, allow_partial=True)
66
66
  except ValidationError:
67
67
  pass
68
68
  if self._raw_stream_response.final_result_event is not None: # pragma: no branch
69
- yield await self._validate_response(self._raw_stream_response.get())
69
+ yield await self.validate_response_output(self._raw_stream_response.get())
70
70
 
71
71
  async def stream_responses(self, *, debounce_by: float | None = 0.1) -> AsyncIterator[_messages.ModelResponse]:
72
72
  """Asynchronously stream the (unvalidated) model responses for the agent."""
@@ -127,9 +127,11 @@ class AgentStream(Generic[AgentDepsT, OutputDataT]):
127
127
  async for _ in self:
128
128
  pass
129
129
 
130
- return await self._validate_response(self._raw_stream_response.get())
130
+ return await self.validate_response_output(self._raw_stream_response.get())
131
131
 
132
- async def _validate_response(self, message: _messages.ModelResponse, *, allow_partial: bool = False) -> OutputDataT:
132
+ async def validate_response_output(
133
+ self, message: _messages.ModelResponse, *, allow_partial: bool = False
134
+ ) -> OutputDataT:
133
135
  """Validate a structured result message."""
134
136
  final_result_event = self._raw_stream_response.final_result_event
135
137
  if final_result_event is None:
@@ -221,8 +223,8 @@ class AgentStream(Generic[AgentDepsT, OutputDataT]):
221
223
  deltas.append(text)
222
224
  yield ''.join(deltas)
223
225
 
224
- def __aiter__(self) -> AsyncIterator[AgentStreamEvent]:
225
- """Stream [`AgentStreamEvent`][pydantic_ai.messages.AgentStreamEvent]s."""
226
+ def __aiter__(self) -> AsyncIterator[ModelResponseStreamEvent]:
227
+ """Stream [`ModelResponseStreamEvent`][pydantic_ai.messages.ModelResponseStreamEvent]s."""
226
228
  if self._agent_stream_iterator is None:
227
229
  self._agent_stream_iterator = _get_usage_checking_stream_response(
228
230
  self._raw_stream_response, self._usage_limits, self.usage
@@ -245,9 +247,9 @@ class StreamedRunResult(Generic[AgentDepsT, OutputDataT]):
245
247
  """Whether the stream has all been received.
246
248
 
247
249
  This is set to `True` when one of
248
- [`stream`][pydantic_ai.result.StreamedRunResult.stream],
250
+ [`stream_output`][pydantic_ai.result.StreamedRunResult.stream_output],
249
251
  [`stream_text`][pydantic_ai.result.StreamedRunResult.stream_text],
250
- [`stream_structured`][pydantic_ai.result.StreamedRunResult.stream_structured] or
252
+ [`stream_responses`][pydantic_ai.result.StreamedRunResult.stream_responses] or
251
253
  [`get_output`][pydantic_ai.result.StreamedRunResult.get_output] completes.
252
254
  """
253
255
 
@@ -318,16 +320,21 @@ class StreamedRunResult(Generic[AgentDepsT, OutputDataT]):
318
320
  self.new_messages(output_tool_return_content=output_tool_return_content)
319
321
  )
320
322
 
323
+ @deprecated('`StreamedRunResult.stream` is deprecated, use `stream_output` instead.')
321
324
  async def stream(self, *, debounce_by: float | None = 0.1) -> AsyncIterator[OutputDataT]:
322
- """Stream the response as an async iterable.
325
+ async for output in self.stream_output(debounce_by=debounce_by):
326
+ yield output
327
+
328
+ async def stream_output(self, *, debounce_by: float | None = 0.1) -> AsyncIterator[OutputDataT]:
329
+ """Stream the output as an async iterable.
323
330
 
324
331
  The pydantic validator for structured data will be called in
325
332
  [partial mode](https://docs.pydantic.dev/dev/concepts/experimental/#partial-validation)
326
333
  on each iteration.
327
334
 
328
335
  Args:
329
- debounce_by: by how much (if at all) to debounce/group the response chunks by. `None` means no debouncing.
330
- Debouncing is particularly important for long structured responses to reduce the overhead of
336
+ debounce_by: by how much (if at all) to debounce/group the output chunks by. `None` means no debouncing.
337
+ Debouncing is particularly important for long structured outputs to reduce the overhead of
331
338
  performing validation as each token is received.
332
339
 
333
340
  Returns:
@@ -354,8 +361,15 @@ class StreamedRunResult(Generic[AgentDepsT, OutputDataT]):
354
361
  yield text
355
362
  await self._marked_completed(self._stream_response.get())
356
363
 
364
+ @deprecated('`StreamedRunResult.stream_structured` is deprecated, use `stream_responses` instead.')
357
365
  async def stream_structured(
358
366
  self, *, debounce_by: float | None = 0.1
367
+ ) -> AsyncIterator[tuple[_messages.ModelResponse, bool]]:
368
+ async for msg, last in self.stream_responses(debounce_by=debounce_by):
369
+ yield msg, last
370
+
371
+ async def stream_responses(
372
+ self, *, debounce_by: float | None = 0.1
359
373
  ) -> AsyncIterator[tuple[_messages.ModelResponse, bool]]:
360
374
  """Stream the response as an async iterable of Structured LLM Messages.
361
375
 
@@ -394,13 +408,17 @@ class StreamedRunResult(Generic[AgentDepsT, OutputDataT]):
394
408
  """Get the timestamp of the response."""
395
409
  return self._stream_response.timestamp()
396
410
 
411
+ @deprecated('`validate_structured_output` is deprecated, use `validate_response_output` instead.')
397
412
  async def validate_structured_output(
398
413
  self, message: _messages.ModelResponse, *, allow_partial: bool = False
414
+ ) -> OutputDataT:
415
+ return await self._stream_response.validate_response_output(message, allow_partial=allow_partial)
416
+
417
+ async def validate_response_output(
418
+ self, message: _messages.ModelResponse, *, allow_partial: bool = False
399
419
  ) -> OutputDataT:
400
420
  """Validate a structured result message."""
401
- return await self._stream_response._validate_response( # pyright: ignore[reportPrivateUsage]
402
- message, allow_partial=allow_partial
403
- )
421
+ return await self._stream_response.validate_response_output(message, allow_partial=allow_partial)
404
422
 
405
423
  async def _marked_completed(self, message: _messages.ModelResponse) -> None:
406
424
  self.is_complete = True
@@ -426,7 +444,7 @@ def _get_usage_checking_stream_response(
426
444
  stream_response: models.StreamedResponse,
427
445
  limits: UsageLimits | None,
428
446
  get_usage: Callable[[], RunUsage],
429
- ) -> AsyncIterator[AgentStreamEvent]:
447
+ ) -> AsyncIterator[ModelResponseStreamEvent]:
430
448
  if limits is not None and limits.has_token_limits():
431
449
 
432
450
  async def _usage_checking_iterator():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pydantic-ai-slim
3
- Version: 0.7.6
3
+ Version: 0.8.1
4
4
  Summary: Agent Framework / shim to use Pydantic with LLMs, slim package
5
5
  Project-URL: Homepage, https://github.com/pydantic/pydantic-ai/tree/main/pydantic_ai_slim
6
6
  Project-URL: Source, https://github.com/pydantic/pydantic-ai/tree/main/pydantic_ai_slim
@@ -35,7 +35,7 @@ Requires-Dist: genai-prices>=0.0.22
35
35
  Requires-Dist: griffe>=1.3.2
36
36
  Requires-Dist: httpx>=0.27
37
37
  Requires-Dist: opentelemetry-api>=1.28.0
38
- Requires-Dist: pydantic-graph==0.7.6
38
+ Requires-Dist: pydantic-graph==0.8.1
39
39
  Requires-Dist: pydantic>=2.10
40
40
  Requires-Dist: typing-inspection>=0.4.0
41
41
  Provides-Extra: a2a
@@ -57,7 +57,7 @@ Requires-Dist: cohere>=5.16.0; (platform_system != 'Emscripten') and extra == 'c
57
57
  Provides-Extra: duckduckgo
58
58
  Requires-Dist: ddgs>=9.0.0; extra == 'duckduckgo'
59
59
  Provides-Extra: evals
60
- Requires-Dist: pydantic-evals==0.7.6; extra == 'evals'
60
+ Requires-Dist: pydantic-evals==0.8.1; extra == 'evals'
61
61
  Provides-Extra: google
62
62
  Requires-Dist: google-genai>=1.31.0; extra == 'google'
63
63
  Provides-Extra: groq
@@ -67,7 +67,7 @@ Requires-Dist: huggingface-hub[inference]>=0.33.5; extra == 'huggingface'
67
67
  Provides-Extra: logfire
68
68
  Requires-Dist: logfire>=3.14.1; extra == 'logfire'
69
69
  Provides-Extra: mcp
70
- Requires-Dist: mcp>=1.10.0; (python_version >= '3.10') and extra == 'mcp'
70
+ Requires-Dist: mcp>=1.12.3; (python_version >= '3.10') and extra == 'mcp'
71
71
  Provides-Extra: mistral
72
72
  Requires-Dist: mistralai>=1.9.2; extra == 'mistral'
73
73
  Provides-Extra: openai
@@ -77,7 +77,7 @@ Requires-Dist: tenacity>=8.2.3; extra == 'retries'
77
77
  Provides-Extra: tavily
78
78
  Requires-Dist: tavily-python>=0.5.0; extra == 'tavily'
79
79
  Provides-Extra: temporal
80
- Requires-Dist: temporalio==1.15.0; extra == 'temporal'
80
+ Requires-Dist: temporalio==1.16.0; extra == 'temporal'
81
81
  Provides-Extra: vertexai
82
82
  Requires-Dist: google-auth>=2.36.0; extra == 'vertexai'
83
83
  Requires-Dist: requests>=2.32.2; extra == 'vertexai'