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 ):