Skip to content

Commit 0a7d2ac

Browse files
authored
Make AbstractBuiltinTool serializable and work with durable execution (#3176)
1 parent bac476f commit 0a7d2ac

File tree

4 files changed

+334
-8
lines changed

4 files changed

+334
-8
lines changed

pydantic_ai_slim/pydantic_ai/builtin_tools.py

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,12 @@
22

33
from abc import ABC
44
from dataclasses import dataclass
5-
from typing import TYPE_CHECKING, Literal
5+
from typing import Annotated, Any, Literal, Union
66

7+
import pydantic
8+
from pydantic_core import core_schema
79
from typing_extensions import TypedDict
810

9-
if TYPE_CHECKING:
10-
from .builtin_tools import AbstractBuiltinTool
11-
1211
__all__ = (
1312
'AbstractBuiltinTool',
1413
'WebSearchTool',
@@ -19,6 +18,8 @@
1918
'MemoryTool',
2019
)
2120

21+
_BUILTIN_TOOL_TYPES: dict[str, type[AbstractBuiltinTool]] = {}
22+
2223

2324
@dataclass(kw_only=True)
2425
class AbstractBuiltinTool(ABC):
@@ -32,6 +33,26 @@ class AbstractBuiltinTool(ABC):
3233
kind: str = 'unknown_builtin_tool'
3334
"""Built-in tool identifier, this should be available on all built-in tools as a discriminator."""
3435

36+
def __init_subclass__(cls, **kwargs: Any) -> None:
37+
super().__init_subclass__(**kwargs)
38+
_BUILTIN_TOOL_TYPES[cls.kind] = cls
39+
40+
@classmethod
41+
def __get_pydantic_core_schema__(
42+
cls, _source_type: Any, handler: pydantic.GetCoreSchemaHandler
43+
) -> core_schema.CoreSchema:
44+
if cls is not AbstractBuiltinTool:
45+
return handler(cls)
46+
47+
tools = _BUILTIN_TOOL_TYPES.values()
48+
if len(tools) == 1: # pragma: no cover
49+
tools_type = next(iter(tools))
50+
else:
51+
tools_annotated = [Annotated[tool, pydantic.Tag(tool.kind)] for tool in tools]
52+
tools_type = Annotated[Union[tuple(tools_annotated)], pydantic.Discriminator(_tool_discriminator)] # noqa: UP007
53+
54+
return handler(tools_type)
55+
3556

3657
@dataclass(kw_only=True)
3758
class WebSearchTool(AbstractBuiltinTool):
@@ -120,6 +141,7 @@ class WebSearchUserLocation(TypedDict, total=False):
120141
"""The timezone of the user's location."""
121142

122143

144+
@dataclass(kw_only=True)
123145
class CodeExecutionTool(AbstractBuiltinTool):
124146
"""A builtin tool that allows your agent to execute code.
125147
@@ -134,6 +156,7 @@ class CodeExecutionTool(AbstractBuiltinTool):
134156
"""The kind of tool."""
135157

136158

159+
@dataclass(kw_only=True)
137160
class UrlContextTool(AbstractBuiltinTool):
138161
"""Allows your agent to access contents from URLs.
139162
@@ -227,6 +250,7 @@ class ImageGenerationTool(AbstractBuiltinTool):
227250
"""The kind of tool."""
228251

229252

253+
@dataclass(kw_only=True)
230254
class MemoryTool(AbstractBuiltinTool):
231255
"""A builtin tool that allows your agent to use memory.
232256
@@ -237,3 +261,10 @@ class MemoryTool(AbstractBuiltinTool):
237261

238262
kind: str = 'memory'
239263
"""The kind of tool."""
264+
265+
266+
def _tool_discriminator(tool_data: dict[str, Any] | AbstractBuiltinTool) -> str:
267+
if isinstance(tool_data, dict):
268+
return tool_data.get('kind', AbstractBuiltinTool.kind)
269+
else:
270+
return tool_data.kind
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
interactions:
2+
- request:
3+
headers:
4+
accept:
5+
- application/json
6+
accept-encoding:
7+
- gzip, deflate
8+
connection:
9+
- keep-alive
10+
content-length:
11+
- '336'
12+
content-type:
13+
- application/json
14+
host:
15+
- api.openai.com
16+
method: POST
17+
parsed_body:
18+
include:
19+
- reasoning.encrypted_content
20+
input:
21+
- content: In one sentence, what is the top news story in my country today?
22+
role: user
23+
model: gpt-5
24+
stream: false
25+
tool_choice: auto
26+
tools:
27+
- search_context_size: medium
28+
type: web_search
29+
user_location:
30+
city: Mexico City
31+
country: MX
32+
type: approximate
33+
uri: https://api.openai.com/v1/responses
34+
response:
35+
headers:
36+
alt-svc:
37+
- h3=":443"; ma=86400
38+
connection:
39+
- keep-alive
40+
content-length:
41+
- '12089'
42+
content-type:
43+
- application/json
44+
openai-organization:
45+
- pydantic-28gund
46+
openai-processing-ms:
47+
- '45591'
48+
openai-project:
49+
- proj_dKobscVY9YJxeEaDJen54e3d
50+
openai-version:
51+
- '2020-10-01'
52+
strict-transport-security:
53+
- max-age=31536000; includeSubDomains; preload
54+
transfer-encoding:
55+
- chunked
56+
parsed_body:
57+
background: false
58+
billing:
59+
payer: developer
60+
created_at: 1760537022
61+
error: null
62+
id: resp_0cc772278fa4f4140068efa9be8878819ca691ebcbe9f1f6be
63+
incomplete_details: null
64+
instructions: null
65+
max_output_tokens: null
66+
max_tool_calls: null
67+
metadata: {}
68+
model: gpt-5-2025-08-07
69+
object: response
70+
output:
71+
- encrypted_content: gAAAAABo76nsGZ3D61NVA0XbnShNHmz5T4RonYmRGx_pRGkEbVDw7m4Ld84NA0js4svXie5FHgTxnkjohBfv597Y9YkQPDKDKkT80tQ41fo4SYkJ4r3BUtgD-DNrpssBBlK_uT-FfWeJlwTTvz6hfl1jP8NK54ZThlLzeSyhoDkQoiJf7d7rmjro49oy8owOCGlod1oiBBcZoF4zXL04lZXeOmrN40e6zIrEXGIb-iC2NL7O2R3GbipQcbq6zy2RpIbmjgmXRTWjtaMmWcJwry4oqYsud2UHojpkhXeN-0Yh4gPfzWbgvYDO47Lc48t5UkNDuBLXezNyRX_Pmlocn-h9RKNEdkq1iZau4UyY_1Fo8LKm0hQuE4x4ZKzRed2KJM4YjwSomtcGshDGKmZw94kabHY9tDI6y5-mWFhHqvjZt50xJukx2iOC6KKKk0iKiqcCeNb4r2K0G_xhLIS-sjbG9rv_762G3AiZ041xEwbStrqovRuTu52YV6b70gnuzvEG0QF9hToUpbZZQx1prAIE1WILXAZrDxvHB2czq2PF3WP0QGWdbJpO3ZSm9osjX1qqNgoiOArBUYtOQUbYM7JtLz92Es53GVeIXlHSlE_VT92ISO6n8UyAGLYuWLd-nTELBbsjNWmOo1LFpUsi7h0rVR0pbs2jTUDRLCn74ucVxYX45dTx0P-OKfNrKDQNaRCyvi-oCFdrJUc-ecm-EaVoqz0b63HHzhKNXFsmPeFZl5gtGnXARqXrbcBgel-wviXlMLt8KaWWMrEU_yq8KjACNeQy8POZsimzWT95oMAjD_J35JgeXajkYIFHuh1ZRb0awKdXPCUp6kR-nL_cBZco6nZARImw-9VR7527KT30aGvPhSYCrtqP9QXmI94IB9mYvGsCCj1ooQ3JW4nzvThJ3KN9xNZaMkvE_pORulsxPLfKfELoy7i74HWBZ7X5_7zLB-Rv-Zj58YJiFtpVYGSzKd2UfbI8GvYaakGFfN2LqzG4k2UA2rIh97nbMfbdVtUL0PuINsHc2Qr1aHbeaiHavya4QCGt1w0cds37gy06dqdL5Hny2NzhSekkUK2vDGV-evUpIkDeK3hY93NQYgwppnmdvMDYOMUl_VnLH5m-3FIW0bTJkxC2zYR4A1lkD_xhawH2KD5lrZS4-qttoDdfWceV6PIW3J7EnP0j9vq5Twkx9phCXWOL0-_7fEKwwmC2VwVvf43rbqbF5ZQpLl_nNiicJX4nmV_umniCS_hUjmHTHHyagnJjUzD7ZHFjF7CenXS4uag4TlNEozrxEv4vcZtWd_A-zSpqYLdGty2Zp16nkeq2DhF137AZTohRpDttSis-oh18GQzgvyDb3kUyb23CHcH9DZwScI4QDiDjoE94mzOe4_MjSyrY4d4stLYr5DYlUc_zE3jWp10QiNz8Yz3K4sm8aM_cPteHRuwGBPmjYWKoNwhsLEiHP3aNE-MYpkcV4oAJk8MR3NU3asRRZUD5CNj66gkRlpcsd6dTUPSJISIZM415mE7uG8HF0inxGn_VXSIFxV7PfjdIs6WE58dwEEe7QXTpxWjkuCCykg43znRi0G3Tscuq8oU7VUe5dTAfGDA-nwkroCFWyfpHT_VX92l4BFM6Pm9GF1XJ3_cpbp_TMkrLNwkklGeZSbghTU0RqKdGfffyrdZNqRW16BESjHc7Z8SsC8QhLjGvavyFHJOBB3vgwRAFDskmFlhTKMAsJqaq_tqJ550UrrRTmjJ1sH3gOozKgryOU9i7w2rlnbn24YkA9yCvIVSaB7bW8_Z0TbhoWY_0D25OQqc8pgOGECEHDzGOX_k5wSevf6WEdxoL9wY0PpNxGz_TwA0DMOd4vModAeCGDujQSfPS7PDUm9qtZMZtwyjZ4PKQ_K09-sKg7X4PUQ9DAdygNmHQCjK0iqY-fhcJwkApX1ichrxy6M9kA1EM8rO5fD4pTaRLoqNVutc1sUBq93DXMEjE73UTCfrygIOK2brw4P1LBNQe_CP3J65obJQ8tMTXyemrnLPYIzw=
72+
id: rs_0cc772278fa4f4140068efa9bf0dcc819ca630d7697c77a632
73+
summary: []
74+
type: reasoning
75+
- action:
76+
query: Mexico top news October 15, 2025
77+
type: search
78+
id: ws_0cc772278fa4f4140068efa9c5aa14819c871c50351e98af40
79+
status: completed
80+
type: web_search_call
81+
- encrypted_content: gAAAAABo76nsliyRJLQ8_6svrujSylQVBRqPDxfWb2I2yr61HB3Rvh8mkk4ZPdzVyktR-gyzPkY-X5VtSV3rjjMCRvU6nGi8j9MmS-pULY8splSAdXCTcxm5LoH6laEAtugW0p6HlxkaehV4RLM2RNqiFbx3KMciX3JTkg9AJcchLtGiSvaV4KrI6gO7BoMY_L9lPsTAOJMCWmD6eK3S13Rc61XPEsUhLq7W4h_mz-A_y6K5Hehqu4sEzLlWgAI3bVogiULK0UYQqgmRkL5VhZCw3RP7lqV_3eDD-7hJEgH7tnW3aYkSt-6f0PmuDoDdF5UOdhgjYLyr2KGyXokoRVF310d4b7OEJsFycF4-mqtD8-r6RxjmSJVp95Oye-3X2HhXKwanOhJbtymEa77qy2hIC_8p222na1_wBo9awrBaq8T1cyGauiMEEbVcqVkhwE3mQzGYz2EzbvM-KZ7fguzOijBP3kCaJV3mF4qAVkYG_VDn1jEPd-s1CW34HwZevytQBNizpEJwysAqNHbW6rUgXQiq8gkL3N0MIGffabwI1ZckYikOpQrUw7C9kgVMgy_8jr3nlJ3c3nqLwM5dxB95orArSk-GWyk7BRa_N6htBB2xwrmcbwO8lSF5LCcW5zc6N3O24Vne3YeDVB9rfVMrOwk0pKukSuYmiZpws5U4Yuc3ZFHJfpU-Nswbl4SrjxZATG5Eyzo-ecUMIW-MsifLMwLadoxaq3nafpwcQhE4AH4U3Rgi_NOeHuP6iMeStRxUEi_Q7cRASg7TGZgRYAXVmRjaB0X_LGjA_oITzmfOKewUxG2SwtTi3X0-_dxMzQXVNvBp6nKv3THhnM-qAp9rXowmseH-WRqvsuZR0rwuYHig1nLA_gibGmWJ9VwPrhasxmA6kc-PlGkvoYE59lLL3HQYUzNuJsosSRtHix7gcmkpASBIJlPzzvo_10aE9peHNoam78vm8QcFxKA6Lrynva5qCndxXGN7Phh13mkFKmYYGwdufaHDEpeNH0qAWv2GsVHpHKENe0Ll2XqJNeK_lF-zvzFBh0DeRfVy3Lq3G6ZtNvV_sJslzcNQzqqU7gRJEsnzC-bfqJsTGc1NWbYG86OgMV9FmsUohGlDVNIMxAWwtEQXJitWR7UQpei528hz6pJj6uKzrTqoNXAUVjJcQz5DW9wR8OYwdl95FzFHGmqKn8cPTVEvnTV9ISCHPNJ4Hy6_qb2OaUh624h51eo8fvOye_qBFE4B0nnrhbmdtN9PYoxdcF_hvk0Dxt5rvOmiqeTlxueE
82+
id: rs_0cc772278fa4f4140068efa9c91ae0819cb57bf002b036ffcb
83+
summary: []
84+
type: reasoning
85+
- action:
86+
query: inundaciones Mexico octubre 2025 muertos desaparecidos Veracruz Hidalgo Puebla 15 de octubre
87+
type: search
88+
id: ws_0cc772278fa4f4140068efa9cb9a94819c86a1054697faa9d6
89+
status: completed
90+
type: web_search_call
91+
- encrypted_content: gAAAAABo76nsdJIaRBHwtMtyHUET6TXVN2QSC9B3268qye-aJa-3t_U3_I8F5URYG19B9IuVC0RDpe2iBHtKklE6KIE_Ugzg57cRDo0wiTQxRDXYwjFxKPXCNyRQHYHkbPh9Dohk8nRcNt9mARzOpwgB6qknrb8odOOhyqe3q8bZ7aAqTh-qimwxkSssyAb-AOjYGAwSXmr2GErt284wA9jughihwSsiRxBmUSwnMx2Tcg8h1Kr4i2lnGcBPgWX_0bCpXGZY4A_JItQv-6Z0erPq5R9xPiYYlMDApTbBBOvFVCUXWuDx8XeAGYm0V18fJxXiEHuktll_Tj7o9c8GDbLF6V_hSsUsPAIQ0RCoKJ6hP4ZgE5CA1ml3WQa7IbPHBU-wKgHcKhwD5eBsRkE4DFfBx7zKTnZtB8FghGYYKkx4Ww7VZVqifoO8JAMvBsmZT3CWp6z69hCg4BOaXnfTE2llfA2B4b0oBTrCl9c4qG5dfx_ELVfRxOJyr8zI_Rxw_xj-fYgCrFb8xfa8XCvbV7thZMAac0eZyq8M9PGRbjvmKn7jNPYK-c2a46Ia6t_s93xxxUtTsCr-rvn2gKTMWm9BPTWqpqkAc2ZTCYxgT7QSs77IAOQuk_9OHDNBM-h0ujCYfzISsieGSnwIwMS3hfg9qiezUcNcwHOHxH7n3T2JfNRa2k37sJmxrFJOPyMezEqar1-XQvAEtXwNcNbYIykNZ4s8G3lCJAnvEzoRzCAmiiAndtejPlVrSZUhsWazjPcDnZ458-1nnMfb-vGK0Cybz3EdNWMFnWN5bjLIP3lc_wLPa_dWSz1Dm_bTckJWKAujV1jQITft04Nlo4i9vLW5vWb7Jsml4fiSYtvuALQkNzhOlMbdKvrL29UFobkfCB1hX4Cgj-Hkn7P5WjeYtxKXkOb171JGIEav5qQcfbImOuFxhg_7EUbaefIyGa1J2eGb7DRCcmbqnur5hvzMzPGNqHxWvEunVxf9ZvUEXsPoNPyC2Jkot_ntLSlwRtuuMqu9teK6NfWi0qCJeqbhEN-OWDpFtrmSEYLr1FNS5_XM6_VZ10o1GRW8notAm5RVdn7zDvjs_s5cXv4efs0UanOOAS6e_OIJeX7VHdiKLnHLXxZ-adp5NR2kKIw0hgtvcrCR7KHvr4itOny8zy_7EfeYgX8B66favNVeu6JLGEVIOs1bN3_evQYUyRPJAn3FwM_DIUZqQ1xJVewm1RAxvaENoT_gh3pib4vdjXxeGjJg7VsAkPpiHf0TSYvGKiAtdUuUHNfNv8X5MGuh40ST8ji-UHeWkUe8fzcVoypPVNApyO9bET3RgirucglYbObm3WwucxyZDkJC9nYb1LptN6K3xvJ8a97njRCnzjE4Z0SfOtRBotblGo0IN7cCVI-SJrOlXXdvm8PUOOofKmzM9Y-Seo_Vapq3NBfLGZar7Sg1RtHXTXpJABZZ1HNgfvmw0qqKka-cUyS2qByP6d1ZRc2MTAofNcJXEiiBF62f1JXK0yMwKyCacPBQ26J7xHvmDtz-lAZGO6n8GvWJy2UkGthYZLIHTfyKfc46t74ZunD1daW5_QK7dCMMdXylK8lQaSBtK8UIzlbDcT7cIeoO-9iGSEMj623RjwMqpAJLYW8cuoccnTnH3cJ0032hhuaTyvPnW98_2QdzERSY6booWeLI5QI4yWcmJgs8gnhQC1-UEZrZ-Y3_344Xv8t2Dk-ihlHMN0aZ5MpBkT_FXqspXn5a6HIVLEhdtckz1LWK6ZZxcMDBVZAy5pqTmIs_Ro303y1_5Z_oiKSl-ISCkUhCGCCb5JpLozG5tJZTqDij8u91L4c3fgzxs2Myk-Dk8eiAtNyR1kJTPLzFTqHrFvlTXbU5UaHqBjGLhlVXY-9H7vRB4Ssk0NWyQY5jv1L2jnhy64l6KtNBqY827HVOnsaT-XWlM4McnFT1MJ-qO41NzR-gTZva-KzjDgVj50_MBmxeuBLsnDVq26Iw2d9OQpyzo_qiN_Fs2G9zpaUr84-W9mNsoV3TljiQk9-D1xdy4ZXJ7oicW1Ah-Ua4lNGI8wKHujDDX-sw0FaEQfKKPrN4Ou_lXAStakNhnvn2C3sbPlUk6Mie52ky1Pyq9L3SMwFtNa_Sx0imAANBAqekA6BMwc_Q_Y8jgmtZatBAibaWfWfcCyWNy832S6wJ5L0_fOhYVwT4PMvOw9AIwQbTSAJtuLN7zxZOv2Qw5_UnatJR6MgXbQc0BrmWI_FGuZdOuv2TMGKZkV9ynaZ5L-Dk0P90lpZwNmpbddh1y7TQNY6Os6YPwwcyPSMgTD1fY3eJPWDEv8OxRIrFJeth_tJMb-IX2fozD7ZAXUXuEFqtCFZ7yqoO-AbW8rgLWU5ILiJJk7neHSyWrsiDf0eyILU-tC1QoGuP-Md5hehvrobjVQgkARdzIUAy_8RIeG956NoG1nqIM-5tu0A8xjv2rBf5vTw5p6rbtCiQtTacMVrprXLz-p23XqrOAZuEvhc7mMJBwEJcGq8w12oil-oakW3gbHWzKWUb_6bLGYaI7uBjSH_HDtcU79uONBYGDKRG_eZa0EcupHQ8twVBNviyz2bSubTN4gWvjz4k1nHkmQBagFQzy6ogcCVldZVyLtib2TfMH64DVeEFBEM-aUKcs81R42r8aZjW_I3RFRGS8hodNs0Y1ZfkYms_kBNOZc3pw2iP4rtOLDWatSGfUWN8owiQETiOI2AD70HoXcVBmjyRpxheI_mBVz6YIxMzf0esd5U118NzOyxqfQ66EvmAzmN0iSrbR2Vucx4CojBzweQ0KoWn_HV4U3qjkdXcmdvncbTK18LPoOhS6z7BhEKXDhd1bhI1f5AyirZhqR8xvK0r6aeQdkL7OWyQoDWJM-CRtn0S4O3qucwNA1XFVPt5fF8rysehSjLN4GH0AAueDBc9_BGx0fQMsmpRjUO71W0P-C4pY-eYvUu60u6ng2YELowM7Poak8xWOsvlGqYuXj2s4Tuj6hJPeD6vPoUDaxfFPjMijSkpfASxJvNbE8DpftoPrfiXIG-LDfGIXiJISgDzBgTllf3cw1lhSYq51WfqcBaWsIvXWGGbvnfqE2bA7nRwNUBkmxd9NC38-DAAySadaHIBjsIgv7VEKKp841cvTQ4bB-8Fp1d7pZOvMsq4GjaKCQNfjNcWL87KyyJ-GonLExJStrdNGxwS_9AYNA-6KEdtz-V9YRFzbkHl4406ueOody8O7jvucmoyKFTU1S2yJ5Wbd1gNYA-6wV_II78hbI6dXjTEGvdbRWIKkTktnD6pgDhHjkdS6uzsTcUYTkj60SxMujKv2xlRevDANdgFDB0-zKkv0ntRINr5_CHmnLbW3NYAAyGQ9wOgvfNEMno0EVLQU9F596lscKSXAl4djL1F_GSSn80lb0ujdu77Xh130yORuio6SSF9DAXg1wYI8O0Il2nyJg-0DWTyy6mCG4zPmzyS6yRH64o4kOGmZBuOJSFvcF2Qy0h0h0xBvYhN9fBJh8AcjuYP9TWB8s8Z3KKYOXYlPTPlbN4BaowBVfmzG6_aJaDvUsT1bk9IruW4ENtwf6OxXs0nqEwQEGBYWDAmcdS5hESe7JuVQ1O2bLkg2fYl63VznOUIrYW5hbHMI6pZ3EMsuJD5ITzULCl-WlX_qmduuAKnnFbSzua4Rv6ZUOk_GC-BCIfcnro0RTJTna2hl8M-YP0g05v68zDFl4x1s1xsvii8VF4p_t6EGaPY00iUgB__4uWwdIkuywTAaLgvdtWonA7aCvEZr6a04ZyUD7jf6Acd1cwxkCe2Ed-GVd9f6vQcuXUwAwnuK_gsWsqLfPfA_9U3mxMABZVXjuLwxZlxGW1rxdRQZUdqeQvIBLVt3ozMyqu4mQfs0r4gaLUFff_qHkckNvbvJwUDquokNysr0e1matigksVRDUUQc5aXvR8KeQZKUvjQLh5Rv_gvUmkBO2pQQUJCG3IPQqf6lU1Y_86sxHVegaoZ52jbbEq32NO9gQ-3LlMPvmm-FTmdfaBxNZZ2GVHgjLdvmshTd3XrdrRlqXc2dwFsLbIi6Efg6Zy6Vr8HeZiHWtJjiNT6jurXE0aNkq3SSWHEW6tNcn1BXToFUI7R4vozxEzwQG9e-acnS6OZsRie5dDmQ02CXSlJYu8s8TwyXEhy3vmaGDrtVIXlBGdb-6lAjspALKC4I6k_6uwiv492gFe7usw9mvHgIe9b5y2h0z1VUZ1lr152G1Fw_2R-1C6RxwUPZuD3ZVSw5LvO18VApN4_jDwD6l6i9QcCAGgEtz5rSp1SfbYXpTCFd5ulbZAh0JgU_OUnK4QlXZL7vdfs0cKU43IQaDAwg2zJjKodk87JDoe6YmDfrFWZ_qkPWdfbVtvKOMg2bMqJVU0LbKGH3H_MKnQVnHH7UkcdTfu7wwbYIdddQHQPEzuzOwBTg9nz46nSKfZCIQ3FES6gP7bBDuw3IhwzZq30hAS0XPspU8OQuvAQcUcpjSYlKkNvWvw1ZaGE-wJsA2UgyUDeQ_gUAmvjEqXTkomk5EDiDWg4R7TMi3bzbVUylLcNOopOi_X6Quij0dWoV6tZJqKq8z1BJUeV7Dl9dcnN24MTQ4YFMV3r59zAD4_DBWkMorwfllcFB1-i3mq2RdtErHFay7BATssuoSvaVhZbfcc6OsqE6UcZTUr7ctBCKIpg5EJTAZRgZcUTUQMQYaJmCgBi5QY4UIf9oxxd8URQKz-nT2G9vKrCTRC7h2J2aG3RaM4TVYzJQUADkbj5ejVMTELy2JjG_LQLMLzd_yR4VeSOfn3RVbo_P7A5P0bOp4ftma8Qe5kDC2L2H252tNr2gU82ozygJdqi_2ZB_ladw5fojKVMe2w1dBg_g-c5HmJlIxWp0N_dj8hDTS_zC2J6X6zIFkhW9JTSRgVzdDJd8oZEGNtTbAo__R74yEHll9L52bRu68NTxrT4cQ4RIbA5dQUl0QgNwRr67roltQeWXj1eS47Pe070ntnScAgTCF8431lM2XiK1iPP6fbi23SWE6oD4JQaaAQkeHoBqjqPcTeoPSHLmpKkZS55e6XPlxbQp_exrt8yLJv9fs7wrfG0egRzJwIRd_H8vxCg7f65EUIIAyUp8TTaHpYWQJMmmWtAa035TcuwzRtswUYcWLZZWbqBB90m9EM9huFAmF6xJypAfjWFRm0Mtwh7AgqTTEuw6cnNw5lzGQKJqnP2kCiZ6gVb97A67qQZMOs_zDp8E68RgEzDIm14JEAKrIs0nI3VqrNyy0DGzyvn6TWZUAYqUdUGjchOYc_tj3_cr7_Icu1VWG48SNIlXMKHi0k09efcyTzfaaTXQS5UdlenHvFg9j1WO6oyORpaGbA3L5XUNwH1yX6d9wN8
92+
id: rs_0cc772278fa4f4140068efa9d096c0819c99fea06cc2948069
93+
summary: []
94+
type: reasoning
95+
- content:
96+
- annotations:
97+
- end_index: 281
98+
start_index: 188
99+
title: Landslides and flooding cut off 300 communities in Mexico with dozens dead and missing
100+
type: url_citation
101+
url: https://apnews.com/article/5d036e18057361281e984b44402d3b1b?utm_source=openai
102+
logprobs: []
103+
text: Severe floods and landslides across Veracruz, Hidalgo, and Puebla have cut off hundreds of communities and
104+
left dozens dead and many missing, prompting a major federal emergency response. ([apnews.com](https://apnews.com/article/5d036e18057361281e984b44402d3b1b?utm_source=openai))
105+
type: output_text
106+
id: msg_0cc772278fa4f4140068efa9e8d5c0819caea9f8f22692335d
107+
role: assistant
108+
status: completed
109+
type: message
110+
parallel_tool_calls: true
111+
previous_response_id: null
112+
prompt_cache_key: null
113+
reasoning:
114+
effort: medium
115+
summary: null
116+
safety_identifier: null
117+
service_tier: default
118+
status: completed
119+
store: true
120+
temperature: 1.0
121+
text:
122+
format:
123+
type: text
124+
verbosity: medium
125+
tool_choice: auto
126+
tools:
127+
- filters: null
128+
search_context_size: medium
129+
type: web_search
130+
user_location:
131+
city: Mexico City
132+
country: MX
133+
region: null
134+
timezone: null
135+
type: approximate
136+
top_logprobs: 0
137+
top_p: 1.0
138+
truncation: disabled
139+
usage:
140+
input_tokens: 12594
141+
input_tokens_details:
142+
cached_tokens: 3200
143+
output_tokens: 1150
144+
output_tokens_details:
145+
reasoning_tokens: 1088
146+
total_tokens: 13744
147+
user: null
148+
status:
149+
code: 200
150+
message: OK
151+
version: 1
Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,30 @@
11
from inline_snapshot import snapshot
22
from pydantic import TypeAdapter
33

4-
from pydantic_ai.models import ModelRequestParameters
4+
from pydantic_ai.builtin_tools import (
5+
CodeExecutionTool,
6+
ImageGenerationTool,
7+
MemoryTool,
8+
UrlContextTool,
9+
WebSearchTool,
10+
WebSearchUserLocation,
11+
)
12+
from pydantic_ai.models import ModelRequestParameters, ToolDefinition
13+
14+
ta = TypeAdapter(ModelRequestParameters)
515

616

717
def test_model_request_parameters_are_serializable():
818
params = ModelRequestParameters(
9-
function_tools=[], output_mode='text', allow_text_output=True, output_tools=[], output_object=None
19+
function_tools=[],
20+
builtin_tools=[],
21+
output_mode='text',
22+
allow_text_output=True,
23+
output_tools=[],
24+
output_object=None,
1025
)
11-
assert TypeAdapter(ModelRequestParameters).dump_python(params) == snapshot(
26+
dumped = ta.dump_python(params)
27+
assert dumped == snapshot(
1228
{
1329
'function_tools': [],
1430
'builtin_tools': [],
@@ -19,3 +35,77 @@ def test_model_request_parameters_are_serializable():
1935
'allow_image_output': False,
2036
}
2137
)
38+
assert ta.validate_python(dumped) == params
39+
40+
params = ModelRequestParameters(
41+
function_tools=[ToolDefinition(name='test')],
42+
builtin_tools=[
43+
WebSearchTool(user_location=WebSearchUserLocation(city='New York', country='US')),
44+
CodeExecutionTool(),
45+
UrlContextTool(),
46+
ImageGenerationTool(size='1024x1024'),
47+
MemoryTool(),
48+
],
49+
output_mode='text',
50+
allow_text_output=True,
51+
output_tools=[ToolDefinition(name='final_result')],
52+
output_object=None,
53+
)
54+
dumped = ta.dump_python(params)
55+
assert dumped == snapshot(
56+
{
57+
'function_tools': [
58+
{
59+
'name': 'test',
60+
'parameters_json_schema': {'type': 'object', 'properties': {}},
61+
'description': None,
62+
'outer_typed_dict_key': None,
63+
'strict': None,
64+
'sequential': False,
65+
'kind': 'function',
66+
'metadata': None,
67+
}
68+
],
69+
'builtin_tools': [
70+
{
71+
'kind': 'web_search',
72+
'search_context_size': 'medium',
73+
'user_location': {'city': 'New York', 'country': 'US'},
74+
'blocked_domains': None,
75+
'allowed_domains': None,
76+
'max_uses': None,
77+
},
78+
{'kind': 'code_execution'},
79+
{'kind': 'url_context'},
80+
{
81+
'kind': 'image_generation',
82+
'background': 'auto',
83+
'input_fidelity': None,
84+
'moderation': 'auto',
85+
'output_compression': 100,
86+
'output_format': None,
87+
'partial_images': 0,
88+
'quality': 'auto',
89+
'size': '1024x1024',
90+
},
91+
{'kind': 'memory'},
92+
],
93+
'output_mode': 'text',
94+
'output_object': None,
95+
'output_tools': [
96+
{
97+
'name': 'final_result',
98+
'parameters_json_schema': {'type': 'object', 'properties': {}},
99+
'description': None,
100+
'outer_typed_dict_key': None,
101+
'strict': None,
102+
'sequential': False,
103+
'kind': 'function',
104+
'metadata': None,
105+
}
106+
],
107+
'allow_text_output': True,
108+
'allow_image_output': False,
109+
}
110+
)
111+
assert ta.validate_python(dumped) == params

0 commit comments

Comments
 (0)