pydantic-ai-slim 0.4.2__tar.gz → 0.4.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pydantic-ai-slim might be problematic. Click here for more details.
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/PKG-INFO +6 -4
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/_agent_graph.py +5 -2
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/_output.py +120 -14
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/agent.py +1 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/common_tools/duckduckgo.py +5 -2
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/exceptions.py +2 -2
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/messages.py +6 -4
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/models/__init__.py +13 -1
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/models/anthropic.py +1 -1
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/models/bedrock.py +1 -1
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/models/cohere.py +1 -1
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/models/gemini.py +2 -2
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/models/google.py +1 -1
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/models/groq.py +1 -1
- pydantic_ai_slim-0.4.3/pydantic_ai/models/huggingface.py +463 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/models/instrumented.py +1 -1
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/models/mistral.py +2 -2
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/models/openai.py +2 -2
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/providers/__init__.py +4 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/providers/google.py +2 -2
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/providers/google_vertex.py +10 -5
- pydantic_ai_slim-0.4.3/pydantic_ai/providers/huggingface.py +88 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/result.py +16 -5
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pyproject.toml +3 -1
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/.gitignore +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/LICENSE +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/README.md +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/__init__.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/__main__.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/_a2a.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/_cli.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/_function_schema.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/_griffe.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/_mcp.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/_parts_manager.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/_run_context.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/_system_prompt.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/_thinking_part.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/_utils.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/common_tools/__init__.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/common_tools/tavily.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/direct.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/ext/__init__.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/ext/aci.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/ext/langchain.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/format_as_xml.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/format_prompt.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/mcp.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/models/fallback.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/models/function.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/models/mcp_sampling.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/models/test.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/models/wrapper.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/output.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/profiles/__init__.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/profiles/_json_schema.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/profiles/amazon.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/profiles/anthropic.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/profiles/cohere.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/profiles/deepseek.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/profiles/google.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/profiles/grok.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/profiles/meta.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/profiles/mistral.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/profiles/openai.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/profiles/qwen.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/providers/anthropic.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/providers/azure.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/providers/bedrock.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/providers/cohere.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/providers/deepseek.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/providers/fireworks.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/providers/github.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/providers/google_gla.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/providers/grok.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/providers/groq.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/providers/heroku.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/providers/mistral.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/providers/openai.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/providers/openrouter.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/providers/together.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/py.typed +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/settings.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/tools.py +0 -0
- {pydantic_ai_slim-0.4.2 → pydantic_ai_slim-0.4.3}/pydantic_ai/usage.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pydantic-ai-slim
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: Agent Framework / shim to use Pydantic with LLMs, slim package
|
|
5
5
|
Author-email: Samuel Colvin <samuel@pydantic.dev>, Marcelo Trylesinski <marcelotryle@gmail.com>, David Montague <david@pydantic.dev>, Alex Hall <alex@pydantic.dev>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -30,7 +30,7 @@ Requires-Dist: exceptiongroup; python_version < '3.11'
|
|
|
30
30
|
Requires-Dist: griffe>=1.3.2
|
|
31
31
|
Requires-Dist: httpx>=0.27
|
|
32
32
|
Requires-Dist: opentelemetry-api>=1.28.0
|
|
33
|
-
Requires-Dist: pydantic-graph==0.4.
|
|
33
|
+
Requires-Dist: pydantic-graph==0.4.3
|
|
34
34
|
Requires-Dist: pydantic>=2.10
|
|
35
35
|
Requires-Dist: typing-inspection>=0.4.0
|
|
36
36
|
Provides-Extra: a2a
|
|
@@ -46,13 +46,15 @@ Requires-Dist: rich>=13; extra == 'cli'
|
|
|
46
46
|
Provides-Extra: cohere
|
|
47
47
|
Requires-Dist: cohere>=5.13.11; (platform_system != 'Emscripten') and extra == 'cohere'
|
|
48
48
|
Provides-Extra: duckduckgo
|
|
49
|
-
Requires-Dist:
|
|
49
|
+
Requires-Dist: ddgs>=9.0.0; extra == 'duckduckgo'
|
|
50
50
|
Provides-Extra: evals
|
|
51
|
-
Requires-Dist: pydantic-evals==0.4.
|
|
51
|
+
Requires-Dist: pydantic-evals==0.4.3; extra == 'evals'
|
|
52
52
|
Provides-Extra: google
|
|
53
53
|
Requires-Dist: google-genai>=1.24.0; extra == 'google'
|
|
54
54
|
Provides-Extra: groq
|
|
55
55
|
Requires-Dist: groq>=0.19.0; extra == 'groq'
|
|
56
|
+
Provides-Extra: huggingface
|
|
57
|
+
Requires-Dist: huggingface-hub[inference]>=0.33.2; extra == 'huggingface'
|
|
56
58
|
Provides-Extra: logfire
|
|
57
59
|
Requires-Dist: logfire>=3.11.0; extra == 'logfire'
|
|
58
60
|
Provides-Extra: mcp
|
|
@@ -341,6 +341,7 @@ class ModelRequestNode(AgentNode[DepsT, NodeRunEndT]):
|
|
|
341
341
|
ctx.deps.output_schema,
|
|
342
342
|
ctx.deps.output_validators,
|
|
343
343
|
build_run_context(ctx),
|
|
344
|
+
_output.build_trace_context(ctx),
|
|
344
345
|
ctx.deps.usage_limits,
|
|
345
346
|
)
|
|
346
347
|
yield agent_stream
|
|
@@ -529,7 +530,8 @@ class CallToolsNode(AgentNode[DepsT, NodeRunEndT]):
|
|
|
529
530
|
if isinstance(output_schema, _output.ToolOutputSchema):
|
|
530
531
|
for call, output_tool in output_schema.find_tool(tool_calls):
|
|
531
532
|
try:
|
|
532
|
-
|
|
533
|
+
trace_context = _output.build_trace_context(ctx)
|
|
534
|
+
result_data = await output_tool.process(call, run_context, trace_context)
|
|
533
535
|
result_data = await _validate_output(result_data, ctx, call)
|
|
534
536
|
except _output.ToolRetryError as e:
|
|
535
537
|
# TODO: Should only increment retry stuff once per node execution, not for each tool call
|
|
@@ -586,7 +588,8 @@ class CallToolsNode(AgentNode[DepsT, NodeRunEndT]):
|
|
|
586
588
|
try:
|
|
587
589
|
if isinstance(output_schema, _output.TextOutputSchema):
|
|
588
590
|
run_context = build_run_context(ctx)
|
|
589
|
-
|
|
591
|
+
trace_context = _output.build_trace_context(ctx)
|
|
592
|
+
result_data = await output_schema.process(text, run_context, trace_context)
|
|
590
593
|
else:
|
|
591
594
|
m = _messages.RetryPromptPart(
|
|
592
595
|
content='Plain text responses are not permitted, please include your response in a tool call',
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations as _annotations
|
|
2
2
|
|
|
3
|
+
import dataclasses
|
|
3
4
|
import inspect
|
|
4
5
|
import json
|
|
5
6
|
from abc import ABC, abstractmethod
|
|
@@ -7,10 +8,13 @@ from collections.abc import Awaitable, Iterable, Iterator, Sequence
|
|
|
7
8
|
from dataclasses import dataclass, field
|
|
8
9
|
from typing import TYPE_CHECKING, Any, Callable, Generic, Literal, Union, cast, overload
|
|
9
10
|
|
|
11
|
+
from opentelemetry.trace import Tracer
|
|
10
12
|
from pydantic import TypeAdapter, ValidationError
|
|
11
13
|
from pydantic_core import SchemaValidator
|
|
12
14
|
from typing_extensions import TypedDict, TypeVar, assert_never
|
|
13
15
|
|
|
16
|
+
from pydantic_graph.nodes import GraphRunContext
|
|
17
|
+
|
|
14
18
|
from . import _function_schema, _utils, messages as _messages
|
|
15
19
|
from ._run_context import AgentDepsT, RunContext
|
|
16
20
|
from .exceptions import ModelRetry, UserError
|
|
@@ -29,6 +33,8 @@ from .output import (
|
|
|
29
33
|
from .tools import GenerateToolJsonSchema, ObjectJsonSchema, ToolDefinition
|
|
30
34
|
|
|
31
35
|
if TYPE_CHECKING:
|
|
36
|
+
from pydantic_ai._agent_graph import DepsT, GraphAgentDeps, GraphAgentState
|
|
37
|
+
|
|
32
38
|
from .profiles import ModelProfile
|
|
33
39
|
|
|
34
40
|
T = TypeVar('T')
|
|
@@ -66,6 +72,71 @@ DEFAULT_OUTPUT_TOOL_NAME = 'final_result'
|
|
|
66
72
|
DEFAULT_OUTPUT_TOOL_DESCRIPTION = 'The final response which ends this conversation'
|
|
67
73
|
|
|
68
74
|
|
|
75
|
+
@dataclass(frozen=True)
|
|
76
|
+
class TraceContext:
|
|
77
|
+
"""A context for tracing output processing."""
|
|
78
|
+
|
|
79
|
+
tracer: Tracer
|
|
80
|
+
include_content: bool
|
|
81
|
+
call: _messages.ToolCallPart | None = None
|
|
82
|
+
|
|
83
|
+
def with_call(self, call: _messages.ToolCallPart):
|
|
84
|
+
return dataclasses.replace(self, call=call)
|
|
85
|
+
|
|
86
|
+
async def execute_function_with_span(
|
|
87
|
+
self,
|
|
88
|
+
function_schema: _function_schema.FunctionSchema,
|
|
89
|
+
run_context: RunContext[AgentDepsT],
|
|
90
|
+
args: dict[str, Any] | Any,
|
|
91
|
+
call: _messages.ToolCallPart,
|
|
92
|
+
include_tool_call_id: bool = True,
|
|
93
|
+
) -> Any:
|
|
94
|
+
"""Execute a function call within a traced span, automatically recording the response."""
|
|
95
|
+
# Set up span attributes
|
|
96
|
+
attributes = {
|
|
97
|
+
'gen_ai.tool.name': call.tool_name,
|
|
98
|
+
'logfire.msg': f'running output function: {call.tool_name}',
|
|
99
|
+
}
|
|
100
|
+
if include_tool_call_id:
|
|
101
|
+
attributes['gen_ai.tool.call.id'] = call.tool_call_id
|
|
102
|
+
if self.include_content:
|
|
103
|
+
attributes['tool_arguments'] = call.args_as_json_str()
|
|
104
|
+
attributes['logfire.json_schema'] = json.dumps(
|
|
105
|
+
{
|
|
106
|
+
'type': 'object',
|
|
107
|
+
'properties': {
|
|
108
|
+
'tool_arguments': {'type': 'object'},
|
|
109
|
+
'tool_response': {'type': 'object'},
|
|
110
|
+
},
|
|
111
|
+
}
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Execute function within span
|
|
115
|
+
with self.tracer.start_as_current_span('running output function', attributes=attributes) as span:
|
|
116
|
+
output = await function_schema.call(args, run_context)
|
|
117
|
+
|
|
118
|
+
# Record response if content inclusion is enabled
|
|
119
|
+
if self.include_content and span.is_recording():
|
|
120
|
+
from .models.instrumented import InstrumentedModel
|
|
121
|
+
|
|
122
|
+
span.set_attribute(
|
|
123
|
+
'tool_response',
|
|
124
|
+
output if isinstance(output, str) else json.dumps(InstrumentedModel.serialize_any(output)),
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
return output
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def build_trace_context(ctx: GraphRunContext[GraphAgentState, GraphAgentDeps[DepsT, Any]]) -> TraceContext:
|
|
131
|
+
"""Build a `TraceContext` from the current agent graph run context."""
|
|
132
|
+
return TraceContext(
|
|
133
|
+
tracer=ctx.deps.tracer,
|
|
134
|
+
include_content=(
|
|
135
|
+
ctx.deps.instrumentation_settings is not None and ctx.deps.instrumentation_settings.include_content
|
|
136
|
+
),
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
|
|
69
140
|
class ToolRetryError(Exception):
|
|
70
141
|
"""Exception used to signal a `ToolRetry` message should be returned to the LLM."""
|
|
71
142
|
|
|
@@ -96,6 +167,7 @@ class OutputValidator(Generic[AgentDepsT, OutputDataT_inv]):
|
|
|
96
167
|
result: The result data after Pydantic validation the message content.
|
|
97
168
|
tool_call: The original tool call message, `None` if there was no tool call.
|
|
98
169
|
run_context: The current run context.
|
|
170
|
+
trace_context: The trace context to use for tracing the output processing.
|
|
99
171
|
|
|
100
172
|
Returns:
|
|
101
173
|
Result of either the validated result data (ok) or a retry message (Err).
|
|
@@ -349,6 +421,7 @@ class TextOutputSchema(OutputSchema[OutputDataT], ABC):
|
|
|
349
421
|
self,
|
|
350
422
|
text: str,
|
|
351
423
|
run_context: RunContext[AgentDepsT],
|
|
424
|
+
trace_context: TraceContext,
|
|
352
425
|
allow_partial: bool = False,
|
|
353
426
|
wrap_validation_errors: bool = True,
|
|
354
427
|
) -> OutputDataT:
|
|
@@ -371,6 +444,7 @@ class PlainTextOutputSchema(TextOutputSchema[OutputDataT]):
|
|
|
371
444
|
self,
|
|
372
445
|
text: str,
|
|
373
446
|
run_context: RunContext[AgentDepsT],
|
|
447
|
+
trace_context: TraceContext,
|
|
374
448
|
allow_partial: bool = False,
|
|
375
449
|
wrap_validation_errors: bool = True,
|
|
376
450
|
) -> OutputDataT:
|
|
@@ -379,6 +453,7 @@ class PlainTextOutputSchema(TextOutputSchema[OutputDataT]):
|
|
|
379
453
|
Args:
|
|
380
454
|
text: The output text to validate.
|
|
381
455
|
run_context: The current run context.
|
|
456
|
+
trace_context: The trace context to use for tracing the output processing.
|
|
382
457
|
allow_partial: If true, allow partial validation.
|
|
383
458
|
wrap_validation_errors: If true, wrap the validation errors in a retry message.
|
|
384
459
|
|
|
@@ -389,7 +464,7 @@ class PlainTextOutputSchema(TextOutputSchema[OutputDataT]):
|
|
|
389
464
|
return cast(OutputDataT, text)
|
|
390
465
|
|
|
391
466
|
return await self.processor.process(
|
|
392
|
-
text, run_context, allow_partial=allow_partial, wrap_validation_errors=wrap_validation_errors
|
|
467
|
+
text, run_context, trace_context, allow_partial=allow_partial, wrap_validation_errors=wrap_validation_errors
|
|
393
468
|
)
|
|
394
469
|
|
|
395
470
|
|
|
@@ -417,6 +492,7 @@ class NativeOutputSchema(StructuredTextOutputSchema[OutputDataT]):
|
|
|
417
492
|
self,
|
|
418
493
|
text: str,
|
|
419
494
|
run_context: RunContext[AgentDepsT],
|
|
495
|
+
trace_context: TraceContext,
|
|
420
496
|
allow_partial: bool = False,
|
|
421
497
|
wrap_validation_errors: bool = True,
|
|
422
498
|
) -> OutputDataT:
|
|
@@ -425,6 +501,7 @@ class NativeOutputSchema(StructuredTextOutputSchema[OutputDataT]):
|
|
|
425
501
|
Args:
|
|
426
502
|
text: The output text to validate.
|
|
427
503
|
run_context: The current run context.
|
|
504
|
+
trace_context: The trace context to use for tracing the output processing.
|
|
428
505
|
allow_partial: If true, allow partial validation.
|
|
429
506
|
wrap_validation_errors: If true, wrap the validation errors in a retry message.
|
|
430
507
|
|
|
@@ -432,7 +509,7 @@ class NativeOutputSchema(StructuredTextOutputSchema[OutputDataT]):
|
|
|
432
509
|
Either the validated output data (left) or a retry message (right).
|
|
433
510
|
"""
|
|
434
511
|
return await self.processor.process(
|
|
435
|
-
text, run_context, allow_partial=allow_partial, wrap_validation_errors=wrap_validation_errors
|
|
512
|
+
text, run_context, trace_context, allow_partial=allow_partial, wrap_validation_errors=wrap_validation_errors
|
|
436
513
|
)
|
|
437
514
|
|
|
438
515
|
|
|
@@ -468,6 +545,7 @@ class PromptedOutputSchema(StructuredTextOutputSchema[OutputDataT]):
|
|
|
468
545
|
self,
|
|
469
546
|
text: str,
|
|
470
547
|
run_context: RunContext[AgentDepsT],
|
|
548
|
+
trace_context: TraceContext,
|
|
471
549
|
allow_partial: bool = False,
|
|
472
550
|
wrap_validation_errors: bool = True,
|
|
473
551
|
) -> OutputDataT:
|
|
@@ -476,6 +554,7 @@ class PromptedOutputSchema(StructuredTextOutputSchema[OutputDataT]):
|
|
|
476
554
|
Args:
|
|
477
555
|
text: The output text to validate.
|
|
478
556
|
run_context: The current run context.
|
|
557
|
+
trace_context: The trace context to use for tracing the output processing.
|
|
479
558
|
allow_partial: If true, allow partial validation.
|
|
480
559
|
wrap_validation_errors: If true, wrap the validation errors in a retry message.
|
|
481
560
|
|
|
@@ -485,7 +564,7 @@ class PromptedOutputSchema(StructuredTextOutputSchema[OutputDataT]):
|
|
|
485
564
|
text = _utils.strip_markdown_fences(text)
|
|
486
565
|
|
|
487
566
|
return await self.processor.process(
|
|
488
|
-
text, run_context, allow_partial=allow_partial, wrap_validation_errors=wrap_validation_errors
|
|
567
|
+
text, run_context, trace_context, allow_partial=allow_partial, wrap_validation_errors=wrap_validation_errors
|
|
489
568
|
)
|
|
490
569
|
|
|
491
570
|
|
|
@@ -568,6 +647,7 @@ class BaseOutputProcessor(ABC, Generic[OutputDataT]):
|
|
|
568
647
|
self,
|
|
569
648
|
data: str,
|
|
570
649
|
run_context: RunContext[AgentDepsT],
|
|
650
|
+
trace_context: TraceContext,
|
|
571
651
|
allow_partial: bool = False,
|
|
572
652
|
wrap_validation_errors: bool = True,
|
|
573
653
|
) -> OutputDataT:
|
|
@@ -637,6 +717,7 @@ class ObjectOutputProcessor(BaseOutputProcessor[OutputDataT]):
|
|
|
637
717
|
self,
|
|
638
718
|
data: str | dict[str, Any] | None,
|
|
639
719
|
run_context: RunContext[AgentDepsT],
|
|
720
|
+
trace_context: TraceContext,
|
|
640
721
|
allow_partial: bool = False,
|
|
641
722
|
wrap_validation_errors: bool = True,
|
|
642
723
|
) -> OutputDataT:
|
|
@@ -645,6 +726,7 @@ class ObjectOutputProcessor(BaseOutputProcessor[OutputDataT]):
|
|
|
645
726
|
Args:
|
|
646
727
|
data: The output data to validate.
|
|
647
728
|
run_context: The current run context.
|
|
729
|
+
trace_context: The trace context to use for tracing the output processing.
|
|
648
730
|
allow_partial: If true, allow partial validation.
|
|
649
731
|
wrap_validation_errors: If true, wrap the validation errors in a retry message.
|
|
650
732
|
|
|
@@ -664,14 +746,24 @@ class ObjectOutputProcessor(BaseOutputProcessor[OutputDataT]):
|
|
|
664
746
|
)
|
|
665
747
|
raise ToolRetryError(m) from e
|
|
666
748
|
else:
|
|
667
|
-
raise
|
|
749
|
+
raise
|
|
668
750
|
|
|
669
751
|
if k := self.outer_typed_dict_key:
|
|
670
752
|
output = output[k]
|
|
671
753
|
|
|
672
754
|
if self._function_schema:
|
|
755
|
+
# Wraps the output function call in an OpenTelemetry span.
|
|
756
|
+
if trace_context.call:
|
|
757
|
+
call = trace_context.call
|
|
758
|
+
include_tool_call_id = True
|
|
759
|
+
else:
|
|
760
|
+
function_name = getattr(self._function_schema.function, '__name__', 'output_function')
|
|
761
|
+
call = _messages.ToolCallPart(tool_name=function_name, args=data)
|
|
762
|
+
include_tool_call_id = False
|
|
673
763
|
try:
|
|
674
|
-
output = await
|
|
764
|
+
output = await trace_context.execute_function_with_span(
|
|
765
|
+
self._function_schema, run_context, output, call, include_tool_call_id
|
|
766
|
+
)
|
|
675
767
|
except ModelRetry as r:
|
|
676
768
|
if wrap_validation_errors:
|
|
677
769
|
m = _messages.RetryPromptPart(
|
|
@@ -679,7 +771,7 @@ class ObjectOutputProcessor(BaseOutputProcessor[OutputDataT]):
|
|
|
679
771
|
)
|
|
680
772
|
raise ToolRetryError(m) from r
|
|
681
773
|
else:
|
|
682
|
-
raise
|
|
774
|
+
raise
|
|
683
775
|
|
|
684
776
|
return output
|
|
685
777
|
|
|
@@ -784,11 +876,12 @@ class UnionOutputProcessor(BaseOutputProcessor[OutputDataT]):
|
|
|
784
876
|
self,
|
|
785
877
|
data: str | dict[str, Any] | None,
|
|
786
878
|
run_context: RunContext[AgentDepsT],
|
|
879
|
+
trace_context: TraceContext,
|
|
787
880
|
allow_partial: bool = False,
|
|
788
881
|
wrap_validation_errors: bool = True,
|
|
789
882
|
) -> OutputDataT:
|
|
790
883
|
union_object = await self._union_processor.process(
|
|
791
|
-
data, run_context, allow_partial=allow_partial, wrap_validation_errors=wrap_validation_errors
|
|
884
|
+
data, run_context, trace_context, allow_partial=allow_partial, wrap_validation_errors=wrap_validation_errors
|
|
792
885
|
)
|
|
793
886
|
|
|
794
887
|
result = union_object.result
|
|
@@ -804,7 +897,7 @@ class UnionOutputProcessor(BaseOutputProcessor[OutputDataT]):
|
|
|
804
897
|
raise
|
|
805
898
|
|
|
806
899
|
return await processor.process(
|
|
807
|
-
data, run_context, allow_partial=allow_partial, wrap_validation_errors=wrap_validation_errors
|
|
900
|
+
data, run_context, trace_context, allow_partial=allow_partial, wrap_validation_errors=wrap_validation_errors
|
|
808
901
|
)
|
|
809
902
|
|
|
810
903
|
|
|
@@ -835,13 +928,20 @@ class PlainTextOutputProcessor(BaseOutputProcessor[OutputDataT]):
|
|
|
835
928
|
self,
|
|
836
929
|
data: str,
|
|
837
930
|
run_context: RunContext[AgentDepsT],
|
|
931
|
+
trace_context: TraceContext,
|
|
838
932
|
allow_partial: bool = False,
|
|
839
933
|
wrap_validation_errors: bool = True,
|
|
840
934
|
) -> OutputDataT:
|
|
841
935
|
args = {self._str_argument_name: data}
|
|
842
|
-
|
|
936
|
+
# Wraps the output function call in an OpenTelemetry span.
|
|
937
|
+
# Note: PlainTextOutputProcessor is used for text responses (not tool calls),
|
|
938
|
+
# so we don't have tool call attributes like gen_ai.tool.name or gen_ai.tool.call.id
|
|
939
|
+
function_name = getattr(self._function_schema.function, '__name__', 'text_output_function')
|
|
940
|
+
call = _messages.ToolCallPart(tool_name=function_name, args=args)
|
|
843
941
|
try:
|
|
844
|
-
output = await
|
|
942
|
+
output = await trace_context.execute_function_with_span(
|
|
943
|
+
self._function_schema, run_context, args, call, include_tool_call_id=False
|
|
944
|
+
)
|
|
845
945
|
except ModelRetry as r:
|
|
846
946
|
if wrap_validation_errors:
|
|
847
947
|
m = _messages.RetryPromptPart(
|
|
@@ -849,7 +949,7 @@ class PlainTextOutputProcessor(BaseOutputProcessor[OutputDataT]):
|
|
|
849
949
|
)
|
|
850
950
|
raise ToolRetryError(m) from r
|
|
851
951
|
else:
|
|
852
|
-
raise # pragma:
|
|
952
|
+
raise # pragma: no cover
|
|
853
953
|
|
|
854
954
|
return cast(OutputDataT, output)
|
|
855
955
|
|
|
@@ -881,6 +981,7 @@ class OutputTool(Generic[OutputDataT]):
|
|
|
881
981
|
self,
|
|
882
982
|
tool_call: _messages.ToolCallPart,
|
|
883
983
|
run_context: RunContext[AgentDepsT],
|
|
984
|
+
trace_context: TraceContext,
|
|
884
985
|
allow_partial: bool = False,
|
|
885
986
|
wrap_validation_errors: bool = True,
|
|
886
987
|
) -> OutputDataT:
|
|
@@ -889,6 +990,7 @@ class OutputTool(Generic[OutputDataT]):
|
|
|
889
990
|
Args:
|
|
890
991
|
tool_call: The tool call from the LLM to validate.
|
|
891
992
|
run_context: The current run context.
|
|
993
|
+
trace_context: The trace context to use for tracing the output processing.
|
|
892
994
|
allow_partial: If true, allow partial validation.
|
|
893
995
|
wrap_validation_errors: If true, wrap the validation errors in a retry message.
|
|
894
996
|
|
|
@@ -897,7 +999,11 @@ class OutputTool(Generic[OutputDataT]):
|
|
|
897
999
|
"""
|
|
898
1000
|
try:
|
|
899
1001
|
output = await self.processor.process(
|
|
900
|
-
tool_call.args,
|
|
1002
|
+
tool_call.args,
|
|
1003
|
+
run_context,
|
|
1004
|
+
trace_context.with_call(tool_call),
|
|
1005
|
+
allow_partial=allow_partial,
|
|
1006
|
+
wrap_validation_errors=False,
|
|
901
1007
|
)
|
|
902
1008
|
except ValidationError as e:
|
|
903
1009
|
if wrap_validation_errors:
|
|
@@ -908,7 +1014,7 @@ class OutputTool(Generic[OutputDataT]):
|
|
|
908
1014
|
)
|
|
909
1015
|
raise ToolRetryError(m) from e
|
|
910
1016
|
else:
|
|
911
|
-
raise # pragma:
|
|
1017
|
+
raise # pragma: no cover
|
|
912
1018
|
except ModelRetry as r:
|
|
913
1019
|
if wrap_validation_errors:
|
|
914
1020
|
m = _messages.RetryPromptPart(
|
|
@@ -918,7 +1024,7 @@ class OutputTool(Generic[OutputDataT]):
|
|
|
918
1024
|
)
|
|
919
1025
|
raise ToolRetryError(m) from r
|
|
920
1026
|
else:
|
|
921
|
-
raise # pragma:
|
|
1027
|
+
raise # pragma: no cover
|
|
922
1028
|
else:
|
|
923
1029
|
return output
|
|
924
1030
|
|
|
@@ -1089,6 +1089,7 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
1089
1089
|
streamed_response,
|
|
1090
1090
|
graph_ctx.deps.output_schema,
|
|
1091
1091
|
_agent_graph.build_run_context(graph_ctx),
|
|
1092
|
+
_output.build_trace_context(graph_ctx),
|
|
1092
1093
|
graph_ctx.deps.output_validators,
|
|
1093
1094
|
final_result_details.tool_name,
|
|
1094
1095
|
on_complete,
|
|
@@ -9,10 +9,13 @@ from typing_extensions import TypedDict
|
|
|
9
9
|
from pydantic_ai.tools import Tool
|
|
10
10
|
|
|
11
11
|
try:
|
|
12
|
-
|
|
12
|
+
try:
|
|
13
|
+
from ddgs import DDGS
|
|
14
|
+
except ImportError: # Fallback for older versions of ddgs
|
|
15
|
+
from duckduckgo_search import DDGS
|
|
13
16
|
except ImportError as _import_error:
|
|
14
17
|
raise ImportError(
|
|
15
|
-
'Please install `
|
|
18
|
+
'Please install `ddgs` to use the DuckDuckGo search tool, '
|
|
16
19
|
'you can use the `duckduckgo` optional group — `pip install "pydantic-ai-slim[duckduckgo]"`'
|
|
17
20
|
) from _import_error
|
|
18
21
|
|
|
@@ -4,9 +4,9 @@ import json
|
|
|
4
4
|
import sys
|
|
5
5
|
|
|
6
6
|
if sys.version_info < (3, 11):
|
|
7
|
-
from exceptiongroup import ExceptionGroup
|
|
7
|
+
from exceptiongroup import ExceptionGroup
|
|
8
8
|
else:
|
|
9
|
-
ExceptionGroup = ExceptionGroup
|
|
9
|
+
ExceptionGroup = ExceptionGroup
|
|
10
10
|
|
|
11
11
|
__all__ = (
|
|
12
12
|
'ModelRetry',
|
|
@@ -411,9 +411,9 @@ class UserPromptPart:
|
|
|
411
411
|
"""Part type identifier, this is available on all parts as a discriminator."""
|
|
412
412
|
|
|
413
413
|
def otel_event(self, settings: InstrumentationSettings) -> Event:
|
|
414
|
-
content: str | list[dict[str, Any] | str]
|
|
414
|
+
content: str | list[dict[str, Any] | str] | dict[str, Any]
|
|
415
415
|
if isinstance(self.content, str):
|
|
416
|
-
content = self.content
|
|
416
|
+
content = self.content if settings.include_content else {'kind': 'text'}
|
|
417
417
|
else:
|
|
418
418
|
content = []
|
|
419
419
|
for part in self.content:
|
|
@@ -433,7 +433,9 @@ class UserPromptPart:
|
|
|
433
433
|
__repr__ = _utils.dataclasses_no_defaults_repr
|
|
434
434
|
|
|
435
435
|
|
|
436
|
-
tool_return_ta: pydantic.TypeAdapter[Any] = pydantic.TypeAdapter(
|
|
436
|
+
tool_return_ta: pydantic.TypeAdapter[Any] = pydantic.TypeAdapter(
|
|
437
|
+
Any, config=pydantic.ConfigDict(defer_build=True, ser_json_bytes='base64', val_json_bytes='base64')
|
|
438
|
+
)
|
|
437
439
|
|
|
438
440
|
|
|
439
441
|
@dataclass(repr=False)
|
|
@@ -743,7 +745,7 @@ class ModelResponse:
|
|
|
743
745
|
'type': 'function', # TODO https://github.com/pydantic/pydantic-ai/issues/888
|
|
744
746
|
'function': {
|
|
745
747
|
'name': part.tool_name,
|
|
746
|
-
'arguments': part.args,
|
|
748
|
+
**({'arguments': part.args} if settings.include_content else {}),
|
|
747
749
|
},
|
|
748
750
|
}
|
|
749
751
|
)
|
|
@@ -227,6 +227,14 @@ KnownModelName = TypeAliasType(
|
|
|
227
227
|
'heroku:claude-3-7-sonnet',
|
|
228
228
|
'heroku:claude-4-sonnet',
|
|
229
229
|
'heroku:claude-3-haiku',
|
|
230
|
+
'huggingface:Qwen/QwQ-32B',
|
|
231
|
+
'huggingface:Qwen/Qwen2.5-72B-Instruct',
|
|
232
|
+
'huggingface:Qwen/Qwen3-235B-A22B',
|
|
233
|
+
'huggingface:Qwen/Qwen3-32B',
|
|
234
|
+
'huggingface:deepseek-ai/DeepSeek-R1',
|
|
235
|
+
'huggingface:meta-llama/Llama-3.3-70B-Instruct',
|
|
236
|
+
'huggingface:meta-llama/Llama-4-Maverick-17B-128E-Instruct',
|
|
237
|
+
'huggingface:meta-llama/Llama-4-Scout-17B-16E-Instruct',
|
|
230
238
|
'mistral:codestral-latest',
|
|
231
239
|
'mistral:mistral-large-latest',
|
|
232
240
|
'mistral:mistral-moderation-latest',
|
|
@@ -560,7 +568,7 @@ def override_allow_model_requests(allow_model_requests: bool) -> Iterator[None]:
|
|
|
560
568
|
ALLOW_MODEL_REQUESTS = old_value # pyright: ignore[reportConstantRedefinition]
|
|
561
569
|
|
|
562
570
|
|
|
563
|
-
def infer_model(model: Model | KnownModelName | str) -> Model:
|
|
571
|
+
def infer_model(model: Model | KnownModelName | str) -> Model: # noqa: C901
|
|
564
572
|
"""Infer the model from the name."""
|
|
565
573
|
if isinstance(model, Model):
|
|
566
574
|
return model
|
|
@@ -624,6 +632,10 @@ def infer_model(model: Model | KnownModelName | str) -> Model:
|
|
|
624
632
|
from .bedrock import BedrockConverseModel
|
|
625
633
|
|
|
626
634
|
return BedrockConverseModel(model_name, provider=provider)
|
|
635
|
+
elif provider == 'huggingface':
|
|
636
|
+
from .huggingface import HuggingFaceModel
|
|
637
|
+
|
|
638
|
+
return HuggingFaceModel(model_name, provider=provider)
|
|
627
639
|
else:
|
|
628
640
|
raise UserError(f'Unknown model: {model}') # pragma: no cover
|
|
629
641
|
|
|
@@ -256,7 +256,7 @@ class AnthropicModel(Model):
|
|
|
256
256
|
except APIStatusError as e:
|
|
257
257
|
if (status_code := e.status_code) >= 400:
|
|
258
258
|
raise ModelHTTPError(status_code=status_code, model_name=self.model_name, body=e.body) from e
|
|
259
|
-
raise # pragma:
|
|
259
|
+
raise # pragma: no cover
|
|
260
260
|
|
|
261
261
|
def _process_response(self, response: BetaMessage) -> ModelResponse:
|
|
262
262
|
"""Process a non-streamed response, and prepare a message to return."""
|
|
@@ -183,7 +183,7 @@ class CohereModel(Model):
|
|
|
183
183
|
except ApiError as e:
|
|
184
184
|
if (status_code := e.status_code) and status_code >= 400:
|
|
185
185
|
raise ModelHTTPError(status_code=status_code, model_name=self.model_name, body=e.body) from e
|
|
186
|
-
raise # pragma:
|
|
186
|
+
raise # pragma: no cover
|
|
187
187
|
|
|
188
188
|
def _process_response(self, response: ChatResponse) -> ModelResponse:
|
|
189
189
|
"""Process a non-streamed response, and prepare a message to return."""
|
|
@@ -253,7 +253,7 @@ class GeminiModel(Model):
|
|
|
253
253
|
|
|
254
254
|
if gemini_labels := model_settings.get('gemini_labels'):
|
|
255
255
|
if self._system == 'google-vertex':
|
|
256
|
-
request_data['labels'] = gemini_labels
|
|
256
|
+
request_data['labels'] = gemini_labels
|
|
257
257
|
|
|
258
258
|
headers = {'Content-Type': 'application/json', 'User-Agent': get_user_agent()}
|
|
259
259
|
url = f'/{self._model_name}:{"streamGenerateContent" if streamed else "generateContent"}'
|
|
@@ -415,7 +415,7 @@ def _settings_to_generation_config(model_settings: GeminiModelSettings) -> _Gemi
|
|
|
415
415
|
if (frequency_penalty := model_settings.get('frequency_penalty')) is not None:
|
|
416
416
|
config['frequency_penalty'] = frequency_penalty
|
|
417
417
|
if (thinkingConfig := model_settings.get('gemini_thinking_config')) is not None:
|
|
418
|
-
config['thinking_config'] = thinkingConfig
|
|
418
|
+
config['thinking_config'] = thinkingConfig
|
|
419
419
|
return config
|
|
420
420
|
|
|
421
421
|
|
|
@@ -166,7 +166,7 @@ class GoogleModel(Model):
|
|
|
166
166
|
self._model_name = model_name
|
|
167
167
|
|
|
168
168
|
if isinstance(provider, str):
|
|
169
|
-
provider = GoogleProvider(vertexai=provider == 'google-vertex')
|
|
169
|
+
provider = GoogleProvider(vertexai=provider == 'google-vertex')
|
|
170
170
|
|
|
171
171
|
self._provider = provider
|
|
172
172
|
self._system = provider.name
|
|
@@ -248,7 +248,7 @@ class GroqModel(Model):
|
|
|
248
248
|
except APIStatusError as e:
|
|
249
249
|
if (status_code := e.status_code) >= 400:
|
|
250
250
|
raise ModelHTTPError(status_code=status_code, model_name=self.model_name, body=e.body) from e
|
|
251
|
-
raise # pragma:
|
|
251
|
+
raise # pragma: no cover
|
|
252
252
|
|
|
253
253
|
def _process_response(self, response: chat.ChatCompletion) -> ModelResponse:
|
|
254
254
|
"""Process a non-streamed response, and prepare a message to return."""
|