From 352dd995e17962b7f2b9007fad60fa102ba65479 Mon Sep 17 00:00:00 2001 From: Jeff Bryner Date: Sun, 9 Nov 2025 20:46:15 -0800 Subject: [PATCH] fix: Add required parameters with defaults if missing Merge https://github.com/google/adk-python/pull/2054 fixes https://github.com/google/adk-python/issues/2053 Co-authored-by: Xuan Yang COPYBARA_INTEGRATE_REVIEW=https://github.com/google/adk-python/pull/2054 from jeffbryner:fix-required-params dd75a0b5798792dccb1ca0ac0240ce0c0b1fad14 PiperOrigin-RevId: 830251203 --- .../openapi_spec_parser/rest_api_tool.py | 12 +++++ .../openapi_spec_parser/test_rest_api_tool.py | 52 +++++++++++++++++++ 2 files changed, 64 insertions(+) diff --git a/src/google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py b/src/google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py index 2f16e8ba..a0fe3e6b 100644 --- a/src/google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py +++ b/src/google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py @@ -23,6 +23,7 @@ from typing import Tuple from typing import Union from fastapi.openapi.models import Operation +from fastapi.openapi.models import Schema from google.genai.types import FunctionDeclaration import requests from typing_extensions import override @@ -392,6 +393,17 @@ class RestApiTool(BaseTool): # Attach parameters from auth into main parameters list api_params, api_args = self._operation_parser.get_parameters().copy(), args + + # Add any required arguments that are missing and have defaults: + for api_param in api_params: + if api_param.py_name not in api_args: + if ( + api_param.required + and isinstance(api_param.param_schema, Schema) + and api_param.param_schema.default is not None + ): + api_args[api_param.py_name] = api_param.param_schema.default + if auth_credential: # Attach parameters from auth into main parameters list auth_param, auth_args = self._prepare_auth_request_params( diff --git a/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py b/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py index 02b496bc..fa963a3b 100644 --- a/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py +++ b/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py @@ -262,6 +262,58 @@ class TestRestApiTool: "message": "Needs your authorization to access your data.", } + @patch( + "google.adk.tools.openapi_tool.openapi_spec_parser.rest_api_tool.requests.request" + ) + @pytest.mark.asyncio + async def test_call_with_required_param_defaults( + self, + mock_request, + mock_tool_context, + sample_endpoint, + sample_auth_scheme, + sample_auth_credential, + ): + """Test that required parameters with defaults are auto-filled.""" + mock_response = MagicMock() + mock_response.json.return_value = {"result": "success"} + mock_request.return_value = mock_response + + # Create operation with required parameter that has default + mock_operation = Operation( + operationId="test_op", + parameters=[ + OpenAPIParameter(**{ + "name": "userId", + "in": "path", + "required": True, + "schema": OpenAPISchema(type="string", default="me"), + }) + ], + ) + + tool = RestApiTool( + name="test_tool", + description="Test Tool", + endpoint=OperationEndpoint( + base_url="https://example.com", + path="/users/{userId}/messages", + method="GET", + ), + operation=mock_operation, + auth_scheme=sample_auth_scheme, + auth_credential=sample_auth_credential, + ) + + # Call without providing userId - should use default "me" + result = await tool.call(args={}, tool_context=mock_tool_context) + + # Verify the default was applied + assert mock_request.called + call_kwargs = mock_request.call_args[1] + assert call_kwargs["url"] == "https://example.com/users/me/messages" + assert result == {"result": "success"} + def test_prepare_request_params_query_body( self, sample_endpoint, sample_auth_credential, sample_auth_scheme ):