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.
- pydantic_ai/__init__.py +19 -1
- pydantic_ai/_agent_graph.py +129 -105
- pydantic_ai/_cli.py +7 -10
- pydantic_ai/_output.py +236 -192
- pydantic_ai/_parts_manager.py +8 -42
- pydantic_ai/_tool_manager.py +9 -16
- pydantic_ai/agent/__init__.py +18 -7
- pydantic_ai/agent/abstract.py +192 -23
- pydantic_ai/agent/wrapper.py +7 -4
- pydantic_ai/builtin_tools.py +82 -0
- pydantic_ai/direct.py +16 -9
- pydantic_ai/durable_exec/dbos/_agent.py +124 -18
- pydantic_ai/durable_exec/temporal/_agent.py +139 -19
- pydantic_ai/durable_exec/temporal/_model.py +8 -0
- pydantic_ai/format_prompt.py +9 -6
- pydantic_ai/mcp.py +20 -10
- pydantic_ai/messages.py +214 -44
- pydantic_ai/models/__init__.py +15 -1
- pydantic_ai/models/anthropic.py +27 -22
- pydantic_ai/models/cohere.py +4 -0
- pydantic_ai/models/function.py +7 -4
- pydantic_ai/models/gemini.py +8 -0
- pydantic_ai/models/google.py +56 -23
- pydantic_ai/models/groq.py +11 -5
- pydantic_ai/models/huggingface.py +5 -3
- pydantic_ai/models/mistral.py +6 -8
- pydantic_ai/models/openai.py +206 -58
- pydantic_ai/models/test.py +4 -0
- pydantic_ai/output.py +5 -2
- pydantic_ai/profiles/__init__.py +2 -0
- pydantic_ai/profiles/google.py +5 -2
- pydantic_ai/profiles/openai.py +2 -1
- pydantic_ai/result.py +51 -35
- pydantic_ai/run.py +35 -7
- pydantic_ai/usage.py +40 -5
- {pydantic_ai_slim-1.0.14.dist-info → pydantic_ai_slim-1.0.16.dist-info}/METADATA +4 -4
- {pydantic_ai_slim-1.0.14.dist-info → pydantic_ai_slim-1.0.16.dist-info}/RECORD +40 -40
- {pydantic_ai_slim-1.0.14.dist-info → pydantic_ai_slim-1.0.16.dist-info}/WHEEL +0 -0
- {pydantic_ai_slim-1.0.14.dist-info → pydantic_ai_slim-1.0.16.dist-info}/entry_points.txt +0 -0
- {pydantic_ai_slim-1.0.14.dist-info → pydantic_ai_slim-1.0.16.dist-info}/licenses/LICENSE +0 -0
pydantic_ai/_parts_manager.py
CHANGED
|
@@ -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
|
|
352
|
+
def handle_part(
|
|
354
353
|
self,
|
|
355
354
|
*,
|
|
356
355
|
vendor_part_id: Hashable | None,
|
|
357
|
-
part:
|
|
356
|
+
part: ModelResponsePart,
|
|
358
357
|
) -> ModelResponseStreamEvent:
|
|
359
|
-
"""Create or overwrite a
|
|
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
|
|
363
|
+
part: The ModelResponsePart.
|
|
365
364
|
|
|
366
365
|
Returns:
|
|
367
|
-
ModelResponseStreamEvent: A `PartStartEvent` indicating that a new
|
|
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
|
|
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
|
|
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],
|
|
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:
|
pydantic_ai/_tool_manager.py
CHANGED
|
@@ -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
|
|
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
|
|
110
|
+
return await self._call_tool(call, allow_partial, wrap_validation_errors)
|
|
112
111
|
else:
|
|
113
|
-
return await self.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
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():
|
pydantic_ai/agent/__init__.py
CHANGED
|
@@ -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
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
pydantic_ai/agent/abstract.py
CHANGED
|
@@ -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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|