Skip to content

Commit 7e8ebec

Browse files
authored
Raise error when trying to use Google built-in tools with user/output tools (#2777)
1 parent 175ef81 commit 7e8ebec

File tree

6 files changed

+162
-32
lines changed

6 files changed

+162
-32
lines changed

docs/builtin-tools.md

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# Builtin Tools
22

3-
Builtin tools are native tools provided by LLM providers that can be used to enhance your agent's capabilities. Unlike [common tools](common-tools.md), which are custom implementations that PydanticAI executes, builtin tools are executed directly by the model provider.
3+
Builtin tools are native tools provided by LLM providers that can be used to enhance your agent's capabilities. Unlike [common tools](common-tools.md), which are custom implementations that Pydantic AI executes, builtin tools are executed directly by the model provider.
44

55
## Overview
66

7-
PydanticAI supports the following builtin tools:
7+
Pydantic AI supports the following builtin tools:
88

99
- **[`WebSearchTool`][pydantic_ai.builtin_tools.WebSearchTool]**: Allows agents to search the web
1010
- **[`CodeExecutionTool`][pydantic_ai.builtin_tools.CodeExecutionTool]**: Enables agents to execute code in a secure environment
@@ -13,7 +13,9 @@ PydanticAI supports the following builtin tools:
1313
These tools are passed to the agent via the `builtin_tools` parameter and are executed by the model provider's infrastructure.
1414

1515
!!! warning "Provider Support"
16-
Not all model providers support builtin tools. If you use a builtin tool with an unsupported provider, PydanticAI will raise a [`UserError`][pydantic_ai.exceptions.UserError] when you try to run the agent.
16+
Not all model providers support builtin tools. If you use a builtin tool with an unsupported provider, Pydantic AI will raise a [`UserError`][pydantic_ai.exceptions.UserError] when you try to run the agent.
17+
18+
If a provider supports a built-in tool that is not currently supported by Pydantic AI, please file an issue.
1719

1820
## Web Search Tool
1921

@@ -26,16 +28,13 @@ making it ideal for queries that require up-to-date data.
2628
|----------|-----------|-------|
2729
| OpenAI || Full feature support |
2830
| Anthropic || Full feature support |
29-
| Groq || Limited parameter support |
30-
| Google || No parameter support |
31+
| Groq || Limited parameter support. To use web search capabilities with Groq, you need to use the [compound models](https://console.groq.com/docs/compound). |
32+
| Google || No parameter support. Google does not support using built-in tools and user tools (including [output tools](output.md#tool-output)) at the same time. To use structured output, use [`PromptedOutput`](output.md#prompted-output) instead. |
3133
| Bedrock || Not supported |
3234
| Mistral || Not supported |
3335
| Cohere || Not supported |
3436
| HuggingFace || Not supported |
3537

36-
!!! note "Groq Support"
37-
To use web search capabilities with Groq, you need to use the [compound models](https://console.groq.com/docs/compound).
38-
3938
### Usage
4039

4140
```py title="web_search_basic.py"
@@ -97,16 +96,16 @@ in a secure environment, making it perfect for computational tasks, data analysi
9796

9897
### Provider Support
9998

100-
| Provider | Supported |
101-
|----------|-----------|
102-
| OpenAI ||
103-
| Anthropic ||
104-
| Google ||
105-
| Groq ||
106-
| Bedrock ||
107-
| Mistral ||
108-
| Cohere ||
109-
| HuggingFace ||
99+
| Provider | Supported | Notes |
100+
|----------|-----------|-------|
101+
| OpenAI || |
102+
| Anthropic || Google does not support using built-in tools and user tools (including [output tools](output.md#tool-output)) at the same time. To use structured output, use [`PromptedOutput`](output.md#prompted-output) instead. |
103+
| Google || |
104+
| Groq || |
105+
| Bedrock || |
106+
| Mistral || |
107+
| Cohere || |
108+
| HuggingFace || |
110109

111110
### Usage
112111

@@ -126,16 +125,16 @@ allowing it to pull up-to-date information from the web.
126125

127126
### Provider Support
128127

129-
| Provider | Supported |
130-
|----------|-----------|
131-
| Google ||
132-
| OpenAI ||
133-
| Anthropic ||
134-
| Groq ||
135-
| Bedrock ||
136-
| Mistral ||
137-
| Cohere ||
138-
| HuggingFace ||
128+
| Provider | Supported | Notes |
129+
|----------|-----------|-------|
130+
| Google || Google does not support using built-in tools and user tools (including [output tools](output.md#tool-output)) at the same time. To use structured output, use [`PromptedOutput`](output.md#prompted-output) instead. |
131+
| OpenAI || |
132+
| Anthropic || |
133+
| Groq || |
134+
| Bedrock || |
135+
| Mistral || |
136+
| Cohere || |
137+
| HuggingFace || |
139138

140139
### Usage
141140

pydantic_ai_slim/pydantic_ai/models/gemini.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,9 @@ async def _make_request(
211211
generation_config = _settings_to_generation_config(model_settings)
212212
if model_request_parameters.output_mode == 'native':
213213
if tools:
214-
raise UserError('Gemini does not support structured output and tools at the same time.')
214+
raise UserError(
215+
'Gemini does not support `NativeOutput` and tools at the same time. Use `output_type=ToolOutput(...)` instead.'
216+
)
215217

216218
generation_config['response_mime_type'] = 'application/json'
217219

pydantic_ai_slim/pydantic_ai/models/google.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,14 @@ async def request_stream(
264264
yield await self._process_streamed_response(response, model_request_parameters) # type: ignore
265265

266266
def _get_tools(self, model_request_parameters: ModelRequestParameters) -> list[ToolDict] | None:
267+
if model_request_parameters.builtin_tools:
268+
if model_request_parameters.output_tools:
269+
raise UserError(
270+
'Gemini does not support output tools and built-in tools at the same time. Use `output_type=PromptedOutput(...)` instead.'
271+
)
272+
if model_request_parameters.function_tools:
273+
raise UserError('Gemini does not support user tools and built-in tools at the same time.')
274+
267275
tools: list[ToolDict] = [
268276
ToolDict(function_declarations=[_function_declaration_from_tool(t)])
269277
for t in model_request_parameters.tool_defs.values()
@@ -334,7 +342,9 @@ async def _build_content_and_config(
334342
response_schema = None
335343
if model_request_parameters.output_mode == 'native':
336344
if tools:
337-
raise UserError('Gemini does not support structured output and tools at the same time.')
345+
raise UserError(
346+
'Gemini does not support `NativeOutput` and tools at the same time. Use `output_type=ToolOutput(...)` instead.'
347+
)
338348
response_mime_type = 'application/json'
339349
output_object = model_request_parameters.output_object
340350
assert output_object is not None
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
interactions:
2+
- request:
3+
headers:
4+
accept:
5+
- '*/*'
6+
accept-encoding:
7+
- gzip, deflate
8+
connection:
9+
- keep-alive
10+
content-length:
11+
- '526'
12+
content-type:
13+
- application/json
14+
host:
15+
- generativelanguage.googleapis.com
16+
method: POST
17+
parsed_body:
18+
contents:
19+
- parts:
20+
- text: What is the largest city in Mexico?
21+
role: user
22+
generationConfig: {}
23+
systemInstruction:
24+
parts:
25+
- text: |-
26+
Always respond with a JSON object that's compatible with this schema:
27+
28+
{"properties": {"city": {"type": "string"}, "country": {"type": "string"}}, "required": ["city", "country"], "title": "CityLocation", "type": "object"}
29+
30+
Don't include any text or Markdown fencing before or after.
31+
role: user
32+
tools:
33+
- urlContext: {}
34+
uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent
35+
response:
36+
headers:
37+
alt-svc:
38+
- h3=":443"; ma=2592000,h3-29=":443"; ma=2592000
39+
content-length:
40+
- '626'
41+
content-type:
42+
- application/json; charset=UTF-8
43+
server-timing:
44+
- gfet4t7; dur=780
45+
transfer-encoding:
46+
- chunked
47+
vary:
48+
- Origin
49+
- X-Origin
50+
- Referer
51+
parsed_body:
52+
candidates:
53+
- content:
54+
parts:
55+
- text: '{"city": "Mexico City", "country": "Mexico"}'
56+
role: model
57+
finishReason: STOP
58+
groundingMetadata: {}
59+
index: 0
60+
modelVersion: gemini-2.5-flash
61+
responseId: 6Xq3aPnXNtqKqtsP8ZuDyAc
62+
usageMetadata:
63+
candidatesTokenCount: 13
64+
promptTokenCount: 83
65+
promptTokensDetails:
66+
- modality: TEXT
67+
tokenCount: 83
68+
thoughtsTokenCount: 33
69+
totalTokenCount: 129
70+
status:
71+
code: 200
72+
message: OK
73+
version: 1

tests/models/test_gemini.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import datetime
66
import json
7+
import re
78
from collections.abc import AsyncIterator, Callable, Sequence
89
from dataclasses import dataclass
910
from datetime import timezone
@@ -1868,7 +1869,12 @@ class CityLocation(BaseModel):
18681869
async def get_user_country() -> str:
18691870
return 'Mexico' # pragma: no cover
18701871

1871-
with pytest.raises(UserError, match='Gemini does not support structured output and tools at the same time.'):
1872+
with pytest.raises(
1873+
UserError,
1874+
match=re.escape(
1875+
'Gemini does not support `NativeOutput` and tools at the same time. Use `output_type=ToolOutput(...)` instead.'
1876+
),
1877+
):
18721878
await agent.run('What is the largest city in the user country?')
18731879

18741880

tests/models/test_google.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import datetime
44
import os
5+
import re
56
from typing import Any
67

78
import pytest
@@ -1418,7 +1419,12 @@ class CityLocation(BaseModel):
14181419
async def get_user_country() -> str:
14191420
return 'Mexico' # pragma: no cover
14201421

1421-
with pytest.raises(UserError, match='Gemini does not support structured output and tools at the same time.'):
1422+
with pytest.raises(
1423+
UserError,
1424+
match=re.escape(
1425+
'Gemini does not support `NativeOutput` and tools at the same time. Use `output_type=ToolOutput(...)` instead.'
1426+
),
1427+
):
14221428
await agent.run('What is the largest city in the user country?')
14231429

14241430

@@ -1787,3 +1793,37 @@ def test_map_usage():
17871793
},
17881794
)
17891795
)
1796+
1797+
1798+
async def test_google_builtin_tools_with_other_tools(allow_model_requests: None, google_provider: GoogleProvider):
1799+
m = GoogleModel('gemini-2.5-flash', provider=google_provider)
1800+
1801+
agent = Agent(m, builtin_tools=[UrlContextTool()])
1802+
1803+
@agent.tool_plain
1804+
async def get_user_country() -> str:
1805+
return 'Mexico' # pragma: no cover
1806+
1807+
with pytest.raises(
1808+
UserError,
1809+
match=re.escape('Gemini does not support user tools and built-in tools at the same time.'),
1810+
):
1811+
await agent.run('What is the largest city in the user country?')
1812+
1813+
class CityLocation(BaseModel):
1814+
city: str
1815+
country: str
1816+
1817+
agent = Agent(m, output_type=ToolOutput(CityLocation), builtin_tools=[UrlContextTool()])
1818+
1819+
with pytest.raises(
1820+
UserError,
1821+
match=re.escape(
1822+
'Gemini does not support output tools and built-in tools at the same time. Use `output_type=PromptedOutput(...)` instead.'
1823+
),
1824+
):
1825+
await agent.run('What is the largest city in Mexico?')
1826+
1827+
agent = Agent(m, output_type=PromptedOutput(CityLocation), builtin_tools=[UrlContextTool()])
1828+
result = await agent.run('What is the largest city in Mexico?')
1829+
assert result.output == snapshot(CityLocation(city='Mexico City', country='Mexico'))

0 commit comments

Comments
 (0)