pydantic-ai-slim 0.0.6a3__py3-none-any.whl → 0.0.7__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 +2 -2
- pydantic_ai/_pydantic.py +10 -10
- pydantic_ai/_result.py +4 -4
- pydantic_ai/_system_prompt.py +2 -2
- pydantic_ai/{_retriever.py → _tool.py} +13 -15
- pydantic_ai/_utils.py +9 -5
- pydantic_ai/agent.py +129 -128
- pydantic_ai/dependencies.py +20 -20
- pydantic_ai/exceptions.py +1 -1
- pydantic_ai/messages.py +12 -12
- pydantic_ai/models/__init__.py +3 -3
- pydantic_ai/models/function.py +10 -14
- pydantic_ai/models/gemini.py +8 -30
- pydantic_ai/models/groq.py +2 -2
- pydantic_ai/models/openai.py +2 -2
- pydantic_ai/models/test.py +30 -28
- pydantic_ai/models/vertexai.py +2 -59
- {pydantic_ai_slim-0.0.6a3.dist-info → pydantic_ai_slim-0.0.7.dist-info}/METADATA +7 -3
- pydantic_ai_slim-0.0.7.dist-info/RECORD +23 -0
- pydantic_ai_slim-0.0.6a3.dist-info/RECORD +0 -23
- {pydantic_ai_slim-0.0.6a3.dist-info → pydantic_ai_slim-0.0.7.dist-info}/WHEEL +0 -0
pydantic_ai/messages.py
CHANGED
|
@@ -55,7 +55,7 @@ json_ta: TypeAdapter[JsonData] = TypeAdapter(JsonData)
|
|
|
55
55
|
|
|
56
56
|
@dataclass
|
|
57
57
|
class ToolReturn:
|
|
58
|
-
"""A tool return message, this encodes the result of running a
|
|
58
|
+
"""A tool return message, this encodes the result of running a tool."""
|
|
59
59
|
|
|
60
60
|
tool_name: str
|
|
61
61
|
"""The name of the "tool" was called."""
|
|
@@ -89,10 +89,10 @@ class RetryPrompt:
|
|
|
89
89
|
|
|
90
90
|
This can be sent for a number of reasons:
|
|
91
91
|
|
|
92
|
-
* Pydantic validation of
|
|
92
|
+
* Pydantic validation of tool arguments failed, here content is derived from a Pydantic
|
|
93
93
|
[`ValidationError`][pydantic_core.ValidationError]
|
|
94
|
-
* a
|
|
95
|
-
* no
|
|
94
|
+
* a tool raised a [`ModelRetry`][pydantic_ai.exceptions.ModelRetry] exception
|
|
95
|
+
* no tool was found for the tool name
|
|
96
96
|
* the model returned plain text when a structured response was expected
|
|
97
97
|
* Pydantic validation of a structured response failed, here content is derived from a Pydantic
|
|
98
98
|
[`ValidationError`][pydantic_core.ValidationError]
|
|
@@ -144,8 +144,8 @@ class ArgsJson:
|
|
|
144
144
|
|
|
145
145
|
|
|
146
146
|
@dataclass
|
|
147
|
-
class
|
|
148
|
-
|
|
147
|
+
class ArgsDict:
|
|
148
|
+
args_dict: dict[str, Any]
|
|
149
149
|
"""A python dictionary of arguments."""
|
|
150
150
|
|
|
151
151
|
|
|
@@ -155,7 +155,7 @@ class ToolCall:
|
|
|
155
155
|
|
|
156
156
|
tool_name: str
|
|
157
157
|
"""The name of the tool to call."""
|
|
158
|
-
args: ArgsJson |
|
|
158
|
+
args: ArgsJson | ArgsDict
|
|
159
159
|
"""The arguments to pass to the tool.
|
|
160
160
|
|
|
161
161
|
Either as JSON or a Python dictionary depending on how data was returned.
|
|
@@ -168,12 +168,12 @@ class ToolCall:
|
|
|
168
168
|
return cls(tool_name, ArgsJson(args_json), tool_id)
|
|
169
169
|
|
|
170
170
|
@classmethod
|
|
171
|
-
def
|
|
172
|
-
return cls(tool_name,
|
|
171
|
+
def from_dict(cls, tool_name: str, args_dict: dict[str, Any]) -> ToolCall:
|
|
172
|
+
return cls(tool_name, ArgsDict(args_dict))
|
|
173
173
|
|
|
174
174
|
def has_content(self) -> bool:
|
|
175
|
-
if isinstance(self.args,
|
|
176
|
-
return any(self.args.
|
|
175
|
+
if isinstance(self.args, ArgsDict):
|
|
176
|
+
return any(self.args.args_dict.values())
|
|
177
177
|
else:
|
|
178
178
|
return bool(self.args.args_json)
|
|
179
179
|
|
|
@@ -182,7 +182,7 @@ class ToolCall:
|
|
|
182
182
|
class ModelStructuredResponse:
|
|
183
183
|
"""A structured response from a model.
|
|
184
184
|
|
|
185
|
-
This is used either to call a
|
|
185
|
+
This is used either to call a tool or to return a structured response from an agent run.
|
|
186
186
|
"""
|
|
187
187
|
|
|
188
188
|
calls: list[ToolCall]
|
pydantic_ai/models/__init__.py
CHANGED
|
@@ -63,7 +63,7 @@ class Model(ABC):
|
|
|
63
63
|
@abstractmethod
|
|
64
64
|
async def agent_model(
|
|
65
65
|
self,
|
|
66
|
-
|
|
66
|
+
function_tools: Mapping[str, AbstractToolDefinition],
|
|
67
67
|
allow_text_result: bool,
|
|
68
68
|
result_tools: Sequence[AbstractToolDefinition] | None,
|
|
69
69
|
) -> AgentModel:
|
|
@@ -72,7 +72,7 @@ class Model(ABC):
|
|
|
72
72
|
This is async in case slow/async config checks need to be performed that can't be done in `__init__`.
|
|
73
73
|
|
|
74
74
|
Args:
|
|
75
|
-
|
|
75
|
+
function_tools: The tools available to the agent.
|
|
76
76
|
allow_text_result: Whether a plain text final response/result is permitted.
|
|
77
77
|
result_tools: Tool definitions for the final result tool(s), if any.
|
|
78
78
|
|
|
@@ -259,7 +259,7 @@ def infer_model(model: Model | KnownModelName) -> Model:
|
|
|
259
259
|
class AbstractToolDefinition(Protocol):
|
|
260
260
|
"""Abstract definition of a function/tool.
|
|
261
261
|
|
|
262
|
-
This is used for both
|
|
262
|
+
This is used for both tools and result tools.
|
|
263
263
|
"""
|
|
264
264
|
|
|
265
265
|
name: str
|
pydantic_ai/models/function.py
CHANGED
|
@@ -1,11 +1,3 @@
|
|
|
1
|
-
"""A model controlled by a local function.
|
|
2
|
-
|
|
3
|
-
[FunctionModel][pydantic_ai.models.function.FunctionModel] is similar to [TestModel][pydantic_ai.models.test.TestModel],
|
|
4
|
-
but allows greater control over the model's behavior.
|
|
5
|
-
|
|
6
|
-
It's primary use case for more advanced unit testing than is possible with `TestModel`.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
1
|
from __future__ import annotations as _annotations
|
|
10
2
|
|
|
11
3
|
import inspect
|
|
@@ -67,13 +59,13 @@ class FunctionModel(Model):
|
|
|
67
59
|
|
|
68
60
|
async def agent_model(
|
|
69
61
|
self,
|
|
70
|
-
|
|
62
|
+
function_tools: Mapping[str, AbstractToolDefinition],
|
|
71
63
|
allow_text_result: bool,
|
|
72
64
|
result_tools: Sequence[AbstractToolDefinition] | None,
|
|
73
65
|
) -> AgentModel:
|
|
74
66
|
result_tools = list(result_tools) if result_tools is not None else None
|
|
75
67
|
return FunctionAgentModel(
|
|
76
|
-
self.function, self.stream_function, AgentInfo(
|
|
68
|
+
self.function, self.stream_function, AgentInfo(function_tools, allow_text_result, result_tools)
|
|
77
69
|
)
|
|
78
70
|
|
|
79
71
|
def name(self) -> str:
|
|
@@ -89,11 +81,15 @@ class FunctionModel(Model):
|
|
|
89
81
|
class AgentInfo:
|
|
90
82
|
"""Information about an agent.
|
|
91
83
|
|
|
92
|
-
This is passed as the second to functions.
|
|
84
|
+
This is passed as the second to functions used within [`FunctionModel`][pydantic_ai.models.function.FunctionModel].
|
|
93
85
|
"""
|
|
94
86
|
|
|
95
|
-
|
|
96
|
-
"""The
|
|
87
|
+
function_tools: Mapping[str, AbstractToolDefinition]
|
|
88
|
+
"""The function tools available on this agent.
|
|
89
|
+
|
|
90
|
+
These are the tools registered via the [`tool`][pydantic_ai.Agent.tool] and
|
|
91
|
+
[`tool_plain`][pydantic_ai.Agent.tool_plain] decorators.
|
|
92
|
+
"""
|
|
97
93
|
allow_text_result: bool
|
|
98
94
|
"""Whether a plain text result is allowed."""
|
|
99
95
|
result_tools: list[AbstractToolDefinition] | None
|
|
@@ -254,7 +250,7 @@ def _estimate_cost(messages: Iterable[Message]) -> result.Cost:
|
|
|
254
250
|
if isinstance(call.args, ArgsJson):
|
|
255
251
|
args_str = call.args.args_json
|
|
256
252
|
else:
|
|
257
|
-
args_str = pydantic_core.to_json(call.args.
|
|
253
|
+
args_str = pydantic_core.to_json(call.args.args_dict).decode()
|
|
258
254
|
|
|
259
255
|
response_tokens += 1 + _string_cost(args_str)
|
|
260
256
|
else:
|
pydantic_ai/models/gemini.py
CHANGED
|
@@ -1,25 +1,3 @@
|
|
|
1
|
-
"""Custom interface to the `generativelanguage.googleapis.com` API using [HTTPX] and [Pydantic].
|
|
2
|
-
|
|
3
|
-
The Google SDK for interacting with the `generativelanguage.googleapis.com` API
|
|
4
|
-
[`google-generativeai`](https://ai.google.dev/gemini-api/docs/quickstart?lang=python) reads like it was written by a
|
|
5
|
-
Java developer who thought they knew everything about OOP, spent 30 minutes trying to learn Python,
|
|
6
|
-
gave up and decided to build the library to prove how horrible Python is. It also doesn't use httpx for HTTP requests,
|
|
7
|
-
and tries to implement tool calling itself, but doesn't use Pydantic or equivalent for validation.
|
|
8
|
-
|
|
9
|
-
We could also use the Google Vertex SDK,
|
|
10
|
-
[`google-cloud-aiplatform`](https://cloud.google.com/vertex-ai/docs/python-sdk/use-vertex-ai-python-sdk)
|
|
11
|
-
which uses the `*-aiplatform.googleapis.com` API, but that requires a service account for authentication
|
|
12
|
-
which is a faff to set up and manage.
|
|
13
|
-
|
|
14
|
-
Both APIs claim compatibility with OpenAI's API, but that breaks down with even the simplest of requests,
|
|
15
|
-
hence this custom interface.
|
|
16
|
-
|
|
17
|
-
Despite these limitations, the Gemini model is actually quite powerful and very fast.
|
|
18
|
-
|
|
19
|
-
[HTTPX]: https://www.python-httpx.org/
|
|
20
|
-
[Pydantic]: https://docs.pydantic.dev/latest/
|
|
21
|
-
"""
|
|
22
|
-
|
|
23
1
|
from __future__ import annotations as _annotations
|
|
24
2
|
|
|
25
3
|
import os
|
|
@@ -38,7 +16,7 @@ from typing_extensions import NotRequired, TypedDict, TypeGuard, assert_never
|
|
|
38
16
|
|
|
39
17
|
from .. import UnexpectedModelBehavior, _pydantic, _utils, exceptions, result
|
|
40
18
|
from ..messages import (
|
|
41
|
-
|
|
19
|
+
ArgsDict,
|
|
42
20
|
Message,
|
|
43
21
|
ModelAnyResponse,
|
|
44
22
|
ModelStructuredResponse,
|
|
@@ -112,7 +90,7 @@ class GeminiModel(Model):
|
|
|
112
90
|
|
|
113
91
|
async def agent_model(
|
|
114
92
|
self,
|
|
115
|
-
|
|
93
|
+
function_tools: Mapping[str, AbstractToolDefinition],
|
|
116
94
|
allow_text_result: bool,
|
|
117
95
|
result_tools: Sequence[AbstractToolDefinition] | None,
|
|
118
96
|
) -> GeminiAgentModel:
|
|
@@ -121,7 +99,7 @@ class GeminiModel(Model):
|
|
|
121
99
|
model_name=self.model_name,
|
|
122
100
|
auth=self.auth,
|
|
123
101
|
url=self.url,
|
|
124
|
-
|
|
102
|
+
function_tools=function_tools,
|
|
125
103
|
allow_text_result=allow_text_result,
|
|
126
104
|
result_tools=result_tools,
|
|
127
105
|
)
|
|
@@ -160,12 +138,12 @@ class GeminiAgentModel(AgentModel):
|
|
|
160
138
|
model_name: GeminiModelName,
|
|
161
139
|
auth: AuthProtocol,
|
|
162
140
|
url: str,
|
|
163
|
-
|
|
141
|
+
function_tools: Mapping[str, AbstractToolDefinition],
|
|
164
142
|
allow_text_result: bool,
|
|
165
143
|
result_tools: Sequence[AbstractToolDefinition] | None,
|
|
166
144
|
):
|
|
167
145
|
check_allow_model_requests()
|
|
168
|
-
tools = [_function_from_abstract_tool(t) for t in
|
|
146
|
+
tools = [_function_from_abstract_tool(t) for t in function_tools.values()]
|
|
169
147
|
if result_tools is not None:
|
|
170
148
|
tools += [_function_from_abstract_tool(t) for t in result_tools]
|
|
171
149
|
|
|
@@ -442,15 +420,15 @@ class _GeminiFunctionCallPart(TypedDict):
|
|
|
442
420
|
|
|
443
421
|
|
|
444
422
|
def _function_call_part_from_call(tool: ToolCall) -> _GeminiFunctionCallPart:
|
|
445
|
-
assert isinstance(tool.args,
|
|
446
|
-
return _GeminiFunctionCallPart(function_call=_GeminiFunctionCall(name=tool.tool_name, args=tool.args.
|
|
423
|
+
assert isinstance(tool.args, ArgsDict), f'Expected ArgsObject, got {tool.args}'
|
|
424
|
+
return _GeminiFunctionCallPart(function_call=_GeminiFunctionCall(name=tool.tool_name, args=tool.args.args_dict))
|
|
447
425
|
|
|
448
426
|
|
|
449
427
|
def _structured_response_from_parts(
|
|
450
428
|
parts: list[_GeminiFunctionCallPart], timestamp: datetime | None = None
|
|
451
429
|
) -> ModelStructuredResponse:
|
|
452
430
|
return ModelStructuredResponse(
|
|
453
|
-
calls=[ToolCall.
|
|
431
|
+
calls=[ToolCall.from_dict(part['function_call']['name'], part['function_call']['args']) for part in parts],
|
|
454
432
|
timestamp=timestamp or _utils.now_utc(),
|
|
455
433
|
)
|
|
456
434
|
|
pydantic_ai/models/groq.py
CHANGED
|
@@ -109,12 +109,12 @@ class GroqModel(Model):
|
|
|
109
109
|
|
|
110
110
|
async def agent_model(
|
|
111
111
|
self,
|
|
112
|
-
|
|
112
|
+
function_tools: Mapping[str, AbstractToolDefinition],
|
|
113
113
|
allow_text_result: bool,
|
|
114
114
|
result_tools: Sequence[AbstractToolDefinition] | None,
|
|
115
115
|
) -> AgentModel:
|
|
116
116
|
check_allow_model_requests()
|
|
117
|
-
tools = [self._map_tool_definition(r) for r in
|
|
117
|
+
tools = [self._map_tool_definition(r) for r in function_tools.values()]
|
|
118
118
|
if result_tools is not None:
|
|
119
119
|
tools += [self._map_tool_definition(r) for r in result_tools]
|
|
120
120
|
return GroqAgentModel(
|
pydantic_ai/models/openai.py
CHANGED
|
@@ -89,12 +89,12 @@ class OpenAIModel(Model):
|
|
|
89
89
|
|
|
90
90
|
async def agent_model(
|
|
91
91
|
self,
|
|
92
|
-
|
|
92
|
+
function_tools: Mapping[str, AbstractToolDefinition],
|
|
93
93
|
allow_text_result: bool,
|
|
94
94
|
result_tools: Sequence[AbstractToolDefinition] | None,
|
|
95
95
|
) -> AgentModel:
|
|
96
96
|
check_allow_model_requests()
|
|
97
|
-
tools = [self._map_tool_definition(r) for r in
|
|
97
|
+
tools = [self._map_tool_definition(r) for r in function_tools.values()]
|
|
98
98
|
if result_tools is not None:
|
|
99
99
|
tools += [self._map_tool_definition(r) for r in result_tools]
|
|
100
100
|
return OpenAIAgentModel(
|
pydantic_ai/models/test.py
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
"""Utilities for testing apps built with PydanticAI."""
|
|
2
|
-
|
|
3
1
|
from __future__ import annotations as _annotations
|
|
4
2
|
|
|
5
3
|
import re
|
|
@@ -7,7 +5,7 @@ import string
|
|
|
7
5
|
from collections.abc import AsyncIterator, Iterable, Iterator, Mapping, Sequence
|
|
8
6
|
from contextlib import asynccontextmanager
|
|
9
7
|
from dataclasses import dataclass, field
|
|
10
|
-
from datetime import datetime
|
|
8
|
+
from datetime import date, datetime, timedelta
|
|
11
9
|
from typing import Any, Literal
|
|
12
10
|
|
|
13
11
|
import pydantic_core
|
|
@@ -45,10 +43,10 @@ UnSet = UnSetType()
|
|
|
45
43
|
class TestModel(Model):
|
|
46
44
|
"""A model specifically for testing purposes.
|
|
47
45
|
|
|
48
|
-
This will (by default) call all
|
|
46
|
+
This will (by default) call all tools in the agent, then return a tool response if possible,
|
|
49
47
|
otherwise a plain response.
|
|
50
48
|
|
|
51
|
-
How useful this
|
|
49
|
+
How useful this model is will vary significantly.
|
|
52
50
|
|
|
53
51
|
Apart from `__init__` derived by the `dataclass` decorator, all methods are private or match those
|
|
54
52
|
of the base class.
|
|
@@ -57,8 +55,8 @@ class TestModel(Model):
|
|
|
57
55
|
# NOTE: Avoid test discovery by pytest.
|
|
58
56
|
__test__ = False
|
|
59
57
|
|
|
60
|
-
|
|
61
|
-
"""List of
|
|
58
|
+
call_tools: list[str] | Literal['all'] = 'all'
|
|
59
|
+
"""List of tools to call. If `'all'`, all tools will be called."""
|
|
62
60
|
custom_result_text: str | None = None
|
|
63
61
|
"""If set, this text is return as the final result."""
|
|
64
62
|
custom_result_args: Any | None = None
|
|
@@ -66,25 +64,25 @@ class TestModel(Model):
|
|
|
66
64
|
seed: int = 0
|
|
67
65
|
"""Seed for generating random data."""
|
|
68
66
|
# these fields are set when the model is called by the agent
|
|
69
|
-
|
|
67
|
+
agent_model_tools: Mapping[str, AbstractToolDefinition] | None = field(default=None, init=False)
|
|
70
68
|
agent_model_allow_text_result: bool | None = field(default=None, init=False)
|
|
71
69
|
agent_model_result_tools: list[AbstractToolDefinition] | None = field(default=None, init=False)
|
|
72
70
|
|
|
73
71
|
async def agent_model(
|
|
74
72
|
self,
|
|
75
|
-
|
|
73
|
+
function_tools: Mapping[str, AbstractToolDefinition],
|
|
76
74
|
allow_text_result: bool,
|
|
77
75
|
result_tools: Sequence[AbstractToolDefinition] | None,
|
|
78
76
|
) -> AgentModel:
|
|
79
|
-
self.
|
|
77
|
+
self.agent_model_tools = function_tools
|
|
80
78
|
self.agent_model_allow_text_result = allow_text_result
|
|
81
79
|
self.agent_model_result_tools = list(result_tools) if result_tools is not None else None
|
|
82
80
|
|
|
83
|
-
if self.
|
|
84
|
-
|
|
81
|
+
if self.call_tools == 'all':
|
|
82
|
+
tool_calls = [(r.name, r) for r in function_tools.values()]
|
|
85
83
|
else:
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
tools_to_call = (function_tools[name] for name in self.call_tools)
|
|
85
|
+
tool_calls = [(r.name, r) for r in tools_to_call]
|
|
88
86
|
|
|
89
87
|
if self.custom_result_text is not None:
|
|
90
88
|
assert allow_text_result, 'Plain response not allowed, but `custom_result_text` is set.'
|
|
@@ -104,7 +102,7 @@ class TestModel(Model):
|
|
|
104
102
|
result = _utils.Either(right=None)
|
|
105
103
|
else:
|
|
106
104
|
result = _utils.Either(left=None)
|
|
107
|
-
return TestAgentModel(
|
|
105
|
+
return TestAgentModel(tool_calls, result, self.agent_model_result_tools, self.seed)
|
|
108
106
|
|
|
109
107
|
def name(self) -> str:
|
|
110
108
|
return 'test-model'
|
|
@@ -117,7 +115,7 @@ class TestAgentModel(AgentModel):
|
|
|
117
115
|
# NOTE: Avoid test discovery by pytest.
|
|
118
116
|
__test__ = False
|
|
119
117
|
|
|
120
|
-
|
|
118
|
+
tool_calls: list[tuple[str, AbstractToolDefinition]]
|
|
121
119
|
# left means the text is plain text; right means it's a function call
|
|
122
120
|
result: _utils.Either[str | None, Any | None]
|
|
123
121
|
result_tools: list[AbstractToolDefinition] | None
|
|
@@ -137,12 +135,12 @@ class TestAgentModel(AgentModel):
|
|
|
137
135
|
else:
|
|
138
136
|
yield TestStreamStructuredResponse(msg, cost)
|
|
139
137
|
|
|
140
|
-
def
|
|
138
|
+
def gen_tool_args(self, tool_def: AbstractToolDefinition) -> Any:
|
|
141
139
|
return _JsonSchemaTestData(tool_def.json_schema, self.seed).generate()
|
|
142
140
|
|
|
143
141
|
def _request(self, messages: list[Message]) -> ModelAnyResponse:
|
|
144
|
-
if self.step == 0 and self.
|
|
145
|
-
calls = [ToolCall.
|
|
142
|
+
if self.step == 0 and self.tool_calls:
|
|
143
|
+
calls = [ToolCall.from_dict(name, self.gen_tool_args(args)) for name, args in self.tool_calls]
|
|
146
144
|
self.step += 1
|
|
147
145
|
self.last_message_count = len(messages)
|
|
148
146
|
return ModelStructuredResponse(calls=calls)
|
|
@@ -152,8 +150,8 @@ class TestAgentModel(AgentModel):
|
|
|
152
150
|
new_retry_names = {m.tool_name for m in new_messages if isinstance(m, RetryPrompt)}
|
|
153
151
|
if new_retry_names:
|
|
154
152
|
calls = [
|
|
155
|
-
ToolCall.
|
|
156
|
-
for name, args in self.
|
|
153
|
+
ToolCall.from_dict(name, self.gen_tool_args(args))
|
|
154
|
+
for name, args in self.tool_calls
|
|
157
155
|
if name in new_retry_names
|
|
158
156
|
]
|
|
159
157
|
self.step += 1
|
|
@@ -162,7 +160,7 @@ class TestAgentModel(AgentModel):
|
|
|
162
160
|
if response_text := self.result.left:
|
|
163
161
|
self.step += 1
|
|
164
162
|
if response_text.value is None:
|
|
165
|
-
# build up details of
|
|
163
|
+
# build up details of tool responses
|
|
166
164
|
output: dict[str, Any] = {}
|
|
167
165
|
for message in messages:
|
|
168
166
|
if isinstance(message, ToolReturn):
|
|
@@ -170,7 +168,7 @@ class TestAgentModel(AgentModel):
|
|
|
170
168
|
if output:
|
|
171
169
|
return ModelTextResponse(content=pydantic_core.to_json(output).decode())
|
|
172
170
|
else:
|
|
173
|
-
return ModelTextResponse(content='success (no
|
|
171
|
+
return ModelTextResponse(content='success (no tool calls)')
|
|
174
172
|
else:
|
|
175
173
|
return ModelTextResponse(content=response_text.value)
|
|
176
174
|
else:
|
|
@@ -179,11 +177,11 @@ class TestAgentModel(AgentModel):
|
|
|
179
177
|
result_tool = self.result_tools[self.seed % len(self.result_tools)]
|
|
180
178
|
if custom_result_args is not None:
|
|
181
179
|
self.step += 1
|
|
182
|
-
return ModelStructuredResponse(calls=[ToolCall.
|
|
180
|
+
return ModelStructuredResponse(calls=[ToolCall.from_dict(result_tool.name, custom_result_args)])
|
|
183
181
|
else:
|
|
184
|
-
response_args = self.
|
|
182
|
+
response_args = self.gen_tool_args(result_tool)
|
|
185
183
|
self.step += 1
|
|
186
|
-
return ModelStructuredResponse(calls=[ToolCall.
|
|
184
|
+
return ModelStructuredResponse(calls=[ToolCall.from_dict(result_tool.name, response_args)])
|
|
187
185
|
|
|
188
186
|
|
|
189
187
|
@dataclass
|
|
@@ -320,8 +318,12 @@ class _JsonSchemaTestData:
|
|
|
320
318
|
|
|
321
319
|
if schema.get('maxLength') == 0:
|
|
322
320
|
return ''
|
|
323
|
-
|
|
324
|
-
|
|
321
|
+
|
|
322
|
+
if fmt := schema.get('format'):
|
|
323
|
+
if fmt == 'date':
|
|
324
|
+
return (date(2024, 1, 1) + timedelta(days=self.seed)).isoformat()
|
|
325
|
+
|
|
326
|
+
return self._char()
|
|
325
327
|
|
|
326
328
|
def _int_gen(self, schema: dict[str, Any]) -> int:
|
|
327
329
|
"""Generate an integer from a JSON Schema integer."""
|
pydantic_ai/models/vertexai.py
CHANGED
|
@@ -1,60 +1,3 @@
|
|
|
1
|
-
"""Custom interface to the `*-aiplatform.googleapis.com` API for Gemini models.
|
|
2
|
-
|
|
3
|
-
This model uses [`GeminiAgentModel`][pydantic_ai.models.gemini.GeminiAgentModel] with just the URL and auth method
|
|
4
|
-
changed from the default `GeminiModel`, it relies on the VertexAI
|
|
5
|
-
[`generateContent` function endpoint](https://cloud.google.com/vertex-ai/docs/reference/rest/v1/projects.locations.endpoints/generateContent)
|
|
6
|
-
and `streamGenerateContent` function endpoints
|
|
7
|
-
having the same schemas as the equivalent [Gemini endpoints][pydantic_ai.models.gemini.GeminiModel].
|
|
8
|
-
|
|
9
|
-
There are four advantages of using this API over the `generativelanguage.googleapis.com` API which
|
|
10
|
-
[`GeminiModel`][pydantic_ai.models.gemini.GeminiModel] uses, and one big disadvantage.
|
|
11
|
-
|
|
12
|
-
Advantages:
|
|
13
|
-
|
|
14
|
-
1. The VertexAI API seems to be less flakey, less likely to occasionally return a 503 response.
|
|
15
|
-
2. You can
|
|
16
|
-
[purchase provisioned throughput](https://cloud.google.com/vertex-ai/generative-ai/docs/provisioned-throughput#purchase-provisioned-throughput)
|
|
17
|
-
with VertexAI.
|
|
18
|
-
3. If you're running PydanticAI inside GCP, you don't need to set up authentication, it should "just work".
|
|
19
|
-
4. You can decide which region to use, which might be important from a regulatory perspective,
|
|
20
|
-
and might improve latency.
|
|
21
|
-
|
|
22
|
-
Disadvantage:
|
|
23
|
-
|
|
24
|
-
1. When authorization doesn't just work, it's much more painful to set up than an API key.
|
|
25
|
-
|
|
26
|
-
## Example Usage
|
|
27
|
-
|
|
28
|
-
With the default google project already configured in your environment:
|
|
29
|
-
|
|
30
|
-
```py title="vertex_example_env.py"
|
|
31
|
-
from pydantic_ai import Agent
|
|
32
|
-
from pydantic_ai.models.vertexai import VertexAIModel
|
|
33
|
-
|
|
34
|
-
model = VertexAIModel('gemini-1.5-flash')
|
|
35
|
-
agent = Agent(model)
|
|
36
|
-
result = agent.run_sync('Tell me a joke.')
|
|
37
|
-
print(result.data)
|
|
38
|
-
#> Did you hear about the toothpaste scandal? They called it Colgate.
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Or using a service account JSON file:
|
|
42
|
-
|
|
43
|
-
```py title="vertex_example_service_account.py"
|
|
44
|
-
from pydantic_ai import Agent
|
|
45
|
-
from pydantic_ai.models.vertexai import VertexAIModel
|
|
46
|
-
|
|
47
|
-
model = VertexAIModel(
|
|
48
|
-
'gemini-1.5-flash',
|
|
49
|
-
service_account_file='path/to/service-account.json',
|
|
50
|
-
)
|
|
51
|
-
agent = Agent(model)
|
|
52
|
-
result = agent.run_sync('Tell me a joke.')
|
|
53
|
-
print(result.data)
|
|
54
|
-
#> Did you hear about the toothpaste scandal? They called it Colgate.
|
|
55
|
-
```
|
|
56
|
-
"""
|
|
57
|
-
|
|
58
1
|
from __future__ import annotations as _annotations
|
|
59
2
|
|
|
60
3
|
from collections.abc import Mapping, Sequence
|
|
@@ -166,7 +109,7 @@ class VertexAIModel(Model):
|
|
|
166
109
|
|
|
167
110
|
async def agent_model(
|
|
168
111
|
self,
|
|
169
|
-
|
|
112
|
+
function_tools: Mapping[str, AbstractToolDefinition],
|
|
170
113
|
allow_text_result: bool,
|
|
171
114
|
result_tools: Sequence[AbstractToolDefinition] | None,
|
|
172
115
|
) -> GeminiAgentModel:
|
|
@@ -176,7 +119,7 @@ class VertexAIModel(Model):
|
|
|
176
119
|
model_name=self.model_name,
|
|
177
120
|
auth=auth,
|
|
178
121
|
url=url,
|
|
179
|
-
|
|
122
|
+
function_tools=function_tools,
|
|
180
123
|
allow_text_result=allow_text_result,
|
|
181
124
|
result_tools=result_tools,
|
|
182
125
|
)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: pydantic-ai-slim
|
|
3
|
-
Version: 0.0.
|
|
4
|
-
Summary: Agent Framework / shim to use Pydantic with LLMs
|
|
3
|
+
Version: 0.0.7
|
|
4
|
+
Summary: Agent Framework / shim to use Pydantic with LLMs, slim package
|
|
5
5
|
Author-email: Samuel Colvin <samuel@pydantic.dev>
|
|
6
6
|
License: MIT
|
|
7
7
|
Classifier: Development Status :: 4 - Beta
|
|
@@ -40,10 +40,14 @@ Requires-Dist: google-auth>=2.36.0; extra == 'vertexai'
|
|
|
40
40
|
Requires-Dist: requests>=2.32.3; extra == 'vertexai'
|
|
41
41
|
Description-Content-Type: text/markdown
|
|
42
42
|
|
|
43
|
-
#
|
|
43
|
+
# PydanticAI Slim
|
|
44
44
|
|
|
45
45
|
[](https://github.com/pydantic/pydantic-ai/actions/workflows/ci.yml?query=branch%3Amain)
|
|
46
46
|
[](https://coverage-badge.samuelcolvin.workers.dev/redirect/pydantic/pydantic-ai)
|
|
47
47
|
[](https://pypi.python.org/pypi/pydantic-ai)
|
|
48
48
|
[](https://github.com/pydantic/pydantic-ai)
|
|
49
49
|
[](https://github.com/pydantic/pydantic-ai/blob/main/LICENSE)
|
|
50
|
+
|
|
51
|
+
PydanticAI core logic with minimal required dependencies.
|
|
52
|
+
|
|
53
|
+
For more information on how to use this package see [ai.pydantic.dev/install](https://ai.pydantic.dev/install/).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
pydantic_ai/__init__.py,sha256=KaTzG8uBSEpfxk_y2a_O_R2Xa53GXYfiigjWtD4PCeI,317
|
|
2
|
+
pydantic_ai/_griffe.py,sha256=pRjCJ6B1hhx6k46XJgl9zF6aRYxRmqEZKFok8unp4Iw,3449
|
|
3
|
+
pydantic_ai/_pydantic.py,sha256=j1kObPIUDwn1VOHbJBwMFbWLxHM_OYRH7GFAda68ZC0,8010
|
|
4
|
+
pydantic_ai/_result.py,sha256=cAqfPipK39cz-p-ftlJ83Q5_LI1TRb3-HH-iivb5rEM,9674
|
|
5
|
+
pydantic_ai/_system_prompt.py,sha256=63egOej8zHsDVOInPayn0EEEDXKd0HVAbbrqXUTV96s,1092
|
|
6
|
+
pydantic_ai/_tool.py,sha256=5Q9XaGOEXbyOLS644osB1AA5EMoJkr4eYK60MVZo0Z8,4528
|
|
7
|
+
pydantic_ai/_utils.py,sha256=eNb7f3-ZQC8WDEa87iUcXGQ-lyuutFQG-5yBCMD4Vvs,8227
|
|
8
|
+
pydantic_ai/agent.py,sha256=RhLl6tf6kogtauqQr3U1ufg712k-g002YPcio9-BPB4,34242
|
|
9
|
+
pydantic_ai/dependencies.py,sha256=EHvD68AFkItxMnfHzJLG7T_AD1RGI2MZOfzm1v89hGQ,2399
|
|
10
|
+
pydantic_ai/exceptions.py,sha256=ko_47M0k6Rhg9mUC9P1cj7N4LCH6cC0pEsF65A2vL-U,1561
|
|
11
|
+
pydantic_ai/messages.py,sha256=tyBs2ucqIrKjjMIX2bvjMaZ6BMxim4VBkmCzTbeH-AI,7493
|
|
12
|
+
pydantic_ai/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
pydantic_ai/result.py,sha256=Gs2ZjuFJgONGJm8M5R2mGph5-lUFLBg7FxVrs2CVDPs,13525
|
|
14
|
+
pydantic_ai/models/__init__.py,sha256=Cx8PjEsi5gkNOVQic32sf4CmM-A3pRu1LcjpM6poiBI,10138
|
|
15
|
+
pydantic_ai/models/function.py,sha256=Mzc-zXnb2RayWAA8N9NS7KGF49do1S-VW3U9fkc661o,10045
|
|
16
|
+
pydantic_ai/models/gemini.py,sha256=2mD5MJT7qaiMAWrySLjeUFSVoMYhYOS4V7ueTOxzkdA,26472
|
|
17
|
+
pydantic_ai/models/groq.py,sha256=Tx2yU3ysmPLBmWGsjzES-XcumzrsoBtB7spCnJBlLiM,14947
|
|
18
|
+
pydantic_ai/models/openai.py,sha256=5ihH25CrS0tnZNW-BZw4GyPe8V-IxIHWw3B9ulPVjQE,14931
|
|
19
|
+
pydantic_ai/models/test.py,sha256=HOS3_u0n3nYWJmBFPsAFpZ9gfHiZLauz24o825q3e9M,14443
|
|
20
|
+
pydantic_ai/models/vertexai.py,sha256=xHatvwRn7_vyqmp3aDtHqryYAq8NoeXgXVATkj7yHuw,9088
|
|
21
|
+
pydantic_ai_slim-0.0.7.dist-info/METADATA,sha256=UeP3i7vUZv1MCKG-VGLZUwEO8kbcCzzLpD6RsAN_Vbs,2561
|
|
22
|
+
pydantic_ai_slim-0.0.7.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
23
|
+
pydantic_ai_slim-0.0.7.dist-info/RECORD,,
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
pydantic_ai/__init__.py,sha256=ghn5MI2Ygw6azR3i7RTw54B041pllt1E520ILK1kM5Y,319
|
|
2
|
-
pydantic_ai/_griffe.py,sha256=pRjCJ6B1hhx6k46XJgl9zF6aRYxRmqEZKFok8unp4Iw,3449
|
|
3
|
-
pydantic_ai/_pydantic.py,sha256=JslSZjj8Ni98oGfrmbS3RGM0B0_PdNZynvgAW2CnXeU,8056
|
|
4
|
-
pydantic_ai/_result.py,sha256=_qVaq_gmQyPWlyFn_9unafPqOiwSgoa23qxvUwMypo8,9683
|
|
5
|
-
pydantic_ai/_retriever.py,sha256=HxYDfpvcOrhv9gq_1CKRIRAFyLs0qquJGDO9LAWy8ok,4638
|
|
6
|
-
pydantic_ai/_system_prompt.py,sha256=PfNCuG45mM65HtoTiYKz6pWv_CQ9dtxPjl-u8BEn2z4,1094
|
|
7
|
-
pydantic_ai/_utils.py,sha256=7tPzgiiDUWAZGH6zmBZ91CUN3aFSv0szEkw0RDbPLJg,8128
|
|
8
|
-
pydantic_ai/agent.py,sha256=nFwHI3w8JpCqze5UX5TAxiZsSSyavsyKo2W5ITNke1g,34454
|
|
9
|
-
pydantic_ai/dependencies.py,sha256=6IawVRoLSefOtO6oCsxB3kndJRVEP1-DHSE9-2T3ZW8,2509
|
|
10
|
-
pydantic_ai/exceptions.py,sha256=bcde_Xg2yCnSYduhTbbtH3_9kQR6UuKqWJNEzfxiKyw,1566
|
|
11
|
-
pydantic_ai/messages.py,sha256=tmZOFCGVIEmSk8YYQ9qiyb6Q-6DTiqOzfWiryCHVQpY,7536
|
|
12
|
-
pydantic_ai/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
pydantic_ai/result.py,sha256=Gs2ZjuFJgONGJm8M5R2mGph5-lUFLBg7FxVrs2CVDPs,13525
|
|
14
|
-
pydantic_ai/models/__init__.py,sha256=gAH9jbSk6ezqwJpCoVHbdMwULz9mu5VuguARUqKfN8w,10140
|
|
15
|
-
pydantic_ai/models/function.py,sha256=dv1a1RhfdXqwXMFI_y8QzPWJj_YW6Q7twvKvO98JVUE,10124
|
|
16
|
-
pydantic_ai/models/gemini.py,sha256=vHg91s9UzqU3teNnvOJLXkYI8Gu9Wt1vhEu6zB3Ttd8,27677
|
|
17
|
-
pydantic_ai/models/groq.py,sha256=7zKpFg3Fvc-S2n_EroQg5AAD5cOj4y7SKQ7VjMiNSUs,14939
|
|
18
|
-
pydantic_ai/models/openai.py,sha256=_XZ0eDbk3Q3_V2MlMMaZArzYGSm2VuBR47lv4TmFgjY,14923
|
|
19
|
-
pydantic_ai/models/test.py,sha256=T53NKS7pQbCgzo7QIFGdObt3zbDs5t-tjSlkDNsKCuo,14523
|
|
20
|
-
pydantic_ai/models/vertexai.py,sha256=YiYB82WOB5Kp4NjYhweN0ZNjE39-dnpDseMyoyf6Evk,11294
|
|
21
|
-
pydantic_ai_slim-0.0.6a3.dist-info/METADATA,sha256=TeguKgNtjlIP01cF8_IUO3w20nDpAPs4i9u1FyBZBmY,2372
|
|
22
|
-
pydantic_ai_slim-0.0.6a3.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
23
|
-
pydantic_ai_slim-0.0.6a3.dist-info/RECORD,,
|
|
File without changes
|