pydantic-ai-slim 1.0.14__py3-none-any.whl → 1.0.16__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.

Files changed (40) hide show
  1. pydantic_ai/__init__.py +19 -1
  2. pydantic_ai/_agent_graph.py +129 -105
  3. pydantic_ai/_cli.py +7 -10
  4. pydantic_ai/_output.py +236 -192
  5. pydantic_ai/_parts_manager.py +8 -42
  6. pydantic_ai/_tool_manager.py +9 -16
  7. pydantic_ai/agent/__init__.py +18 -7
  8. pydantic_ai/agent/abstract.py +192 -23
  9. pydantic_ai/agent/wrapper.py +7 -4
  10. pydantic_ai/builtin_tools.py +82 -0
  11. pydantic_ai/direct.py +16 -9
  12. pydantic_ai/durable_exec/dbos/_agent.py +124 -18
  13. pydantic_ai/durable_exec/temporal/_agent.py +139 -19
  14. pydantic_ai/durable_exec/temporal/_model.py +8 -0
  15. pydantic_ai/format_prompt.py +9 -6
  16. pydantic_ai/mcp.py +20 -10
  17. pydantic_ai/messages.py +214 -44
  18. pydantic_ai/models/__init__.py +15 -1
  19. pydantic_ai/models/anthropic.py +27 -22
  20. pydantic_ai/models/cohere.py +4 -0
  21. pydantic_ai/models/function.py +7 -4
  22. pydantic_ai/models/gemini.py +8 -0
  23. pydantic_ai/models/google.py +56 -23
  24. pydantic_ai/models/groq.py +11 -5
  25. pydantic_ai/models/huggingface.py +5 -3
  26. pydantic_ai/models/mistral.py +6 -8
  27. pydantic_ai/models/openai.py +206 -58
  28. pydantic_ai/models/test.py +4 -0
  29. pydantic_ai/output.py +5 -2
  30. pydantic_ai/profiles/__init__.py +2 -0
  31. pydantic_ai/profiles/google.py +5 -2
  32. pydantic_ai/profiles/openai.py +2 -1
  33. pydantic_ai/result.py +51 -35
  34. pydantic_ai/run.py +35 -7
  35. pydantic_ai/usage.py +40 -5
  36. {pydantic_ai_slim-1.0.14.dist-info → pydantic_ai_slim-1.0.16.dist-info}/METADATA +4 -4
  37. {pydantic_ai_slim-1.0.14.dist-info → pydantic_ai_slim-1.0.16.dist-info}/RECORD +40 -40
  38. {pydantic_ai_slim-1.0.14.dist-info → pydantic_ai_slim-1.0.16.dist-info}/WHEEL +0 -0
  39. {pydantic_ai_slim-1.0.14.dist-info → pydantic_ai_slim-1.0.16.dist-info}/entry_points.txt +0 -0
  40. {pydantic_ai_slim-1.0.14.dist-info → pydantic_ai_slim-1.0.16.dist-info}/licenses/LICENSE +0 -0
@@ -20,7 +20,6 @@ from typing import Any
20
20
  from pydantic_ai.exceptions import UnexpectedModelBehavior
