+ webSearchQueries:
+ - current date Utrecht
+ modelVersion: gemini-2.0-flash
+ responseId: LN42aKzNCaqvgLUP1Lz-8As
+ usageMetadata:
+ candidatesTokenCount: 19
+ candidatesTokensDetails:
+ - modality: TEXT
+ tokenCount: 19
+ promptTokenCount: 13
+ promptTokensDetails:
+ - modality: TEXT
+ tokenCount: 13
+ totalTokenCount: 32
+ status:
+ code: 200
+ message: OK
+version: 1
diff --git a/tests/models/cassettes/test_groq/test_groq_model_web_search_tool.yaml b/tests/models/cassettes/test_groq/test_groq_model_web_search_tool.yaml
new file mode 100644
index 000000000..73e9b042c
--- /dev/null
+++ b/tests/models/cassettes/test_groq/test_groq_model_web_search_tool.yaml
@@ -0,0 +1,273 @@
+interactions:
+- request:
+ headers:
+ accept:
+ - application/json
+ accept-encoding:
+ - gzip, deflate
+ connection:
+ - keep-alive
+ content-length:
+ - '106'
+ content-type:
+ - application/json
+ host:
+ - api.groq.com
+ method: POST
+ parsed_body:
+ messages:
+ - content: What day is today?
+ role: user
+ model: compound-beta
+ n: 1
+ stream: false
+ uri: https://api.groq.com/openai/v1/chat/completions
+ response:
+ headers:
+ alt-svc:
+ - h3=":443"; ma=86400
+ cache-control:
+ - private, max-age=0, no-store, no-cache, must-revalidate
+ connection:
+ - keep-alive
+ content-length:
+ - '13044'
+ content-type:
+ - application/json
+ transfer-encoding:
+ - chunked
+ vary:
+ - Origin
+ parsed_body:
+ choices:
+ - finish_reason: stop
+ index: 0
+ logprobs: null
+ message:
+ content: The current day is Tuesday.
+ executed_tools:
+ - arguments: '{"query": "What is the current date?"}'
+ index: 0
+ output: |+
+ Title: Today's Date - Find Out Quickly What's The Date Today ️
+ URL: https://calendarhours.com/todays-date/
+ Content: The current date in RFC 2822 Format with shortened day of week, numerical date, three-letter month abbreviation, year, time, and time zone is: Tue, 13 May 2025 06:07:56 -0400; The current date in Unix Epoch Format with number of seconds that have elapsed since January 1, 1970 (midnight UTC/GMT) is:
+ Score: 0.8299
+
+ Title: Today's Date | Current date now - MaxTables
+ URL: https://maxtables.com/tools/todays-date.html
+ Content: The current date, including day of the week, month, day, and year. The exact time, down to seconds. Details on the time zone, its location, and its GMT difference. A tool to select the present date. A visual calendar chart. Why would I need to check Today's Date on this platform instead of my device?
+ Score: 0.7223
+
+ Title: Current Time and Date - Exact Time!
+ URL: https://time-and-calendar.com/
+ Content: The actual time is: Mon May 12 2025 22:14:39 GMT-0700 (Pacific Daylight Time) Your computer time is: 22:14:38 The time of your computer is synchronized with our web server. This mean that it is synchonizing in real time with our server clock.
+ Score: 0.6799
+
+ Title: Today's Date - CalendarDate.com
+ URL: https://www.calendardate.com/todays.htm
+ Content: Details about today's date with count of days, weeks, and months, Sun and Moon cycles, Zodiac signs and holidays. Monday May 12, 2025 . Home; Calendars. 2025 Calendar; ... Current Season Today: Spring with 40 days until the start of Summer. S. Hemishpere flip seasons - i.e. Winter is Summer.
+ Score: 0.6416
+
+ Title: What is the date today | Today's Date
+ URL: https://www.datetoday.info/
+ Content: Master time tracking with Today's Date. Stay updated with real-time information on current date, time, day of the week, days left in the week, current day and remaining days of the year. Explore time in globally accepted formats. Keep up with the current week and month, along with the remaining weeks and months for the year. Embrace efficient time tracking with Today's Date.
+ Score: 0.6282
+
+ Title: Explore Today's Date, Time Zones, Holidays & More
+ URL: https://whatdateis.today/
+ Content: Check what date and time it is today (May 8, 2025). View current time across different time zones, upcoming holidays, and use our date calculator. Your one-stop destination for all date and time information.
+ Score: 0.6181
+
+ Title: Today's Date and Time - Date and Time Tools
+ URL: https://todaysdatetime.com/
+ Content: Discover today's exact date and time, learn about time zones, date formats, and explore our comprehensive collection of date and time tools including calculators, converters, and calendars. ... Get the exact current date and time, along with powerful calculation tools for all your scheduling needs. 12h. Today. Day 76 of year (366) Yesterday
+ Score: 0.5456
+
+ Title: Current Time Now - What time is it? - RapidTables.com
+ URL: https://www.rapidtables.com/tools/current-time.html
+ Content: This page includes the following information: Current time: hours, minutes, seconds. Today's date: day of week, month, day, year. Time zone with location and GMT offset.
+ Score: 0.4255
+
+ Title: Current Time
+ URL: https://www.timeanddate.com/
+ Content: Welcome to the world's top site for time, time zones, and astronomy. Organize your life with free online info and tools you can rely on. No sign-up needed. Sign in. News. News Home; Astronomy News; ... Current Time. Monday May 12, 2025 Roanoke Rapids, North Carolina, USA. Set home location. 11:27: 03 pm. World Clock.
+ Score: 0.3876
+
+ Title: Current local time in the United States - World clock
+ URL: https://dateandtime.info/country.php?code=US
+ Content: Time and Date of DST Change Time Change; DST started: Sunday, March 9, 2025 at 2:00 AM: The clocks were put forward an hour to 3:00 AM. DST ends: Sunday, November 2, 2025 at 2:00 AM: The clocks will be put back an hour to 1:00 AM. DST starts: Sunday, March 8, 2026 at 2:00 AM: The clocks will be put forward an hour to 3:00 AM.
+ Score: 0.3042
+
+ Title: Time.is - exact time, any time zone
+ URL: https://time.is/
+ Content: 7 million locations, 58 languages, synchronized with atomic clock time. Time.is. Get Time.is Ad-free! Exact time now: 05:08:45. Tuesday, 13 May, 2025, week 20. Sun: ↑ 05:09 ↓ 20:45 (15h 36m) - More info - Make London time default - Remove from favorite locations
+ Score: 0.2796
+
+ Title: Time in United States now
+ URL: https://time.is/United_States
+ Content: Exact time now, time zone, time difference, sunrise/sunset time and key facts for United States. Time.is. Get Time.is Ad-free! Time in United States now . 11:17:42 PM. Monday, May 12, 2025. United States (incl. dependent territories) has 11 time zones. The time zone for the capital Washington, D.C. is used here.
+ Score: 0.2726
+
+ Title: Current Local Time in the United States - timeanddate.com
+ URL: https://www.timeanddate.com/worldclock/usa
+ Content: United States time now. USA time zones and time zone map with current time in each state.
+ Score: 0.2519
+
+ Title: Current local time in United States - World Time Clock & Map
+ URL: https://24timezones.com/United-States/time
+ Content: Check the current time in United States and time zone information, the UTC offset and daylight saving time dates in 2025.
+ Score: 0.2221
+
+ Title: The World Clock — Worldwide - timeanddate.com
+ URL: https://www.timeanddate.com/worldclock/
+ Content: World time and date for cities in all time zones. International time right now. Takes into account all DST clock changes.
+ Score: 0.2134
+
+ type: search
+ reasoning: |2-
+
+ To determine the current day, I need to access real-time information. I will use the search tool to find out the current date.
+
+
+ search(What is the current date?)
+
+
+ The current date is Tuesday, May 13, 2025.
+
+
+
+ The current day is Tuesday.
+ role: assistant
+ created: 1747232793
+ id: stub
+ model: compound-beta
+ object: chat.completion
+ system_fingerprint: null
+ usage:
+ completion_time: 0.312814
+ completion_tokens: 117
+ prompt_time: 0.737839
+ prompt_tokens: 4287
+ queue_time: 1.687734
+ total_time: 1.050655
+ total_tokens: 4404
+ usage_breakdown:
+ models:
+ - model: llama-3.3-70b-versatile
+ usage:
+ completion_time: 0.033092212
+ completion_tokens: 4
+ prompt_time: 0.008749282
+ prompt_tokens: 134
+ queue_time: 0.251916212
+ total_time: 0.041841494
+ total_tokens: 138
+ - model: meta-llama/llama-4-scout-17b-16e-instruct
+ usage:
+ completion_time: 0.198660413
+ completion_tokens: 82
+ prompt_time: 0.026323206
+ prompt_tokens: 491
+ queue_time: 0.556546565
+ total_time: 0.224983619
+ total_tokens: 573
+ - model: meta-llama/llama-4-scout-17b-16e-instruct
+ usage:
+ completion_time: 0.047136438
+ completion_tokens: 24
+ prompt_time: 0.077990192
+ prompt_tokens: 1926
+ queue_time: 0.619169638
+ total_time: 0.12512663
+ total_tokens: 1950
+ - model: llama-3.3-70b-versatile
+ usage:
+ completion_time: 0.033926045
+ completion_tokens: 7
+ prompt_time: 0.624777225
+ prompt_tokens: 1736
+ queue_time: 0.26010064499999996
+ total_time: 0.65870327
+ total_tokens: 1743
+ x_groq:
+ id: stub
+ status:
+ code: 200
+ message: OK
+version: 1
diff --git a/tests/models/cassettes/test_openai/test_openai_web_search_tool.yaml b/tests/models/cassettes/test_openai/test_openai_web_search_tool.yaml
new file mode 100644
index 000000000..cd48b3ea9
--- /dev/null
+++ b/tests/models/cassettes/test_openai/test_openai_web_search_tool.yaml
@@ -0,0 +1,79 @@
+interactions:
+- request:
+ headers:
+ accept:
+ - application/json
+ accept-encoding:
+ - gzip, deflate
+ connection:
+ - keep-alive
+ content-length:
+ - '218'
+ content-type:
+ - application/json
+ host:
+ - api.openai.com
+ method: POST
+ parsed_body:
+ messages:
+ - content: You are a helpful assistant.
+ role: system
+ - content: What day is today?
+ role: user
+ model: gpt-4o-search-preview
+ stream: false
+ web_search_options:
+ search_context_size: low
+ uri: https://api.openai.com/v1/chat/completions
+ response:
+ headers:
+ access-control-expose-headers:
+ - X-Request-ID
+ alt-svc:
+ - h3=":443"; ma=86400
+ connection:
+ - keep-alive
+ content-length:
+ - '785'
+ content-type:
+ - application/json
+ openai-organization:
+ - pydantic-28gund
+ openai-processing-ms:
+ - '2051'
+ openai-version:
+ - '2020-10-01'
+ strict-transport-security:
+ - max-age=31536000; includeSubDomains; preload
+ transfer-encoding:
+ - chunked
+ parsed_body:
+ choices:
+ - finish_reason: stop
+ index: 0
+ message:
+ annotations: []
+ content: 'May 14, 2025, 8:51:29 AM '
+ refusal: null
+ role: assistant
+ created: 1747227087
+ id: chatcmpl-e05fbf7b-44c0-406d-8662-bc7a9a518747
+ model: gpt-4o-search-preview-2025-03-11
+ object: chat.completion
+ system_fingerprint: ''
+ usage:
+ completion_tokens: 17
+ completion_tokens_details:
+ accepted_prediction_tokens: 0
+ audio_tokens: 0
+ reasoning_tokens: 0
+ rejected_prediction_tokens: 0
+ prompt_tokens: 11
+ prompt_tokens_details:
+ audio_tokens: 0
+ cached_tokens: 0
+ total_tokens: 28
+ status:
+ code: 200
+ message: OK
+version: 1
diff --git a/tests/models/cassettes/test_openai/test_openai_web_search_tool_model_not_supported.yaml b/tests/models/cassettes/test_openai/test_openai_web_search_tool_model_not_supported.yaml
new file mode 100644
index 000000000..0a102fa79
--- /dev/null
+++ b/tests/models/cassettes/test_openai/test_openai_web_search_tool_model_not_supported.yaml
@@ -0,0 +1,57 @@
+interactions:
+- request:
+ headers:
+ accept:
+ - application/json
+ accept-encoding:
+ - gzip, deflate
+ connection:
+ - keep-alive
+ content-length:
+ - '203'
+ content-type:
+ - application/json
+ host:
+ - api.openai.com
+ method: POST
+ parsed_body:
+ messages:
+ - content: You are a helpful assistant.
+ role: system
+ - content: What day is today?
+ role: user
+ model: gpt-4o
+ stream: false
+ web_search_options:
+ search_context_size: low
+ uri: https://api.openai.com/v1/chat/completions
+ response:
+ headers:
+ access-control-expose-headers:
+ - X-Request-ID
+ alt-svc:
+ - h3=":443"; ma=86400
+ connection:
+ - keep-alive
+ content-length:
+ - '177'
+ content-type:
+ - application/json
+ openai-organization:
+ - pydantic-28gund
+ openai-processing-ms:
+ - '23'
+ openai-version:
+ - '2020-10-01'
+ strict-transport-security:
+ - max-age=31536000; includeSubDomains; preload
+ parsed_body:
+ error:
+ code: null
+ message: Web search options not supported with this model.
+ param: web_search_options
+ type: invalid_request_error
+ status:
+ code: 400
+ message: Bad Request
+version: 1
diff --git a/tests/models/cassettes/test_openai/test_openai_web_search_tool_with_user_location.yaml b/tests/models/cassettes/test_openai/test_openai_web_search_tool_with_user_location.yaml
new file mode 100644
index 000000000..30a557451
--- /dev/null
+++ b/tests/models/cassettes/test_openai/test_openai_web_search_tool_with_user_location.yaml
@@ -0,0 +1,91 @@
+interactions:
+- request:
+ headers:
+ accept:
+ - application/json
+ accept-encoding:
+ - gzip, deflate
+ connection:
+ - keep-alive
+ content-length:
+ - '322'
+ content-type:
+ - application/json
+ host:
+ - api.openai.com
+ method: POST
+ parsed_body:
+ messages:
+ - content: You are a helpful assistant.
+ role: system
+ - content: What is the current temperature?
+ role: user
+ model: gpt-4o-search-preview
+ stream: false
+ web_search_options:
+ search_context_size: medium
+ user_location:
+ approximate:
+ city: Utrecht
+ country: NL
+ type: approximate
+ uri: https://api.openai.com/v1/chat/completions
+ response:
+ headers:
+ access-control-expose-headers:
+ - X-Request-ID
+ alt-svc:
+ - h3=":443"; ma=86400
+ connection:
+ - keep-alive
+ content-length:
+ - '1656'
+ content-type:
+ - application/json
+ openai-organization:
+ - pydantic-28gund
+ openai-processing-ms:
+ - '3979'
+ openai-version:
+ - '2020-10-01'
+ strict-transport-security:
+ - max-age=31536000; includeSubDomains; preload
+ transfer-encoding:
+ - chunked
+ parsed_body:
+ choices:
+ - finish_reason: stop
+ index: 0
+ message:
+ annotations: []
+ content: "Het is momenteel zonnig in Utrecht met een temperatuur van 22°C.\n\n## Weer voor Utrecht, Nederland:\nHuidige
+ omstandigheden: Zonnig, 72°F (22°C)\n\nDagvoorspelling:\n* woensdag, mei 14: minimum: 48°F (9°C), maximum: 71°F
+ (22°C), beschrijving: Afnemende bewolking\n* donderdag, mei 15: minimum: 43°F (6°C), maximum: 67°F (20°C), beschrijving:
+ Na een bewolkt begin keert de zon terug\n* vrijdag, mei 16: minimum: 45°F (7°C), maximum: 64°F (18°C), beschrijving:
+ Overwegend zonnig\n* zaterdag, mei 17: minimum: 47°F (9°C), maximum: 68°F (20°C), beschrijving: Overwegend zonnig\n*
+ zondag, mei 18: minimum: 47°F (8°C), maximum: 68°F (20°C), beschrijving: Deels zonnig\n* maandag, mei 19: minimum:
+ 49°F (9°C), maximum: 70°F (21°C), beschrijving: Deels zonnig\n* dinsdag, mei 20: minimum: 49°F (10°C), maximum:
+ 72°F (22°C), beschrijving: Zonnig tot gedeeltelijk bewolkt\n "
+ refusal: null
+ role: assistant
+ created: 1747227540
+ id: chatcmpl-da029146-a630-4224-9d12-7d808b031fbc
+ model: gpt-4o-search-preview-2025-03-11
+ object: chat.completion
+ system_fingerprint: ''
+ usage:
+ completion_tokens: 293
+ completion_tokens_details:
+ accepted_prediction_tokens: 0
+ audio_tokens: 0
+ reasoning_tokens: 0
+ rejected_prediction_tokens: 0
+ prompt_tokens: 12
+ prompt_tokens_details:
+ audio_tokens: 0
+ cached_tokens: 0
+ total_tokens: 305
+ status:
+ code: 200
+ message: OK
+version: 1
diff --git a/tests/models/cassettes/test_openai_responses/test_openai_responses_model_web_search_tool.yaml b/tests/models/cassettes/test_openai_responses/test_openai_responses_model_web_search_tool.yaml
new file mode 100644
index 000000000..5996ed6c4
--- /dev/null
+++ b/tests/models/cassettes/test_openai_responses/test_openai_responses_model_web_search_tool.yaml
@@ -0,0 +1,114 @@
+interactions:
+- request:
+ headers:
+ accept:
+ - application/json
+ accept-encoding:
+ - gzip, deflate
+ connection:
+ - keep-alive
+ content-length:
+ - '231'
+ content-type:
+ - application/json
+ host:
+ - api.openai.com
+ method: POST
+ parsed_body:
+ input:
+ - content: What day is it today?
+ role: user
+ instructions: You are a helpful assistant.
+ model: gpt-4o
+ stream: false
+ tool_choice: auto
+ tools:
+ - search_context_size: medium
+ type: web_search_preview
+ uri: https://api.openai.com/v1/responses
+ response:
+ headers:
+ alt-svc:
+ - h3=":443"; ma=86400
+ connection:
+ - keep-alive
+ content-length:
+ - '2569'
+ content-type:
+ - application/json
+ openai-organization:
+ - pydantic-28gund
+ openai-processing-ms:
+ - '3632'
+ openai-version:
+ - '2020-10-01'
+ strict-transport-security:
+ - max-age=31536000; includeSubDomains; preload
+ transfer-encoding:
+ - chunked
+ parsed_body:
+ created_at: 1747227360
+ error: null
+ id: resp_682492e0aab88191afcadcbbb85e1a490e7f27fcb18d04ed
+ incomplete_details: null
+ instructions: You are a helpful assistant.
+ max_output_tokens: null
+ metadata: {}
+ model: gpt-4o-2024-08-06
+ object: response
+ output:
+ - id: ws_682492e1b7a08191b30170317ea7ee570e7f27fcb18d04ed
+ status: completed
+ type: web_search_call
+ - content:
+ - annotations: []
+ text: "Today is Wednesday, May 14, 2025.\n\n## Weather for San Francisco, CA:\nCurrent Conditions: Mostly clear,
+ 50°F (10°C)\n\nDaily Forecast:\n* Wednesday, May 14: Low: 51°F (10°C), High: 65°F (18°C), Description: Areas of
+ low clouds early; otherwise, mostly sunny\n* Thursday, May 15: Low: 53°F (12°C), High: 66°F (19°C), Description:
+ Areas of low clouds, then sun\n* Friday, May 16: Low: 53°F (12°C), High: 64°F (18°C), Description: Partly sunny\n*
+ Saturday, May 17: Low: 52°F (11°C), High: 63°F (17°C), Description: Low clouds breaking for some sun; breezy in
+ the afternoon\n* Sunday, May 18: Low: 51°F (10°C), High: 68°F (20°C), Description: Clouds yielding to sun\n* Monday,
+ May 19: Low: 53°F (12°C), High: 68°F (20°C), Description: Sunny\n* Tuesday, May 20: Low: 52°F (11°C), High: 70°F
+ (21°C), Description: Mostly sunny\n "
+ type: output_text
+ id: msg_682492e36ab08191813ec707a61c272f0e7f27fcb18d04ed
+ role: assistant
+ status: completed
+ type: message
+ parallel_tool_calls: true
+ previous_response_id: null
+ reasoning:
+ effort: null
+ summary: null
+ service_tier: default
+ status: completed
+ store: true
+ temperature: 1.0
+ text:
+ format:
+ type: text
+ tool_choice: auto
+ tools:
+ - search_context_size: medium
+ type: web_search_preview
+ user_location:
+ city: null
+ country: US
+ region: null
+ timezone: null
+ type: approximate
+ top_p: 1.0
+ truncation: disabled
+ usage:
+ input_tokens: 317
+ input_tokens_details:
+ cached_tokens: 0
+ output_tokens: 286
+ output_tokens_details:
+ reasoning_tokens: 0
+ total_tokens: 603
+ user: null
+ status:
+ code: 200
+ message: OK
+version: 1
diff --git a/tests/models/cassettes/test_openai_responses/test_openai_responses_model_web_search_tool_with_user_location.yaml b/tests/models/cassettes/test_openai_responses/test_openai_responses_model_web_search_tool_with_user_location.yaml
new file mode 100644
index 000000000..6ad0ff843
--- /dev/null
+++ b/tests/models/cassettes/test_openai_responses/test_openai_responses_model_web_search_tool_with_user_location.yaml
@@ -0,0 +1,118 @@
+interactions:
+- request:
+ headers:
+ accept:
+ - application/json
+ accept-encoding:
+ - gzip, deflate
+ connection:
+ - keep-alive
+ content-length:
+ - '313'
+ content-type:
+ - application/json
+ host:
+ - api.openai.com
+ method: POST
+ parsed_body:
+ input:
+ - content: What is the current temperature?
+ role: user
+ instructions: You are a helpful assistant.
+ model: gpt-4o
+ stream: false
+ tool_choice: auto
+ tools:
+ - search_context_size: medium
+ type: web_search_preview
+ user_location:
+ city: Utrecht
+ country: NL
+ type: approximate
+ uri: https://api.openai.com/v1/responses
+ response:
+ headers:
+ alt-svc:
+ - h3=":443"; ma=86400
+ connection:
+ - keep-alive
+ content-length:
+ - '2631'
+ content-type:
+ - application/json
+ openai-organization:
+ - pydantic-28gund
+ openai-processing-ms:
+ - '3405'
+ openai-version:
+ - '2020-10-01'
+ strict-transport-security:
+ - max-age=31536000; includeSubDomains; preload
+ transfer-encoding:
+ - chunked
+ parsed_body:
+ created_at: 1747227481
+ error: null
+ id: resp_682493599ce08191a7d279acb969c28b05f032fc13ed3d8b
+ incomplete_details: null
+ instructions: You are a helpful assistant.
+ max_output_tokens: null
+ metadata: {}
+ model: gpt-4o-2024-08-06
+ object: response
+ output:
+ - id: ws_6824935a6ca481918cae37433d3e2f8005f032fc13ed3d8b
+ status: completed
+ type: web_search_call
+ - content:
+ - annotations: []
+ text: "As of 12:58 PM on Wednesday, May 14, 2025, in Utrecht, Netherlands, the weather is sunny with a temperature
+ of 22°C (71°F).\n\n## Weather for Utrecht, Netherlands:\nCurrent Conditions: Sunny, 71°F (22°C)\n\nDaily Forecast:\n*
+ Wednesday, May 14: Low: 48°F (9°C), High: 71°F (22°C), Description: Clouds yielding to sun\n* Thursday, May 15:
+ Low: 43°F (6°C), High: 67°F (20°C), Description: After a cloudy start, sun returns\n* Friday, May 16: Low: 45°F
+ (7°C), High: 64°F (18°C), Description: Mostly sunny\n* Saturday, May 17: Low: 47°F (9°C), High: 68°F (20°C), Description:
+ Mostly sunny\n* Sunday, May 18: Low: 47°F (8°C), High: 68°F (20°C), Description: Some sun\n* Monday, May 19: Low:
+ 49°F (9°C), High: 70°F (21°C), Description: Delightful with partial sunshine\n* Tuesday, May 20: Low: 49°F (10°C),
+ High: 72°F (22°C), Description: Warm with sunshine and a few clouds\n "
+ type: output_text
+ id: msg_6824935c4dfc8191b0abacb1c0fd0a7805f032fc13ed3d8b
+ role: assistant
+ status: completed
+ type: message
+ parallel_tool_calls: true
+ previous_response_id: null
+ reasoning:
+ effort: null
+ summary: null
+ service_tier: default
+ status: completed
+ store: true
+ temperature: 1.0
+ text:
+ format:
+ type: text
+ tool_choice: auto
+ tools:
+ - search_context_size: medium
+ type: web_search_preview
+ user_location:
+ city: Utrecht
+ country: NL
+ region: null
+ timezone: null
+ type: approximate
+ top_p: 1.0
+ truncation: disabled
+ usage:
+ input_tokens: 317
+ input_tokens_details:
+ cached_tokens: 0
+ output_tokens: 300
+ output_tokens_details:
+ reasoning_tokens: 0
+ total_tokens: 617
+ user: null
+ status:
+ code: 200
+ message: OK
+version: 1
diff --git a/tests/models/test_anthropic.py b/tests/models/test_anthropic.py
index 3891c5108..2b514b53a 100644
--- a/tests/models/test_anthropic.py
+++ b/tests/models/test_anthropic.py
@@ -14,6 +14,7 @@
from pydantic import BaseModel
from pydantic_ai import Agent, ModelHTTPError, ModelRetry
+from pydantic_ai.builtin_tools import CodeExecutionTool, WebSearchTool
from pydantic_ai.exceptions import UserError
from pydantic_ai.messages import (
BinaryContent,
@@ -25,6 +26,8 @@
PartDeltaEvent,
PartStartEvent,
RetryPromptPart,
+ ServerToolCallPart,
+ ServerToolReturnPart,
SystemPromptPart,
TextPart,
TextPartDelta,
@@ -45,6 +48,8 @@
from anthropic import NOT_GIVEN, APIStatusError, AsyncAnthropic
from anthropic.resources.beta import AsyncBeta
from anthropic.types.beta import (
+ BetaCodeExecutionResultBlock,
+ BetaCodeExecutionToolResultBlock,
BetaContentBlock,
BetaInputJSONDelta,
BetaMessage,
@@ -56,9 +61,12 @@
BetaRawMessageStartEvent,
BetaRawMessageStopEvent,
BetaRawMessageStreamEvent,
+ BetaServerToolUseBlock,
BetaTextBlock,
BetaToolUseBlock,
BetaUsage,
+ BetaWebSearchResultBlock,
+ BetaWebSearchToolResultBlock,
)
from anthropic.types.beta.beta_raw_message_delta_event import Delta
@@ -1306,6 +1314,259 @@ async def test_anthropic_model_empty_message_on_history(allow_model_requests: No
""")
+@pytest.mark.vcr()
+async def test_anthropic_web_search_tool(allow_model_requests: None, anthropic_api_key: str):
+ m = AnthropicModel('claude-3-5-sonnet-latest', provider=AnthropicProvider(api_key=anthropic_api_key))
+ agent = Agent(m, builtin_tools=[WebSearchTool()])
+
+ result = await agent.run('What day is today?')
+ assert (
+ result.all_messages()
+ == snapshot(
+ [
+ ModelRequest(parts=[UserPromptPart(content='What day is today?', timestamp=IsDatetime())]),
+ ModelResponse(
+ parts=[
+ TextPart(content="Let me search for current events to help establish today's date."),
+ ServerToolCallPart(
+ tool_name='web_search',
+ args={'query': 'current events news today May 26 2025'},
+ tool_call_id='srvtoolu_01MqVvTi9LWTrMRuZ2KttD3M',
+ model_name='anthropic',
+ ),
+ ServerToolReturnPart(
+ tool_name='web_search_tool_result',
+ content=[
+ BetaWebSearchResultBlock(
+ encrypted_content='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',
+ title='News: U.S. and World News Headlines : NPR',
+ type='web_search_result',
+ url='https://www.npr.org/sections/news/',
+ ),
+ BetaWebSearchResultBlock(
+ encrypted_content='ErwHCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDK2W0Qu0wNgI6mf5EhoMNCqr1gVeM/Fj3PSYIjBrgyGKmTOrJLgDCXcOvRtrbigKDeccd5oypMBGnMVhm6h3Ade/9+vNOwI3ByflKmwqvwZqUUdfJ6+k9ZDrmb7VM6ktRqsZ4Z++yOdyubDNbsyM6RdwYuNi+bS5ZUON+rMd8+ZrQYlGYqq7NF43o5klxpac+Dsgx3OlKbu6Hq6eiKOQ3rdPGYlUYKdDouAx6RjypXjhYkqErPrjlFZhNv2lO6cohI0QU66p6b7G8UMVyweqYZ+2QYTFfbwU5VdIAOiQW8PBgNwPC5LRnidfbiT04VY+cEsNW04zOq9coXs4NgFRw2WDCZDBPGTEJex0xv7vD0/D0YpBhfiawNJ8FgBTI6q0gXQ2+YwqelVaZ+BDpu2JeRABLXiXQAMIiBBiayofacvfgJZ4omPY1JRiJwX5IpbLFqLcNz2fWr8veYedwrDZV/lOjyn755WTp2i89GD4Pv53htWrDOH8/YJBQ9u5KA2DFz7zAtRLyPqvPz3YaLMr3ATFvs8m0igrllgC5uaWPWfO/28RU7QNnxyBLGNonF3dtz3Uu2naeNvxjRhqCtUOON5odOahtPrRs5qkjv/UrL2YzlnfsRL4Qb/qsGJE6YWScvLhjBaum29Whk2p6RtYJqzzSqDbk0jxKe/hNatl3s2JF1bAW4L7p9FnsK1v/G7AYSaIYl4RDLGuL1bFOKGKVlUZtohNMws+gvTCYKdhQzfurimTsNIpBP4Ci6aJ+/yACa22AXGhZQqyiOS7yxI6zj3vZdQGFBle1TjDpzveY2Nz/kuuTCPbGsWt5kd9v7BkWvkNacqZ70KijyIk5dVt3H0q4eavyNLU0gF4hSCPDHW7eeWXTmNs1YniKiaHrwmOOqXjw2PCQrZv0i7UQRjDmRQqx9NtuqzMup9DRPbQuZM23b8JwzqA0Qjyxc5pTlWRL9aU+U7ZKOD1OdBszAU54c5N9jOca8S2Plt4TGJcAv2Wy73Bex74GPlkHcKWO8TJYhrV4ZF2nMjssncQEKCltJaZg6TJpazpLKoQ1XmYmgzebbVMRc8RTDXk335AYKkN62xRnfrDd5T5wBhGbPNQeF7PGigtAK/SpSpTna/vmGOBul/cONWOFFKNdY+FtCAGd4AOo3s/N8QUnKR66TEv9ocuVep1UZxV2fJcqIuJukutfT9eWPcou6VImLUzRQMYAw==',
+ page_age='4 days ago',
+ title='The Biggest News Stories Of 2025',
+ type='web_search_result',
+ url='https://92q.com/playlist/the-biggest-news-stories-of-2025/',
+ ),
+ BetaWebSearchResultBlock(
+ encrypted_content='EuYCCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDCmLeaYgzUtJ4Mi+fBoMpktwxWvlSdpeNRRlIjDckkEHzeMKWP+vkSJ+7ci91OlLvn1nTU4wG+0am9miZ+68Q8XjsyCBGekIPeSsgpkq6QFTAvqrNhd55GMbj8VQtB/7oV66lwp8PzaymgQlLzCnxBdZ6IRYyEd6XwFOPrWCwyjtlKbwRiM2NIaNsGcrraBVrDfsjCz20qsDPGNsQf587z/TD3zWUSelhjhf+T5nDCEXUkYM2+4MaGP5Ty57Khh3WQr5q6Q46m85jBBF+akWf1uKZEgjgFug1ufj/8TXEEAaKCVY9YeXTXfYH8DocKveCXH4Bp9TNbgx55UrL8NdXiwdtpI/zqY+8hM/SiaVeXXI/Rbmjg3HzFTLfrH4wSrl5awdKWuGwQy8nqRISZlwNVnwFY0e8uc08xgD',
+ title='ABC News – Breaking News, Latest News and Videos',
+ type='web_search_result',
+ url='https://abcnews.go.com/',
+ ),
+ BetaWebSearchResultBlock(
+ encrypted_content='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',
+ title='Breaking News, Latest News and Videos | CNN',
+ type='web_search_result',
+ url='https://www.cnn.com/',
+ ),
+ BetaWebSearchResultBlock(
+ encrypted_content='ErQCCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDGPat3RtBffd6jd9uxoMkx9uAfM4hgJRbrR4IjCBVNWqux+TsqDP0poLm+ss84SLrVR4rAcjrQSDPna9ZfR0OFhPjv2ko1ZVuBzeRE4qtwGOPV6my0G/y4nPEH8gNVc3y/8uZzh7O8CBrduzchEMd5RRXLlsC+bU/SjZ+5LBYGzAVwRCfVXIdaJ0/d8RYdJWHo3bvKc5Lu/WFPV6Po9gVHLOU5WVDsyzwmrvqzCYC0UhkUMa0yf5j7WTFaT+kgHZcFcbvYPG53USqNh0seahaaCC5fJRjRBTAvuyj4md+ppTjIXGZEp3rTMG3MTkv8t60MgPzn4ObLGEmBQIQrfES9G2BT1k7lUYAw==', # codespell:ignore
+ title='Philippines Top Stories: Politics, Environment, Education, Trending | Inquirer.net',
+ type='web_search_result',
+ url='https://newsinfo.inquirer.net',
+ ),
+ BetaWebSearchResultBlock(
+ encrypted_content='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',
+ title='Portal:Current events/May 2025 - Wikipedia',
+ type='web_search_result',
+ url='https://en.wikipedia.org/wiki/Portal:Current_events/May_2025',
+ ),
+ BetaWebSearchResultBlock(
+ encrypted_content='EtsnCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDKlvRKONWAUxyGn85xoMfeV+KuQr0lm9kmZSIjCqpqeZoxBvTankmAHSd51eQbmxHgBmDSSbu1eGpcY3u4Gf8joO1Y6tH1cw7MYyh7cq3iZM8rhAJuFyfAIpcDxvJ5ROlkmPNvMDR8MBKZKtSQ9p3R2lI/QOWE9Fk9kfDgxdHFVeCUtPiCmmF7wPi3GMXArw4FjQXDNHUZ8ECNxkKCmSf0vlLQMfwNtqAqJZ6vLqjCuDst0d7pBfaDW9YcrY6du/9UAWGlCP8BudfzzUI5ds7+lMTkOTR9nrlQyby73AQmuY1IaPEiNhuj2vohNT+t21qan4WGxrXFJ3iFoq1nUPJAjfmcLaJVNbDzksiRlo9HCgql49Nqau1BEyN9OQfF0W9KMa5rLtbyeQUn4tpsAAdA5UoePHbl9jHSWlu3GddU30F8YclCGIDVAnyhbLAGbrjHuJUjdx2t6XeDldk6ZAIH51s9+TGl+23lYsu2kinQpczecJFzcc9sCtVszDs3Z6iqecJo0Qp3hAJvVwX3U2W2p/m71rIrYO67RpSOvplwZxXQNKfrDWT3ZdcfWTxHvZf5bFSzeI+desA/K+bZ6g8gsBeJZoAHm2QDp7vdoAt6x/K9Ys1lIMxoOCQoUHWFFSmuMKUYUo/D8SvqBQmrAiqbZV9qQ81cX8li1X+pmbRrFA8oesTmv15yMHid5ZH6KV60WUZ/lVMgpDJ+LphK0qcLJNdPzoDPvwelEhC9VTH1uo3DhmBpkvQlsLBOB0mXFHGx3tmn7nXCZIAf67imp30xyTcJP/Rj4MUwxzBuABtl06dwNXnfEvljBs9dbte6EN/lSwwiudVMjFhR8HH4xJ2zU6wsahLzJ+Zi7HO+QZ6zpuy+Yc9XqTH8WT03q7CvzE83AHYIKbEAsuk3JksWpWsi/7Mac16SRHN98fMbvkna9XsHAH7b447t4Rl7ZYACG7LkU6CIp/IVeDckUyZpRn2yeflAC4iO4miUzhMcxStLNt0ChTOg1B/8Trx7w2IIagJ/Xmom3GBrINk2Gop+Mai76aNsByT1M0M0BgmbhW8uVL1yopAX2njno076astUxPG9yGtWB9DChr1+5zPAN6nD0wu330UBTxA7NLCfK4TWpEiYKgG7b/UKy08CVBJA0Oo8ay7IBBEQB3LosyVJ7hAVsDsLA8T+PZ1nmjG5v14MqsPWnsjD/PMWLE2GE+fn1ZrHV7XsH+OvfNDB2cqJY7YnllCfq5+AYebJ8hiP4aFWQO/ybvTXy+cRXZ4DpyFWdWPBKS6qL7ULL+AsA1H983Q1r6FurTLmx22LKJ4+g4fSyey4dUFPPJ86t9D4C6eS2Q2cBy0xzyMnmlh1uVqBuuNUOONCv3FpGdopOwoC9geLsbDSuqvBbLrd5Spu32fLPysA5gWtHY7kqWwMsYe3P6iyKm5kqEOFD/UvtDL//ObEKRWVG6/bXzbmnrKSAj7jzeLQ4ojFofQ9NVNc0KnLIelmuhdksHmiToE4nneiLAx4Xjmp4i8xmKGXxlDe+f9/VAAdAlzXx8vMTmDK2ddpdKk5oxxV7LNiSqaFA2Zm/f2o2qKlp4IcOUzaGtGEneX5xNwcE8p+Z+HyIP35JFPc5/2xUz12lKsiF7e1m3k7S4VrWTRUvZv2kopDxpUsAYm04CirONP5Orr4zrEXTOeosLgvg7IzbBLNfltuI7cK8kr1Hsrn3fRnvYyf4jkDIK6IY9/UHmWgnkAvpgRymCq59Z/k/RXFVlP+BiNyuKwzQHIcKcYFKvQUdJPOB41Nx1xoUwupxle5CtLZukszM8sUC/XrvW0yfaldWNZilgi2hqq1xoQR5t7TBmpaX5QMfkRGcs/tGYptR6xc66QYI+SRjv7cY643U9n9DG9xouqZ5GLMAfYzhNYrAVqX5jXmo2xy+eYMI0oeO545i3kaAx9bVAXFf/NTcLFSl4EEnZrQbV3KujuSU8SGM0TVoWbPKtA+Nqnpi50g7LTs1KOLB565Hi5SNo6T4nNIYLrT86w0dgk4lK1H6rh4Q2yvS3xwdDucaD1ZMmP3H9GJGHdZew0p5ioyY3n3xokTIm+vI9M4eo/qbxZiuYVlkEHvdDJgKZHdZxBHdLL8vDxbUOHv8qhvoLYmlJuLJOVlPvUAy92u8r/VTcePrVKVhos21L72+OC3E1f6PSIHLg1bfBrbrqtdzeNhzZIt0EYx+Jh8FU5Qp+e5HeETfVH/M1Hpkdma3VZdkOcApQZxIsNROy6Na5mp3VnVQo/mUrB51DjWpF1JmtXTahS0Te+Rqryi6pkx5Hn3FVc9CkPBt19xzMBv5gA82XV+k/dLENFneXwGOFIppop77oGs1hu7WzMDN/kW4lBSbm9UykcL8C+s7zV9hl3rgwJPPu5THVIb4wuKNoJe8StfSC/KJkgMYOxN1kch1NQijMKPK1YbX4x6O90WBRZN21qx96xYbjrhga0VUQauqXeZ5fgltT1htvgo4gdJXu1oJCUhB2PGyFINAvUvrZ7YfK/Ssu7+Iafm2hQ3dsKlXWGpLqzE7nxNzjbheN6weAkV1BM87NLKRJw3I6w1naeE5ja4jM9nMX3I9sUcFUW836PvsKn7ZUqg32cit+3KpAub1ArF3Gt82RtcGZlXJ/0+GCzT8I/xp3uWfo2wy/jHkqQgfaKajth1x2vmEqLXUiee1UXwSl4uWqFD8N4LGiVyua86gLW8j1CWguW5cNqBTmUhuteCNXsYjMS4qHUfoTR5dRzcUN0KJj50Rx00gqpQXywaMAVaXBm2WupDuuxtrhK2+vwUIX9kSYeudE0oFkzsnb6pRo0Bl4BttcBf7fy1dAu8zorI3wGHMBXaq6r+8e+v90hXv4XCmBg5NrntRPHUqJTspJXTPZsKRCMkWsC4mnoKA1lbcbkth3KzVORoYjSfsNI2Q1nu2CwWJstkFlSwmR16FwXqVxT92yrGgwcynV2uSOmjLsSv6mekTZfuarV42IfrJwdLMM3ALAod4UAxecQFsykabJTfbR8Ja84SqKvNw4vXnSwnhmlnvc0y6iIqckO/fKzqv8QQUHNt21nGhJrQkYByQ6fPWJBhze0zXE6MsAt7/UWPF7j7qqgzJcx+8FUPUu7vfgvLnK82uijqkQAMo9BYImR7rvWmo4TqzSJ2iQlzmhvseRdtNRUZTqft03qou3lHuHVtBpN7PzpEZil11otLWVOcO84+PFVHqLmaO0dGygwPcHsQcAyIy7cRd4uQKvq6T4W5dcd/UVDuR7/LMd912FPljz+/ntGUPNXLS+Y0ZoEA+ekfH6nJfZX2B3pkmNl1vuB2xzosHO+In/yZfl+sjgOltxrmPfcJD+U8NSZi38QtGfR98D0OB0/QAnk5tUV9Q3s8Gk7nQ9CB2TSwHRF7l38asuQnUkXWiv7NF/fGbVEZ1qIFSUukHTRYwhgwJmhjstMhyhQkAvbJaIw2esbjokJZUaQ2UhCQl2Dri6hfziVA3Pwb4oZ3KZzj/4rvKX0a5jJ/RpqUyA40EcTq8XdC3TgteYluQmIbBfTztVLStOV9uJz3wdReS8REGuRsPT/+PCatxFyab+ioz/vLxhcecaGQlz60zL6FsDUgNFvzhrP/MAbU+ga+CoLOsVH+yk5Lv9s+tYNAwZkxygQ1ALf15hujHxbz71rLGnteHZKP1exgnPc/jbLfxgywQ8MZHALySDE4Qo3EWROHLarcueJbIrCyMXKf7iNc5scqmIHRNYBKueZQ5Ngqb7I/tgGWagGcP14B9w3La5i2n8Psqe8Nj0lPGLjxAxEofzFf0RZH7d0GxSACOb7Ntxt2FYRH8p95L1Z4jHYs+yNvpNUklImyVPkSC3H5bfNzWrgWQc5jmXLvxyNFjRimWyGi8B+TS0dIf4nfFhFP0/ZwyeIgLdfSI0ms1IfPBzdyALN+vGnYai0igM3lgt2NFQ6YXLX++jzSof/7Nc/PH3jCQnl4if3eZyshS8fCwdjUFjg8HpsWmqmS5pP+E0a7mVLpHUICRApRV+EJwqz8cpRSC/YRf7N0RaitCgN7ky769o+wmYdGBMVsVbbuASObsbG2JtrbuXZxHZsYHWpaGoJPZHtad4fA+hEGpYNfrnJRNkO3g1ySIJM2jptXHCItHpAOwtWTDrLfdaBfFMelbsm+Sh+HrwL6uviumZ1N1MfF8FraiiM+E17WEgCSihgFaCQpm60ES+eKokLlXe3/7Ifh++gKfLnhkoe38fj15j4hi7BzDstjeQefVDYMoqEV2vHTTg6FZ+iuFcBIqnvnUhx2xEqURDvrPZNPXvHlpbWPWqNK5LlAFYqsEh9MwG4NfrJ3oaxTSwgQ5JT09FsF81cKdNs6wyGfi6e/UVFCJ0eQzOqc3eweqvF9WROkWVwi/C8uf8yZqTfCFlcQMs4OeSHVs+Qr0MEkOl1BZU9hFrsSfT3rLZJB4q8hmNnjW4Ff97LH0gZHKsdOpZ0AC0UKj/dcspdmVcr+I40OfUF3agJDRLi13BOHKfsnJLyzfAQudUKXFIDhdgn7y1xm7GFbVb6n4Y0j1konREyFbKuu9m704oOvfmlyB/rESkcNgc3L/Gtrxdt4i7Igqjhrk2gO8hncDe/ewkr1JX1erIOCgURwPikq2avxQAG6pt5B5Cgj9IXkqYem+evRRROFKjag7TaHx2chkYHpapiteeHnlho6ErOKeZuK6WRZGrjVBaOpX9n8VHG5C2v6NBmDGuaQdd9wJPtRq6GwQM+eGTVfZed76hLH4w3QIPOgVYI0BKk4vRC+c9jLbc8RqL9XqLcjnqFd6erRyr2aHiQFO2CHrreZcucKlSQWeciIc2+6lg4zcshyVLuDk+2n9obbrWcJlAwaekMJVTaKWdPf5HCudIrStjoRndXCM6YItRi5CTyAQo2TJVPTUEpy0ogqvviSQsVl1t0x/rdC8N0kLZqQ9sYVC8jSzVo7xpp3U/VT8oX6eh4qi/IZAKHah0D0W2pJ0WTET5Bfo82pCv/hMIM+BmgGp7nryn30o5ObBgOpNhgi6GJ6zhkPGnXcgCY3OxstP64ZSWeOaIIq8rLk3ygw9+oLGm4U0sIW8sk0+kruChvKkAmGD3Nobr44DAuSZoQbc6N2yMQuFkMhOgyqFDKmpGiUy+wcR+R/tQNWGaXxKq+SFjmwqV4meCIhKm3R45rcUorI9+betozfVsfpa+fGJ4B4UjWR8NHnUSd5710tkR452IB8S4RsYLtp+tyoZQLKJkL707Qkf4rJp57J8SGWCzMtvtu8c5Rn2Dxzh5KBAE44ayTV1go2wOrmaVV6uWOhYtWQFOEU69ZJvLSFlonC6vM/n5G6I+4xOknhBugQNpsbB8WQvs4yPtsaeke7dttmLcswj82sHezAl/8ESZ+NCsoKbNVV9zXSmIbaCjXNjUcBU7/EgmT8QNGlKiv3C2nvSI42ibUQmwnj4NU2itYgNLx+FhXarKV1VuUE4dGVJCNztQhxBhkf0dNZT5fIuEsWHsHjTIbCPyFoXvHF+PmVXg59y2eUfk7qrwknjLfe7KIXNKTxq0gzq4RXLvIqwFK5bOBNHrfdDChbs6KCzlvYQMDemIHVOImUJBkl6UgdzI/4+JMgso8X70i9UIbZWGPn0kGUkCprryuNCBBC1PaKuyRnIj5DFBrU9RtbRzkcZmUdeOvY5H7018t9UB8hpKBy1fjXx7f5Vqmd8eqa9z56M506ACTCTOX4RvUu/nuJ/aziHt4ax4yPA69TwBMB3Iyrp8XYq2DekeOR1Bb/UH11UCNFrp80OxtS70baasxUIjv5Qx1lzPOBh74WIQhKZC7kQHdJJgzs8eKN/bU4QFf+m9ch/VxnUivxvKsbfKqP60LiUaB7PA9Ocp0DhJLgbSoj2YudBYrqkZtF37lFrjVE8Z32iJBrR/mLmrzcmGzDsGzpgFx+UDLdJSHyY2PHcctjXLreI6K0JFwcKwMV4U/J0FyWod5S/ZbIJFrYZs1ao2v8od47Bk5N6TpQX9J8Lkyj3xrm4PJThxp/MBbmra9ZCTmkgoLasgx9e5o6Y8N7OPzmUDoXix9j4U9X782NCnyY2t2VoXUUCjWo4N/vufLb2ZpcCIycJATs41LI7jphb5EwHDpKxat71RscO3Jm0JwOsyV8jC8SgpJegd9LAXbZdrpGH3yoMWhPhU5xhS0CLjaPpyLHnZdlPPlWAGkS7bxpM9mUUv/SFGHNiqBryuUoxS8eCAZvGuIfa1qVbXIE9bLEoOxHH/h1E/QGgQsZvPCHMoF9ywZiRAnjFn21J2JEACDmAWEL5o2oHO+rI/rfeMFNJ+U7k3B+12xn999WHr0d1FeQIHdqJU0tQUrKDT8w3zNYdRyaM3VDQAn9uRRzSjTdvjkCSC75T5ojfK8dabiYrp4rCq4pKTg+PdGKkJt02L8E1mhSKFL5ZFl1Raq+Jde6TX1qGbKZTiQubr2h51Ha019OTO5aHZOFRl6awl+NauRNJutrrTTLs3VfYSkf/jaAP9wFpcfypV6ZO6NWzaRLGWH6EkbFaDvV8+9g+ul0t4HVVjKvYBGhCsxIOcpO8C5MOmioId89J8BVAD3okW1AFi/PJQUhZdG1+0CAy+xybaK5YGHsDyGzmFaCpRQ7e/vW74SvFs7LH/ReSOqBNTwilF0jKR53QhY88NJyZLhekO1sy668dsz3XXRTf+aKWZHNtgDlHKbNT93R8bD9+vTfd6vxgD',
+ title='Portal:Current events - Wikipedia',
+ type='web_search_result',
+ url='https://en.wikipedia.org/wiki/Portal:Current_events',
+ ),
+ BetaWebSearchResultBlock(
+ encrypted_content='EsoJCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDAYjLeq1Kci5o2pi8BoMqJacE/46sh+pR01VIjDPMDUx0d3wj2zbUnowpxFwHvCCnCwLoPMxQsQm/dnvm0Fzfga5o6zXqDwWpXvUzhEqzQg/X9w2opL/m5o7bUw/TubgkYaR6l4t2n0oQlGgetbSj1gOEor/WWJ8bWXL3BZnS2xkIzwGrbLdlPDn/NoXICDQZ+P3IlA6B8CVwiijOnNq+x5nTpX0m794VFJIfO5SdupAiWqhWmtqt8XhcWs8W2gnhDDvNsBG6oH2ZsRenxt3n7a7eWo7yRk/KSHdBM9c+L5r3wu3Ul81DW9CuE6KqUdFjjePJfWKL8OvzfkvJjIqcRaqc/3RIRZbSPbimBiRMXtCBZeCYE1yeBs3xLQ7TJXgRM/9ScKromcFWckYpGXBYSGL8SiXXoBUD7pLsuP5FnRnZUkQLCHTLoId0/w4jVbuXmDh3oipIlGUQCSbp3FkogFB/CZFpKz4tY5E9WQ4pBkApGYgAeGgOOStiUW3pE9oCy5TRpCfilrg66RtJozGI+LWM/XYuuOwSK+f+/c6AaUJ7av+LCUSPFI6G1XErfHK/KeBSJp7ZVoRXn/f7yJXlZvybKQXdN6UtxqxRJbil9RnmmXsBc6cesWW/cHbz01V8tkaqcYdrtJdVM/LesICK77C/JYiA6PQsneeg5xdZDCUp7yUO9P/CHMBqhPB8geS9y4dG7UIdJrFbv43cGOiqoSBsBGCLCc7crptYYGydT6YBgKb+ktUJm14MfbF8lzKt6SVYpn8KWL2dyhsDbfi88h51fvZqDV6loTDpyHbMHeJoA4pIxLhkBIriQOLNnEIEwqTGy2XFy326bahzINKJVTY1mMq2v3O0Snl0DNcAZ1X/iHt393xPgdcSy6c2+sDRexvpU4grX1GGFD4E8kg1QP0fErasq17XzRVpnU7Kedk/ntU/X6zeI3aTEeyRNG7IPH67w6GyIF8XmgCh25H6bCBGN87N8hnPSVAy8/qIMcfZYaF1c8W/QB9n7HBWhQgdyZv3relj0Ur0xdRi2osqo+k2c0a9mmIVupbzpLAxfY7LiwU8Edsr+1WY62x1omk+b4XNiGnhHnrF4B2o+f89icgAVSqRo2ydqIUDnZUYewu8jjUg/j+WUI8yKqZHCgCRdkm4fDSOcK8faTeaITl1iI6XFbUicEWZzG87tFykNSv5fz+ueDbMj936cm97rPUhp/qMnS2uloAxmiWLcS2/oV605i97ccR9IlwB0tt259e9iCvltjxzcC6P95vbhLS94+xVNOG2fmQtzE8oyaREZBkwSjVHuJ3lDAxvHDRYY8F+lkuLE4AvLiye3CDAMXNyCrG+/xiQIBNUGs+1aV9edHMmwpCVs99Q+nHO1RBVPljY607Q6u06Wt4VHnY+45+IxzpHHWXxg3Jn8Lh1AuzFKEaRWaI7JDSCgJmYxjIwkUO7988PWjOmFLquOd6mQsQ6iVG/89zSwr019RlAQRDIbMimefHIYhLm4S/Y8TzPhLFXJ6FaxrPFAkkkp2LnLQUoNKlo2h64JaAerGAku2FEwn/vo3hsCXwILg6R4QYAw==',
+ title='Current Affairs Today – Current Affairs – 2025-26 - GKToday',
+ type='web_search_result',
+ url='https://www.gktoday.in/current-affairs/',
+ ),
+ BetaWebSearchResultBlock(
+ encrypted_content='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',
+ page_age='3 weeks ago',
+ title='May 6, 2025: Top news events to look out for today - People Daily',
+ type='web_search_result',
+ url='https://peopledaily.digital/news/may-6-2025-top-news-events-to-look-out-for-today',
+ ),
+ BetaWebSearchResultBlock(
+ encrypted_content='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',
+ page_age='7 hours ago',
+ title='26 May 2025 UPSC Current Affairs - Daily News Headlines',
+ type='web_search_result',
+ url='https://testbook.com/ias-preparation/upsc-current-affairs-for-26-may-2025',
+ ),
+ ],
+ tool_call_id=IsStr(),
+ timestamp=IsDatetime(),
+ ),
+ TextPart(
+ content="""\
+
+
+Based on the search results, today is Monday, May 26, 2025. This is confirmed by several sources:
+
+1. \
+"""
+ ),
+ TextPart(content="It's Memorial Day today, May 26, 2025"),
+ TextPart(
+ content="""\
+
+
+2. \
+"""
+ ),
+ TextPart(
+ content='May 2025 is the fifth month of the current common year. The month began on a Thursday and will end on a Saturday after 31 days'
+ ),
+ TextPart(
+ content="""\
+
+
+3. \
+"""
+ ),
+ TextPart(
+ content="On May 26, 2025, there are significant developments happening, including India's launch of the Bharat Forecasting System to boost weather prediction and disaster preparedness"
+ ),
+ ],
+ usage=Usage(
+ requests=1,
+ request_tokens=16312,
+ response_tokens=258,
+ total_tokens=16570,
+ details={
+ 'cache_creation_input_tokens': 0,
+ 'cache_read_input_tokens': 0,
+ 'input_tokens': 16312,
+ 'output_tokens': 258,
+ },
+ ),
+ model_name='claude-3-5-sonnet-20241022',
+ timestamp=IsDatetime(),
+ vendor_id=IsStr(),
+ ),
+ ]
+ )
+ )
+
+
+@pytest.mark.vcr()
+async def test_anthropic_code_execution_tool(allow_model_requests: None, anthropic_api_key: str):
+ m = AnthropicModel('claude-sonnet-4-0', provider=AnthropicProvider(api_key=anthropic_api_key))
+ agent = Agent(m, builtin_tools=[CodeExecutionTool()])
+
+ result = await agent.run('How much is 3 * 12390?')
+ assert result.all_messages() == snapshot(
+ [
+ ModelRequest(parts=[UserPromptPart(content='How much is 3 * 12390?', timestamp=IsDatetime())]),
+ ModelResponse(
+ parts=[
+ TextPart(content="I'll calculate 3 * 12390 for you."),
+ ServerToolCallPart(
+ tool_name='code_execution',
+ args={
+ 'code': """\
+result = 3 * 12390
+print(f"3 * 12390 = {result}")\
+"""
+ },
+ tool_call_id=IsStr(),
+ model_name='anthropic',
+ ),
+ ServerToolReturnPart(
+ tool_name='code_execution_tool_result',
+ content=BetaCodeExecutionResultBlock(
+ content=[],
+ return_code=0,
+ stderr='',
+ stdout='3 * 12390 = 37170\n',
+ type='code_execution_result',
+ ),
+ tool_call_id=IsStr(),
+ timestamp=IsDatetime(),
+ ),
+ TextPart(content='The answer is **37,170**.'),
+ ],
+ usage=Usage(
+ requests=1,
+ request_tokens=1630,
+ response_tokens=105,
+ total_tokens=1735,
+ details={
+ 'cache_creation_input_tokens': 0,
+ 'cache_read_input_tokens': 0,
+ 'input_tokens': 1630,
+ 'output_tokens': 105,
+ },
+ ),
+ model_name='claude-sonnet-4-20250514',
+ timestamp=IsDatetime(),
+ vendor_id=IsStr(),
+ ),
+ ]
+ )
+
+
+@pytest.mark.vcr
+async def test_anthropic_server_tool_pass_history_to_another_provider(
+ allow_model_requests: None, anthropic_api_key: str, openai_api_key: str
+):
+ try:
+ from pydantic_ai.models.openai import OpenAIResponsesModel
+ from pydantic_ai.providers.openai import OpenAIProvider
+ except ImportError:
+ pytest.skip('OpenAI is not installed')
+
+ openai_model = OpenAIResponsesModel('gpt-4.1', provider=OpenAIProvider(api_key=openai_api_key))
+ anthropic_model = AnthropicModel('claude-3-5-sonnet-latest', provider=AnthropicProvider(api_key=anthropic_api_key))
+ agent = Agent(anthropic_model, builtin_tools=[WebSearchTool()])
+
+ result = await agent.run('What day is today?')
+ assert result.output == snapshot("""\
+Let me search for today's date.
+
+
+
+Based on the search results, \n\
+
+today is Monday, May 26, 2025 (Week 22)
+
+. This is notably \n\
+
+Memorial Day, which was originally known as Decoration Day
+
+. \n\
+
+The year 2025 is a regular year with 365 days
+
+.\
+""")
+ result = await agent.run('What day is tomorrow?', model=openai_model, message_history=result.all_messages())
+ assert result.new_messages() == snapshot(
+ [
+ ModelRequest(parts=[UserPromptPart(content='What day is tomorrow?', timestamp=IsDatetime())]),
+ ModelResponse(
+ parts=[TextPart(content='Tomorrow will be **Tuesday, May 27, 2025**.')],
+ usage=Usage(
+ request_tokens=410,
+ response_tokens=17,
+ total_tokens=427,
+ details={'reasoning_tokens': 0, 'cached_tokens': 0},
+ ),
+ model_name='gpt-4.1-2025-04-14',
+ timestamp=IsDatetime(),
+ vendor_id='resp_6834631faf2481918638284f62855ddf040b4e5d7e74f261',
+ ),
+ ]
+ )
+
+
async def test_anthropic_empty_content_filtering(env: TestEnv):
"""Test the empty content filtering logic directly."""
@@ -1723,3 +1984,134 @@ async def get_user_country() -> str:
Mexico City is not only the largest city in Mexico but also one of the largest metropolitan areas in the world, with a metropolitan population of over 21 million people. The city proper has a population of approximately 9 million people and serves as the capital and political, cultural, and economic center of Mexico.\
""")
+
+
+async def test_anthropic_web_search_tool_pass_history_back(env: TestEnv, allow_model_requests: None):
+ """Test passing web search tool history back to Anthropic."""
+ # Create the first mock response with server tool blocks
+ first_response = completion_message(
+ [
+ BetaTextBlock(text='Let me search for the current date.', type='text'),
+ BetaServerToolUseBlock(
+ id='server_tool_123', name='web_search', input={'query': 'current date today'}, type='server_tool_use'
+ ),
+ BetaWebSearchToolResultBlock(
+ tool_use_id='server_tool_123',
+ type='web_search_tool_result',
+ content=[
+ BetaWebSearchResultBlock(
+ title='Current Date and Time',
+ url='https://example.com/date',
+ type='web_search_result',
+ encrypted_content='dummy_encrypted_content',
+ )
+ ],
+ ),
+ BetaTextBlock(text='Today is January 2, 2025.', type='text'),
+ ],
+ BetaUsage(input_tokens=10, output_tokens=20),
+ )
+
+ # Create the second mock response that references the history
+ second_response = completion_message(
+ [BetaTextBlock(text='The web search result showed that today is January 2, 2025.', type='text')],
+ BetaUsage(input_tokens=50, output_tokens=30),
+ )
+
+ mock_client = MockAnthropic.create_mock([first_response, second_response])
+ m = AnthropicModel('claude-3-5-sonnet-latest', provider=AnthropicProvider(anthropic_client=mock_client))
+ agent = Agent(m, builtin_tools=[WebSearchTool()])
+
+ # First run to get server tool history
+ result = await agent.run('What day is today?')
+
+ # Verify we have server tool parts in the history
+ server_tool_calls = [p for m in result.all_messages() for p in m.parts if isinstance(p, ServerToolCallPart)]
+ server_tool_returns = [p for m in result.all_messages() for p in m.parts if isinstance(p, ServerToolReturnPart)]
+ assert len(server_tool_calls) == 1
+ assert len(server_tool_returns) == 1
+ assert server_tool_calls[0].tool_name == 'web_search'
+ assert server_tool_returns[0].tool_name == 'web_search_tool_result'
+
+ # Pass the history back to another Anthropic agent run
+ agent2 = Agent(m)
+ result2 = await agent2.run('What was the web search result?', message_history=result.all_messages())
+ assert result2.output == 'The web search result showed that today is January 2, 2025.'
+
+
+async def test_anthropic_code_execution_tool_pass_history_back(env: TestEnv, allow_model_requests: None):
+ """Test passing code execution tool history back to Anthropic."""
+ # Create the first mock response with server tool blocks
+ first_response = completion_message(
+ [
+ BetaTextBlock(text='Let me calculate 2 + 2.', type='text'),
+ BetaServerToolUseBlock(
+ id='server_tool_456', name='code_execution', input={'code': 'print(2 + 2)'}, type='server_tool_use'
+ ),
+ BetaCodeExecutionToolResultBlock(
+ tool_use_id='server_tool_456',
+ type='code_execution_tool_result',
+ content=BetaCodeExecutionResultBlock(
+ content=[],
+ return_code=0,
+ stderr='',
+ stdout='4\n',
+ type='code_execution_result',
+ ),
+ ),
+ BetaTextBlock(text='The result is 4.', type='text'),
+ ],
+ BetaUsage(input_tokens=10, output_tokens=20),
+ )
+
+ # Create the second mock response that references the history
+ second_response = completion_message(
+ [BetaTextBlock(text='The code execution returned the result: 4', type='text')],
+ BetaUsage(input_tokens=50, output_tokens=30),
+ )
+
+ mock_client = MockAnthropic.create_mock([first_response, second_response])
+ m = AnthropicModel('claude-3-5-sonnet-latest', provider=AnthropicProvider(anthropic_client=mock_client))
+ agent = Agent(m, builtin_tools=[CodeExecutionTool()])
+
+ # First run to get server tool history
+ result = await agent.run('What is 2 + 2?')
+
+ # Verify we have server tool parts in the history
+ server_tool_calls = [p for m in result.all_messages() for p in m.parts if isinstance(p, ServerToolCallPart)]
+ server_tool_returns = [p for m in result.all_messages() for p in m.parts if isinstance(p, ServerToolReturnPart)]
+ assert len(server_tool_calls) == 1
+ assert len(server_tool_returns) == 1
+ assert server_tool_calls[0].tool_name == 'code_execution'
+ assert server_tool_returns[0].tool_name == 'code_execution_tool_result'
+
+ # Pass the history back to another Anthropic agent run
+ agent2 = Agent(m)
+ result2 = await agent2.run('What was the code execution result?', message_history=result.all_messages())
+ assert result2.output == 'The code execution returned the result: 4'
+
+
+async def test_anthropic_unsupported_server_tool_name_error():
+ """Test that unsupported server tool names raise an error."""
+ from pydantic_ai.messages import ModelMessage, ModelResponse, ServerToolReturnPart
+
+ env = TestEnv()
+ env.set('ANTHROPIC_API_KEY', 'test-key')
+ model = AnthropicModel('claude-3-5-sonnet-latest', provider='anthropic')
+
+ # Create a message with an unsupported server tool name
+ messages: list[ModelMessage] = [
+ ModelResponse(
+ parts=[
+ ServerToolReturnPart(
+ tool_name='unsupported_tool', # This should trigger the error
+ content='some content',
+ tool_call_id='test_id',
+ )
+ ]
+ )
+ ]
+
+ # This should raise a ValueError
+ with pytest.raises(ValueError, match='Unsupported tool name: unsupported_tool'):
+ await model._map_message(messages) # type: ignore[attr-defined]
diff --git a/tests/models/test_fallback.py b/tests/models/test_fallback.py
index 89709d4a2..38aad6332 100644
--- a/tests/models/test_fallback.py
+++ b/tests/models/test_fallback.py
@@ -127,7 +127,7 @@ def test_first_failed_instrumented(capfire: CaptureLogfire) -> None:
'end_time': 3000000000,
'attributes': {
'gen_ai.operation.name': 'chat',
- 'model_request_parameters': '{"function_tools": [], "output_mode": "text", "output_object": null, "output_tools": [], "allow_text_output": true}',
+ 'model_request_parameters': '{"function_tools": [], "builtin_tools": [], "output_mode": "text", "output_object": null, "output_tools": [], "allow_text_output": true}',
'logfire.span_type': 'span',
'logfire.msg': 'chat fallback:function:failure_response:,function:success_response:',
'gen_ai.system': 'function',
@@ -200,7 +200,7 @@ async def test_first_failed_instrumented_stream(capfire: CaptureLogfire) -> None
'end_time': 3000000000,
'attributes': {
'gen_ai.operation.name': 'chat',
- 'model_request_parameters': '{"function_tools": [], "output_mode": "text", "output_object": null, "output_tools": [], "allow_text_output": true}',
+ 'model_request_parameters': '{"function_tools": [], "builtin_tools": [], "output_mode": "text", "output_object": null, "output_tools": [], "allow_text_output": true}',
'logfire.span_type': 'span',
'logfire.msg': 'chat fallback:function::failure_response_stream,function::success_response_stream',
'gen_ai.system': 'function',
@@ -272,7 +272,7 @@ def test_all_failed_instrumented(capfire: CaptureLogfire) -> None:
'gen_ai.operation.name': 'chat',
'gen_ai.system': 'fallback:function,function',
'gen_ai.request.model': 'fallback:function:failure_response:,function:failure_response:',
- 'model_request_parameters': '{"function_tools": [], "output_mode": "text", "output_object": null, "output_tools": [], "allow_text_output": true}',
+ 'model_request_parameters': '{"function_tools": [], "builtin_tools": [], "output_mode": "text", "output_object": null, "output_tools": [], "allow_text_output": true}',
'logfire.json_schema': '{"type": "object", "properties": {"model_request_parameters": {"type": "object"}}}',
'logfire.span_type': 'span',
'logfire.msg': 'chat fallback:function:failure_response:,function:failure_response:',
diff --git a/tests/models/test_google.py b/tests/models/test_google.py
index 29bd5d201..a8549b93e 100644
--- a/tests/models/test_google.py
+++ b/tests/models/test_google.py
@@ -5,12 +5,14 @@
from typing import Any
import pytest
+from google.genai.types import Language
from httpx import Timeout
from inline_snapshot import Is, snapshot
from pydantic import BaseModel
from typing_extensions import TypedDict
from pydantic_ai.agent import Agent
+from pydantic_ai.builtin_tools import CodeExecutionTool, WebSearchTool
from pydantic_ai.exceptions import ModelRetry, UnexpectedModelBehavior, UserError
from pydantic_ai.messages import (
AudioUrl,
@@ -25,6 +27,8 @@
PartDeltaEvent,
PartStartEvent,
RetryPromptPart,
+ ServerToolCallPart,
+ ServerToolReturnPart,
SystemPromptPart,
TextPart,
TextPartDelta,
@@ -553,6 +557,78 @@ async def test_google_model_safety_settings(allow_model_requests: None, google_p
await agent.run('Tell me a joke about a Brazilians.')
+async def test_google_model_web_search_tool(allow_model_requests: None, google_provider: GoogleProvider):
+ m = GoogleModel('gemini-2.0-flash', provider=google_provider)
+ agent = Agent(m, system_prompt='You are a helpful chatbot.', builtin_tools=[WebSearchTool()])
+
+ result = await agent.run('What day is today in Utrecht?')
+ assert result.output == snapshot('Today is Wednesday, May 28, 2025, in Utrecht.\n')
+
+
+async def test_google_model_code_execution_tool(allow_model_requests: None, google_provider: GoogleProvider):
+ m = GoogleModel('gemini-2.0-flash', provider=google_provider)
+ agent = Agent(m, system_prompt='You are a helpful chatbot.', builtin_tools=[CodeExecutionTool()])
+
+ result = await agent.run('What day is today in Utrecht?')
+ assert result.all_messages() == snapshot(
+ [
+ ModelRequest(
+ parts=[
+ SystemPromptPart(content='You are a helpful chatbot.', timestamp=IsDatetime()),
+ UserPromptPart(content='What day is today in Utrecht?', timestamp=IsDatetime()),
+ ]
+ ),
+ ModelResponse(
+ parts=[
+ TextPart(
+ content="""\
+To determine the current day in Utrecht, I need to know the current date and time. I will use a tool to get this information.
+
+"""
+ ),
+ ServerToolCallPart(
+ tool_name='code_execution',
+ args={
+ 'code': """\
+import datetime
+import pytz
+
+utrecht_timezone = pytz.timezone('Europe/Amsterdam')
+now_utrecht = datetime.datetime.now(utrecht_timezone)
+print(now_utrecht.strftime("%A, %Y-%m-%d"))
+""",
+ 'language': Language.PYTHON,
+ },
+ tool_call_id=IsStr(),
+ ),
+ ServerToolReturnPart(
+ tool_name='code_execution',
+ content='Wednesday, 2025-05-28\n',
+ tool_call_id="It doesn't have.",
+ timestamp=IsDatetime(),
+ ),
+ TextPart(content='Today is Wednesday, May 28, 2025 in Utrecht.\n'),
+ ],
+ usage=Usage(
+ requests=1,
+ request_tokens=13,
+ response_tokens=119,
+ total_tokens=246,
+ details={
+ 'tool_use_prompt_tokens': 114,
+ 'text_candidates_tokens': 119,
+ 'text_prompt_tokens': 13,
+ 'text_tool_use_prompt_tokens': 114,
+ },
+ ),
+ model_name='gemini-2.0-flash',
+ timestamp=IsDatetime(),
+ vendor_details={'finish_reason': 'STOP'},
+ ),
+ ]
+ )
+
+
async def test_google_model_empty_user_prompt(allow_model_requests: None, google_provider: GoogleProvider):
m = GoogleModel('gemini-1.5-flash', provider=google_provider)
agent = Agent(m, instructions='You are a helpful assistant.')
@@ -747,7 +823,6 @@ async def test_google_url_input(
@pytest.mark.skipif(
not os.getenv('CI', False), reason='Requires properly configured local google vertex config to pass'
)
-@pytest.mark.vcr()
async def test_google_url_input_force_download(allow_model_requests: None) -> None:
provider = GoogleProvider(project='pydantic-ai', location='us-central1')
m = GoogleModel('gemini-2.0-flash', provider=provider)
diff --git a/tests/models/test_groq.py b/tests/models/test_groq.py
index 0cb4bef99..2764a7bb6 100644
--- a/tests/models/test_groq.py
+++ b/tests/models/test_groq.py
@@ -15,12 +15,15 @@
from typing_extensions import TypedDict
from pydantic_ai import Agent, ModelHTTPError, ModelRetry, UnexpectedModelBehavior
+from pydantic_ai.builtin_tools import WebSearchTool
from pydantic_ai.messages import (
BinaryContent,
ImageUrl,
ModelRequest,
ModelResponse,
RetryPromptPart,
+ ServerToolCallPart,
+ ServerToolReturnPart,
SystemPromptPart,
TextPart,
ThinkingPart,
@@ -521,7 +524,6 @@ async def test_no_delta(allow_model_requests: None):
assert result.is_complete
-@pytest.mark.vcr()
async def test_extra_headers(allow_model_requests: None, groq_api_key: str):
# This test doesn't do anything, it's just here to ensure that calls with `extra_headers` don't cause errors, including type.
m = GroqModel('llama-3.3-70b-versatile', provider=GroqProvider(api_key=groq_api_key))
@@ -529,7 +531,6 @@ async def test_extra_headers(allow_model_requests: None, groq_api_key: str):
await agent.run('hello')
-@pytest.mark.vcr()
async def test_image_url_input(allow_model_requests: None, groq_api_key: str):
m = GroqModel('meta-llama/llama-4-scout-17b-16e-instruct', provider=GroqProvider(api_key=groq_api_key))
agent = Agent(m)
@@ -545,7 +546,6 @@ async def test_image_url_input(allow_model_requests: None, groq_api_key: str):
)
-@pytest.mark.vcr()
async def test_image_as_binary_content_tool_response(
allow_model_requests: None, groq_api_key: str, image_content: BinaryContent
):
@@ -684,6 +684,207 @@ async def test_groq_model_instructions(allow_model_requests: None, groq_api_key:
)
+async def test_groq_model_web_search_tool(allow_model_requests: None, groq_api_key: str):
+ m = GroqModel('compound-beta', provider=GroqProvider(api_key=groq_api_key))
+ agent = Agent(m, builtin_tools=[WebSearchTool()])
+
+ result = await agent.run('What day is today?')
+ assert result.output == snapshot('The current day is Tuesday.')
+ assert result.all_messages() == snapshot(
+ [
+ ModelRequest(parts=[UserPromptPart(content='What day is today?', timestamp=IsDatetime())]),
+ ModelResponse(
+ parts=[
+ ServerToolCallPart(
+ tool_name='search',
+ args='{"query": "What is the current date?"}',
+ tool_call_id=IsStr(),
+ model_name='groq',
+ ),
+ ServerToolReturnPart(
+ tool_name='search',
+ content="""\
+Title: Today's Date - Find Out Quickly What's The Date Today ️
+URL: https://calendarhours.com/todays-date/
+Content: The current date in RFC 2822 Format with shortened day of week, numerical date, three-letter month abbreviation, year, time, and time zone is: Tue, 13 May 2025 06:07:56 -0400; The current date in Unix Epoch Format with number of seconds that have elapsed since January 1, 1970 (midnight UTC/GMT) is:
+Score: 0.8299
+
+Title: Today's Date | Current date now - MaxTables
+URL: https://maxtables.com/tools/todays-date.html
+Content: The current date, including day of the week, month, day, and year. The exact time, down to seconds. Details on the time zone, its location, and its GMT difference. A tool to select the present date. A visual calendar chart. Why would I need to check Today's Date on this platform instead of my device?
+Score: 0.7223
+
+Title: Current Time and Date - Exact Time!
+URL: https://time-and-calendar.com/
+Content: The actual time is: Mon May 12 2025 22:14:39 GMT-0700 (Pacific Daylight Time) Your computer time is: 22:14:38 The time of your computer is synchronized with our web server. This mean that it is synchonizing in real time with our server clock.
+Score: 0.6799
+
+Title: Today's Date - CalendarDate.com
+URL: https://www.calendardate.com/todays.htm
+Content: Details about today's date with count of days, weeks, and months, Sun and Moon cycles, Zodiac signs and holidays. Monday May 12, 2025 . Home; Calendars. 2025 Calendar; ... Current Season Today: Spring with 40 days until the start of Summer. S. Hemishpere flip seasons - i.e. Winter is Summer.
+Score: 0.6416
+
+Title: What is the date today | Today's Date
+URL: https://www.datetoday.info/
+Content: Master time tracking with Today's Date. Stay updated with real-time information on current date, time, day of the week, days left in the week, current day and remaining days of the year. Explore time in globally accepted formats. Keep up with the current week and month, along with the remaining weeks and months for the year. Embrace efficient time tracking with Today's Date.
+Score: 0.6282
+
+Title: Explore Today's Date, Time Zones, Holidays & More
+URL: https://whatdateis.today/
+Content: Check what date and time it is today (May 8, 2025). View current time across different time zones, upcoming holidays, and use our date calculator. Your one-stop destination for all date and time information.
+Score: 0.6181
+
+Title: Today's Date and Time - Date and Time Tools
+URL: https://todaysdatetime.com/
+Content: Discover today's exact date and time, learn about time zones, date formats, and explore our comprehensive collection of date and time tools including calculators, converters, and calendars. ... Get the exact current date and time, along with powerful calculation tools for all your scheduling needs. 12h. Today. Day 76 of year (366) Yesterday
+Score: 0.5456
+
+Title: Current Time Now - What time is it? - RapidTables.com
+URL: https://www.rapidtables.com/tools/current-time.html
+Content: This page includes the following information: Current time: hours, minutes, seconds. Today's date: day of week, month, day, year. Time zone with location and GMT offset.
+Score: 0.4255
+
+Title: Current Time
+URL: https://www.timeanddate.com/
+Content: Welcome to the world's top site for time, time zones, and astronomy. Organize your life with free online info and tools you can rely on. No sign-up needed. Sign in. News. News Home; Astronomy News; ... Current Time. Monday May 12, 2025 Roanoke Rapids, North Carolina, USA. Set home location. 11:27: 03 pm. World Clock.
+Score: 0.3876
+
+Title: Current local time in the United States - World clock
+URL: https://dateandtime.info/country.php?code=US
+Content: Time and Date of DST Change Time Change; DST started: Sunday, March 9, 2025 at 2:00 AM: The clocks were put forward an hour to 3:00 AM. DST ends: Sunday, November 2, 2025 at 2:00 AM: The clocks will be put back an hour to 1:00 AM. DST starts: Sunday, March 8, 2026 at 2:00 AM: The clocks will be put forward an hour to 3:00 AM.
+Score: 0.3042
+
+Title: Time.is - exact time, any time zone
+URL: https://time.is/
+Content: 7 million locations, 58 languages, synchronized with atomic clock time. Time.is. Get Time.is Ad-free! Exact time now: 05:08:45. Tuesday, 13 May, 2025, week 20. Sun: ↑ 05:09 ↓ 20:45 (15h 36m) - More info - Make London time default - Remove from favorite locations
+Score: 0.2796
+
+Title: Time in United States now
+URL: https://time.is/United_States
+Content: Exact time now, time zone, time difference, sunrise/sunset time and key facts for United States. Time.is. Get Time.is Ad-free! Time in United States now . 11:17:42 PM. Monday, May 12, 2025. United States (incl. dependent territories) has 11 time zones. The time zone for the capital Washington, D.C. is used here.
+Score: 0.2726
+
+Title: Current Local Time in the United States - timeanddate.com
+URL: https://www.timeanddate.com/worldclock/usa
+Content: United States time now. USA time zones and time zone map with current time in each state.
+Score: 0.2519
+
+Title: Current local time in United States - World Time Clock & Map
+URL: https://24timezones.com/United-States/time
+Content: Check the current time in United States and time zone information, the UTC offset and daylight saving time dates in 2025.
+Score: 0.2221
+
+Title: The World Clock — Worldwide - timeanddate.com
+URL: https://www.timeanddate.com/worldclock/
+Content: World time and date for cities in all time zones. International time right now. Takes into account all DST clock changes.
+Score: 0.2134
+
+""",
+ tool_call_id=IsStr(),
+ timestamp=IsDatetime(),
+ ),
+ ThinkingPart(
+ content="""\
+
+To determine the current day, I need to access real-time information. I will use the search tool to find out the current date.
+
+
+search(What is the current date?)
+
+
+The current date is Tuesday, May 13, 2025.
+
+
+
+The current day is Tuesday.\
+"""
+ ),
+ TextPart(content='The current day is Tuesday.'),
+ ],
+ usage=Usage(requests=1, request_tokens=4287, response_tokens=117, total_tokens=4404),
+ model_name='compound-beta',
+ timestamp=IsDatetime(),
+ vendor_id='stub',
+ ),
+ ]
+ )
+
+
async def test_groq_model_thinking_part(allow_model_requests: None, groq_api_key: str):
m = GroqModel('deepseek-r1-distill-llama-70b', provider=GroqProvider(api_key=groq_api_key))
settings = GroqModelSettings(groq_reasoning_format='raw')
@@ -697,64 +898,7 @@ async def test_groq_model_thinking_part(allow_model_requests: None, groq_api_key
instructions='You are a chef.',
),
ModelResponse(
- parts=[
- ThinkingPart(content=IsStr()),
- TextPart(
- content="""\
-
-
-To make Uruguayan alfajores, follow these organized steps for a delightful baking experience:
-
-### Ingredients:
-- 2 cups all-purpose flour
-- 1 cup cornstarch
-- 1 tsp baking powder
-- 1/2 tsp salt
-- 1 cup unsalted butter, softened
-- 1 cup powdered sugar
-- 1 egg
-- 1 tsp vanilla extract
-- Dulce de leche (store-bought or homemade)
-- Powdered sugar for coating
-
-### Instructions:
-
-1. **Preheat Oven:**
- - Preheat your oven to 300°F (150°C). Line a baking sheet with parchment paper.
-
-2. **Prepare Dough:**
- - In a bowl, whisk together flour, cornstarch, baking powder, and salt.
- - In another bowl, cream butter and powdered sugar until smooth. Add egg and vanilla, mixing well.
- - Gradually incorporate the dry ingredients into the wet mixture until a dough forms. Wrap and let rest for 30 minutes.
-
-3. **Roll and Cut:**
- - Roll dough to 1/4 inch thickness. Cut into 2-inch circles using a cutter or glass.
-
-4. **Bake:**
- - Place cookies on the prepared baking sheet, bake for 15-20 minutes until edges are lightly golden. Cool on the sheet for 5 minutes, then transfer to a wire rack to cool completely.
-
-5. **Assemble Alfajores:**
- - Spread a layer of dulce de leche on one cookie half. Sandwich with another cookie. Handle gently to avoid breaking.
-
-6. **Coat with Powdered Sugar:**
- - Roll each alfajor in powdered sugar, pressing gently to adhere.
-
-7. **Optional Chocolate Coating:**
- - For a chocolate version, melt chocolate and dip alfajores, then chill to set.
-
-8. **Storage:**
- - Store in an airtight container at room temperature for up to a week. Freeze for longer storage.
-
-### Tips:
-- Ensure butter is softened for smooth creaming.
-- Check cookies after 15 minutes to avoid over-browning.
-- Allow cookies to cool completely before handling.
-- Homemade dulce de leche can be made by heating condensed milk until thickened.
-
-Enjoy your traditional Uruguayan alfajores with a cup of coffee or tea!\
-"""
- ),
- ],
+ parts=[IsInstance(ThinkingPart), IsInstance(TextPart)],
usage=Usage(requests=1, request_tokens=21, response_tokens=1414, total_tokens=1435),
model_name='deepseek-r1-distill-llama-70b',
timestamp=IsDatetime(),
@@ -779,7 +923,7 @@ async def test_groq_model_thinking_part(allow_model_requests: None, groq_api_key
usage=Usage(requests=1, request_tokens=21, response_tokens=1414, total_tokens=1435),
model_name='deepseek-r1-distill-llama-70b',
timestamp=IsDatetime(),
- vendor_id=IsStr(),
+ vendor_id='chatcmpl-9748c1af-1065-410a-969a-d7fb48039fbb',
),
ModelRequest(
parts=[
@@ -791,112 +935,11 @@ async def test_groq_model_thinking_part(allow_model_requests: None, groq_api_key
instructions='You are a chef.',
),
ModelResponse(
- parts=[
- ThinkingPart(
- content="""\
-Alright, so I want to make Argentinian alfajores after successfully making the Uruguayan ones. I know that both countries have their own versions of alfajores, but I'm not entirely sure how they differ. Maybe the ingredients or the preparation steps are slightly different? I should probably start by researching what makes Argentinian alfajores unique compared to the Uruguayan ones.
-
-First, I remember that in the Uruguayan recipe, the cookies were more delicate, and the filling was a generous amount of dulce de leche. The process involved making the dough from scratch, baking the cookies, and then assembling them with the filling and sometimes coating them in powdered sugar or chocolate.
-
-For Argentinian alfajores, I think the cookies might have a different texture—perhaps they're crunchier or have a different flavor profile. Maybe the type of flour used is different, or there's an addition like cocoa powder for a chocolate version. Also, the filling might have variations, like adding caramel or nuts to the dulce de leche.
-
-I should look up some authentic Argentinian recipes to see the differences. Maybe the method of making the dough is slightly different, or the baking time and temperature vary. I also wonder if Argentinian alfajores are typically coated in something else besides powdered sugar, like cinnamon or coconut flakes.
-
-Another thing to consider is the size and shape of the cookies. Uruguayan alfajores might be smaller and rounder, while Argentinian ones could be larger or have a different shape. I should also check if there are any additional steps, like toasting the cookies or adding a layer of meringue.
-
-I need to make sure I have all the necessary ingredients. If the Argentinian version requires something different, like a specific type of flour or a particular flavoring, I'll need to adjust my shopping list. Also, if the filling is different, I might need to prepare it in a different way or add extra ingredients.
-
-I should also think about the assembly process. Maybe Argentinian alfajores are sandwiched with more filling or have a different way of sealing the cookies together. Perhaps they're rolled in coconut after being filled, or there's a step involving dipping them in chocolate.
-
-It would be helpful to watch a video or read a detailed recipe from an Argentinian source to get a better understanding. That way, I can follow the traditional method and ensure that my alfajores turn out authentic. I should also consider any tips or tricks that Argentinian bakers use to make their alfajores special.
-
-Finally, I need to plan the timing. Making alfajores can be a bit time-consuming, especially if you're making the dulce de leche from scratch. I should allocate enough time for preparing the dough, baking the cookies, and assembling the alfajores.
-
-Overall, the key steps I think I'll need to follow are:
-
-1. Research authentic Argentinian alfajores recipes to identify unique ingredients and steps.
-2. Adjust the ingredient list based on the differences from the Uruguayan version.
-3. Prepare the dough according to the Argentinian method, which might involve different mixing techniques or additional ingredients.
-4. Bake the cookies, possibly at a different temperature or for a different duration.
-5. Prepare the filling, which might include variations like caramel or nuts.
-6. Assemble the alfajores, possibly adding extra coatings or layers.
-7. Allow the alfajores to set before serving, to ensure the flavors meld together.
-
-By carefully following these steps and paying attention to the unique aspects of Argentinian alfajores, I should be able to create a delicious and authentic batch that captures the essence of this traditional South American treat.
-"""
- ),
- TextPart(
- content="""\
-To create authentic Argentinian alfajores, follow these organized steps, which highlight the unique characteristics and differences from the Uruguayan version:
-
-### Ingredients:
-- **For the Cookies:**
- - 2 cups all-purpose flour
- - 1/2 cup cornstarch
- - 1/4 cup unsweetened cocoa powder (optional for chocolate version)
- - 1 teaspoon baking powder
- - 1/2 teaspoon salt
- - 1 cup unsalted butter, softened
- - 1 cup powdered sugar
- - 1 egg
- - 1 teaspoon vanilla extract
-
-- **For the Filling:**
- - Dulce de leche (store-bought or homemade)
- - Optional: caramel sauce, chopped nuts, or cinnamon for added flavor
-
-- **For Coating:**
- - Powdered sugar
- - Optional: cinnamon, shredded coconut, or melted chocolate
-
-### Instructions:
-
-1. **Prepare the Dough:**
- - In a large bowl, whisk together the flour, cornstarch, cocoa powder (if using), baking powder, and salt.
- - In another bowl, cream the softened butter and powdered sugar until smooth. Add the egg and vanilla extract, mixing well.
- - Gradually incorporate the dry ingredients into the wet mixture until a dough forms. Wrap the dough in plastic wrap and let it rest for 30 minutes.
-
-2. **Roll and Cut the Cookies:**
- - Roll the dough to about 1/4 inch thickness on a lightly floured surface.
- - Use a round cookie cutter or the rim of a glass to cut out circles of dough. Argentinian alfajores are often slightly larger than their Uruguayan counterparts.
-
-3. **Bake the Cookies:**
- - Preheat the oven to 300°F (150°C). Line a baking sheet with parchment paper.
- - Place the cookie rounds on the prepared baking sheet, leaving about 1 inch of space between each cookie.
- - Bake for 15-20 minutes, or until the edges are lightly golden. Allow the cookies to cool on the baking sheet for 5 minutes before transferring them to a wire rack to cool completely.
-
-4. **Prepare the Filling:**
- - Use store-bought dulce de leche or make your own by heating sweetened condensed milk until thickened.
- - Optional: Stir in caramel sauce or chopped nuts into the dulce de leche for added flavor.
-
-5. **Assemble the Alfajores:**
- - Once the cookies are completely cool, spread a generous amount of dulce de leche on the flat side of one cookie.
- - Sandwich with another cookie, pressing gently to adhere. For an extra touch, roll the edges in chopped nuts or shredded coconut.
-
-6. **Coat with Powdered Sugar or Chocolate:**
- - Roll each alfajor in powdered sugar, pressing gently to ensure it adheres.
- - Optional: Melt chocolate and dip the alfajores, then sprinkle with cinnamon or coconut before the chocolate sets.
-
-7. **Allow to Set:**
- - Let the alfajores sit for about 30 minutes to allow the flavors to meld together.
-
-8. **Serve and Enjoy:**
- - Serve with a cup of coffee or tea. Store any leftovers in an airtight container at room temperature for up to a week.
-
-### Tips:
-- **Dough Handling:** Ensure the butter is softened for a smooth dough, and don't overwork the dough to maintain the cookies' tender texture.
-- **Baking:** Keep an eye on the cookies after 15 minutes to prevent over-browning.
-- **Filling Variations:** Experiment with different fillings like caramel or nuts to create unique flavor profiles.
-- **Coating Options:** Try different coatings such as cinnamon or coconut for a varied texture and taste.
-
-By following these steps, you'll create authentic Argentinian alfajores that capture the rich flavors and traditions of this beloved South American treat.\
-"""
- ),
- ],
+ parts=[IsInstance(ThinkingPart), IsInstance(TextPart)],
usage=Usage(requests=1, request_tokens=524, response_tokens=1590, total_tokens=2114),
model_name='deepseek-r1-distill-llama-70b',
timestamp=IsDatetime(),
- vendor_id=IsStr(),
+ vendor_id='chatcmpl-994aa228-883a-498c-8b20-9655d770b697',
),
]
)
diff --git a/tests/models/test_instrumented.py b/tests/models/test_instrumented.py
index c0befafef..132cd1613 100644
--- a/tests/models/test_instrumented.py
+++ b/tests/models/test_instrumented.py
@@ -153,7 +153,7 @@ async def test_instrumented_model(capfire: CaptureLogfire):
'gen_ai.request.model': 'my_model',
'server.address': 'example.com',
'server.port': 8000,
- 'model_request_parameters': '{"function_tools": [], "output_mode": "text", "output_object": null, "output_tools": [], "allow_text_output": true}',
+ 'model_request_parameters': '{"function_tools": [], "builtin_tools": [], "output_mode": "text", "output_object": null, "output_tools": [], "allow_text_output": true}',
'logfire.json_schema': '{"type": "object", "properties": {"model_request_parameters": {"type": "object"}}}',
'gen_ai.request.temperature': 1,
'logfire.msg': 'chat my_model',
@@ -382,7 +382,7 @@ async def test_instrumented_model_stream(capfire: CaptureLogfire):
'gen_ai.request.model': 'my_model',
'server.address': 'example.com',
'server.port': 8000,
- 'model_request_parameters': '{"function_tools": [], "output_mode": "text", "output_object": null, "output_tools": [], "allow_text_output": true}',
+ 'model_request_parameters': '{"function_tools": [], "builtin_tools": [], "output_mode": "text", "output_object": null, "output_tools": [], "allow_text_output": true}',
'logfire.json_schema': '{"type": "object", "properties": {"model_request_parameters": {"type": "object"}}}',
'gen_ai.request.temperature': 1,
'logfire.msg': 'chat my_model',
@@ -469,7 +469,7 @@ async def test_instrumented_model_stream_break(capfire: CaptureLogfire):
'gen_ai.request.model': 'my_model',
'server.address': 'example.com',
'server.port': 8000,
- 'model_request_parameters': '{"function_tools": [], "output_mode": "text", "output_object": null, "output_tools": [], "allow_text_output": true}',
+ 'model_request_parameters': '{"function_tools": [], "builtin_tools": [], "output_mode": "text", "output_object": null, "output_tools": [], "allow_text_output": true}',
'logfire.json_schema': '{"type": "object", "properties": {"model_request_parameters": {"type": "object"}}}',
'gen_ai.request.temperature': 1,
'logfire.msg': 'chat my_model',
@@ -571,7 +571,7 @@ async def test_instrumented_model_attributes_mode(capfire: CaptureLogfire):
'gen_ai.request.model': 'my_model',
'server.address': 'example.com',
'server.port': 8000,
- 'model_request_parameters': '{"function_tools": [], "output_mode": "text", "output_object": null, "output_tools": [], "allow_text_output": true}',
+ 'model_request_parameters': '{"function_tools": [], "builtin_tools": [], "output_mode": "text", "output_object": null, "output_tools": [], "allow_text_output": true}',
'gen_ai.request.temperature': 1,
'logfire.msg': 'chat my_model',
'logfire.span_type': 'span',
diff --git a/tests/models/test_model_request_parameters.py b/tests/models/test_model_request_parameters.py
index 98a6d1ccc..2915796ab 100644
--- a/tests/models/test_model_request_parameters.py
+++ b/tests/models/test_model_request_parameters.py
@@ -9,6 +9,7 @@ def test_model_request_parameters_are_serializable():
)
assert TypeAdapter(ModelRequestParameters).dump_python(params) == {
'function_tools': [],
+ 'builtin_tools': [],
'output_mode': 'text',
'allow_text_output': True,
'output_tools': [],
diff --git a/tests/models/test_openai.py b/tests/models/test_openai.py
index 74f08b190..31f721870 100644
--- a/tests/models/test_openai.py
+++ b/tests/models/test_openai.py
@@ -16,6 +16,7 @@
from typing_extensions import TypedDict
from pydantic_ai import Agent, ModelHTTPError, ModelRetry, UnexpectedModelBehavior
+from pydantic_ai.builtin_tools import WebSearchTool
from pydantic_ai.messages import (
AudioUrl,
BinaryContent,
@@ -1828,10 +1829,7 @@ async def test_openai_instructions_with_logprobs(allow_model_requests: None):
'gpt-4o',
provider=OpenAIProvider(openai_client=mock_client),
)
- agent = Agent(
- m,
- instructions='You are a helpful assistant.',
- )
+ agent = Agent(m, instructions='You are a helpful assistant.')
result = await agent.run(
'What is the capital of Minas Gerais?',
model_settings=OpenAIModelSettings(openai_logprobs=True),
@@ -1849,6 +1847,56 @@ async def test_openai_instructions_with_logprobs(allow_model_requests: None):
]
+@pytest.mark.vcr()
+async def test_openai_web_search_tool_model_not_supported(allow_model_requests: None, openai_api_key: str):
+ m = OpenAIModel('gpt-4o', provider=OpenAIProvider(api_key=openai_api_key))
+ agent = Agent(
+ m, instructions='You are a helpful assistant.', builtin_tools=[WebSearchTool(search_context_size='low')]
+ )
+
+ with pytest.raises(ModelHTTPError, match='.*Web search options not supported with this model.*'):
+ await agent.run('What day is today?')
+
+
+@pytest.mark.vcr()
+async def test_openai_web_search_tool(allow_model_requests: None, openai_api_key: str):
+ m = OpenAIModel('gpt-4o-search-preview', provider=OpenAIProvider(api_key=openai_api_key))
+ agent = Agent(
+ m, instructions='You are a helpful assistant.', builtin_tools=[WebSearchTool(search_context_size='low')]
+ )
+
+ result = await agent.run('What day is today?')
+ assert result.output == snapshot('May 14, 2025, 8:51:29 AM ')
+
+
+@pytest.mark.vcr()
+async def test_openai_web_search_tool_with_user_location(allow_model_requests: None, openai_api_key: str):
+ m = OpenAIModel('gpt-4o-search-preview', provider=OpenAIProvider(api_key=openai_api_key))
+ agent = Agent(
+ m,
+ instructions='You are a helpful assistant.',
+ builtin_tools=[WebSearchTool(user_location={'city': 'Utrecht', 'country': 'NL'})],
+ )
+
+ result = await agent.run('What is the current temperature?')
+ assert result.output == snapshot("""\
+Het is momenteel zonnig in Utrecht met een temperatuur van 22°C.
+
+## Weer voor Utrecht, Nederland:
+Huidige omstandigheden: Zonnig, 72°F (22°C)
+
+Dagvoorspelling:
+* woensdag, mei 14: minimum: 48°F (9°C), maximum: 71°F (22°C), beschrijving: Afnemende bewolking
+* donderdag, mei 15: minimum: 43°F (6°C), maximum: 67°F (20°C), beschrijving: Na een bewolkt begin keert de zon terug
+* vrijdag, mei 16: minimum: 45°F (7°C), maximum: 64°F (18°C), beschrijving: Overwegend zonnig
+* zaterdag, mei 17: minimum: 47°F (9°C), maximum: 68°F (20°C), beschrijving: Overwegend zonnig
+* zondag, mei 18: minimum: 47°F (8°C), maximum: 68°F (20°C), beschrijving: Deels zonnig
+* maandag, mei 19: minimum: 49°F (9°C), maximum: 70°F (21°C), beschrijving: Deels zonnig
+* dinsdag, mei 20: minimum: 49°F (10°C), maximum: 72°F (22°C), beschrijving: Zonnig tot gedeeltelijk bewolkt
+ \
+""")
+
+
@pytest.mark.vcr()
async def test_reasoning_model_with_temperature(allow_model_requests: None, openai_api_key: str):
m = OpenAIModel('o3-mini', provider=OpenAIProvider(api_key=openai_api_key))
diff --git a/tests/models/test_openai_responses.py b/tests/models/test_openai_responses.py
index 46a38262b..61652f9b6 100644
--- a/tests/models/test_openai_responses.py
+++ b/tests/models/test_openai_responses.py
@@ -7,6 +7,7 @@
from typing_extensions import TypedDict
from pydantic_ai.agent import Agent
+from pydantic_ai.builtin_tools import WebSearchTool
from pydantic_ai.exceptions import ModelHTTPError, ModelRetry
from pydantic_ai.messages import (
BinaryContent,
@@ -471,6 +472,58 @@ async def test_openai_responses_model_instructions(allow_model_requests: None, o
)
+async def test_openai_responses_model_web_search_tool(allow_model_requests: None, openai_api_key: str):
+ m = OpenAIResponsesModel('gpt-4o', provider=OpenAIProvider(api_key=openai_api_key))
+ agent = Agent(m, instructions='You are a helpful assistant.', builtin_tools=['web-search'])
+
+ result = await agent.run('What day is it today?')
+ assert result.output == snapshot("""\
+Today is Wednesday, May 14, 2025.
+
+## Weather for San Francisco, CA:
+Current Conditions: Mostly clear, 50°F (10°C)
+
+Daily Forecast:
+* Wednesday, May 14: Low: 51°F (10°C), High: 65°F (18°C), Description: Areas of low clouds early; otherwise, mostly sunny
+* Thursday, May 15: Low: 53°F (12°C), High: 66°F (19°C), Description: Areas of low clouds, then sun
+* Friday, May 16: Low: 53°F (12°C), High: 64°F (18°C), Description: Partly sunny
+* Saturday, May 17: Low: 52°F (11°C), High: 63°F (17°C), Description: Low clouds breaking for some sun; breezy in the afternoon
+* Sunday, May 18: Low: 51°F (10°C), High: 68°F (20°C), Description: Clouds yielding to sun
+* Monday, May 19: Low: 53°F (12°C), High: 68°F (20°C), Description: Sunny
+* Tuesday, May 20: Low: 52°F (11°C), High: 70°F (21°C), Description: Mostly sunny
+ \
+""")
+
+
+async def test_openai_responses_model_web_search_tool_with_user_location(
+ allow_model_requests: None, openai_api_key: str
+):
+ m = OpenAIResponsesModel('gpt-4o', provider=OpenAIProvider(api_key=openai_api_key))
+ agent = Agent(
+ m,
+ instructions='You are a helpful assistant.',
+ builtin_tools=[WebSearchTool(user_location={'city': 'Utrecht', 'country': 'NL'})],
+ )
+
+ result = await agent.run('What is the current temperature?')
+ assert result.output == snapshot("""\
+As of 12:58 PM on Wednesday, May 14, 2025, in Utrecht, Netherlands, the weather is sunny with a temperature of 22°C (71°F).
+
+## Weather for Utrecht, Netherlands:
+Current Conditions: Sunny, 71°F (22°C)
+
+Daily Forecast:
+* Wednesday, May 14: Low: 48°F (9°C), High: 71°F (22°C), Description: Clouds yielding to sun
+* Thursday, May 15: Low: 43°F (6°C), High: 67°F (20°C), Description: After a cloudy start, sun returns
+* Friday, May 16: Low: 45°F (7°C), High: 64°F (18°C), Description: Mostly sunny
+* Saturday, May 17: Low: 47°F (9°C), High: 68°F (20°C), Description: Mostly sunny
+* Sunday, May 18: Low: 47°F (8°C), High: 68°F (20°C), Description: Some sun
+* Monday, May 19: Low: 49°F (9°C), High: 70°F (21°C), Description: Delightful with partial sunshine
+* Tuesday, May 20: Low: 49°F (10°C), High: 72°F (22°C), Description: Warm with sunshine and a few clouds
+ \
+""")
+
+
def test_model_profile_strict_not_supported():
my_tool = ToolDefinition(
'my_tool',
diff --git a/tests/test_logfire.py b/tests/test_logfire.py
index 34aff7514..b0261ca15 100644
--- a/tests/test_logfire.py
+++ b/tests/test_logfire.py
@@ -291,6 +291,7 @@ async def my_ret(x: int) -> str:
'strict': None,
}
],
+ 'builtin_tools': [],
'output_mode': 'text',
'output_tools': [],
'output_object': None,
@@ -474,14 +475,44 @@ async def test_feedback(capfire: CaptureLogfire) -> None:
'gen_ai.operation.name': 'chat',
'gen_ai.system': 'test',
'gen_ai.request.model': 'test',
- 'model_request_parameters': '{"function_tools": [], "output_mode": "text", "output_object": null, "output_tools": [], "allow_text_output": true}',
+ 'model_request_parameters': IsJson(
+ {
+ 'function_tools': [],
+ 'builtin_tools': [],
+ 'output_mode': 'text',
+ 'output_object': None,
+ 'output_tools': [],
+ 'allow_text_output': True,
+ }
+ ),
'logfire.span_type': 'span',
'logfire.msg': 'chat test',
'gen_ai.usage.input_tokens': 51,
'gen_ai.usage.output_tokens': 4,
'gen_ai.response.model': 'test',
- 'events': '[{"content": "Hello", "role": "user", "gen_ai.system": "test", "gen_ai.message.index": 0, "event.name": "gen_ai.user.message"}, {"index": 0, "message": {"role": "assistant", "content": "success (no tool calls)"}, "gen_ai.system": "test", "event.name": "gen_ai.choice"}]',
- 'logfire.json_schema': '{"type": "object", "properties": {"events": {"type": "array"}, "model_request_parameters": {"type": "object"}}}',
+ 'events': IsJson(
+ [
+ {
+ 'content': 'Hello',
+ 'role': 'user',
+ 'gen_ai.system': 'test',
+ 'gen_ai.message.index': 0,
+ 'event.name': 'gen_ai.user.message',
+ },
+ {
+ 'index': 0,
+ 'message': {'role': 'assistant', 'content': 'success (no tool calls)'},
+ 'gen_ai.system': 'test',
+ 'event.name': 'gen_ai.choice',
+ },
+ ]
+ ),
+ 'logfire.json_schema': IsJson(
+ {
+ 'type': 'object',
+ 'properties': {'events': {'type': 'array'}, 'model_request_parameters': {'type': 'object'}},
+ }
+ ),
},
},
{
diff --git a/uv.lock b/uv.lock
index 1cfb6f386..32707d647 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1288,7 +1288,7 @@ wheels = [
[[package]]
name = "groq"
-version = "0.22.0"
+version = "0.25.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
@@ -1298,9 +1298,9 @@ dependencies = [
{ name = "sniffio" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/fd/59/7e03f5b12c097b7af48d5fe847a5bd00c90ccfc04b801c2ae68a043ddf1e/groq-0.22.0.tar.gz", hash = "sha256:9d090fbe4a051655faff649890d18aaacb3121393ad9d55399171fe081f1057b", size = 122956, upload-time = "2025-04-02T20:34:07.722Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/a4/fc/29e9c24ab59602747027f41b9d761d24cf9e5771014c9a731137f51e9cce/groq-0.25.0.tar.gz", hash = "sha256:6e1c7466b0da0130498187b825bd239f86fb77bf7551eacfbfa561d75048746a", size = 128199, upload-time = "2025-05-16T19:57:43.381Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/70/77/e6b60636f648922cc53144c36e1e7b77c9670539aab9e8b84a5fd1a53880/groq-0.22.0-py3-none-any.whl", hash = "sha256:f53d3966dff713aaa635671c2d075ebb932b0d48e3c4031ede9b84a2a6694c79", size = 126685, upload-time = "2025-04-02T20:34:06.637Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/11/1019a6cfdb2e520cb461cf70d859216be8ca122ddf5ad301fc3b0ee45fd4/groq-0.25.0-py3-none-any.whl", hash = "sha256:aadc78b40b1809cdb196b1aa8c7f7293108767df1508cafa3e0d5045d9328e7a", size = 129371, upload-time = "2025-05-16T19:57:41.786Z" },
]
[[package]]
@@ -3149,7 +3149,7 @@ requires-dist = [
{ name = "google-auth", marker = "extra == 'vertexai'", specifier = ">=2.36.0" },
{ name = "google-genai", marker = "extra == 'google'", specifier = ">=1.15.0" },
{ name = "griffe", specifier = ">=1.3.2" },
- { name = "groq", marker = "extra == 'groq'", specifier = ">=0.19.0" },
+ { name = "groq", marker = "extra == 'groq'", specifier = ">=0.25.0" },
{ name = "httpx", specifier = ">=0.27" },
{ name = "logfire", marker = "extra == 'logfire'", specifier = ">=3.11.0" },
{ name = "mcp", marker = "python_full_version >= '3.10' and extra == 'mcp'", specifier = ">=1.9.4" },