pydantic-ai-slim 0.2.11__tar.gz → 0.2.12__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.11 → pydantic_ai_slim-0.2.12}/PKG-INFO +4 -4
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/_agent_graph.py +29 -35
- pydantic_ai_slim-0.2.11/pydantic_ai/_pydantic.py → pydantic_ai_slim-0.2.12/pydantic_ai/_function_schema.py +48 -8
- pydantic_ai_slim-0.2.12/pydantic_ai/_output.py +439 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/agent.py +15 -15
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/mcp.py +1 -1
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/messages.py +2 -2
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/models/__init__.py +39 -3
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/models/anthropic.py +4 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/models/bedrock.py +43 -16
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/models/cohere.py +4 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/models/gemini.py +68 -108
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/models/google.py +45 -110
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/models/groq.py +17 -2
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/models/mistral.py +4 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/models/openai.py +22 -157
- pydantic_ai_slim-0.2.12/pydantic_ai/profiles/__init__.py +39 -0
- {pydantic_ai_slim-0.2.11/pydantic_ai/models → pydantic_ai_slim-0.2.12/pydantic_ai/profiles}/_json_schema.py +23 -2
- pydantic_ai_slim-0.2.12/pydantic_ai/profiles/amazon.py +9 -0
- pydantic_ai_slim-0.2.12/pydantic_ai/profiles/anthropic.py +8 -0
- pydantic_ai_slim-0.2.12/pydantic_ai/profiles/cohere.py +8 -0
- pydantic_ai_slim-0.2.12/pydantic_ai/profiles/deepseek.py +8 -0
- pydantic_ai_slim-0.2.12/pydantic_ai/profiles/google.py +100 -0
- pydantic_ai_slim-0.2.12/pydantic_ai/profiles/grok.py +8 -0
- pydantic_ai_slim-0.2.12/pydantic_ai/profiles/meta.py +9 -0
- pydantic_ai_slim-0.2.12/pydantic_ai/profiles/mistral.py +8 -0
- pydantic_ai_slim-0.2.12/pydantic_ai/profiles/openai.py +144 -0
- pydantic_ai_slim-0.2.12/pydantic_ai/profiles/qwen.py +9 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/providers/__init__.py +18 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/providers/anthropic.py +5 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/providers/azure.py +34 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/providers/bedrock.py +60 -1
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/providers/cohere.py +5 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/providers/deepseek.py +12 -0
- pydantic_ai_slim-0.2.12/pydantic_ai/providers/fireworks.py +99 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/providers/google.py +5 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/providers/google_gla.py +5 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/providers/google_vertex.py +5 -0
- pydantic_ai_slim-0.2.11/pydantic_ai/providers/openrouter.py → pydantic_ai_slim-0.2.12/pydantic_ai/providers/grok.py +22 -9
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/providers/groq.py +25 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/providers/mistral.py +5 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/providers/openai.py +5 -0
- pydantic_ai_slim-0.2.12/pydantic_ai/providers/openrouter.py +105 -0
- pydantic_ai_slim-0.2.12/pydantic_ai/providers/together.py +96 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/result.py +34 -103
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/tools.py +28 -58
- pydantic_ai_slim-0.2.11/pydantic_ai/_output.py +0 -292
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/.gitignore +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/LICENSE +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/README.md +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/__init__.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/__main__.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/_a2a.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/_cli.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/_griffe.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/_parts_manager.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/_system_prompt.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/_utils.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/common_tools/__init__.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/common_tools/duckduckgo.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/common_tools/tavily.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/direct.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/exceptions.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/format_as_xml.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/format_prompt.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/models/fallback.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/models/function.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/models/instrumented.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/models/test.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/models/wrapper.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/py.typed +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/settings.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/pydantic_ai/usage.py +0 -0
- {pydantic_ai_slim-0.2.11 → pydantic_ai_slim-0.2.12}/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.12
|
|
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.12
|
|
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.12; 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.12; extra == 'evals'
|
|
52
52
|
Provides-Extra: google
|
|
53
53
|
Requires-Dist: google-genai>=1.15.0; extra == 'google'
|
|
54
54
|
Provides-Extra: groq
|
|
@@ -24,7 +24,7 @@ from . import (
|
|
|
24
24
|
result,
|
|
25
25
|
usage as _usage,
|
|
26
26
|
)
|
|
27
|
-
from .result import OutputDataT
|
|
27
|
+
from .result import OutputDataT
|
|
28
28
|
from .settings import ModelSettings, merge_model_settings
|
|
29
29
|
from .tools import RunContext, Tool, ToolDefinition, ToolsPrepareFunc
|
|
30
30
|
|
|
@@ -64,12 +64,14 @@ class GraphAgentState:
|
|
|
64
64
|
retries: int
|
|
65
65
|
run_step: int
|
|
66
66
|
|
|
67
|
-
def increment_retries(self, max_result_retries: int) -> None:
|
|
67
|
+
def increment_retries(self, max_result_retries: int, error: Exception | None = None) -> None:
|
|
68
68
|
self.retries += 1
|
|
69
69
|
if self.retries > max_result_retries:
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
70
|
+
message = f'Exceeded maximum retries ({max_result_retries}) for result validation'
|
|
71
|
+
if error:
|
|
72
|
+
raise exceptions.UnexpectedModelBehavior(message) from error
|
|
73
|
+
else:
|
|
74
|
+
raise exceptions.UnexpectedModelBehavior(message)
|
|
73
75
|
|
|
74
76
|
|
|
75
77
|
@dataclasses.dataclass
|
|
@@ -264,7 +266,7 @@ async def _prepare_request_parameters(
|
|
|
264
266
|
output_schema = ctx.deps.output_schema
|
|
265
267
|
return models.ModelRequestParameters(
|
|
266
268
|
function_tools=function_tool_defs,
|
|
267
|
-
allow_text_output=allow_text_output(output_schema),
|
|
269
|
+
allow_text_output=_output.allow_text_output(output_schema),
|
|
268
270
|
output_tools=output_schema.tool_defs() if output_schema is not None else [],
|
|
269
271
|
)
|
|
270
272
|
|
|
@@ -450,7 +452,7 @@ class CallToolsNode(AgentNode[DepsT, NodeRunEndT]):
|
|
|
450
452
|
# when the model has already returned text along side tool calls
|
|
451
453
|
# in this scenario, if text responses are allowed, we return text from the most recent model
|
|
452
454
|
# response, if any
|
|
453
|
-
if allow_text_output(ctx.deps.output_schema):
|
|
455
|
+
if _output.allow_text_output(ctx.deps.output_schema):
|
|
454
456
|
for message in reversed(ctx.state.message_history):
|
|
455
457
|
if isinstance(message, _messages.ModelResponse):
|
|
456
458
|
last_texts = [p.content for p in message.parts if isinstance(p, _messages.TextPart)]
|
|
@@ -471,6 +473,7 @@ class CallToolsNode(AgentNode[DepsT, NodeRunEndT]):
|
|
|
471
473
|
tool_calls: list[_messages.ToolCallPart],
|
|
472
474
|
) -> AsyncIterator[_messages.HandleResponseEvent]:
|
|
473
475
|
output_schema = ctx.deps.output_schema
|
|
476
|
+
run_context = build_run_context(ctx)
|
|
474
477
|
|
|
475
478
|
# first, look for the output tool call
|
|
476
479
|
final_result: result.FinalResult[NodeRunEndT] | None = None
|
|
@@ -478,12 +481,12 @@ class CallToolsNode(AgentNode[DepsT, NodeRunEndT]):
|
|
|
478
481
|
if output_schema is not None:
|
|
479
482
|
for call, output_tool in output_schema.find_tool(tool_calls):
|
|
480
483
|
try:
|
|
481
|
-
result_data = output_tool.
|
|
484
|
+
result_data = await output_tool.process(call, run_context)
|
|
482
485
|
result_data = await _validate_output(result_data, ctx, call)
|
|
483
486
|
except _output.ToolRetryError as e:
|
|
484
487
|
# TODO: Should only increment retry stuff once per node execution, not for each tool call
|
|
485
488
|
# Also, should increment the tool-specific retry count rather than the run retry count
|
|
486
|
-
ctx.state.increment_retries(ctx.deps.max_result_retries)
|
|
489
|
+
ctx.state.increment_retries(ctx.deps.max_result_retries, e)
|
|
487
490
|
parts.append(e.tool_retry)
|
|
488
491
|
else:
|
|
489
492
|
final_result = result.FinalResult(result_data, call.tool_name, call.tool_call_id)
|
|
@@ -505,7 +508,6 @@ class CallToolsNode(AgentNode[DepsT, NodeRunEndT]):
|
|
|
505
508
|
else:
|
|
506
509
|
if tool_responses:
|
|
507
510
|
parts.extend(tool_responses)
|
|
508
|
-
run_context = build_run_context(ctx)
|
|
509
511
|
instructions = await ctx.deps.get_instructions(run_context)
|
|
510
512
|
self._next_node = ModelRequestNode[DepsT, NodeRunEndT](
|
|
511
513
|
_messages.ModelRequest(parts=parts, instructions=instructions)
|
|
@@ -533,27 +535,22 @@ class CallToolsNode(AgentNode[DepsT, NodeRunEndT]):
|
|
|
533
535
|
output_schema = ctx.deps.output_schema
|
|
534
536
|
|
|
535
537
|
text = '\n\n'.join(texts)
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
result_data = await _validate_output(result_data_input, ctx, None)
|
|
541
|
-
except _output.ToolRetryError as e:
|
|
542
|
-
ctx.state.increment_retries(ctx.deps.max_result_retries)
|
|
543
|
-
return ModelRequestNode[DepsT, NodeRunEndT](_messages.ModelRequest(parts=[e.tool_retry]))
|
|
538
|
+
try:
|
|
539
|
+
if _output.allow_text_output(output_schema):
|
|
540
|
+
# The following cast is safe because we know `str` is an allowed result type
|
|
541
|
+
result_data = cast(NodeRunEndT, text)
|
|
544
542
|
else:
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
ctx.state.increment_retries(ctx.deps.max_result_retries)
|
|
548
|
-
return ModelRequestNode[DepsT, NodeRunEndT](
|
|
549
|
-
_messages.ModelRequest(
|
|
550
|
-
parts=[
|
|
551
|
-
_messages.RetryPromptPart(
|
|
552
|
-
content='Plain text responses are not permitted, please include your response in a tool call',
|
|
553
|
-
)
|
|
554
|
-
]
|
|
543
|
+
m = _messages.RetryPromptPart(
|
|
544
|
+
content='Plain text responses are not permitted, please include your response in a tool call',
|
|
555
545
|
)
|
|
556
|
-
|
|
546
|
+
raise _output.ToolRetryError(m)
|
|
547
|
+
|
|
548
|
+
result_data = await _validate_output(result_data, ctx, None)
|
|
549
|
+
except _output.ToolRetryError as e:
|
|
550
|
+
ctx.state.increment_retries(ctx.deps.max_result_retries, e)
|
|
551
|
+
return ModelRequestNode[DepsT, NodeRunEndT](_messages.ModelRequest(parts=[e.tool_retry]))
|
|
552
|
+
else:
|
|
553
|
+
return self._handle_final_result(ctx, result.FinalResult(result_data, None, None), [])
|
|
557
554
|
|
|
558
555
|
|
|
559
556
|
def build_run_context(ctx: GraphRunContext[GraphAgentState, GraphAgentDeps[DepsT, Any]]) -> RunContext[DepsT]:
|
|
@@ -795,11 +792,6 @@ async def _validate_output(
|
|
|
795
792
|
return result_data
|
|
796
793
|
|
|
797
794
|
|
|
798
|
-
def allow_text_output(output_schema: _output.OutputSchema[Any] | None) -> bool:
|
|
799
|
-
"""Check if the result schema allows text results."""
|
|
800
|
-
return output_schema is None or output_schema.allow_text_output
|
|
801
|
-
|
|
802
|
-
|
|
803
795
|
@dataclasses.dataclass
|
|
804
796
|
class _RunMessages:
|
|
805
797
|
messages: list[_messages.ModelMessage]
|
|
@@ -849,7 +841,9 @@ def get_captured_run_messages() -> _RunMessages:
|
|
|
849
841
|
|
|
850
842
|
|
|
851
843
|
def build_agent_graph(
|
|
852
|
-
name: str | None,
|
|
844
|
+
name: str | None,
|
|
845
|
+
deps_type: type[DepsT],
|
|
846
|
+
output_type: _output.OutputType[OutputT],
|
|
853
847
|
) -> Graph[GraphAgentState, GraphAgentDeps[DepsT, result.FinalResult[OutputT]], result.FinalResult[OutputT]]:
|
|
854
848
|
"""Build the execution [Graph][pydantic_graph.Graph] for a given agent."""
|
|
855
849
|
nodes = (
|
|
@@ -5,6 +5,9 @@ This module has to use numerous internal Pydantic APIs and is therefore brittle
|
|
|
5
5
|
|
|
6
6
|
from __future__ import annotations as _annotations
|
|
7
7
|
|
|
8
|
+
import inspect
|
|
9
|
+
from collections.abc import Awaitable
|
|
10
|
+
from dataclasses import dataclass
|
|
8
11
|
from inspect import Parameter, signature
|
|
9
12
|
from typing import TYPE_CHECKING, Any, Callable, cast
|
|
10
13
|
|
|
@@ -15,10 +18,12 @@ from pydantic.fields import FieldInfo
|
|
|
15
18
|
from pydantic.json_schema import GenerateJsonSchema
|
|
16
19
|
from pydantic.plugin._schema_validator import create_schema_validator
|
|
17
20
|
from pydantic_core import SchemaValidator, core_schema
|
|
18
|
-
from typing_extensions import
|
|
21
|
+
from typing_extensions import get_origin
|
|
22
|
+
|
|
23
|
+
from pydantic_ai.tools import RunContext
|
|
19
24
|
|
|
20
25
|
from ._griffe import doc_descriptions
|
|
21
|
-
from ._utils import check_object_json_schema, is_model_like
|
|
26
|
+
from ._utils import check_object_json_schema, is_model_like, run_in_executor
|
|
22
27
|
|
|
23
28
|
if TYPE_CHECKING:
|
|
24
29
|
from .tools import DocstringFormat, ObjectJsonSchema
|
|
@@ -27,24 +32,53 @@ if TYPE_CHECKING:
|
|
|
27
32
|
__all__ = ('function_schema',)
|
|
28
33
|
|
|
29
34
|
|
|
30
|
-
|
|
35
|
+
@dataclass
|
|
36
|
+
class FunctionSchema:
|
|
31
37
|
"""Internal information about a function schema."""
|
|
32
38
|
|
|
39
|
+
function: Callable[..., Any]
|
|
33
40
|
description: str
|
|
34
41
|
validator: SchemaValidator
|
|
35
42
|
json_schema: ObjectJsonSchema
|
|
36
43
|
# if not None, the function takes a single by that name (besides potentially `info`)
|
|
44
|
+
takes_ctx: bool
|
|
45
|
+
is_async: bool
|
|
37
46
|
single_arg_name: str | None
|
|
38
47
|
positional_fields: list[str]
|
|
39
48
|
var_positional_field: str | None
|
|
40
49
|
|
|
50
|
+
async def call(self, args_dict: dict[str, Any], ctx: RunContext[Any]) -> Any:
|
|
51
|
+
args, kwargs = self._call_args(args_dict, ctx)
|
|
52
|
+
if self.is_async:
|
|
53
|
+
function = cast(Callable[[Any], Awaitable[str]], self.function)
|
|
54
|
+
return await function(*args, **kwargs)
|
|
55
|
+
else:
|
|
56
|
+
function = cast(Callable[[Any], str], self.function)
|
|
57
|
+
return await run_in_executor(function, *args, **kwargs)
|
|
58
|
+
|
|
59
|
+
def _call_args(
|
|
60
|
+
self,
|
|
61
|
+
args_dict: dict[str, Any],
|
|
62
|
+
ctx: RunContext[Any],
|
|
63
|
+
) -> tuple[list[Any], dict[str, Any]]:
|
|
64
|
+
if self.single_arg_name:
|
|
65
|
+
args_dict = {self.single_arg_name: args_dict}
|
|
66
|
+
|
|
67
|
+
args = [ctx] if self.takes_ctx else []
|
|
68
|
+
for positional_field in self.positional_fields:
|
|
69
|
+
args.append(args_dict.pop(positional_field)) # pragma: no cover
|
|
70
|
+
if self.var_positional_field:
|
|
71
|
+
args.extend(args_dict.pop(self.var_positional_field))
|
|
72
|
+
|
|
73
|
+
return args, args_dict
|
|
74
|
+
|
|
41
75
|
|
|
42
76
|
def function_schema( # noqa: C901
|
|
43
77
|
function: Callable[..., Any],
|
|
44
|
-
takes_ctx: bool,
|
|
45
|
-
docstring_format: DocstringFormat,
|
|
46
|
-
require_parameter_descriptions: bool,
|
|
47
78
|
schema_generator: type[GenerateJsonSchema],
|
|
79
|
+
takes_ctx: bool | None = None,
|
|
80
|
+
docstring_format: DocstringFormat = 'auto',
|
|
81
|
+
require_parameter_descriptions: bool = False,
|
|
48
82
|
) -> FunctionSchema:
|
|
49
83
|
"""Build a Pydantic validator and JSON schema from a tool function.
|
|
50
84
|
|
|
@@ -58,6 +92,9 @@ def function_schema( # noqa: C901
|
|
|
58
92
|
Returns:
|
|
59
93
|
A `FunctionSchema` instance.
|
|
60
94
|
"""
|
|
95
|
+
if takes_ctx is None:
|
|
96
|
+
takes_ctx = _takes_ctx(function)
|
|
97
|
+
|
|
61
98
|
config = ConfigDict(title=function.__name__, use_attribute_docstrings=True)
|
|
62
99
|
config_wrapper = ConfigWrapper(config)
|
|
63
100
|
gen_schema = _generate_schema.GenerateSchema(config_wrapper)
|
|
@@ -176,10 +213,13 @@ def function_schema( # noqa: C901
|
|
|
176
213
|
single_arg_name=single_arg_name,
|
|
177
214
|
positional_fields=positional_fields,
|
|
178
215
|
var_positional_field=var_positional_field,
|
|
216
|
+
takes_ctx=takes_ctx,
|
|
217
|
+
is_async=inspect.iscoroutinefunction(function),
|
|
218
|
+
function=function,
|
|
179
219
|
)
|
|
180
220
|
|
|
181
221
|
|
|
182
|
-
def
|
|
222
|
+
def _takes_ctx(function: Callable[..., Any]) -> bool:
|
|
183
223
|
"""Check if a function takes a `RunContext` first argument.
|
|
184
224
|
|
|
185
225
|
Args:
|
|
@@ -196,7 +236,7 @@ def takes_ctx(function: Callable[..., Any]) -> bool:
|
|
|
196
236
|
else:
|
|
197
237
|
type_hints = _typing_extra.get_function_type_hints(function)
|
|
198
238
|
annotation = type_hints[first_param_name]
|
|
199
|
-
return
|
|
239
|
+
return True is not sig.empty and _is_call_ctx(annotation)
|
|
200
240
|
|
|
201
241
|
|
|
202
242
|
def _build_schema(
|