21
21
  from pydantic_ai.messages import (
22
22
  BuiltinToolCallPart,
23
- BuiltinToolReturnPart,
24
23
  ModelResponsePart,
25
24
  ModelResponseStreamEvent,
26
25
  PartDeltaEvent,
@@ -350,64 +349,31 @@ class ModelResponsePartsManager:
350
349
  self._vendor_id_to_part_index[vendor_part_id] = new_part_index
351
350
  return PartStartEvent(index=new_part_index, part=new_part)
352
351
 
353
- def handle_builtin_tool_call_part(
352
+ def handle_part(
354
353
  self,
355
354
  *,
356
355
  vendor_part_id: Hashable | None,
357
- part: BuiltinToolCallPart,
356
+ part: ModelResponsePart,
358
357
  ) -> ModelResponseStreamEvent:
359
- """Create or overwrite a BuiltinToolCallPart.
358
+ """Create or overwrite a ModelResponsePart.
360
359
 
361
360
  Args:
362
361
  vendor_part_id: The vendor's ID for this tool call part. If not
363
362
  None and an existing part is found, that part is overwritten.
364
- part: The BuiltinToolCallPart.
363
+ part: The ModelResponsePart.
365
364
 
366
365
  Returns:
367
- ModelResponseStreamEvent: A `PartStartEvent` indicating that a new tool call part
368
- has been added to the manager, or replaced an existing part.
369
- """
370
- if vendor_part_id is None:
371
- # vendor_part_id is None, so we unconditionally append a new BuiltinToolCallPart to the end of the list
372
- new_part_index = len(self._parts)
373
- self._parts.append(part)
374
- else:
375
- # vendor_part_id is provided, so find and overwrite or create a new BuiltinToolCallPart.
376
- maybe_part_index = self._vendor_id_to_part_index.get(vendor_part_id)
377
- if maybe_part_index is not None and isinstance(self._parts[maybe_part_index], BuiltinToolCallPart):
378
- new_part_index = maybe_part_index
379
- self._parts[new_part_index] = part
380
- else:
381
- new_part_index = len(self._parts)
382
- self._parts.append(part)
383
- self._vendor_id_to_part_index[vendor_part_id] = new_part_index
384
- return PartStartEvent(index=new_part_index, part=part)
385
-
386
- def handle_builtin_tool_return_part(
387
- self,
388
- *,
389
- vendor_part_id: Hashable | None,
390
- part: BuiltinToolReturnPart,
391
- ) -> ModelResponseStreamEvent:
392
- """Create or overwrite a BuiltinToolReturnPart.
393
-
394
- Args:
395
- vendor_part_id: The vendor's ID for this tool call part. If not
396
- None and an existing part is found, that part is overwritten.
397
- part: The BuiltinToolReturnPart.
398
-
399
- Returns:
400
- ModelResponseStreamEvent: A `PartStartEvent` indicating that a new tool call part
366
+ ModelResponseStreamEvent: A `PartStartEvent` indicating that a new part
401
367
  has been added to the manager, or replaced an existing part.
402
368
  """
403
369
  if vendor_part_id is None:
404
- # vendor_part_id is None, so we unconditionally append a new BuiltinToolReturnPart to the end of the list
370
+ # vendor_part_id is None, so we unconditionally append a new part to the end of the list
405
371
  new_part_index = len(self._parts)
406
372
  self._parts.append(part)
407
373
  else:
408
- # vendor_part_id is provided, so find and overwrite or create a new BuiltinToolReturnPart.
374
+ # vendor_part_id is provided, so find and overwrite or create a new part.
409
375
  maybe_part_index = self._vendor_id_to_part_index.get(vendor_part_id)
410
- if maybe_part_index is not None and isinstance(self._parts[maybe_part_index], BuiltinToolReturnPart):
376
+ if maybe_part_index is not None and isinstance(self._parts[maybe_part_index], type(part)):
411
377
  new_part_index = maybe_part_index
412
378
  self._parts[new_part_index] = part
413
379
  else:
@@ -18,7 +18,7 @@ from .exceptions import ModelRetry, ToolRetryError, UnexpectedModelBehavior
18
18
  from .messages import ToolCallPart
19
19
  from .tools import ToolDefinition
20
20
  from .toolsets.abstract import AbstractToolset, ToolsetTool
21
- from .usage import UsageLimits
21
+ from .usage import RunUsage
22
22
 
23
23
  _sequential_tool_calls_ctx_var: ContextVar[bool] = ContextVar('sequential_tool_calls', default=False)
24
24
 
@@ -93,7 +93,6 @@ class ToolManager(Generic[AgentDepsT]):
93
93
  call: ToolCallPart,
94
94
  allow_partial: bool = False,
95
95
  wrap_validation_errors: bool = True,
96
- usage_limits: UsageLimits | None = None,
97
96
  ) -> Any:
98
97
  """Handle a tool call by validating the arguments, calling the tool, and handling retries.
99
98
 
@@ -108,16 +107,16 @@ class ToolManager(Generic[AgentDepsT]):
108
107
 
109
108
  if (tool := self.tools.get(call.tool_name)) and tool.tool_def.kind == 'output':
110
109
  # Output tool calls are not traced and not counted
111
- return await self._call_tool(call, allow_partial, wrap_validation_errors, count_tool_usage=False)
110
+ return await self._call_tool(call, allow_partial, wrap_validation_errors)
112
111
  else:
113
- return await self._call_tool_traced(
112
+ return await self._call_function_tool(
114
113
  call,
115
114
  allow_partial,
116
115
  wrap_validation_errors,
117
116
  self.ctx.tracer,
118
117
  self.ctx.trace_include_content,
119
118
  self.ctx.instrumentation_version,
120
- usage_limits,
119
+ self.ctx.usage,
121
120
  )
122
121
 
123
122
  async def _call_tool(
@@ -125,8 +124,6 @@ class ToolManager(Generic[AgentDepsT]):
125
124
  call: ToolCallPart,
126
125
  allow_partial: bool,
127
126
  wrap_validation_errors: bool,
128
- usage_limits: UsageLimits | None = None,
129
- count_tool_usage: bool = True,
130
127
  ) -> Any:
131
128
  if self.tools is None or self.ctx is None:
132
129
  raise ValueError('ToolManager has not been prepared for a run step yet') # pragma: no cover
@@ -159,14 +156,8 @@ class ToolManager(Generic[AgentDepsT]):
159
156
  else:
160
157
  args_dict = validator.validate_python(call.args or {}, allow_partial=pyd_allow_partial)
161
158
 
162
- if usage_limits is not None and count_tool_usage:
163
- usage_limits.check_before_tool_call(self.ctx.usage)
164
-
165
159
  result = await self.toolset.call_tool(name, args_dict, ctx, tool)
166
160
 
167
- if count_tool_usage:
168
- self.ctx.usage.tool_calls += 1
169
-
170
161
  return result
171
162
  except (ValidationError, ModelRetry) as e:
172
163
  max_retries = tool.max_retries if tool is not None else 1
@@ -199,7 +190,7 @@ class ToolManager(Generic[AgentDepsT]):
199
190
 
200
191
  raise e
201
192
 
202
- async def _call_tool_traced(
193
+ async def _call_function_tool(
203
194
  self,
204
195
  call: ToolCallPart,
205
196
  allow_partial: bool,
@@ -207,7 +198,7 @@ class ToolManager(Generic[AgentDepsT]):
207
198
  tracer: Tracer,
208
199
  include_content: bool,
209
200
  instrumentation_version: int,
210
- usage_limits: UsageLimits | None = None,
201
+ usage: RunUsage,
211
202
  ) -> Any:
212
203
  """See <https://opentelemetry.io/docs/specs/semconv/gen-ai/gen-ai-spans/#execute-tool-span>."""
213
204
  instrumentation_names = InstrumentationNames.for_version(instrumentation_version)
@@ -242,7 +233,9 @@ class ToolManager(Generic[AgentDepsT]):
242
233
  attributes=span_attributes,
243
234
  ) as span:
244
235
  try:
245
- tool_result = await self._call_tool(call, allow_partial, wrap_validation_errors, usage_limits)
236
+ tool_result = await self._call_tool(call, allow_partial, wrap_validation_errors)
237
+ usage.tool_calls += 1
238
+
246
239
  except ToolRetryError as e:
247
240
  part = e.tool_retry
248
241
  if include_content and span.is_recording():
@@ -344,6 +344,7 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
344
344
 
345
345
  self._event_stream_handler = event_stream_handler
346
346
 
347
+ self._override_name: ContextVar[_utils.Option[str]] = ContextVar('_override_name', default=None)
347
348
  self._override_deps: ContextVar[_utils.Option[AgentDepsT]] = ContextVar('_override_deps', default=None)
348
349
  self._override_model: ContextVar[_utils.Option[models.Model]] = ContextVar('_override_model', default=None)
349
350
  self._override_toolsets: ContextVar[_utils.Option[Sequence[AbstractToolset[AgentDepsT]]]] = ContextVar(
@@ -384,7 +385,8 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
384
385
 
385
386
  If `None`, we try to infer the agent name from the call frame when the agent is first run.
386
387
  """
387
- return self._name
388
+ name_ = self._override_name.get()
389
+ return name_.value if name_ else self._name
388
390
 
389
391
  @name.setter
390
392
  def name(self, value: str | None) -> None:
@@ -415,7 +417,7 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
415
417
  user_prompt: str | Sequence[_messages.UserContent] | None = None,
416
418
  *,
417
419
  output_type: None = None,
418
- message_history: list[_messages.ModelMessage] | None = None,
420
+ message_history: Sequence[_messages.ModelMessage] | None = None,
419
421
  deferred_tool_results: DeferredToolResults | None = None,
420
422
  model: models.Model | models.KnownModelName | str | None = None,
421
423
  deps: AgentDepsT = None,
@@ -432,7 +434,7 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
432
434
  user_prompt: str | Sequence[_messages.UserContent] | None = None,
433
435
  *,
434
436
  output_type: OutputSpec[RunOutputDataT],
435
- message_history: list[_messages.ModelMessage] | None = None,
437
+ message_history: Sequence[_messages.ModelMessage] | None = None,
436
438
  deferred_tool_results: DeferredToolResults | None = None,
437
439
  model: models.Model | models.KnownModelName | str | None = None,
438
440
  deps: AgentDepsT = None,
@@ -449,7 +451,7 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
449
451
  user_prompt: str | Sequence[_messages.UserContent] | None = None,
450
452
  *,
451
453
  output_type: OutputSpec[RunOutputDataT] | None = None,
452
- message_history: list[_messages.ModelMessage] | None = None,
454
+ message_history: Sequence[_messages.ModelMessage] | None = None,
453
455
  deferred_tool_results: DeferredToolResults | None = None,
454
456
  model: models.Model | models.KnownModelName | str | None = None,
455
457
  deps: AgentDepsT = None,
@@ -566,7 +568,7 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
566
568
  # Build the initial state
567
569
  usage = usage or _usage.RunUsage()
568
570
  state = _agent_graph.GraphAgentState(
569
- message_history=message_history[:] if message_history else [],
571
+ message_history=list(message_history) if message_history else [],
570
572
  usage=usage,
571
573
  retries=0,
572
574
  run_step=0,
@@ -690,7 +692,7 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
690
692
  }
691
693
  else:
692
694
  attrs = {
693
- 'pydantic_ai.all_messages': json.dumps(settings.messages_to_otel_messages(state.message_history)),
695
+ 'pydantic_ai.all_messages': json.dumps(settings.messages_to_otel_messages(list(state.message_history))),
694
696
  **settings.system_instructions_attributes(literal_instructions),
695
697
  }
696
698
 
@@ -712,24 +714,31 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
712
714
  def override(
713
715
  self,
714
716
  *,
717
+ name: str | _utils.Unset = _utils.UNSET,
715
718
  deps: AgentDepsT | _utils.Unset = _utils.UNSET,
716
719
  model: models.Model | models.KnownModelName | str | _utils.Unset = _utils.UNSET,
717
720
  toolsets: Sequence[AbstractToolset[AgentDepsT]] | _utils.Unset = _utils.UNSET,
718
721
  tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] | _utils.Unset = _utils.UNSET,
719
722
  instructions: Instructions[AgentDepsT] | _utils.Unset = _utils.UNSET,
720
723
  ) -> Iterator[None]:
721
- """Context manager to temporarily override agent dependencies, model, toolsets, tools, or instructions.
724
+ """Context manager to temporarily override agent name, dependencies, model, toolsets, tools, or instructions.
722
725
 
723
726
  This is particularly useful when testing.
724
727
  You can find an example of this [here](../testing.md#overriding-model-via-pytest-fixtures).
725
728
 
726
729
  Args:
730
+ name: The name to use instead of the name passed to the agent constructor and agent run.
727
731
  deps: The dependencies to use instead of the dependencies passed to the agent run.
728
732
  model: The model to use instead of the model passed to the agent run.
729
733
  toolsets: The toolsets to use instead of the toolsets passed to the agent constructor and agent run.
730
734
  tools: The tools to use instead of the tools registered with the agent.
731
735
  instructions: The instructions to use instead of the instructions registered with the agent.
732
736
  """
737
+ if _utils.is_set(name):
738
+ name_token = self._override_name.set(_utils.Some(name))
739
+ else:
740
+ name_token = None
741
+
733
742
  if _utils.is_set(deps):
734
743
  deps_token = self._override_deps.set(_utils.Some(deps))
735
744
  else:
@@ -759,6 +768,8 @@ class Agent(AbstractAgent[AgentDepsT, OutputDataT]):
759
768
  try:
760
769
  yield
761
770
  finally:
771
+ if name_token is not None:
772
+ self._override_name.reset(name_token)
762
773
  if deps_token is not None:
763
774
  self._override_deps.reset(deps_token)
764
775
  if model_token is not None:
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations as _annotations
2
2
 
3
+ import asyncio
3
4
  import inspect
4
5
  from abc import ABC, abstractmethod
5
6
  from collections.abc import AsyncIterable, AsyncIterator, Awaitable, Callable, Iterator, Mapping, Sequence
@@ -7,6 +8,7 @@ from contextlib import AbstractAsyncContextManager, asynccontextmanager, context
7
8
  from types import FrameType
8
9
  from typing import TYPE_CHECKING, Any, Generic, TypeAlias, cast, overload
9
10
 
11
+ import anyio
10
12
  from typing_extensions import Self, TypeIs, TypeVar
11
13
 
12
14
  from pydantic_graph import End
@@ -25,7 +27,7 @@ from .. import (
25
27
  from .._tool_manager import ToolManager
26
28
  from ..output import OutputDataT, OutputSpec
27
29
  from ..result import AgentStream, FinalResult, StreamedRunResult
28
- from ..run import AgentRun, AgentRunResult
30
+ from ..run import AgentRun, AgentRunResult, AgentRunResultEvent
29
31
  from ..settings import ModelSettings
30
32
  from ..tools import (
31
33
  AgentDepsT,
@@ -126,7 +128,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
126
128
  user_prompt: str | Sequence[_messages.UserContent] | None = None,
127
129
  *,
128
130
  output_type: None = None,
129
- message_history: list[_messages.ModelMessage] | None = None,
131
+ message_history: Sequence[_messages.ModelMessage] | None = None,
130
132
  deferred_tool_results: DeferredToolResults | None = None,
131
133
  model: models.Model | models.KnownModelName | str | None = None,
132
134
  deps: AgentDepsT = None,
@@ -144,7 +146,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
144
146
  user_prompt: str | Sequence[_messages.UserContent] | None = None,
145
147
  *,
146
148
  output_type: OutputSpec[RunOutputDataT],
147
- message_history: list[_messages.ModelMessage] | None = None,
149
+ message_history: Sequence[_messages.ModelMessage] | None = None,
148
150
  deferred_tool_results: DeferredToolResults | None = None,
149
151
  model: models.Model | models.KnownModelName | str | None = None,
150
152
  deps: AgentDepsT = None,
@@ -161,7 +163,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
161
163
  user_prompt: str | Sequence[_messages.UserContent] | None = None,
162
164
  *,
163
165
  output_type: OutputSpec[RunOutputDataT] | None = None,
164
- message_history: list[_messages.ModelMessage] | None = None,
166
+ message_history: Sequence[_messages.ModelMessage] | None = None,
165
167
  deferred_tool_results: DeferredToolResults | None = None,
166
168
  model: models.Model | models.KnownModelName | str | None = None,
167
169
  deps: AgentDepsT = None,
@@ -240,7 +242,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
240
242
  user_prompt: str | Sequence[_messages.UserContent] | None = None,
241
243
  *,
242
244
  output_type: None = None,
243
- message_history: list[_messages.ModelMessage] | None = None,
245
+ message_history: Sequence[_messages.ModelMessage] | None = None,
244
246
  deferred_tool_results: DeferredToolResults | None = None,
245
247
  model: models.Model | models.KnownModelName | str | None = None,
246
248
  deps: AgentDepsT = None,
@@ -258,7 +260,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
258
260
  user_prompt: str | Sequence[_messages.UserContent] | None = None,
259
261
  *,
260
262
  output_type: OutputSpec[RunOutputDataT],
261
- message_history: list[_messages.ModelMessage] | None = None,
263
+ message_history: Sequence[_messages.ModelMessage] | None = None,
262
264
  deferred_tool_results: DeferredToolResults | None = None,
263
265
  model: models.Model | models.KnownModelName | str | None = None,
264
266
  deps: AgentDepsT = None,
@@ -275,7 +277,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
275
277
  user_prompt: str | Sequence[_messages.UserContent] | None = None,
276
278
  *,
277
279
  output_type: OutputSpec[RunOutputDataT] | None = None,
278
- message_history: list[_messages.ModelMessage] | None = None,
280
+ message_history: Sequence[_messages.ModelMessage] | None = None,
279
281
  deferred_tool_results: DeferredToolResults | None = None,
280
282
  model: models.Model | models.KnownModelName | str | None = None,
281
283
  deps: AgentDepsT = None,
@@ -346,7 +348,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
346
348
  user_prompt: str | Sequence[_messages.UserContent] | None = None,
347
349
  *,
348
350
  output_type: None = None,
349
- message_history: list[_messages.ModelMessage] | None = None,
351
+ message_history: Sequence[_messages.ModelMessage] | None = None,
350
352
  deferred_tool_results: DeferredToolResults | None = None,
351
353
  model: models.Model | models.KnownModelName | str | None = None,
352
354
  deps: AgentDepsT = None,
@@ -364,7 +366,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
364
366
  user_prompt: str | Sequence[_messages.UserContent] | None = None,
365
367
  *,
366
368
  output_type: OutputSpec[RunOutputDataT],
367
- message_history: list[_messages.ModelMessage] | None = None,
369
+ message_history: Sequence[_messages.ModelMessage] | None = None,
368
370
  deferred_tool_results: DeferredToolResults | None = None,
369
371
  model: models.Model | models.KnownModelName | str | None = None,
370
372
  deps: AgentDepsT = None,
@@ -382,7 +384,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
382
384
  user_prompt: str | Sequence[_messages.UserContent] | None = None,
383
385
  *,
384
386
  output_type: OutputSpec[RunOutputDataT] | None = None,
385
- message_history: list[_messages.ModelMessage] | None = None,
387
+ message_history: Sequence[_messages.ModelMessage] | None = None,
386
388
  deferred_tool_results: DeferredToolResults | None = None,
387
389
  model: models.Model | models.KnownModelName | str | None = None,
388
390
  deps: AgentDepsT = None,
@@ -487,7 +489,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
487
489
 
488
490
  if final_result_event is not None:
489
491
  final_result = FinalResult(
490
- stream, final_result_event.tool_name, final_result_event.tool_call_id
492
+ None, final_result_event.tool_name, final_result_event.tool_call_id
491
493
  )
492
494
  if yielded:
493
495
  raise exceptions.AgentRunError('Agent run produced final results') # pragma: no cover
@@ -501,16 +503,15 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
501
503
  The model response will have been added to messages by now
502
504
  by `StreamedRunResult._marked_completed`.
503
505
  """
504
- last_message = messages[-1]
505
- assert isinstance(last_message, _messages.ModelResponse)
506
- tool_calls = [
507
- part for part in last_message.parts if isinstance(part, _messages.ToolCallPart)
508
- ]
506
+ nonlocal final_result
507
+ final_result = FinalResult(
508
+ await stream.get_output(), final_result.tool_name, final_result.tool_call_id
509
+ )
509
510
 
510
511
  parts: list[_messages.ModelRequestPart] = []
511
512
  async for _event in _agent_graph.process_tool_calls(
512
513
  tool_manager=graph_ctx.deps.tool_manager,
513
- tool_calls=tool_calls,
514
+ tool_calls=stream.response.tool_calls,
514
515
  tool_call_results=None,
515
516
  final_result=final_result,
516
517
  ctx=graph_ctx,
@@ -552,13 +553,179 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
552
553
  if not yielded:
553
554
  raise exceptions.AgentRunError('Agent run finished without producing a final result') # pragma: no cover
554
555
 
556
+ @overload
557
+ def run_stream_events(
558
+ self,
559
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
560
+ *,
561
+ output_type: None = None,
562
+ message_history: Sequence[_messages.ModelMessage] | None = None,
563
+ deferred_tool_results: DeferredToolResults | None = None,
564
+ model: models.Model | models.KnownModelName | str | None = None,
565
+ deps: AgentDepsT = None,
566
+ model_settings: ModelSettings | None = None,
567
+ usage_limits: _usage.UsageLimits | None = None,
568
+ usage: _usage.RunUsage | None = None,
569
+ infer_name: bool = True,
570
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
571
+ ) -> AsyncIterator[_messages.AgentStreamEvent | AgentRunResultEvent[OutputDataT]]: ...
572
+
573
+ @overload
574
+ def run_stream_events(
575
+ self,
576
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
577
+ *,
578
+ output_type: OutputSpec[RunOutputDataT],
579
+ message_history: Sequence[_messages.ModelMessage] | None = None,
580
+ deferred_tool_results: DeferredToolResults | None = None,
581
+ model: models.Model | models.KnownModelName | str | None = None,
582
+ deps: AgentDepsT = None,
583
+ model_settings: ModelSettings | None = None,
584
+ usage_limits: _usage.UsageLimits | None = None,
585
+ usage: _usage.RunUsage | None = None,
586
+ infer_name: bool = True,
587
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
588
+ ) -> AsyncIterator[_messages.AgentStreamEvent | AgentRunResultEvent[RunOutputDataT]]: ...
589
+
590
+ def run_stream_events(
591
+ self,
592
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
593
+ *,
594
+ output_type: OutputSpec[RunOutputDataT] | None = None,
595
+ message_history: Sequence[_messages.ModelMessage] | None = None,
596
+ deferred_tool_results: DeferredToolResults | None = None,
597
+ model: models.Model | models.KnownModelName | str | None = None,
598
+ deps: AgentDepsT = None,
599
+ model_settings: ModelSettings | None = None,
600
+ usage_limits: _usage.UsageLimits | None = None,
601
+ usage: _usage.RunUsage | None = None,
602
+ infer_name: bool = True,
603
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
604
+ ) -> AsyncIterator[_messages.AgentStreamEvent | AgentRunResultEvent[Any]]:
605
+ """Run the agent with a user prompt in async mode and stream events from the run.
606
+
607
+ This is a convenience method that wraps [`self.run`][pydantic_ai.agent.AbstractAgent.run] and
608
+ uses the `event_stream_handler` kwarg to get a stream of events from the run.
609
+
610
+ Example:
611
+ ```python
612
+ from pydantic_ai import Agent, AgentRunResultEvent, AgentStreamEvent
613
+
614
+ agent = Agent('openai:gpt-4o')
615
+
616
+ async def main():
617
+ events: list[AgentStreamEvent | AgentRunResultEvent] = []
618
+ async for event in agent.run_stream_events('What is the capital of France?'):
619
+ events.append(event)
620
+ print(events)
621
+ '''
622
+ [
623
+ PartStartEvent(index=0, part=TextPart(content='The capital of ')),
624
+ FinalResultEvent(tool_name=None, tool_call_id=None),
625
+ PartDeltaEvent(index=0, delta=TextPartDelta(content_delta='France is Paris. ')),
626
+ AgentRunResultEvent(
627
+ result=AgentRunResult(output='The capital of France is Paris. ')
628
+ ),
629
+ ]
630
+ '''
631
+ ```
632
+
633
+ Arguments are the same as for [`self.run`][pydantic_ai.agent.AbstractAgent.run],
634
+ except that `event_stream_handler` is now allowed.
635
+
636
+ Args:
637
+ user_prompt: User input to start/continue the conversation.
638
+ output_type: Custom output type to use for this run, `output_type` may only be used if the agent has no
639
+ output validators since output validators would expect an argument that matches the agent's output type.
640
+ message_history: History of the conversation so far.
641
+ deferred_tool_results: Optional results for deferred tool calls in the message history.
642
+ model: Optional model to use for this run, required if `model` was not set when creating the agent.
643
+ deps: Optional dependencies to use for this run.
644
+ model_settings: Optional settings to use for this model's request.
645
+ usage_limits: Optional limits on model request count or token usage.
646
+ usage: Optional usage to start with, useful for resuming a conversation or agents used in tools.
647
+ infer_name: Whether to try to infer the agent name from the call frame if it's not set.
648
+ toolsets: Optional additional toolsets for this run.
649
+
650
+ Returns:
651
+ An async iterable of stream events `AgentStreamEvent` and finally a `AgentRunResultEvent` with the final
652
+ run result.
653
+ """
654
+ # unfortunately this hack of returning a generator rather than defining it right here is
655
+ # required to allow overloads of this method to work in python's typing system, or at least with pyright
656
+ # or at least I couldn't make it work without
657
+ return self._run_stream_events(
658
+ user_prompt,
659
+ output_type=output_type,
660
+ message_history=message_history,
661
+ deferred_tool_results=deferred_tool_results,
662
+ model=model,
663
+ deps=deps,
664
+ model_settings=model_settings,
665
+ usage_limits=usage_limits,
666
+ usage=usage,
667
+ infer_name=infer_name,
668
+ toolsets=toolsets,
669
+ )
670
+
671
+ async def _run_stream_events(
672
+ self,
673
+ user_prompt: str | Sequence[_messages.UserContent] | None = None,
674
+ *,
675
+ output_type: OutputSpec[RunOutputDataT] | None = None,
676
+ message_history: Sequence[_messages.ModelMessage] | None = None,
677
+ deferred_tool_results: DeferredToolResults | None = None,
678
+ model: models.Model | models.KnownModelName | str | None = None,
679
+ deps: AgentDepsT = None,
680
+ model_settings: ModelSettings | None = None,
681
+ usage_limits: _usage.UsageLimits | None = None,
682
+ usage: _usage.RunUsage | None = None,
683
+ infer_name: bool = True,
684
+ toolsets: Sequence[AbstractToolset[AgentDepsT]] | None = None,
685
+ ) -> AsyncIterator[_messages.AgentStreamEvent | AgentRunResultEvent[Any]]:
686
+ send_stream, receive_stream = anyio.create_memory_object_stream[
687
+ _messages.AgentStreamEvent | AgentRunResultEvent[Any]
688
+ ]()
689
+
690
+ async def event_stream_handler(
691
+ _: RunContext[AgentDepsT], events: AsyncIterable[_messages.AgentStreamEvent]
692
+ ) -> None:
693
+ async for event in events:
694
+ await send_stream.send(event)
695
+
696
+ async def run_agent() -> AgentRunResult[Any]:
697
+ async with send_stream:
698
+ return await self.run(
699
+ user_prompt,
700
+ output_type=output_type,
701
+ message_history=message_history,
702
+ deferred_tool_results=deferred_tool_results,
703
+ model=model,
704
+ deps=deps,
705
+ model_settings=model_settings,
706
+ usage_limits=usage_limits,
707
+ usage=usage,
708
+ infer_name=infer_name,
709
+ toolsets=toolsets,
710
+ event_stream_handler=event_stream_handler,
711
+ )
712
+
713
+ task = asyncio.create_task(run_agent())
714
+
715
+ async with receive_stream:
716
+ async for message in receive_stream:
717
+ yield message
718
+
719
+ result = await task
720
+ yield AgentRunResultEvent(result)
721
+
555
722
  @overload
556
723
  def iter(
557
724
  self,
558
725
  user_prompt: str | Sequence[_messages.UserContent] | None = None,
559
726
  *,
560
727
  output_type: None = None,
561
- message_history: list[_messages.ModelMessage] | None = None,
728
+ message_history: Sequence[_messages.ModelMessage] | None = None,
562
729
  deferred_tool_results: DeferredToolResults | None = None,
563
730
  model: models.Model | models.KnownModelName | str | None = None,
564
731
  deps: AgentDepsT = None,
@@ -575,7 +742,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
575
742
  user_prompt: str | Sequence[_messages.UserContent] | None = None,
576
743
  *,
577
744
  output_type: OutputSpec[RunOutputDataT],
578
- message_history: list[_messages.ModelMessage] | None = None,
745
+ message_history: Sequence[_messages.ModelMessage] | None = None,
579
746
  deferred_tool_results: DeferredToolResults | None = None,
580
747
  model: models.Model | models.KnownModelName | str | None = None,
581
748
  deps: AgentDepsT = None,
@@ -593,7 +760,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
593
760
  user_prompt: str | Sequence[_messages.UserContent] | None = None,
594
761
  *,
595
762
  output_type: OutputSpec[RunOutputDataT] | None = None,
596
- message_history: list[_messages.ModelMessage] | None = None,
763
+ message_history: Sequence[_messages.ModelMessage] | None = None,
597
764
  deferred_tool_results: DeferredToolResults | None = None,
598
765
  model: models.Model | models.KnownModelName | str | None = None,
599
766
  deps: AgentDepsT = None,
@@ -686,18 +853,20 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
686
853
  def override(
687
854
  self,
688
855
  *,
856
+ name: str | _utils.Unset = _utils.UNSET,
689
857
  deps: AgentDepsT | _utils.Unset = _utils.UNSET,
690
858
  model: models.Model | models.KnownModelName | str | _utils.Unset = _utils.UNSET,
691
859
  toolsets: Sequence[AbstractToolset[AgentDepsT]] | _utils.Unset = _utils.UNSET,
692
860
  tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] | _utils.Unset = _utils.UNSET,
693
861
  instructions: Instructions[AgentDepsT] | _utils.Unset = _utils.UNSET,
694
862
  ) -> Iterator[None]:
695
- """Context manager to temporarily override agent dependencies, model, toolsets, tools, or instructions.
863
+ """Context manager to temporarily override agent name, dependencies, model, toolsets, tools, or instructions.
696
864
 
697
865
  This is particularly useful when testing.
698
866
  You can find an example of this [here](../testing.md#overriding-model-via-pytest-fixtures).
699
867
 
700
868
  Args:
869
+ name: The name to use instead of the name passed to the agent constructor and agent run.
701
870
  deps: The dependencies to use instead of the dependencies passed to the agent run.
702
871
  model: The model to use instead of the model passed to the agent run.
703
872
  toolsets: The toolsets to use instead of the toolsets passed to the agent constructor and agent run.
@@ -944,7 +1113,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
944
1113
  self: Self,
945
1114
  deps: AgentDepsT = None,
946
1115
  prog_name: str = 'pydantic-ai',
947
- message_history: list[_messages.ModelMessage] | None = None,
1116
+ message_history: Sequence[_messages.ModelMessage] | None = None,
948
1117
  ) -> None:
949
1118
  """Run the agent in a CLI chat interface.
950
1119
 
@@ -981,7 +1150,7 @@ class AbstractAgent(Generic[AgentDepsT, OutputDataT], ABC):
981
1150
  self: Self,
982
1151
  deps: AgentDepsT = None,
983
1152
  prog_name: str = 'pydantic-ai',
984
- message_history: list[_messages.ModelMessage] | None = None,
1153
+ message_history: Sequence[_messages.ModelMessage] | None = None,
985
1154
  ) -> None:
986
1155
  """Run the agent in a CLI chat interface with the non-async interface.
987
1156