pydantic-ai-slim 0.2.19__tar.gz → 0.2.20__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.2.19 → pydantic_ai_slim-0.2.20}/PKG-INFO +4 -4
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/_agent_graph.py +43 -9
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/_function_schema.py +12 -3
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/agent.py +3 -3
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/mcp.py +66 -5
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/models/google.py +13 -3
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/.gitignore +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/LICENSE +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/README.md +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/__init__.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/__main__.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/_a2a.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/_cli.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/_griffe.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/_output.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/_parts_manager.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/_system_prompt.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/_utils.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/common_tools/__init__.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/common_tools/duckduckgo.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/common_tools/tavily.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/direct.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/exceptions.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/ext/__init__.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/ext/langchain.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/format_as_xml.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/format_prompt.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/messages.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/models/__init__.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/models/anthropic.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/models/bedrock.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/models/cohere.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/models/fallback.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/models/function.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/models/gemini.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/models/groq.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/models/instrumented.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/models/mistral.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/models/openai.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/models/test.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/models/wrapper.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/profiles/__init__.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/profiles/_json_schema.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/profiles/amazon.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/profiles/anthropic.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/profiles/cohere.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/profiles/deepseek.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/profiles/google.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/profiles/grok.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/profiles/meta.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/profiles/mistral.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/profiles/openai.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/profiles/qwen.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/providers/__init__.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/providers/anthropic.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/providers/azure.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/providers/bedrock.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/providers/cohere.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/providers/deepseek.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/providers/fireworks.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/providers/google.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/providers/google_gla.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/providers/google_vertex.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/providers/grok.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/providers/groq.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/providers/heroku.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/providers/mistral.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/providers/openai.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/providers/openrouter.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/providers/together.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/py.typed +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/result.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/settings.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/tools.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pydantic_ai/usage.py +0 -0
- {pydantic_ai_slim-0.2.19 → pydantic_ai_slim-0.2.20}/pyproject.toml +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pydantic-ai-slim
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.20
|
|
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,11 +30,11 @@ 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.2.
|
|
33
|
+
Requires-Dist: pydantic-graph==0.2.20
|
|
34
34
|
Requires-Dist: pydantic>=2.10
|
|
35
35
|
Requires-Dist: typing-inspection>=0.4.0
|
|
36
36
|
Provides-Extra: a2a
|
|
37
|
-
Requires-Dist: fasta2a==0.2.
|
|
37
|
+
Requires-Dist: fasta2a==0.2.20; extra == 'a2a'
|
|
38
38
|
Provides-Extra: anthropic
|
|
39
39
|
Requires-Dist: anthropic>=0.52.0; extra == 'anthropic'
|
|
40
40
|
Provides-Extra: bedrock
|
|
@@ -48,7 +48,7 @@ Requires-Dist: cohere>=5.13.11; (platform_system != 'Emscripten') and extra == '
|
|
|
48
48
|
Provides-Extra: duckduckgo
|
|
49
49
|
Requires-Dist: duckduckgo-search>=7.0.0; extra == 'duckduckgo'
|
|
50
50
|
Provides-Extra: evals
|
|
51
|
-
Requires-Dist: pydantic-evals==0.2.
|
|
51
|
+
Requires-Dist: pydantic-evals==0.2.20; extra == 'evals'
|
|
52
52
|
Provides-Extra: google
|
|
53
53
|
Requires-Dist: google-genai>=1.15.0; extra == 'google'
|
|
54
54
|
Provides-Extra: groq
|
|
@@ -12,6 +12,7 @@ from typing import TYPE_CHECKING, Any, Callable, Generic, Literal, Union, cast
|
|
|
12
12
|
from opentelemetry.trace import Tracer
|
|
13
13
|
from typing_extensions import TypeGuard, TypeVar, assert_never
|
|
14
14
|
|
|
15
|
+
from pydantic_ai._function_schema import _takes_ctx as is_takes_ctx # type: ignore
|
|
15
16
|
from pydantic_ai._utils import is_async_callable, run_in_executor
|
|
16
17
|
from pydantic_graph import BaseNode, Graph, GraphRunContext
|
|
17
18
|
from pydantic_graph.nodes import End, NodeRunEndT
|
|
@@ -50,8 +51,20 @@ OutputT = TypeVar('OutputT')
|
|
|
50
51
|
|
|
51
52
|
_HistoryProcessorSync = Callable[[list[_messages.ModelMessage]], list[_messages.ModelMessage]]
|
|
52
53
|
_HistoryProcessorAsync = Callable[[list[_messages.ModelMessage]], Awaitable[list[_messages.ModelMessage]]]
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
_HistoryProcessorSyncWithCtx = Callable[[RunContext[DepsT], list[_messages.ModelMessage]], list[_messages.ModelMessage]]
|
|
55
|
+
_HistoryProcessorAsyncWithCtx = Callable[
|
|
56
|
+
[RunContext[DepsT], list[_messages.ModelMessage]], Awaitable[list[_messages.ModelMessage]]
|
|
57
|
+
]
|
|
58
|
+
HistoryProcessor = Union[
|
|
59
|
+
_HistoryProcessorSync,
|
|
60
|
+
_HistoryProcessorAsync,
|
|
61
|
+
_HistoryProcessorSyncWithCtx[DepsT],
|
|
62
|
+
_HistoryProcessorAsyncWithCtx[DepsT],
|
|
63
|
+
]
|
|
64
|
+
"""A function that processes a list of model messages and returns a list of model messages.
|
|
65
|
+
|
|
66
|
+
Can optionally accept a `RunContext` as a parameter.
|
|
67
|
+
"""
|
|
55
68
|
|
|
56
69
|
|
|
57
70
|
@dataclasses.dataclass
|
|
@@ -92,7 +105,7 @@ class GraphAgentDeps(Generic[DepsT, OutputDataT]):
|
|
|
92
105
|
output_schema: _output.OutputSchema[OutputDataT] | None
|
|
93
106
|
output_validators: list[_output.OutputValidator[DepsT, OutputDataT]]
|
|
94
107
|
|
|
95
|
-
history_processors: Sequence[HistoryProcessor]
|
|
108
|
+
history_processors: Sequence[HistoryProcessor[DepsT]]
|
|
96
109
|
|
|
97
110
|
function_tools: dict[str, Tool[DepsT]] = dataclasses.field(repr=False)
|
|
98
111
|
mcp_servers: Sequence[MCPServer] = dataclasses.field(repr=False)
|
|
@@ -328,7 +341,9 @@ class ModelRequestNode(AgentNode[DepsT, NodeRunEndT]):
|
|
|
328
341
|
|
|
329
342
|
model_settings, model_request_parameters = await self._prepare_request(ctx)
|
|
330
343
|
model_request_parameters = ctx.deps.model.customize_request_parameters(model_request_parameters)
|
|
331
|
-
message_history = await _process_message_history(
|
|
344
|
+
message_history = await _process_message_history(
|
|
345
|
+
ctx.state.message_history, ctx.deps.history_processors, build_run_context(ctx)
|
|
346
|
+
)
|
|
332
347
|
async with ctx.deps.model.request_stream(
|
|
333
348
|
message_history, model_settings, model_request_parameters
|
|
334
349
|
) as streamed_response:
|
|
@@ -352,7 +367,9 @@ class ModelRequestNode(AgentNode[DepsT, NodeRunEndT]):
|
|
|
352
367
|
|
|
353
368
|
model_settings, model_request_parameters = await self._prepare_request(ctx)
|
|
354
369
|
model_request_parameters = ctx.deps.model.customize_request_parameters(model_request_parameters)
|
|
355
|
-
message_history = await _process_message_history(
|
|
370
|
+
message_history = await _process_message_history(
|
|
371
|
+
ctx.state.message_history, ctx.deps.history_processors, build_run_context(ctx)
|
|
372
|
+
)
|
|
356
373
|
model_response = await ctx.deps.model.request(message_history, model_settings, model_request_parameters)
|
|
357
374
|
ctx.state.usage.incr(_usage.Usage())
|
|
358
375
|
|
|
@@ -762,7 +779,12 @@ async def _tool_from_mcp_server(
|
|
|
762
779
|
# some weird edge case occurs.
|
|
763
780
|
if not server.is_running: # pragma: no cover
|
|
764
781
|
raise exceptions.UserError(f'MCP server is not running: {server}')
|
|
765
|
-
|
|
782
|
+
|
|
783
|
+
if server.process_tool_call is not None:
|
|
784
|
+
result = await server.process_tool_call(ctx, server.call_tool, tool_name, args)
|
|
785
|
+
else:
|
|
786
|
+
result = await server.call_tool(tool_name, args)
|
|
787
|
+
|
|
766
788
|
return result
|
|
767
789
|
|
|
768
790
|
for server in ctx.deps.mcp_servers:
|
|
@@ -876,12 +898,24 @@ def build_agent_graph(
|
|
|
876
898
|
|
|
877
899
|
async def _process_message_history(
|
|
878
900
|
messages: list[_messages.ModelMessage],
|
|
879
|
-
processors: Sequence[HistoryProcessor],
|
|
901
|
+
processors: Sequence[HistoryProcessor[DepsT]],
|
|
902
|
+
run_context: RunContext[DepsT],
|
|
880
903
|
) -> list[_messages.ModelMessage]:
|
|
881
904
|
"""Process message history through a sequence of processors."""
|
|
882
905
|
for processor in processors:
|
|
906
|
+
takes_ctx = is_takes_ctx(processor)
|
|
907
|
+
|
|
883
908
|
if is_async_callable(processor):
|
|
884
|
-
|
|
909
|
+
if takes_ctx:
|
|
910
|
+
messages = await processor(run_context, messages)
|
|
911
|
+
else:
|
|
912
|
+
async_processor = cast(_HistoryProcessorAsync, processor)
|
|
913
|
+
messages = await async_processor(messages)
|
|
885
914
|
else:
|
|
886
|
-
|
|
915
|
+
if takes_ctx:
|
|
916
|
+
sync_processor_with_ctx = cast(_HistoryProcessorSyncWithCtx[DepsT], processor)
|
|
917
|
+
messages = await run_in_executor(sync_processor_with_ctx, run_context, messages)
|
|
918
|
+
else:
|
|
919
|
+
sync_processor = cast(_HistoryProcessorSync, processor)
|
|
920
|
+
messages = await run_in_executor(sync_processor, messages)
|
|
887
921
|
return messages
|
|
@@ -8,7 +8,7 @@ from __future__ import annotations as _annotations
|
|
|
8
8
|
from collections.abc import Awaitable
|
|
9
9
|
from dataclasses import dataclass, field
|
|
10
10
|
from inspect import Parameter, signature
|
|
11
|
-
from typing import TYPE_CHECKING, Any, Callable, cast
|
|
11
|
+
from typing import TYPE_CHECKING, Any, Callable, Union, cast
|
|
12
12
|
|
|
13
13
|
from pydantic import ConfigDict
|
|
14
14
|
from pydantic._internal import _decorators, _generate_schema, _typing_extra
|
|
@@ -17,7 +17,7 @@ from pydantic.fields import FieldInfo
|
|
|
17
17
|
from pydantic.json_schema import GenerateJsonSchema
|
|
18
18
|
from pydantic.plugin._schema_validator import create_schema_validator
|
|
19
19
|
from pydantic_core import SchemaValidator, core_schema
|
|
20
|
-
from typing_extensions import get_origin
|
|
20
|
+
from typing_extensions import Concatenate, ParamSpec, TypeIs, TypeVar, get_origin
|
|
21
21
|
|
|
22
22
|
from pydantic_ai.tools import RunContext
|
|
23
23
|
|
|
@@ -218,7 +218,16 @@ def function_schema( # noqa: C901
|
|
|
218
218
|
)
|
|
219
219
|
|
|
220
220
|
|
|
221
|
-
|
|
221
|
+
P = ParamSpec('P')
|
|
222
|
+
R = TypeVar('R')
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
WithCtx = Callable[Concatenate[RunContext[Any], P], R]
|
|
226
|
+
WithoutCtx = Callable[P, R]
|
|
227
|
+
TargetFunc = Union[WithCtx[P, R], WithoutCtx[P, R]]
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _takes_ctx(function: TargetFunc[P, R]) -> TypeIs[WithCtx[P, R]]:
|
|
222
231
|
"""Check if a function takes a `RunContext` first argument.
|
|
223
232
|
|
|
224
233
|
Args:
|
|
@@ -180,7 +180,7 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
180
180
|
defer_model_check: bool = False,
|
|
181
181
|
end_strategy: EndStrategy = 'early',
|
|
182
182
|
instrument: InstrumentationSettings | bool | None = None,
|
|
183
|
-
history_processors: Sequence[HistoryProcessor] | None = None,
|
|
183
|
+
history_processors: Sequence[HistoryProcessor[AgentDepsT]] | None = None,
|
|
184
184
|
) -> None: ...
|
|
185
185
|
|
|
186
186
|
@overload
|
|
@@ -210,7 +210,7 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
210
210
|
defer_model_check: bool = False,
|
|
211
211
|
end_strategy: EndStrategy = 'early',
|
|
212
212
|
instrument: InstrumentationSettings | bool | None = None,
|
|
213
|
-
history_processors: Sequence[HistoryProcessor] | None = None,
|
|
213
|
+
history_processors: Sequence[HistoryProcessor[AgentDepsT]] | None = None,
|
|
214
214
|
) -> None: ...
|
|
215
215
|
|
|
216
216
|
def __init__(
|
|
@@ -235,7 +235,7 @@ class Agent(Generic[AgentDepsT, OutputDataT]):
|
|
|
235
235
|
defer_model_check: bool = False,
|
|
236
236
|
end_strategy: EndStrategy = 'early',
|
|
237
237
|
instrument: InstrumentationSettings | bool | None = None,
|
|
238
|
-
history_processors: Sequence[HistoryProcessor] | None = None,
|
|
238
|
+
history_processors: Sequence[HistoryProcessor[AgentDepsT]] | None = None,
|
|
239
239
|
**_deprecated_kwargs: Any,
|
|
240
240
|
):
|
|
241
241
|
"""Create an agent.
|
|
@@ -4,7 +4,7 @@ import base64
|
|
|
4
4
|
import functools
|
|
5
5
|
import json
|
|
6
6
|
from abc import ABC, abstractmethod
|
|
7
|
-
from collections.abc import AsyncIterator, Sequence
|
|
7
|
+
from collections.abc import AsyncIterator, Awaitable, Sequence
|
|
8
8
|
from contextlib import AbstractAsyncContextManager, AsyncExitStack, asynccontextmanager
|
|
9
9
|
from dataclasses import dataclass
|
|
10
10
|
from pathlib import Path
|
|
@@ -15,14 +15,20 @@ import anyio
|
|
|
15
15
|
import httpx
|
|
16
16
|
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
|
|
17
17
|
from mcp.client.streamable_http import GetSessionIdCallback, streamablehttp_client
|
|
18
|
+
from mcp.shared.exceptions import McpError
|
|
18
19
|
from mcp.shared.message import SessionMessage
|
|
19
20
|
from mcp.types import (
|
|
20
21
|
AudioContent,
|
|
21
22
|
BlobResourceContents,
|
|
23
|
+
CallToolRequest,
|
|
24
|
+
CallToolRequestParams,
|
|
25
|
+
CallToolResult,
|
|
26
|
+
ClientRequest,
|
|
22
27
|
Content,
|
|
23
28
|
EmbeddedResource,
|
|
24
29
|
ImageContent,
|
|
25
30
|
LoggingLevel,
|
|
31
|
+
RequestParams,
|
|
26
32
|
TextContent,
|
|
27
33
|
TextResourceContents,
|
|
28
34
|
)
|
|
@@ -30,7 +36,7 @@ from typing_extensions import Self, assert_never, deprecated
|
|
|
30
36
|
|
|
31
37
|
from pydantic_ai.exceptions import ModelRetry
|
|
32
38
|
from pydantic_ai.messages import BinaryContent
|
|
33
|
-
from pydantic_ai.tools import ToolDefinition
|
|
39
|
+
from pydantic_ai.tools import RunContext, ToolDefinition
|
|
34
40
|
|
|
35
41
|
try:
|
|
36
42
|
from mcp.client.session import ClientSession
|
|
@@ -60,6 +66,9 @@ class MCPServer(ABC):
|
|
|
60
66
|
e.g. if `tool_prefix='foo'`, then a tool named `bar` will be registered as `foo_bar`
|
|
61
67
|
"""
|
|
62
68
|
|
|
69
|
+
process_tool_call: ProcessToolCallback | None = None
|
|
70
|
+
"""Hook to customize tool calling and optionally pass extra metadata."""
|
|
71
|
+
|
|
63
72
|
_client: ClientSession
|
|
64
73
|
_read_stream: MemoryObjectReceiveStream[SessionMessage | Exception]
|
|
65
74
|
_write_stream: MemoryObjectSendStream[SessionMessage]
|
|
@@ -113,13 +122,17 @@ class MCPServer(ABC):
|
|
|
113
122
|
]
|
|
114
123
|
|
|
115
124
|
async def call_tool(
|
|
116
|
-
self,
|
|
117
|
-
|
|
125
|
+
self,
|
|
126
|
+
tool_name: str,
|
|
127
|
+
arguments: dict[str, Any],
|
|
128
|
+
metadata: dict[str, Any] | None = None,
|
|
129
|
+
) -> ToolResult:
|
|
118
130
|
"""Call a tool on the server.
|
|
119
131
|
|
|
120
132
|
Args:
|
|
121
133
|
tool_name: The name of the tool to call.
|
|
122
134
|
arguments: The arguments to pass to the tool.
|
|
135
|
+
metadata: Request-level metadata (optional)
|
|
123
136
|
|
|
124
137
|
Returns:
|
|
125
138
|
The result of the tool call.
|
|
@@ -127,7 +140,23 @@ class MCPServer(ABC):
|
|
|
127
140
|
Raises:
|
|
128
141
|
ModelRetry: If the tool call fails.
|
|
129
142
|
"""
|
|
130
|
-
|
|
143
|
+
try:
|
|
144
|
+
# meta param is not provided by session yet, so build and can send_request directly.
|
|
145
|
+
result = await self._client.send_request(
|
|
146
|
+
ClientRequest(
|
|
147
|
+
CallToolRequest(
|
|
148
|
+
method='tools/call',
|
|
149
|
+
params=CallToolRequestParams(
|
|
150
|
+
name=self.get_unprefixed_tool_name(tool_name),
|
|
151
|
+
arguments=arguments,
|
|
152
|
+
_meta=RequestParams.Meta(**metadata) if metadata else None,
|
|
153
|
+
),
|
|
154
|
+
)
|
|
155
|
+
),
|
|
156
|
+
CallToolResult,
|
|
157
|
+
)
|
|
158
|
+
except McpError as e:
|
|
159
|
+
raise ModelRetry(e.error.message)
|
|
131
160
|
|
|
132
161
|
content = [self._map_tool_result_part(part) for part in result.content]
|
|
133
162
|
|
|
@@ -265,6 +294,9 @@ class MCPServerStdio(MCPServer):
|
|
|
265
294
|
e.g. if `tool_prefix='foo'`, then a tool named `bar` will be registered as `foo_bar`
|
|
266
295
|
"""
|
|
267
296
|
|
|
297
|
+
process_tool_call: ProcessToolCallback | None = None
|
|
298
|
+
"""Hook to customize tool calling and optionally pass extra metadata."""
|
|
299
|
+
|
|
268
300
|
timeout: float = 5
|
|
269
301
|
""" The timeout in seconds to wait for the client to initialize."""
|
|
270
302
|
|
|
@@ -359,6 +391,9 @@ class _MCPServerHTTP(MCPServer):
|
|
|
359
391
|
For example, if `tool_prefix='foo'`, then a tool named `bar` will be registered as `foo_bar`
|
|
360
392
|
"""
|
|
361
393
|
|
|
394
|
+
process_tool_call: ProcessToolCallback | None = None
|
|
395
|
+
"""Hook to customize tool calling and optionally pass extra metadata."""
|
|
396
|
+
|
|
362
397
|
@property
|
|
363
398
|
@abstractmethod
|
|
364
399
|
def _transport_client(
|
|
@@ -517,3 +552,29 @@ class MCPServerStreamableHTTP(_MCPServerHTTP):
|
|
|
517
552
|
@property
|
|
518
553
|
def _transport_client(self):
|
|
519
554
|
return streamablehttp_client # pragma: no cover
|
|
555
|
+
|
|
556
|
+
|
|
557
|
+
ToolResult = (
|
|
558
|
+
str | BinaryContent | dict[str, Any] | list[Any] | Sequence[str | BinaryContent | dict[str, Any] | list[Any]]
|
|
559
|
+
)
|
|
560
|
+
"""The result type of a tool call."""
|
|
561
|
+
|
|
562
|
+
CallToolFunc = Callable[[str, dict[str, Any], dict[str, Any] | None], Awaitable[ToolResult]]
|
|
563
|
+
"""A function type that represents a tool call."""
|
|
564
|
+
|
|
565
|
+
ProcessToolCallback = Callable[
|
|
566
|
+
[
|
|
567
|
+
RunContext[Any],
|
|
568
|
+
CallToolFunc,
|
|
569
|
+
str,
|
|
570
|
+
dict[str, Any],
|
|
571
|
+
],
|
|
572
|
+
Awaitable[ToolResult],
|
|
573
|
+
]
|
|
574
|
+
"""A process tool callback.
|
|
575
|
+
|
|
576
|
+
It accepts a run context, the original tool call function, a tool name, and arguments.
|
|
577
|
+
|
|
578
|
+
Allows wrapping an MCP server tool call to customize it, including adding extra request
|
|
579
|
+
metadata.
|
|
580
|
+
"""
|
|
@@ -10,9 +10,8 @@ from uuid import uuid4
|
|
|
10
10
|
|
|
11
11
|
from typing_extensions import assert_never
|
|
12
12
|
|
|
13
|
-
from pydantic_ai.providers import Provider
|
|
14
|
-
|
|
15
13
|
from .. import UnexpectedModelBehavior, _utils, usage
|
|
14
|
+
from ..exceptions import UserError
|
|
16
15
|
from ..messages import (
|
|
17
16
|
BinaryContent,
|
|
18
17
|
FileUrl,
|
|
@@ -30,6 +29,7 @@ from ..messages import (
|
|
|
30
29
|
VideoUrl,
|
|
31
30
|
)
|
|
32
31
|
from ..profiles import ModelProfileSpec
|
|
32
|
+
from ..providers import Provider
|
|
33
33
|
from ..settings import ModelSettings
|
|
34
34
|
from ..tools import ToolDefinition
|
|
35
35
|
from . import (
|
|
@@ -52,6 +52,7 @@ try:
|
|
|
52
52
|
FunctionDeclarationDict,
|
|
53
53
|
GenerateContentConfigDict,
|
|
54
54
|
GenerateContentResponse,
|
|
55
|
+
HttpOptionsDict,
|
|
55
56
|
Part,
|
|
56
57
|
PartDict,
|
|
57
58
|
SafetySettingDict,
|
|
@@ -252,8 +253,17 @@ class GoogleModel(Model):
|
|
|
252
253
|
tool_config = self._get_tool_config(model_request_parameters, tools)
|
|
253
254
|
system_instruction, contents = await self._map_messages(messages)
|
|
254
255
|
|
|
256
|
+
http_options: HttpOptionsDict = {
|
|
257
|
+
'headers': {'Content-Type': 'application/json', 'User-Agent': get_user_agent()}
|
|
258
|
+
}
|
|
259
|
+
if timeout := model_settings.get('timeout'):
|
|
260
|
+
if isinstance(timeout, (int, float)):
|
|
261
|
+
http_options['timeout'] = int(1000 * timeout)
|
|
262
|
+
else:
|
|
263
|
+
raise UserError('Google does not support setting ModelSettings.timeout to a httpx.Timeout')
|
|
264
|
+
|
|
255
265
|
config = GenerateContentConfigDict(
|
|
256
|
-
http_options=
|
|
266
|
+
http_options=http_options,
|
|
257
267
|
system_instruction=system_instruction,
|
|
258
268
|
temperature=model_settings.get('temperature'),
|
|
259
269
|
top_p=model_settings.get('top_p'),
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|