diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b2b440ec0..9c7927295 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -60,6 +60,6 @@ repos: rev: v2.3.0 hooks: - id: codespell - args: ['--skip', 'tests/models/cassettes/*,docs/a2a/fasta2a.md'] + args: ['--skip', 'tests/models/cassettes/*,docs/a2a/fasta2a.md,tests/models/test_groq.py'] additional_dependencies: - tomli diff --git a/pydantic_ai_slim/pydantic_ai/_agent_graph.py b/pydantic_ai_slim/pydantic_ai/_agent_graph.py index bf8397174..ecd72ec25 100644 --- a/pydantic_ai_slim/pydantic_ai/_agent_graph.py +++ b/pydantic_ai_slim/pydantic_ai/_agent_graph.py @@ -12,6 +12,7 @@ from opentelemetry.trace import Tracer from typing_extensions import TypeGuard, TypeVar, assert_never +from pydantic_ai.builtin_tools import AbstractBuiltinTool from pydantic_graph import BaseNode, Graph, GraphRunContext from pydantic_graph.nodes import End, NodeRunEndT @@ -94,6 +95,7 @@ class GraphAgentDeps(Generic[DepsT, OutputDataT]): output_validators: list[_output.OutputValidator[DepsT, OutputDataT]] function_tools: dict[str, Tool[DepsT]] = dataclasses.field(repr=False) + builtin_tools: list[AbstractBuiltinTool] = dataclasses.field(repr=False) mcp_servers: Sequence[MCPServer] = dataclasses.field(repr=False) default_retries: int @@ -266,6 +268,7 @@ async def add_mcp_server_tools(server: MCPServer) -> None: output_schema = ctx.deps.output_schema return models.ModelRequestParameters( function_tools=function_tool_defs, + builtin_tools=ctx.deps.builtin_tools, allow_text_output=_output.allow_text_output(output_schema), output_tools=output_schema.tool_defs() if output_schema is not None else [], ) @@ -418,7 +421,7 @@ async def stream( async for _event in stream: pass - async def _run_stream( + async def _run_stream( # noqa C901 self, ctx: GraphRunContext[GraphAgentState, GraphAgentDeps[DepsT, NodeRunEndT]] ) -> AsyncIterator[_messages.HandleResponseEvent]: if self._events_iterator is None: @@ -434,6 +437,10 @@ async def _run_stream() -> AsyncIterator[_messages.HandleResponseEvent]: texts.append(part.content) elif isinstance(part, _messages.ToolCallPart): tool_calls.append(part) + elif isinstance(part, _messages.ServerToolCallPart): + yield _messages.ServerToolCallEvent(part) + elif isinstance(part, _messages.ServerToolReturnPart): + yield _messages.ServerToolResultEvent(part) else: assert_never(part) diff --git a/pydantic_ai_slim/pydantic_ai/_utils.py b/pydantic_ai_slim/pydantic_ai/_utils.py index fe8501e6c..5b653129f 100644 --- a/pydantic_ai_slim/pydantic_ai/_utils.py +++ b/pydantic_ai_slim/pydantic_ai/_utils.py @@ -205,7 +205,13 @@ def now_utc() -> datetime: return datetime.now(tz=timezone.utc) -def guard_tool_call_id(t: _messages.ToolCallPart | _messages.ToolReturnPart | _messages.RetryPromptPart) -> str: +def guard_tool_call_id( + t: _messages.ToolCallPart + | _messages.ToolReturnPart + | _messages.RetryPromptPart + | _messages.ServerToolCallPart + | _messages.ServerToolReturnPart, +) -> str: """Type guard that either returns the tool call id or generates a new one if it's None.""" return t.tool_call_id or generate_tool_call_id() diff --git a/pydantic_ai_slim/pydantic_ai/agent.py b/pydantic_ai_slim/pydantic_ai/agent.py index 57010f5f8..c1a1eab38 100644 --- a/pydantic_ai_slim/pydantic_ai/agent.py +++ b/pydantic_ai_slim/pydantic_ai/agent.py @@ -14,6 +14,7 @@ from pydantic.json_schema import GenerateJsonSchema from typing_extensions import Literal, Never, Self, TypeIs, TypeVar, deprecated +from pydantic_ai.builtin_tools import AbstractBuiltinTool, WebSearchTool from pydantic_graph import End, Graph, GraphRun, GraphRunContext from pydantic_graph._utils import get_event_loop @@ -174,6 +175,7 @@ def __init__( retries: int = 1, output_retries: int | None = None, tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] = (), + builtin_tools: Sequence[Literal['web-search'] | AbstractBuiltinTool] = (), prepare_tools: ToolsPrepareFunc[AgentDepsT] | None = None, mcp_servers: Sequence[MCPServer] = (), defer_model_check: bool = False, @@ -203,6 +205,7 @@ def __init__( result_tool_description: str | None = None, result_retries: int | None = None, tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] = (), + builtin_tools: Sequence[Literal['web-search'] | AbstractBuiltinTool] = (), prepare_tools: ToolsPrepareFunc[AgentDepsT] | None = None, mcp_servers: Sequence[MCPServer] = (), defer_model_check: bool = False, @@ -227,6 +230,7 @@ def __init__( retries: int = 1, output_retries: int | None = None, tools: Sequence[Tool[AgentDepsT] | ToolFuncEither[AgentDepsT, ...]] = (), + builtin_tools: Sequence[Literal['web-search'] | AbstractBuiltinTool] = (), prepare_tools: ToolsPrepareFunc[AgentDepsT] | None = None, mcp_servers: Sequence[MCPServer] = (), defer_model_check: bool = False, @@ -256,6 +260,8 @@ def __init__( output_retries: The maximum number of retries to allow for result validation, defaults to `retries`. tools: Tools to register with the agent, you can also register tools via the decorators [`@agent.tool`][pydantic_ai.Agent.tool] and [`@agent.tool_plain`][pydantic_ai.Agent.tool_plain]. + builtin_tools: The builtin tools that the agent will use. This depends on the model, as some models may not + support certain tools. On models that don't support certain tools, the tool will be ignored. prepare_tools: custom method to prepare the tool definition of all tools for each step. This is useful if you want to customize the definition of multiple tools or you want to register a subset of tools for a given step. See [`ToolsPrepareFunc`][pydantic_ai.tools.ToolsPrepareFunc] @@ -342,6 +348,14 @@ def __init__( self._default_retries = retries self._max_result_retries = output_retries if output_retries is not None else retries self._mcp_servers = mcp_servers + self._builtin_tools: list[AbstractBuiltinTool] = [] + + for tool in builtin_tools: + if tool == 'web-search': + self._builtin_tools.append(WebSearchTool()) + else: + self._builtin_tools.append(tool) + self._prepare_tools = prepare_tools for tool in tools: if isinstance(tool, Tool): @@ -691,6 +705,7 @@ async def get_instructions(run_context: RunContext[AgentDepsT]) -> str | None: output_schema=output_schema, output_validators=output_validators, function_tools=self._function_tools, + builtin_tools=self._builtin_tools, mcp_servers=self._mcp_servers, default_retries=self._default_retries, tracer=tracer, diff --git a/pydantic_ai_slim/pydantic_ai/builtin_tools.py b/pydantic_ai_slim/pydantic_ai/builtin_tools.py new file mode 100644 index 000000000..46cc7b628 --- /dev/null +++ b/pydantic_ai_slim/pydantic_ai/builtin_tools.py @@ -0,0 +1,95 @@ +from __future__ import annotations as _annotations + +from abc import ABC +from dataclasses import dataclass +from typing import Any, Literal + +from typing_extensions import TypedDict + +__all__ = ('AbstractBuiltinTool', 'WebSearchTool', 'UserLocation') + + +@dataclass +class AbstractBuiltinTool(ABC): + """A builtin tool that can be used by an agent. + + This class is abstract and cannot be instantiated directly. + + The builtin tools are passed to the model as part of the `ModelRequestParameters`. + """ + + def handle_custom_tool_definition(self, model: str) -> Any: ... + + +@dataclass +class WebSearchTool(AbstractBuiltinTool): + """A builtin tool that allows your agent to search the web for information. + + The parameters that PydanticAI passes depend on the model, as some parameters may not be supported by certain models. + """ + + search_context_size: Literal['low', 'medium', 'high'] = 'medium' + """The `search_context_size` parameter controls how much context is retrieved from the web to help the tool formulate a response. + + Supported by: + * OpenAI + """ + + user_location: UserLocation | None = None + """The `user_location` parameter allows you to localize search results based on a user's location. + + Supported by: + * Anthropic + * OpenAI + """ + + blocked_domains: list[str] | None = None + """If provided, these domains will never appear in results. + + With Anthropic, you can only use one of `blocked_domains` or `allowed_domains`, not both. + + Supported by: + * Anthropic (https://docs.anthropic.com/en/docs/build-with-claude/tool-use/web-search-tool#domain-filtering) + * Groq (https://console.groq.com/docs/agentic-tooling#search-settings) + * MistralAI + """ + + allowed_domains: list[str] | None = None + """If provided, only these domains will be included in results. + + With Anthropic, you can only use one of `blocked_domains` or `allowed_domains`, not both. + + Supported by: + * Anthropic (https://docs.anthropic.com/en/docs/build-with-claude/tool-use/web-search-tool#domain-filtering) + * Groq (https://console.groq.com/docs/agentic-tooling#search-settings) + """ + + max_uses: int | None = None + """If provided, the tool will stop searching the web after the given number of uses. + + Supported by: + * Anthropic + """ + + +class UserLocation(TypedDict, total=False): + """Allows you to localize search results based on a user's location. + + Supported by: + * Anthropic + * OpenAI + """ + + city: str + country: str + region: str + timezone: str + + +class CodeExecutionTool(AbstractBuiltinTool): + """A builtin tool that allows your agent to execute code. + + Supported by: + * Anthropic + * OpenAI + """ diff --git a/pydantic_ai_slim/pydantic_ai/messages.py b/pydantic_ai_slim/pydantic_ai/messages.py index f19afea8d..9b7d02cb6 100644 --- a/pydantic_ai_slim/pydantic_ai/messages.py +++ b/pydantic_ai_slim/pydantic_ai/messages.py @@ -349,8 +349,8 @@ def otel_event(self, settings: InstrumentationSettings) -> Event: @dataclass(repr=False) -class ToolReturnPart: - """A tool return message, this encodes the result of running a tool.""" +class BaseToolReturnPart: + """Base class for tool return parts.""" tool_name: str """The name of the "tool" was called.""" @@ -364,9 +364,6 @@ class ToolReturnPart: timestamp: datetime = field(default_factory=_now_utc) """The timestamp, when the tool returned.""" - part_kind: Literal['tool-return'] = 'tool-return' - """Part type identifier, this is available on all parts as a discriminator.""" - def model_response_str(self) -> str: """Return a string representation of the content for the model.""" if isinstance(self.content, str): @@ -388,9 +385,29 @@ def otel_event(self, _settings: InstrumentationSettings) -> Event: body={'content': self.content, 'role': 'tool', 'id': self.tool_call_id, 'name': self.tool_name}, ) + def has_content(self) -> bool: + """Return `True` if the tool return has content.""" + return self.content is not None + __repr__ = _utils.dataclasses_no_defaults_repr +@dataclass(repr=False) +class ToolReturnPart(BaseToolReturnPart): + """A tool return message, this encodes the result of running a tool.""" + + part_kind: Literal['tool-return'] = 'tool-return' + """Part type identifier, this is available on all parts as a discriminator.""" + + +@dataclass(repr=False) +class ServerToolReturnPart(BaseToolReturnPart): + """A tool return message from a server tool.""" + + part_kind: Literal['server-tool-return'] = 'server-tool-return' + """Part type identifier, this is available on all parts as a discriminator.""" + + error_details_ta = pydantic.TypeAdapter(list[pydantic_core.ErrorDetails], config=pydantic.ConfigDict(defer_build=True)) @@ -503,7 +520,7 @@ def has_content(self) -> bool: @dataclass(repr=False) -class ToolCallPart: +class BaseToolCallPart: """A tool call from a model.""" tool_name: str @@ -521,9 +538,6 @@ class ToolCallPart: In case the tool call id is not provided by the model, PydanticAI will generate a random one. """ - part_kind: Literal['tool-call'] = 'tool-call' - """Part type identifier, this is available on all parts as a discriminator.""" - def args_as_dict(self) -> dict[str, Any]: """Return the arguments as a Python dictionary. @@ -560,7 +574,28 @@ def has_content(self) -> bool: __repr__ = _utils.dataclasses_no_defaults_repr -ModelResponsePart = Annotated[Union[TextPart, ToolCallPart], pydantic.Discriminator('part_kind')] +@dataclass(repr=False) +class ToolCallPart(BaseToolCallPart): + """A tool call from a model.""" + + part_kind: Literal['tool-call'] = 'tool-call' + """Part type identifier, this is available on all parts as a discriminator.""" + + +@dataclass(repr=False) +class ServerToolCallPart(BaseToolCallPart): + """A tool call from a server tool.""" + + model_name: str | None = None + """The name of the model that generated the response.""" + + part_kind: Literal['server-tool-call'] = 'server-tool-call' + """Part type identifier, this is available on all parts as a discriminator.""" + + +ModelResponsePart = Annotated[ + Union[TextPart, ToolCallPart, ServerToolCallPart, ServerToolReturnPart], pydantic.Discriminator('part_kind') +] """A message part returned by a model.""" @@ -883,6 +918,29 @@ class FunctionToolResultEvent: __repr__ = _utils.dataclasses_no_defaults_repr +@dataclass(repr=False) +class ServerToolCallEvent: + """An event indicating the start to a call to a server tool.""" + + part: ServerToolCallPart + """The server tool call to make.""" + + event_kind: Literal['server_tool_call'] = 'server_tool_call' + """Event type identifier, used as a discriminator.""" + + +@dataclass(repr=False) +class ServerToolResultEvent: + """An event indicating the result of a server tool call.""" + + result: ServerToolReturnPart + """The result of the call to the server tool.""" + + event_kind: Literal['server_tool_result'] = 'server_tool_result' + """Event type identifier, used as a discriminator.""" + + HandleResponseEvent = Annotated[ - Union[FunctionToolCallEvent, FunctionToolResultEvent], pydantic.Discriminator('event_kind') + Union[FunctionToolCallEvent, FunctionToolResultEvent, ServerToolCallEvent, ServerToolResultEvent], + pydantic.Discriminator('event_kind'), ] diff --git a/pydantic_ai_slim/pydantic_ai/models/__init__.py b/pydantic_ai_slim/pydantic_ai/models/__init__.py index 714080305..b09045ff8 100644 --- a/pydantic_ai_slim/pydantic_ai/models/__init__.py +++ b/pydantic_ai_slim/pydantic_ai/models/__init__.py @@ -16,6 +16,7 @@ import httpx from typing_extensions import Literal, TypeAliasType +from pydantic_ai.builtin_tools import AbstractBuiltinTool from pydantic_ai.profiles import DEFAULT_PROFILE, ModelProfile, ModelProfileSpec from .._parts_manager import ModelResponsePartsManager @@ -292,6 +293,7 @@ class ModelRequestParameters: """Configuration for an agent's request to a model, specifically related to tools and output handling.""" function_tools: list[ToolDefinition] = field(default_factory=list) + builtin_tools: list[AbstractBuiltinTool] = field(default_factory=list) allow_text_output: bool = True output_tools: list[ToolDefinition] = field(default_factory=list) diff --git a/pydantic_ai_slim/pydantic_ai/models/anthropic.py b/pydantic_ai_slim/pydantic_ai/models/anthropic.py index 69808aa5f..f3b20185a 100644 --- a/pydantic_ai_slim/pydantic_ai/models/anthropic.py +++ b/pydantic_ai_slim/pydantic_ai/models/anthropic.py @@ -7,8 +7,16 @@ from datetime import datetime, timezone from typing import Any, Literal, Union, cast, overload +from anthropic.types.beta import ( + BetaCodeExecutionToolResultBlock, + BetaCodeExecutionToolResultBlockParam, + BetaServerToolUseBlockParam, + BetaWebSearchToolResultBlockParam, +) from typing_extensions import assert_never +from pydantic_ai.builtin_tools import CodeExecutionTool, WebSearchTool + from .. import ModelHTTPError, UnexpectedModelBehavior, _utils, usage from .._utils import guard_tool_call_id as _guard_tool_call_id from ..messages import ( @@ -21,6 +29,8 @@ ModelResponsePart, ModelResponseStreamEvent, RetryPromptPart, + ServerToolCallPart, + ServerToolReturnPart, SystemPromptPart, TextPart, ToolCallPart, @@ -45,6 +55,7 @@ from anthropic.types.beta import ( BetaBase64PDFBlockParam, BetaBase64PDFSourceParam, + BetaCodeExecutionTool20250522Param, BetaContentBlock, BetaContentBlockParam, BetaImageBlockParam, @@ -59,15 +70,20 @@ BetaRawMessageStartEvent, BetaRawMessageStopEvent, BetaRawMessageStreamEvent, + BetaServerToolUseBlock, BetaTextBlock, BetaTextBlockParam, BetaTextDelta, BetaToolChoiceParam, BetaToolParam, BetaToolResultBlockParam, + BetaToolUnionParam, BetaToolUseBlock, BetaToolUseBlockParam, + BetaWebSearchTool20250305Param, + BetaWebSearchToolResultBlock, ) + from anthropic.types.beta.beta_web_search_tool_20250305_param import UserLocation from anthropic.types.model_param import ModelParam except ImportError as _import_error: @@ -208,6 +224,7 @@ async def _messages_create( ) -> BetaMessage | AsyncStream[BetaRawMessageStreamEvent]: # standalone function to make it easier to override tools = self._get_tools(model_request_parameters) + tools += self._get_builtin_tools(model_request_parameters) tool_choice: BetaToolChoiceParam | None if not tools: @@ -226,6 +243,7 @@ async def _messages_create( try: extra_headers = model_settings.get('extra_headers', {}) extra_headers.setdefault('User-Agent', get_user_agent()) + extra_headers.setdefault('anthropic-beta', 'code-execution-2025-05-22') return await self.client.beta.messages.create( max_tokens=model_settings.get('max_tokens', 1024), system=system_prompt or NOT_GIVEN, @@ -253,6 +271,31 @@ def _process_response(self, response: BetaMessage) -> ModelResponse: for item in response.content: if isinstance(item, BetaTextBlock): items.append(TextPart(content=item.text)) + elif isinstance(item, BetaWebSearchToolResultBlock): + items.append( + ServerToolReturnPart( + tool_name=item.type, + content=item.content, + tool_call_id=item.tool_use_id, + ) + ) + elif isinstance(item, BetaServerToolUseBlock): + items.append( + ServerToolCallPart( + model_name='anthropic', + tool_name=item.name, + args=cast(dict[str, Any], item.input), + tool_call_id=item.id, + ) + ) + elif isinstance(item, BetaCodeExecutionToolResultBlock): + items.append( + ServerToolReturnPart( + tool_name=item.type, + content=item.content, + tool_call_id=item.tool_use_id, + ) + ) else: assert isinstance(item, BetaToolUseBlock), f'unexpected item type {type(item)}' items.append( @@ -283,7 +326,25 @@ def _get_tools(self, model_request_parameters: ModelRequestParameters) -> list[B tools += [self._map_tool_definition(r) for r in model_request_parameters.output_tools] return tools - async def _map_message(self, messages: list[ModelMessage]) -> tuple[str, list[BetaMessageParam]]: + def _get_builtin_tools(self, model_request_parameters: ModelRequestParameters) -> list[BetaToolUnionParam]: + tools: list[BetaToolUnionParam] = [] + for tool in model_request_parameters.builtin_tools: + if isinstance(tool, WebSearchTool): + user_location = UserLocation(type='approximate', **tool.user_location) if tool.user_location else None + tools.append( + BetaWebSearchTool20250305Param( + name='web_search', + type='web_search_20250305', + allowed_domains=tool.allowed_domains, + blocked_domains=tool.blocked_domains, + user_location=user_location, + ) + ) + elif isinstance(tool, CodeExecutionTool): + tools.append(BetaCodeExecutionTool20250522Param(name='code_execution', type='code_execution_20250522')) + return tools + + async def _map_message(self, messages: list[ModelMessage]) -> tuple[str, list[BetaMessageParam]]: # noqa: C901 """Just maps a `pydantic_ai.Message` to a `anthropic.types.MessageParam`.""" system_prompt_parts: list[str] = [] anthropic_messages: list[BetaMessageParam] = [] @@ -319,11 +380,17 @@ async def _map_message(self, messages: list[ModelMessage]) -> tuple[str, list[Be if len(user_content_params) > 0: anthropic_messages.append(BetaMessageParam(role='user', content=user_content_params)) elif isinstance(m, ModelResponse): - assistant_content_params: list[BetaTextBlockParam | BetaToolUseBlockParam] = [] + assistant_content_params: list[ + BetaTextBlockParam + | BetaToolUseBlockParam + | BetaServerToolUseBlockParam + | BetaWebSearchToolResultBlockParam + | BetaCodeExecutionToolResultBlockParam + ] = [] for response_part in m.parts: if isinstance(response_part, TextPart): assistant_content_params.append(BetaTextBlockParam(text=response_part.content, type='text')) - else: + elif isinstance(response_part, ToolCallPart): tool_use_block_param = BetaToolUseBlockParam( id=_guard_tool_call_id(t=response_part), type='tool_use', @@ -331,6 +398,29 @@ async def _map_message(self, messages: list[ModelMessage]) -> tuple[str, list[Be input=response_part.args_as_dict(), ) assistant_content_params.append(tool_use_block_param) + elif isinstance(response_part, ServerToolCallPart): + server_tool_use_block_param = BetaServerToolUseBlockParam( + id=_guard_tool_call_id(t=response_part), + type='server_tool_use', + name=cast(Literal['web_search', 'code_execution'], response_part.tool_name), + input=response_part.args_as_dict(), + ) + assistant_content_params.append(server_tool_use_block_param) + elif isinstance(response_part, ServerToolReturnPart): + tool_use_id = _guard_tool_call_id(t=response_part) + if response_part.tool_name == 'web_search_tool_result': + server_tool_result_block_param = BetaWebSearchToolResultBlockParam( + tool_use_id=tool_use_id, type=response_part.tool_name, content=response_part.content + ) + elif response_part.tool_name == 'code_execution_tool_result': + server_tool_result_block_param = BetaCodeExecutionToolResultBlockParam( + tool_use_id=tool_use_id, type=response_part.tool_name, content=response_part.content + ) + else: + raise ValueError(f'Unsupported tool name: {response_part.tool_name}') + assistant_content_params.append(server_tool_result_block_param) + else: + assert_never(response_part) anthropic_messages.append(BetaMessageParam(role='assistant', content=assistant_content_params)) else: assert_never(m) diff --git a/pydantic_ai_slim/pydantic_ai/models/google.py b/pydantic_ai_slim/pydantic_ai/models/google.py index 99f9950f0..84631e00c 100644 --- a/pydantic_ai_slim/pydantic_ai/models/google.py +++ b/pydantic_ai_slim/pydantic_ai/models/google.py @@ -10,6 +10,8 @@ from typing_extensions import assert_never +from pydantic_ai.builtin_tools import CodeExecutionTool, WebSearchTool +from pydantic_ai.exceptions import UserError from pydantic_ai.providers import Provider from .. import UnexpectedModelBehavior, _utils, usage @@ -24,6 +26,8 @@ ModelResponsePart, ModelResponseStreamEvent, RetryPromptPart, + ServerToolCallPart, + ServerToolReturnPart, SystemPromptPart, TextPart, ToolCallPart, @@ -54,10 +58,12 @@ FunctionDeclarationDict, GenerateContentConfigDict, GenerateContentResponse, + GoogleSearchDict, Part, PartDict, SafetySettingDict, ThinkingConfigDict, + ToolCodeExecutionDict, ToolConfigDict, ToolDict, ToolListUnionDict, @@ -208,6 +214,13 @@ def _get_tools(self, model_request_parameters: ModelRequestParameters) -> list[T ToolDict(function_declarations=[_function_declaration_from_tool(t)]) for t in model_request_parameters.output_tools ] + for tool in model_request_parameters.builtin_tools: + if isinstance(tool, WebSearchTool): + tools.append(ToolDict(google_search=GoogleSearchDict())) + elif isinstance(tool, CodeExecutionTool): + tools.append(ToolDict(code_execution=ToolCodeExecutionDict())) + else: + raise UserError(f'Unsupported builtin tool: {tool}') return tools or None def _get_tool_config( @@ -447,7 +460,18 @@ def _process_response_from_parts( ) -> ModelResponse: items: list[ModelResponsePart] = [] for part in parts: - if part.text is not None: + if part.executable_code is not None: + items.append(ServerToolCallPart(args=part.executable_code.model_dump(), tool_name='code_execution')) + elif part.code_execution_result is not None: + # TODO(Marcelo): Is the idea to generate the tool_call_id on the `executable_code`, and then pass it here? + items.append( + ServerToolReturnPart( + tool_name='code_execution', + content=part.code_execution_result.output, + tool_call_id="It doesn't have.", + ) + ) + elif part.text is not None: items.append(TextPart(content=part.text)) elif part.function_call: assert part.function_call.name is not None diff --git a/pydantic_ai_slim/pydantic_ai/models/groq.py b/pydantic_ai_slim/pydantic_ai/models/groq.py index 067cfb516..e161e8e25 100644 --- a/pydantic_ai_slim/pydantic_ai/models/groq.py +++ b/pydantic_ai_slim/pydantic_ai/models/groq.py @@ -10,7 +10,7 @@ from typing_extensions import assert_never from .. import ModelHTTPError, UnexpectedModelBehavior, _utils, usage -from .._utils import guard_tool_call_id as _guard_tool_call_id +from .._utils import generate_tool_call_id, guard_tool_call_id as _guard_tool_call_id from ..messages import ( BinaryContent, DocumentUrl, @@ -21,6 +21,8 @@ ModelResponsePart, ModelResponseStreamEvent, RetryPromptPart, + ServerToolCallPart, + ServerToolReturnPart, SystemPromptPart, TextPart, ToolCallPart, @@ -220,7 +222,7 @@ async def _completions_create( extra_headers = model_settings.get('extra_headers', {}) extra_headers.setdefault('User-Agent', get_user_agent()) return await self.client.chat.completions.create( - model=str(self._model_name), + model=self._model_name, messages=groq_messages, n=1, parallel_tool_calls=model_settings.get('parallel_tool_calls', NOT_GIVEN), @@ -249,6 +251,15 @@ def _process_response(self, response: chat.ChatCompletion) -> ModelResponse: timestamp = datetime.fromtimestamp(response.created, tz=timezone.utc) choice = response.choices[0] items: list[ModelResponsePart] = [] + if choice.message.executed_tools: + for tool in choice.message.executed_tools: + tool_call_id = generate_tool_call_id() + items.append( + ServerToolCallPart( + tool_name=tool.type, args=tool.arguments, model_name='groq', tool_call_id=tool_call_id + ) + ) + items.append(ServerToolReturnPart(tool_name=tool.type, content=tool.output, tool_call_id=tool_call_id)) if choice.message.content is not None: items.append(TextPart(content=choice.message.content)) if choice.message.tool_calls is not None: diff --git a/pydantic_ai_slim/pydantic_ai/models/openai.py b/pydantic_ai_slim/pydantic_ai/models/openai.py index 4e99fb574..e0693b601 100644 --- a/pydantic_ai_slim/pydantic_ai/models/openai.py +++ b/pydantic_ai_slim/pydantic_ai/models/openai.py @@ -10,6 +10,7 @@ from typing_extensions import assert_never +from pydantic_ai.builtin_tools import WebSearchTool from pydantic_ai.profiles.openai import OpenAIModelProfile from pydantic_ai.providers import Provider, infer_provider @@ -26,6 +27,8 @@ ModelResponsePart, ModelResponseStreamEvent, RetryPromptPart, + ServerToolCallPart, + ServerToolReturnPart, SystemPromptPart, TextPart, ToolCallPart, @@ -58,6 +61,11 @@ from openai.types.chat.chat_completion_content_part_image_param import ImageURL from openai.types.chat.chat_completion_content_part_input_audio_param import InputAudio from openai.types.chat.chat_completion_content_part_param import File, FileFile + from openai.types.chat.completion_create_params import ( + WebSearchOptions, + WebSearchOptionsUserLocation, + WebSearchOptionsUserLocationApproximate, + ) from openai.types.responses import ComputerToolParam, FileSearchToolParam, WebSearchToolParam from openai.types.responses.response_input_param import FunctionCallOutput, Message from openai.types.shared import ReasoningEffort @@ -263,6 +271,7 @@ async def _completions_create( model_request_parameters: ModelRequestParameters, ) -> chat.ChatCompletion | AsyncStream[ChatCompletionChunk]: tools = self._get_tools(model_request_parameters) + web_search_options = self._get_web_search_options(model_request_parameters) # standalone function to make it easier to override if not tools: @@ -298,6 +307,7 @@ async def _completions_create( logprobs=model_settings.get('openai_logprobs', NOT_GIVEN), top_logprobs=model_settings.get('openai_top_logprobs', NOT_GIVEN), user=model_settings.get('openai_user', NOT_GIVEN), + web_search_options=web_search_options or NOT_GIVEN, extra_headers=extra_headers, extra_body=model_settings.get('extra_body'), ) @@ -365,6 +375,19 @@ def _get_tools(self, model_request_parameters: ModelRequestParameters) -> list[c tools += [self._map_tool_definition(r) for r in model_request_parameters.output_tools] return tools + def _get_web_search_options(self, model_request_parameters: ModelRequestParameters) -> WebSearchOptions | None: + for tool in model_request_parameters.builtin_tools: + if isinstance(tool, WebSearchTool): + if tool.user_location: + return WebSearchOptions( + search_context_size=tool.search_context_size, + user_location=WebSearchOptionsUserLocation( + type='approximate', + approximate=WebSearchOptionsUserLocationApproximate(**tool.user_location), + ), + ) + return WebSearchOptions(search_context_size=tool.search_context_size) + async def _map_messages(self, messages: list[ModelMessage]) -> list[chat.ChatCompletionMessageParam]: """Just maps a `pydantic_ai.Message` to a `openai.types.ChatCompletionMessageParam`.""" openai_messages: list[chat.ChatCompletionMessageParam] = [] @@ -380,6 +403,9 @@ async def _map_messages(self, messages: list[ModelMessage]) -> list[chat.ChatCom texts.append(item.content) elif isinstance(item, ToolCallPart): tool_calls.append(self._map_tool_call(item)) + # OpenAI doesn't return server tools calls. + elif isinstance(item, (ServerToolCallPart, ServerToolReturnPart)): + continue else: assert_never(item) message_param = chat.ChatCompletionAssistantMessageParam(role='assistant') @@ -642,6 +668,7 @@ async def _responses_create( ) -> responses.Response | AsyncStream[responses.ResponseStreamEvent]: tools = self._get_tools(model_request_parameters) tools = list(model_settings.get('openai_builtin_tools', [])) + tools + tools = self._get_builtin_tools(model_request_parameters) + tools # standalone function to make it easier to override if not tools: @@ -694,6 +721,20 @@ def _get_tools(self, model_request_parameters: ModelRequestParameters) -> list[r tools += [self._map_tool_definition(r) for r in model_request_parameters.output_tools] return tools + def _get_builtin_tools(self, model_request_parameters: ModelRequestParameters) -> list[responses.ToolParam]: + tools: list[responses.ToolParam] = [] + for tool in model_request_parameters.builtin_tools: + if isinstance(tool, WebSearchTool): + web_search_tool = responses.WebSearchToolParam( + type='web_search_preview', search_context_size=tool.search_context_size + ) + if tool.user_location: + web_search_tool['user_location'] = responses.web_search_tool_param.UserLocation( + type='approximate', **tool.user_location + ) + tools.append(web_search_tool) + return tools + def _map_tool_definition(self, f: ToolDefinition) -> responses.FunctionToolParam: return { 'name': f.name, @@ -747,6 +788,9 @@ async def _map_messages( openai_messages.append(responses.EasyInputMessageParam(role='assistant', content=item.content)) elif isinstance(item, ToolCallPart): openai_messages.append(self._map_tool_call(item)) + # OpenAI doesn't return server tools calls. + elif isinstance(item, (ServerToolCallPart, ServerToolReturnPart)): + continue else: assert_never(item) else: diff --git a/pydantic_ai_slim/pyproject.toml b/pydantic_ai_slim/pyproject.toml index 631cc196d..3625c392d 100644 --- a/pydantic_ai_slim/pyproject.toml +++ b/pydantic_ai_slim/pyproject.toml @@ -66,7 +66,7 @@ cohere = ["cohere>=5.13.11; platform_system != 'Emscripten'"] vertexai = ["google-auth>=2.36.0", "requests>=2.32.2"] google = ["google-genai>=1.15.0"] anthropic = ["anthropic>=0.52.0"] -groq = ["groq>=0.15.0"] +groq = ["groq>=0.25.0"] mistral = ["mistralai>=1.2.5"] bedrock = ["boto3>=1.35.74"] # Tools diff --git a/tests/models/cassettes/test_anthropic/test_anthropic_code_execution_tool.yaml b/tests/models/cassettes/test_anthropic/test_anthropic_code_execution_tool.yaml new file mode 100644 index 000000000..76f19dc00 --- /dev/null +++ b/tests/models/cassettes/test_anthropic/test_anthropic_code_execution_tool.yaml @@ -0,0 +1,86 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '250' + content-type: + - application/json + host: + - api.anthropic.com + method: POST + parsed_body: + max_tokens: 1024 + messages: + - content: + - text: How much is 3 * 12390? + type: text + role: user + model: claude-sonnet-4-0 + stream: false + tool_choice: + type: auto + tools: + - name: code_execution + type: code_execution_20250522 + uri: https://api.anthropic.com/v1/messages?beta=true + response: + headers: + connection: + - keep-alive + content-length: + - '927' + content-type: + - application/json + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + container: + expires_at: '2025-05-26T13:30:45.703429+00:00' + id: container_011CPW9LpfbF8dmXMvVNCiQJ + content: + - text: I'll calculate 3 * 12390 for you. + type: text + - id: srvtoolu_01CPfaeVC7ju4VsdzxjSLDrY + input: + code: |- + result = 3 * 12390 + print(f"3 * 12390 = {result}") + name: code_execution + type: server_tool_use + - content: + content: [] + return_code: 0 + stderr: '' + stdout: | + 3 * 12390 = 37170 + type: code_execution_result + tool_use_id: srvtoolu_01CPfaeVC7ju4VsdzxjSLDrY + type: code_execution_tool_result + - text: The answer is **37,170**. + type: text + id: msg_015H6Emn2T8vZhE52mU2jF1U + model: claude-sonnet-4-20250514 + role: assistant + stop_reason: end_turn + stop_sequence: null + type: message + usage: + cache_creation_input_tokens: 0 + cache_read_input_tokens: 0 + input_tokens: 1630 + output_tokens: 105 + server_tool_use: + web_search_requests: 0 + service_tier: standard + status: + code: 200 + message: OK +version: 1 diff --git a/tests/models/cassettes/test_anthropic/test_anthropic_server_tool_pass_history_to_another_provider.yaml b/tests/models/cassettes/test_anthropic/test_anthropic_server_tool_pass_history_to_another_provider.yaml new file mode 100644 index 000000000..a73ea3aa4 --- /dev/null +++ b/tests/models/cassettes/test_anthropic/test_anthropic_server_tool_pass_history_to_another_provider.yaml @@ -0,0 +1,280 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '312' + content-type: + - application/json + host: + - api.anthropic.com + method: POST + parsed_body: + max_tokens: 1024 + messages: + - content: + - text: What day is today? + type: text + role: user + model: claude-3-5-sonnet-latest + stream: false + tool_choice: + type: auto + tools: + - allowed_domains: null + blocked_domains: null + name: web_search + type: web_search_20250305 + user_location: null + uri: https://api.anthropic.com/v1/messages?beta=true + response: + headers: + connection: + - keep-alive + content-length: + - '15304' + content-type: + - application/json + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + content: + - text: Let me search for today's date. + type: text + - id: srvtoolu_01BJh8n7va96puUF3hhNnYnY + input: + query: current date today May 26 2025 + name: web_search + type: server_tool_use + - content: + - encrypted_content: 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 + page_age: null + title: May 2025 Calendar + type: web_search_result + url: https://www.calendar-365.com/calendar/2025/May.html + - encrypted_content: EuwCCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDNOs+T2nScTnsxVJZRoM3g9Mm5RlKN0sgiPBIjC1PJPZRfoUgRpmpfVZcF8sBTMUr6Lu9BK9xOxTQ8nMMBGjC7kFOrwJZ3ENgXLhRzcq7wF51cgLQ+Pb0MiXBGozKXa1r7DbogMoIacrDz92bvzbwXbo4mTQMipnow47//PYVelJXipxVAA6ISswEiYPGXyCjqSy6QEIde1TOc8nvMpaM0Y4u3DdOXez5EsgP+ZxIYly/I7JS35h/azV0nbpzEKsptZwNWwuBtNIYbkklPaluda13QbkE0KK1Cl6PnkNMVbO0Uo9XURcwLn5aDLJol0GJGVpuiApXVJoMk7e7L/Ib+p1BE77VW619KzDmQvuAjQXZR28Fb5KEbRxXkSaq7xAZnYZyQFtOlqg/07Tv+V4g0WYZhI3tn0phBe0w+95NhgD + page_age: null + title: May 26, 2025 Calendar with Holidays & Count Down - USA + type: web_search_result + url: https://www.wincalendar.com/Calendar/Date/May-26-2025 + - encrypted_content: Es0ICioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDE+yP8aOxk+46r0AERoMFOlAyJD/AGdtDxeVIjDDhlbQbqqMmuMK9bN7ZNTwHWQwahkAaZd/LltU3eIPifLnfk4Apnck+mUOM61zRiQq0Ac12DCFUzhrlxvkXqpK3gWjgosGbUyzt8WbmwSaEmCbXf9F4bNc7+LKvQUtEceYmCesYcQa0lOT+88IhhRTxYBMyXo0Ws3Pw1yrLJi9P1JqfpcnWebg8McOGY/pD/MFeJORQvH+0UvGjBoMD0uaPPY1U6cFC7EWL+IebMjjmpj80RqAi/xjOOnU2XO8N98dFCzW4GiI9IWut8OBv8u3kLpmyYOV6cVxr3WTEntDv3onveQvTIyJFiDG8bmOKK72ONWrevnrKGQ7JY2gPYp5CakS+SYahIKiJOOY3cWQfUEheIfEZeuGadI907uvREyb1bf4h/0KsFjnOBWrHXdl7LIDRSfz8OIwfJufTERGCfG0vzMiHzFJbFcezciz3K9m25QjxL676NcrYbgZGFhIXar9jNvPAjGcUecpaOLAEztHx7pd0nHMsW5KKUTS0yU5sMFNY1+RMSKk8kVoCcBtOuOsiegf+BJV/J997IvbQG5A7h/72VsVyUbiBDT1z77VjKTPcerZr4EReCobAoyGdMKt1mihwvYvsyvZepAkvrxOoKnb+2pkzhZQtxGt0ub23yhSZyLgi0frVSS04KeYncVWW7cQJJygzfDS37pdSPnU0wZmAgZLzQ3X1NVmR9AQVSQ+IcIoVSCuru/WiBcS7aJPC4uh6cn5sB9C6yThrD8i7T7DJGbR/uutIADP9IgGUzdoppStgQugx4TwnQI9SC6e36zjfsK6NeUkxLi2b4T1uuRDvjgUDc0OXGZdoGQgc8nHoe7rexoLJqeWat19KNwy4DqnM7oywivVJWDJ2ThV4863f3+hYRlXeuxnojBsmENJsSxZtF46ye1gfPSvBYlEpO/Uu9FzR3yy4bB3b/qOh2+JMTLkk3R5u4C6Ca/Nye0bpnsKG7xjW2cKguqfKhUuUFaSucCCJqg/hT21DlUiHXZQ6wq6Ywi9C9l+wJbzfJWWHczjkTJvWhFq8saXSAzfWDd8tTKH1zje7NN38jRQPUDxS0CWMM78/+BgWoqsB5+IjkEl5QHV55GSCg+05HQPLyHQWWjBdMLWr+Uu3T0kKI0B/QDJPHO1mSX5AsGot5HZzva5qEPzn3vGly1n1vs14JWE+RrtIJTBHi0ypFhaO6oCRGejMRDbZhotP4gWW1VW0gLNikgfW0Z5waOb8wmLcN4vQhsHRFfZiXsqUF0dTXsme8+kF8qcVZNnPtghyf1ACvFgZ8TAF4PaM1wzpa8kDeBFvyp14U39R8XDJ3T6lGWR2rpv0WjS8d5h0RCZ3XlFuqPUXWEQ89OpEH8vEiYXGAM= + page_age: null + title: Daily Calendar for Monday, May 26, 2025 | Almanac.com + type: web_search_result + url: https://www.almanac.com/calendar/date/2025-05-26 + - encrypted_content: EpoFCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDClGwxB0YzJdub7VGhoMASvr8D1KqeBCa4knIjAV1lHpDjDIw6nag+Y/ax/aq2LhfZ1/CXJoRyRFiUGLVQHpERodH6/Kg+l8aLy01gAqnQQR8cCm+MlfMM3pSSNah8dZ0m5wam3enQ+5A3VLu0P/CF/TlwL5yB21pfaiu0lad9Ee99YFvs7LInvXpyFcUgLuhankIzNNZZbUIeS4sZIK39RrN+pmifOl6KQqT1f2tpJE6x7erDlAscnMvbRIr9U8Pwf8Au709bI6zcHqCg4GNJk2CqDf6kAXRHRRhcWYFtm8b0wvg9HaWVYZFOunZMJ1gFD3D82dSm3m4awyDSZm54A/9hmzE/lcQyiSVBC73NOmJMBxbNec16qaeJdUTZp33JYk0Fdro1VnkUiJyq5Kb2W7VYvfeQ1a2dLXPTYIsXeQXLHesmVTe7m0X70w0BgCAC+yKZPp68bbsEbsbjwxaWOxQ50seu8kkXDiEgscUK5e4N5YnPXMNjyMNZmlbhmFtw3abOmOCVdpcDORXGOgJZBWBkEqQ4mE8b+aEzXMGRqLzYjDKZHx11wolF3MGu/V/+f4lWEyNwR2Kipp67teOx1ypvl4DiZiPVkZwx9lt08u+TJGEGbTYtdPMQRLVX1K2K4YOEcP5/abNrdT9KdYH7dNscRDqDY04gi+47JyyTSRObcCjQun9LUqh0q6/NfIIWQwjguZ6dbYL67ymiuRgHJDXsPnc/P58qP4kRsClz9lu/XPTFuAzCQwaXioK3XiBNW73nSKyRaxm6/OxbYetPGIG2JUTkQtrmGL5AYJTfFbI6XOB1AFDcsoSC7nGAM= + page_age: null + title: How long ago was May 26th 2025? | howlongagogo.com + type: web_search_result + url: https://howlongagogo.com/date/2025/may/26 + - encrypted_content: Eq0DCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDDT/5zRyvf5kzhhLgxoM5vWn/Zjmu43rvWgPIjCf4z9SIoZxgNKyiLOqk422DLmHbFXGX+mhy6FPo43NOogfLhu/a3F3EPkiMxfd0UMqsAKIhRJKuEhfoi2saPCA3LpNhJ3YPO2NZyYL75SJdCbkfACe2YJOcCXHmt4hkrbA98Mrvh9DmtP6HNA9iYdsotXkDi0BuTlmiIcspblXnkpXLIURVpNYqqZ2jbjBEmi/mVlTonm9NJwx1rZHzwciKhaIrz440Vk/uTIqpiMb22U00hWrQ563gNlMSXpvIpyayhJcCnNEjQybJjQUsKaMqb/3Xbp8NUoWtidUb4ARNeoNe4BlzMEEcIPc0QWi1CKoQJH3JazxAhMSqvc4SVWXRyspZZP6lSHhMcIYlf/yorVYVF3FmL1djZfxXbVnI/VbFbpoNejvnOh5QIdbDrHu6UN3u2iGZBUJLgs6i9OUwMWw8kpi2WUm+3JeRkdyThE0M9WAhK+4qYD3UHgFCcucX7ZeGAM= + page_age: null + title: How many days until 26th May 2025? + type: web_search_result + url: https://days.to/26-may/2025 + - encrypted_content: EpACCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDNXuvVNnWjvXgGql0RoMXwqBXk4fJbTzoM0pIjC1HFbYVJOlp39xpYzw/uJVUO6cC2L9ScOu9ceP2MDjyBWuE0X2LxBsyFjM3ZwzQtYqkwHukWybclFQu5CuM7gntDFNVYsSAVDbslXo5WwOlljEXnC7ohNjuB4lJfXS5HomdOmnUs0/thHHvR5mvCvHt5o+XrnTV8PSOXJvZonVctfjBeMe/3VWaQX2Pr8F71hiBVghYnc4lqnvA/nebBXcqdz2VTlG7V2/u+KCGVfR27l0+BX9s8vroR6dZ7flEBZBSDDzt3QYAw== + page_age: null + title: What day of the week will May 26th 2025 be on? | howlongagogo.com + type: web_search_result + url: https://howlongagogo.com/day-of-the-week/2025-05-26 + - encrypted_content: EtkGCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDNqnvrEdyAJMqZiouRoMl3zAD6YOqFa5Y+LGIjCFzjG2cb9Uv/fUGE2iFHKU3CP5i7yVrpvusD4UBOrARkME7qTDdfIczSbj1PvU7ygq3AUQ9yGVSgg9AB7qawws2Zx43sVi5sDW/thrC8owactbSzc2qE9IsmucxwknSZXemJFSMW3I2vWhlpJVcfipESwRyVSnVNoTivU7/aIbnI15/NOGATtd/TR0mkyO2MMMAFIHiEfq0iuvMUX/EmnEqlSzDngrlWdPvb0qDrZIkSVEYFE5FEPHXuXEFEWZ/yLs7rUK7M9kc161gTbQ/NMALrWDjKtFtgI20Wf2luvbfX8G8kivyPttdUk9aiBxStWetpJZqsBEGT1auj3hk7FaHU2FTNW0LGvIe++QZNL+w/nKa+YuNGpJCxAiLt3X+2hnZmz8Mskfyfl5I5EWt7eOQmoR0qStl7Fh9Myb3vZ0rpZ8gZeaLd1xn6pKKa+tBonm1Oq1zQvsoMkMwTcMRK3LA53dF3rtkgSFyBAWis/tenbdrRVUGlicecn/bSd9FVFoGM5RWYvXR9/3zr3SBf2qNTT2jmeLqcl07LYUk+WDGcas6vAVGDXxVoAh+uAC0WziUtKfv//V4IeleGEOLJNdX1jDC8uQMKyGuRCXNRJAoHRhgUr2F46Ax9xJOHWXKDheklfqTFff0wf93d5U/d4p52RaeM7jcBexjz0wMQRBL91ZOZVweRHbV2BVTBpPH79Y9W/UmBEycRnTmurJJspZre10EVH7MIX/4DRhxBipdZrQFgqTrCgbN6dRFLlQR9w1n+Y2pSEXnSeHdwOTPVHSKBKQO43XCKWmJPgNFELcRBZdVxGcE9xd2/dzs0C6Aa7vXZ/5ZPx9rgis1TY2UegdpiyWmKV0yw1K2LKpq8BOdH4t3OPDTPvJrymaZwldTb+w6rLGf0p1YAkZIBPpUX9tAstQ1HAz58zHnLhQfUzpm5ft4S3m2i+WkyjzIkh4F2Y1bIvbwQvqT2x3rGJRRWJGyg9PDEVCY4gjKObhjkQbanRG58uPuf7mBofFqKbUBJOngnAvSmGibWNn9uSftf8YAw== + page_age: null + title: Calendar 2025 + type: web_search_result + url: https://www.timeanddate.com/calendar/ + - encrypted_content: Eq0CCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDJI9LlFy1IIRylVnDxoMIXCLnspt86G8qxeIIjD0AiJH0r4ULSsSOXmrBj0ntQEjeADrBeCC4P53iCMga7fv0Uxic9EYRY73zIkr7aAqsAFql3nOsfaopCrkf/WC9qvKgDAeuBfKcTNjj+rfHn5HXgVM4BOpomSXxBc0CHUe3Heqw2mD9+Hy99flWI60pgPB+umunXeOjz5HC4W1q39ROTzvMkdpJzvXoUgG/dbgjJ9vEvZ0VC4z+ZwRAYSNgOgiRt3zApcZJ4iSsF/XgrQNs2L2ThcQyc84+lKKAaKPrSl6Lwm1bH+HTFYIm9KPyhvlbfEbp5buj/wQgn4Na/3qrhgD + page_age: null + title: May 26 + type: web_search_result + url: https://www.nationaldaycalendar.com/may/may-26 + - encrypted_content: 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 + page_age: null + title: Day Numbers for 2025 + type: web_search_result + url: https://www.epochconverter.com/days/2025 + - encrypted_content: EosGCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDDVXhko+GaoaARLiWxoMhhwixTlmn7YeimyXIjBqLMdGYisjngIKCxw2kKV4whivBWinF+rFSah3+1JUtoRUor9o+HmU0StGZxaPydsqjgUGl0OUiZ7Dlc0YqCN8bOGaoUeUv7MpoODHTH97tj67PKZmSbEkDp0J1bJzxHd4WeL1SdNnExDsDcSk4joJFA4lBVAAgxvBogir7w/ws8fRoANAp3F4T9IoZHSQVR2PhtRpymmT1JIaelypYDnda1cLVKrOhF26o8zSp3sDKsilZRW7TSGG1YmzEEKdFK0h/jrGO9yr35kL8vzIF1vdXeJbJa79pFo8e1OLxq9Z2e3y5thBAB3aMK+WMT6n3vRaaRpG4/sqL69lUB/J8Ju1X2VAqfUhYe1GbTKVwAqv+Fllq/NQfZywAaqkHF0YqzMFNYYNB0KTpzrPetfmvXMvog2I0qn8Xuuefw6ROAgsUJaIbyY2b2AbO9Ohxlqv08Lgt/CEmghIi2I/zvt4Y5AjUZzpRUqYAomrkagze50qj5P5Mj191pU+SLehkArpjDp131C5k3jhJ/6m7EaRpv7R4JWxWZCETvqonBRkooPRYqTZkHAD61WjNrFzDOn3fOHjrK9XjQMYE6C9rLOQf/Cy3XdlJgmSa6OKzak0NhRzIA8h7O16Mlf5kaMz5gW4vhIY/luKVQCgo7O39nxitIw94BlW0u0qT0XzCFgS05ADWdIC7SHJaU8NB4x2h0zQTTX2P5m0U2giLYPO86lUWicae08l14+JUB+uPlpDhNU7UQl29SM77+XnzdGiX/Kk99k8FcMrgp6+X5riQWuE0+rBhuDzOnVhGOgFCazxWCIpXIAvYqin429shbJtttU/upLPrbj9HsXpCJjOPI7+FLK7JXH5tH3rz1OCa/14jkNd4GTg5eGNnJSDgSJ2dWxC9oRQyFub6rPc58n9BCkHzcqrF502Q4TTEfNUuQsqBMJL8yEYAw== + page_age: null + title: Countdown to May 26, 2025 in Washington DC, District of Columbia + type: web_search_result + url: https://www.timeanddate.com/countdown/to?msg=Cressida_Glitter's+birthday&year=2025&month=5&day=26&hour=0&min=0&sec=0&fromtheme=birthday + tool_use_id: srvtoolu_01BJh8n7va96puUF3hhNnYnY + type: web_search_tool_result + - text: "\n\nBased on the search results, " + type: text + - citations: + - cited_text: Current moon phase · Moon calendar 2025 · Moon phases (Full Moon) 2025 & 2026 · World Clock · World + Clock · Calculate · Period between two dates · Mon... + encrypted_index: Eo8BCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDEgJNRfMEmnW19PSuBoMpvnoqkDvIRkx6yxdIjBNsAkQg3SrCCN1zCEKO4LtptywdqMNvgULDnwzA4e8JoIj6nDuov4EZIdg37AOh2QqEx0w/3OTuMMUW8z0FsIsEkBStrkYBA== + title: May 2025 Calendar + type: web_search_result_location + url: https://www.calendar-365.com/calendar/2025/May.html + text: today is Monday, May 26, 2025 (Week 22) + type: text + - text: '. This is notably ' + type: text + - citations: + - cited_text: The custom of honoring ancestors by cleaning cemeteries and decorating graves is an ancient and worldwide + tradition, but the specific origin of Memori... + encrypted_index: Eo8BCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDDh1HLd3wIXrMlbIYRoMJbIyO6jhVRytVw9tIjBp5E9G6P/4oCUT+AvhQia2q7og82j1ZKFJMEyZllOT5wNB8wxo/hua8sprObTuApUqEwNbgxwhB3Rzdzbu8jotXE3VzSYYBA== + title: Daily Calendar for Monday, May 26, 2025 | Almanac.com + type: web_search_result_location + url: https://www.almanac.com/calendar/date/2025-05-26 + text: Memorial Day, which was originally known as Decoration Day + type: text + - text: '. ' + type: text + - citations: + - cited_text: 'The year 2025 has 365 days. ' + encrypted_index: Eo8BCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDGSJfkRRZt9POYx8TRoM9ZykWxngb2XQ3vvZIjDwg7OYkVRW8t1wzFmTQ1TL5akbS2y3zcQRN/I2nU4v3NsNZbfOSHEBhWOql3U/nekqE4EtVZ7XYLel8iTWpvOfRFX8iUAYBA== + title: Day Numbers for 2025 + type: web_search_result_location + url: https://www.epochconverter.com/days/2025 + text: The year 2025 is a regular year with 365 days + type: text + - text: . + type: text + id: msg_014DEwKKUs2hUThC8aqhrc5d + model: claude-3-5-sonnet-20241022 + role: assistant + stop_reason: end_turn + stop_sequence: null + type: message + usage: + cache_creation_input_tokens: 0 + cache_read_input_tokens: 0 + input_tokens: 9582 + output_tokens: 178 + server_tool_use: + web_search_requests: 1 + service_tier: standard + status: + code: 200 + message: OK +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '732' + content-type: + - application/json + host: + - api.openai.com + method: POST + parsed_body: + input: + - content: What day is today? + role: user + - content: Let me search for today's date. + role: assistant + - content: "\n\nBased on the search results, " + role: assistant + - content: today is Monday, May 26, 2025 (Week 22) + role: assistant + - content: '. This is notably ' + role: assistant + - content: Memorial Day, which was originally known as Decoration Day + role: assistant + - content: '. ' + role: assistant + - content: The year 2025 is a regular year with 365 days + role: assistant + - content: . + role: assistant + - content: What day is tomorrow? + role: user + model: gpt-4.1 + 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: + - '1494' + content-type: + - application/json + openai-organization: + - pydantic-28gund + openai-processing-ms: + - '1271' + openai-version: + - '2020-10-01' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + background: false + created_at: 1748263711 + error: null + id: resp_6834631faf2481918638284f62855ddf040b4e5d7e74f261 + incomplete_details: null + instructions: null + max_output_tokens: null + metadata: {} + model: gpt-4.1-2025-04-14 + object: response + output: + - content: + - annotations: [] + text: Tomorrow will be **Tuesday, May 27, 2025**. + type: output_text + id: msg_68346320b7608191a49fcd12e06dd3b5040b4e5d7e74f261 + 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: 410 + input_tokens_details: + cached_tokens: 0 + output_tokens: 17 + output_tokens_details: + reasoning_tokens: 0 + total_tokens: 427 + user: null + status: + code: 200 + message: OK +version: 1 diff --git a/tests/models/cassettes/test_anthropic/test_anthropic_web_search_tool.yaml b/tests/models/cassettes/test_anthropic/test_anthropic_web_search_tool.yaml new file mode 100644 index 000000000..57f5e285c --- /dev/null +++ b/tests/models/cassettes/test_anthropic/test_anthropic_web_search_tool.yaml @@ -0,0 +1,166 @@ +interactions: +- request: + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '312' + content-type: + - application/json + host: + - api.anthropic.com + method: POST + parsed_body: + max_tokens: 1024 + messages: + - content: + - text: What day is today? + type: text + role: user + model: claude-3-5-sonnet-latest + stream: false + tool_choice: + type: auto + tools: + - allowed_domains: null + blocked_domains: null + name: web_search + type: web_search_20250305 + user_location: null + uri: https://api.anthropic.com/v1/messages?beta=true + response: + headers: + connection: + - keep-alive + content-length: + - '42593' + content-type: + - application/json + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + transfer-encoding: + - chunked + parsed_body: + content: + - text: Let me search for current events to help establish today's date. + type: text + - id: srvtoolu_01MqVvTi9LWTrMRuZ2KttD3M + input: + query: current events news today May 26 2025 + name: web_search + type: server_tool_use + - content: + - encrypted_content: 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 + page_age: null + title: 'News: U.S. and World News Headlines : NPR' + type: web_search_result + url: https://www.npr.org/sections/news/ + - 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/ + - encrypted_content: EuYCCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDCmLeaYgzUtJ4Mi+fBoMpktwxWvlSdpeNRRlIjDckkEHzeMKWP+vkSJ+7ci91OlLvn1nTU4wG+0am9miZ+68Q8XjsyCBGekIPeSsgpkq6QFTAvqrNhd55GMbj8VQtB/7oV66lwp8PzaymgQlLzCnxBdZ6IRYyEd6XwFOPrWCwyjtlKbwRiM2NIaNsGcrraBVrDfsjCz20qsDPGNsQf587z/TD3zWUSelhjhf+T5nDCEXUkYM2+4MaGP5Ty57Khh3WQr5q6Q46m85jBBF+akWf1uKZEgjgFug1ufj/8TXEEAaKCVY9YeXTXfYH8DocKveCXH4Bp9TNbgx55UrL8NdXiwdtpI/zqY+8hM/SiaVeXXI/Rbmjg3HzFTLfrH4wSrl5awdKWuGwQy8nqRISZlwNVnwFY0e8uc08xgD + page_age: null + title: ABC News – Breaking News, Latest News and Videos + type: web_search_result + url: https://abcnews.go.com/ + - encrypted_content: EtEWCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDHm43fQ50ug337S3LRoMMpBq69Qh8QJrLUwbIjCQZpwOKhXhp64xZ6VuO9jUsfumcGFVwLXHbCFUYK9256rmPdiT1B5qecHMx35qI7cq1BWGwSfTqoKrKdwCLT5GuFP1tDnXee0s1GL8tn4WwqVUe+FgYiHiknLq4+RvdZOXOZv2yrffRh+6FAMtYPdhUfkBVONku44BxAkQabLafuw0pofhEMh1wj5i3HhmjMNIqr4fgMCqHpre7nt052sFkxzlgvrwtKPdAL+bC/QmL9aXPzmbtE2V+LVxLQ5XpcR7OyhTL82S3ds+uNLDhbtUYZtVcLHgdPIk422XzZeRxZ4Sdpe0uIss38kVVPI1G3luS5oJkIUnIaTlsFxgrNKYPZYX+eOwVj/9NdTUIkXtCik32wTOexcIgBmw7JJcUL9V5i1SqhHISSnOG6t4ttUAfBivPS1IWCiUPNwWYWOjgwX8dIVIyW7JpC9Vev/gMJ5WdroYLyKNLK80uXfwxcCtQknjSBaKHm/0wnVERviHapls3prWbiPKG95pcHB4QSK+toTEVh+rzhKRYIBAAJp1hQrVtSceyjPKd78Dkv8nXVzcQxWlD3Go0fis8p2n4g2eZJKMLBpvu/CyOxhGumxAOdM3RrirdDKm8tLIIqDil+caVQyvGNvvgtJW10fRi2S7atagwzI6/oVH9lNCn9P+53ERe6zUAYJ9V9HavpoijPm1mm0KRyC5ktHNRWuAONdQC1z4BbqyMInQTGMuUkB55uy97FyuzxPitICF2Q6VCvDpQaJsqqyG76+oiDTcY5wZodyKYTOjmQQjMOVf2rgwrpaKhLHpcnXAzFpmOWO1WqE8g8W4fhr+G3T6PtaLNyY/wZ7KP8EwwSIhKFyAuoOxNSzfAYu1sg9ZhG/nkOHBLbGyHzXiYTDprlhy+s9Qm8dxJXBM3uWnSuSL2zB1dCkkBJITv1Vo7DfRC0lfq5C1dJXy9wAoCCyO9Zs6Fuzh2N/rnPQsNreVRkfMS2kiswIBl+olyvgm8cx0pD+cFsT8OgVhVOaEA44BQ0T9r0nsPFs6h87t5ybk9XZKM8CwzwNXoD+dFPy/z4B0EaO88U4uhQmZ+qlKeOR6hMbYclZLSdd14bS+SKeNSdYmdlylHpYuRM01ZuSWZjwbe8QwQxG8hV7Eau+cQ62uR+PMZucirkTeAjJyR5n0hjxyofwsZq8dMvKSUtdSwLYsAT2QJj0MJ15Q3/l7YwsJXiemHZ0Cjd3kRFHWr3oFI84r02gC2O/1jrg4QZUR5JjbHATRwIjOr4qCNzEXFZkOcHZ5gWn02eznraY6bNx409r6naIEsUhNknKS8NU45ifdaaTSQQMKAu+g1P9X3r/BERoSYclxZIcWCnPPuXrMF3/IWAHBSvXn+Raa2ljcj2+/B7LnTxazMogM5xfSLdloFn3HaUkkpREh2Q+Ilph/kP5an9aZmlui1mHoFPi4flpyywgo2R0fNHo/ug42kjjH2qaBAjiwmQIptaMdAL24tiszm33/VGcGIMpbwgBNtpAev5PVFVNh7Cetj5ueidjt/E5XC3+YwUbefeEWdbmlp1IpM01r87i1GOeaSUudOupIm2zfDxHUfK/MH09KXPoppZVVEIFbbY6jW923vgrYapGmB+aupBCMSEaLg5p/7nTq7etnYFYVqg4RtYYMt0kz4am84HCQJgLKBOxgUzxVFGZyB4o0cdmLm7UEBOV7LEoYl09I1jO2KrhCYEpJ2HEZ2KermMSXfNvCi01wRnVv0PuJ8/MmyaUzNpF8Z/YIecOoXQSseBIFewm5AX4LKzVR/mJTQEWqk8bg4eFWXBzlK393TJZcEAv5p/4gc4ZeIpgyNKd3vg0t92kPS9sAjwNrusM7O7gU8xIWz9He4mkEnls4Y2AhC+9Wn/QERSG5wzPUKjFLQqlpFB51quSe72/bCROqqySKGstbqq8kpcoEgY7ALOKnUh+NHKELcc9vrLj7dKEB4al+aHI22gciBW73wPk/6rhS/1pDr2eQFv6wSB7mgexnSUf6L51QftN23jbxjptpA1B8ltPwNBx6HDJprIdjl3wWQixhxK2zhTbAeGgS7Kw6p15rwEpKPBSud1TXq7l48s7K+qxjsPMpXD/NG4fMb6NqeV17BvW9SIxooSvBfgwJm3NaLUhVfWQ4YnayUaraVWl5MektWJ6yP8fM/iKkOeIwBOf9SUxbCGkzNFFECACrMrdluCU7bmnz2v2oIxo9mT8BwrKXhCZ5Fwe/Eq/UBy46Citkh4UibUQSbx2158Pn26VJ7chWYXaLr7I0k8KLuYS1pCATLIsWoAzMVjR6wLVm1bn7PdQlph5dCcefGOStzTZjm6OwlRwVsmkBv0gkjcsZoy85Ka05THdJVl70Id5Wndg8+aIlWJnsO+2PQY1rOSASKgg2hYCE3KeTVUdw7hvXwkPVKOuzaY5MztGzeVHx45sackdFTE4fchEDf0XCWpiQ17YaLqIfd97WfPq1HNJ3wnDp4ZvVr/GLil4snKtnVTfrXpvpX7q1slcCCVifMKGFh9XnIq3sC16+Lqua/tS/CuH6VqOv0SpPZUP3khKAkZC2Qoba79uBRdZlWljAvnNSZyqLHNtgMgMcUWyRsfg+l92MSS8aWOAKwYnoL76GFNxKl2N+/MwuBWA+H9e0qKzwkJFZOhPjlwkLFwpC+4PpnM5UlLa0UG8QtXZH+l/oBlIBMoEQPzCt0k+uDu72xY2wWalRWXTKtrnlCRDzpOqhCNfca2pYkvbF7Q49DKZCpZlQYjGRlJ9oSg7VCLMhNE02AN1hIx/0EMxPe8oKx9f8lmGdWd/i9PtGV4xOETAZkS2BgQEwLgtsJ9eZUhq4wGRzCcOsx1pHaWaRAHRZ9rr/ReTqvOuU5DGULqzAHfNOJ4xv6TCmlLwiQ6ByWT7sKu0BC6SODSmQnLLm+/I3ilPdm5jCp8mvC/LKI7fYPEXH0ylvWccN22OgF6g354t6KS88F9AXatU+Xf7WH6+TiVFAhyhf0b7hZMGxCahnj+ZPjfqNt4OpeXO9+vz2isVZ4pEf6b/8l69oPq5Vwwb21DoRpErZjbVPPXgQZgjKPuXNEiua/kKep4eHMau8pZxZlFa+xunNSRox7q1AJE4AZ0lF3b/gJBQ46TTS1eyTEe76w1Vk79cTcFoWhMDT20a9JQ+UpJVGKSGlHBd3923sjsZwb+cxSIDdOrcpXrL2fRvwsU5g0Tc0hkQOhAagBgi+IudxBNFa4lGhj9PrqjTAPTWj5HCkcSEiehs6goVMvrovqWts9bfrfS0HxheEAa75MM6/tn6JBkR1Fc5ENK/XVq/ccWEtQZ9IM6eGZCg37nT/nB7FmGv/iiYS6N09TK8oPST+zWpRDxIETarKqPCBxnlKZkr8D0GJIX9HhzdFkOL6BWTvwTOIz9ilC5SFRAhX8DfzLmPHn7gV+xf4U5h7ZCnvXJfQV8vx0IaMXPcLE4wJkFV+e33SGOLKbWwgrgHv4cyWKY8MOfTHEQo+wiwykQqHPageS+kXR01tTytP+103eLkmLjnPldoO+E1OJ3TReO7HQwCY1jxghsmWyDctKYjgm34Pp3v721RQoVp7buV98bWm1LhjPecsQlvAyzckizfVvIz31y5+QLgt35GiMhnijWAgxED0avEybJ1gQZzj4utmhsH7TCT0wO+MJKaCLS7FFku4VCestJtf2T1nY2Sk05WuRSi4twDIYEp4dgPHpVjEMt9rJfwog1URFtuPQZXBATrmRhUkmEEwTziB+4s5+5QS7dNwoDIYAw== + page_age: null + title: Breaking News, Latest News and Videos | CNN + type: web_search_result + url: https://www.cnn.com/ + - encrypted_content: ErQCCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDGPat3RtBffd6jd9uxoMkx9uAfM4hgJRbrR4IjCBVNWqux+TsqDP0poLm+ss84SLrVR4rAcjrQSDPna9ZfR0OFhPjv2ko1ZVuBzeRE4qtwGOPV6my0G/y4nPEH8gNVc3y/8uZzh7O8CBrduzchEMd5RRXLlsC+bU/SjZ+5LBYGzAVwRCfVXIdaJ0/d8RYdJWHo3bvKc5Lu/WFPV6Po9gVHLOU5WVDsyzwmrvqzCYC0UhkUMa0yf5j7WTFaT+kgHZcFcbvYPG53USqNh0seahaaCC5fJRjRBTAvuyj4md+ppTjIXGZEp3rTMG3MTkv8t60MgPzn4ObLGEmBQIQrfES9G2BT1k7lUYAw== + page_age: null + title: 'Philippines Top Stories: Politics, Environment, Education, Trending | Inquirer.net' + type: web_search_result + url: https://newsinfo.inquirer.net + - encrypted_content: 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 + page_age: null + title: Portal:Current events/May 2025 - Wikipedia + type: web_search_result + url: https://en.wikipedia.org/wiki/Portal:Current_events/May_2025 + - 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 + page_age: null + title: Portal:Current events - Wikipedia + type: web_search_result + url: https://en.wikipedia.org/wiki/Portal:Current_events + - 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== + page_age: null + title: Current Affairs Today – Current Affairs – 2025-26 - GKToday + type: web_search_result + url: https://www.gktoday.in/current-affairs/ + - 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 + - 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_use_id: srvtoolu_01MqVvTi9LWTrMRuZ2KttD3M + type: web_search_tool_result + - text: "\n\nBased on the search results, today is Monday, May 26, 2025. This is confirmed by several sources:\n\n1. " + type: text + - citations: + - cited_text: "Katja Ridderbusch hide caption toggle caption Katja Ridderbusch · May 26, 2025 \x95 Efforts to improve + officers' mental health have grown over the p..." + encrypted_index: Eo8BCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDDbwqv5slJiinAczsRoMhSEnYjGPJgqqehvhIjD+BvlqTC7HRjtE/aw2RZWQrImtkFRHsMc+fLuePmKCTV6CchthAUcDrpTbM2sVfeoqE2azhBPbtwuu/6GGPopddo8c5owYBA== + title: 'News: U.S. and World News Headlines : NPR' + type: web_search_result_location + url: https://www.npr.org/sections/news/ + - cited_text: "May 26, 2025 \x95 It's Memorial Day. " + encrypted_index: EpABCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDP+cEzOyt7FZcwJ+GBoMyXx/ouZvvFOJk680IjAWGSs9Nrqov5SEum6N3Gk0FGVUoIgOxF4aOdlcLDVixGpcVil7pLWnwcKf6aH1DhIqFFQUWnEOplUMZH6nQ1RQprK0YB59GAQ= + title: 'News: U.S. and World News Headlines : NPR' + type: web_search_result_location + url: https://www.npr.org/sections/news/ + text: It's Memorial Day today, May 26, 2025 + type: text + - text: "\n\n2. " + type: text + - citations: + - cited_text: 'May 2025 is the fifth month of the current common year. The month, which began on a Thursday, will + end on a Saturday after 31 days. ' + encrypted_index: EpMBCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDKg1IA50Gwr+acWT5BoMGYg/QqdGIWZ1xBQKIjAkgAmLPkrAfHcJ5l/9cBkuIDZFR5HqWIMts7szVwtYu+KUtw636GWrp1YgAWw+PHkqF0xqh9pIZ2wPGUKGN4beDYqhKyS3yKNSGAQ= + title: Portal:Current events/May 2025 - Wikipedia + type: web_search_result_location + url: https://en.wikipedia.org/wiki/Portal:Current_events/May_2025 + text: 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 + type: text + - text: "\n\n3. " + type: text + - citations: + - cited_text: On May 26, 2025, India and the world witnessed significant developments across various sectors. In recent + developments shaping global and national aff... + encrypted_index: EpIBCioIAxgCIiQ0NGFlNjc2Yy05NThmLTRkNjgtOTEwOC1lYWU5ZGU3YjM2NmISDFkt41LpatakOxyiFhoMv3NkIlXjrN17gwJmIjBYF1Ds1OSA6s5ysr6ObQrej6qB8TY2C7CLY3A9izWkBKF92leDuAKzxrbr0q2HuTYqFvMJ033dlXOze2WorB4B4VxlNRvBw7EYBA== + title: 26 May 2025 UPSC Current Affairs - Daily News Headlines + type: web_search_result_location + url: https://testbook.com/ias-preparation/upsc-current-affairs-for-26-may-2025 + text: 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 + type: text + id: msg_01WWvGdHT1E2kYaV5ZLWMB2N + model: claude-3-5-sonnet-20241022 + role: assistant + stop_reason: end_turn + stop_sequence: null + type: message + usage: + cache_creation_input_tokens: 0 + cache_read_input_tokens: 0 + input_tokens: 16312 + output_tokens: 258 + server_tool_use: + web_search_requests: 1 + service_tier: standard + status: + code: 200 + message: OK +version: 1 diff --git a/tests/models/cassettes/test_google/test_google_model_code_execution_tool.yaml b/tests/models/cassettes/test_google/test_google_model_code_execution_tool.yaml new file mode 100644 index 000000000..7301ab260 --- /dev/null +++ b/tests/models/cassettes/test_google/test_google_model_code_execution_tool.yaml @@ -0,0 +1,89 @@ +interactions: +- request: + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '234' + content-type: + - application/json + host: + - generativelanguage.googleapis.com + method: POST + parsed_body: + contents: + - parts: + - text: What day is today in Utrecht? + role: user + generationConfig: {} + systemInstruction: + parts: + - text: You are a helpful chatbot. + role: user + tools: + - codeExecution: {} + uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent + response: + headers: + alt-svc: + - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 + content-length: + - '1462' + content-type: + - application/json; charset=UTF-8 + server-timing: + - gfet4t7; dur=7674 + transfer-encoding: + - chunked + vary: + - Origin + - X-Origin + - Referer + parsed_body: + candidates: + - content: + parts: + - text: |+ + 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. + + - executableCode: + 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: PYTHON + - codeExecutionResult: + outcome: OUTCOME_OK + output: | + Wednesday, 2025-05-28 + - text: | + Today is Wednesday, May 28, 2025 in Utrecht. + role: model + finishReason: STOP + modelVersion: gemini-2.0-flash + responseId: 8ww3aLDxJY24qsMP97vYeA + usageMetadata: + candidatesTokenCount: 119 + candidatesTokensDetails: + - modality: TEXT + tokenCount: 119 + promptTokenCount: 13 + promptTokensDetails: + - modality: TEXT + tokenCount: 13 + toolUsePromptTokenCount: 114 + toolUsePromptTokensDetails: + - modality: TEXT + tokenCount: 114 + totalTokenCount: 246 + status: + code: 200 + message: OK +version: 1 diff --git a/tests/models/cassettes/test_google/test_google_model_web_search_tool.yaml b/tests/models/cassettes/test_google/test_google_model_web_search_tool.yaml new file mode 100644 index 000000000..e880750ac --- /dev/null +++ b/tests/models/cassettes/test_google/test_google_model_web_search_tool.yaml @@ -0,0 +1,199 @@ +interactions: +- request: + headers: + accept: + - '*/*' + accept-encoding: + - gzip, deflate + connection: + - keep-alive + content-length: + - '233' + content-type: + - application/json + host: + - generativelanguage.googleapis.com + method: POST + parsed_body: + contents: + - parts: + - text: What day is today in Utrecht? + role: user + generationConfig: {} + systemInstruction: + parts: + - text: You are a helpful chatbot. + role: user + tools: + - googleSearch: {} + uri: https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent + response: + headers: + alt-svc: + - h3=":443"; ma=2592000,h3-29=":443"; ma=2592000 + content-length: + - '5991' + content-type: + - application/json; charset=UTF-8 + server-timing: + - gfet4t7; dur=1340 + transfer-encoding: + - chunked + vary: + - Origin + - X-Origin + - Referer + parsed_body: + candidates: + - content: + parts: + - text: | + Today is Wednesday, May 28, 2025, in Utrecht. + role: model + finishReason: STOP + groundingMetadata: + retrievalMetadata: {} + searchEntryPoint: + renderedContent: | + +