google-adk 0.0.3__py3-none-any.whl → 0.0.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (184) hide show
  1. google/adk/agents/run_config.py +4 -0
  2. google/adk/auth/auth_preprocessor.py +19 -16
  3. google/adk/cli/browser/index.html +1 -1
  4. google/adk/cli/browser/{main-SY2WYYGV.js → main-CU22TRPI.js} +30 -30
  5. google/adk/cli/cli.py +8 -8
  6. google/adk/cli/cli_deploy.py +2 -4
  7. google/adk/cli/cli_tools_click.py +6 -6
  8. google/adk/flows/llm_flows/contents.py +21 -1
  9. google/adk/flows/llm_flows/functions.py +3 -1
  10. google/adk/models/google_llm.py +0 -1
  11. google/adk/runners.py +13 -2
  12. google/adk/version.py +1 -1
  13. {google_adk-0.0.3.dist-info → google_adk-0.0.4.dist-info}/METADATA +4 -2
  14. google_adk-0.0.4.dist-info/RECORD +175 -0
  15. {google_adk-0.0.3.dist-info → google_adk-0.0.4.dist-info}/WHEEL +1 -1
  16. google_adk-0.0.4.dist-info/licenses/LICENSE +202 -0
  17. google/adk/._version.py +0 -0
  18. google/adk/docs/Makefile +0 -20
  19. google/adk/docs/build/doctrees/google-adk.doctree +0 -0
  20. google/adk/docs/build/html/_sources/google-adk.rst.txt +0 -98
  21. google/adk/docs/build/html/_sources/index.rst.txt +0 -7
  22. google/adk/docs/build/html/_static/autodoc_pydantic.css +0 -27
  23. google/adk/docs/build/html/_static/basic.css +0 -925
  24. google/adk/docs/build/html/_static/debug.css +0 -85
  25. google/adk/docs/build/html/_static/doctools.js +0 -156
  26. google/adk/docs/build/html/_static/documentation_options.js +0 -29
  27. google/adk/docs/build/html/_static/file.png +0 -0
  28. google/adk/docs/build/html/_static/language_data.js +0 -199
  29. google/adk/docs/build/html/_static/minus.png +0 -0
  30. google/adk/docs/build/html/_static/plus.png +0 -0
  31. google/adk/docs/build/html/_static/pygments.css +0 -274
  32. google/adk/docs/build/html/_static/scripts/furo-extensions.js +0 -16
  33. google/adk/docs/build/html/_static/scripts/furo.js +0 -19
  34. google/adk/docs/build/html/_static/scripts/furo.js.LICENSE.txt +0 -7
  35. google/adk/docs/build/html/_static/scripts/furo.js.map +0 -1
  36. google/adk/docs/build/html/_static/searchtools.js +0 -620
  37. google/adk/docs/build/html/_static/skeleton.css +0 -312
  38. google/adk/docs/build/html/_static/sphinx_highlight.js +0 -170
  39. google/adk/docs/build/html/_static/styles/furo-extensions.css +0 -18
  40. google/adk/docs/build/html/_static/styles/furo-extensions.css.map +0 -1
  41. google/adk/docs/build/html/_static/styles/furo.css +0 -18
  42. google/adk/docs/build/html/_static/styles/furo.css.map +0 -1
  43. google/adk/docs/build/html/genindex.html +0 -861
  44. google/adk/docs/build/html/google-adk.html +0 -5461
  45. google/adk/docs/build/html/index.html +0 -567
  46. google/adk/docs/build/html/objects.inv +0 -0
  47. google/adk/docs/build/html/py-modindex.html +0 -373
  48. google/adk/docs/build/html/search.html +0 -333
  49. google/adk/docs/build/html/searchindex.js +0 -17
  50. google/adk/docs/source/conf.py +0 -133
  51. google/adk/docs/source/google-adk.rst +0 -98
  52. google/adk/docs/source/index.rst +0 -7
  53. google/adk/tests/__init__.py +0 -14
  54. google/adk/tests/integration/.env.example +0 -10
  55. google/adk/tests/integration/__init__.py +0 -18
  56. google/adk/tests/integration/conftest.py +0 -119
  57. google/adk/tests/integration/fixture/__init__.py +0 -14
  58. google/adk/tests/integration/fixture/agent_with_config/__init__.py +0 -15
  59. google/adk/tests/integration/fixture/agent_with_config/agent.py +0 -88
  60. google/adk/tests/integration/fixture/callback_agent/__init__.py +0 -15
  61. google/adk/tests/integration/fixture/callback_agent/agent.py +0 -105
  62. google/adk/tests/integration/fixture/context_update_test/OWNERS +0 -1
  63. google/adk/tests/integration/fixture/context_update_test/__init__.py +0 -15
  64. google/adk/tests/integration/fixture/context_update_test/agent.py +0 -43
  65. google/adk/tests/integration/fixture/context_update_test/successful_test.session.json +0 -582
  66. google/adk/tests/integration/fixture/context_variable_agent/__init__.py +0 -15
  67. google/adk/tests/integration/fixture/context_variable_agent/agent.py +0 -115
  68. google/adk/tests/integration/fixture/customer_support_ma/__init__.py +0 -15
  69. google/adk/tests/integration/fixture/customer_support_ma/agent.py +0 -172
  70. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/__init__.py +0 -15
  71. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/agent.py +0 -338
  72. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/order_query.test.json +0 -69
  73. google/adk/tests/integration/fixture/ecommerce_customer_service_agent/test_config.json +0 -6
  74. google/adk/tests/integration/fixture/flow_complex_spark/__init__.py +0 -15
  75. google/adk/tests/integration/fixture/flow_complex_spark/agent.py +0 -182
  76. google/adk/tests/integration/fixture/flow_complex_spark/sample.session.json +0 -190
  77. google/adk/tests/integration/fixture/hello_world_agent/__init__.py +0 -15
  78. google/adk/tests/integration/fixture/hello_world_agent/agent.py +0 -95
  79. google/adk/tests/integration/fixture/hello_world_agent/roll_die.test.json +0 -24
  80. google/adk/tests/integration/fixture/hello_world_agent/test_config.json +0 -6
  81. google/adk/tests/integration/fixture/home_automation_agent/__init__.py +0 -15
  82. google/adk/tests/integration/fixture/home_automation_agent/agent.py +0 -304
  83. google/adk/tests/integration/fixture/home_automation_agent/simple_test.test.json +0 -5
  84. google/adk/tests/integration/fixture/home_automation_agent/simple_test2.test.json +0 -5
  85. google/adk/tests/integration/fixture/home_automation_agent/test_config.json +0 -5
  86. google/adk/tests/integration/fixture/home_automation_agent/test_files/dependent_tool_calls.test.json +0 -18
  87. google/adk/tests/integration/fixture/home_automation_agent/test_files/memorizing_past_events/eval_data.test.json +0 -17
  88. google/adk/tests/integration/fixture/home_automation_agent/test_files/memorizing_past_events/test_config.json +0 -6
  89. google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_multi_turn_conversation.test.json +0 -18
  90. google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_test.test.json +0 -17
  91. google/adk/tests/integration/fixture/home_automation_agent/test_files/simple_test2.test.json +0 -5
  92. google/adk/tests/integration/fixture/home_automation_agent/test_files/test_config.json +0 -5
  93. google/adk/tests/integration/fixture/tool_agent/__init__.py +0 -15
  94. google/adk/tests/integration/fixture/tool_agent/agent.py +0 -218
  95. google/adk/tests/integration/fixture/tool_agent/files/Agent_test_plan.pdf +0 -0
  96. google/adk/tests/integration/fixture/trip_planner_agent/__init__.py +0 -15
  97. google/adk/tests/integration/fixture/trip_planner_agent/agent.py +0 -110
  98. google/adk/tests/integration/fixture/trip_planner_agent/initial.session.json +0 -13
  99. google/adk/tests/integration/fixture/trip_planner_agent/test_config.json +0 -5
  100. google/adk/tests/integration/fixture/trip_planner_agent/test_files/initial.session.json +0 -13
  101. google/adk/tests/integration/fixture/trip_planner_agent/test_files/test_config.json +0 -5
  102. google/adk/tests/integration/fixture/trip_planner_agent/test_files/trip_inquiry_sub_agent.test.json +0 -7
  103. google/adk/tests/integration/fixture/trip_planner_agent/trip_inquiry.test.json +0 -19
  104. google/adk/tests/integration/models/__init__.py +0 -14
  105. google/adk/tests/integration/models/test_google_llm.py +0 -65
  106. google/adk/tests/integration/test_callback.py +0 -70
  107. google/adk/tests/integration/test_context_variable.py +0 -67
  108. google/adk/tests/integration/test_evalute_agent_in_fixture.py +0 -76
  109. google/adk/tests/integration/test_multi_agent.py +0 -28
  110. google/adk/tests/integration/test_multi_turn.py +0 -42
  111. google/adk/tests/integration/test_single_agent.py +0 -23
  112. google/adk/tests/integration/test_sub_agent.py +0 -26
  113. google/adk/tests/integration/test_system_instruction.py +0 -177
  114. google/adk/tests/integration/test_tools.py +0 -287
  115. google/adk/tests/integration/test_with_test_file.py +0 -34
  116. google/adk/tests/integration/tools/__init__.py +0 -14
  117. google/adk/tests/integration/utils/__init__.py +0 -16
  118. google/adk/tests/integration/utils/asserts.py +0 -75
  119. google/adk/tests/integration/utils/test_runner.py +0 -97
  120. google/adk/tests/unittests/__init__.py +0 -14
  121. google/adk/tests/unittests/agents/__init__.py +0 -14
  122. google/adk/tests/unittests/agents/test_base_agent.py +0 -407
  123. google/adk/tests/unittests/agents/test_langgraph_agent.py +0 -191
  124. google/adk/tests/unittests/agents/test_llm_agent_callbacks.py +0 -138
  125. google/adk/tests/unittests/agents/test_llm_agent_fields.py +0 -231
  126. google/adk/tests/unittests/agents/test_loop_agent.py +0 -136
  127. google/adk/tests/unittests/agents/test_parallel_agent.py +0 -92
  128. google/adk/tests/unittests/agents/test_sequential_agent.py +0 -114
  129. google/adk/tests/unittests/artifacts/__init__.py +0 -14
  130. google/adk/tests/unittests/artifacts/test_artifact_service.py +0 -276
  131. google/adk/tests/unittests/auth/test_auth_handler.py +0 -575
  132. google/adk/tests/unittests/conftest.py +0 -73
  133. google/adk/tests/unittests/fast_api/__init__.py +0 -14
  134. google/adk/tests/unittests/fast_api/test_fast_api.py +0 -269
  135. google/adk/tests/unittests/flows/__init__.py +0 -14
  136. google/adk/tests/unittests/flows/llm_flows/__init__.py +0 -14
  137. google/adk/tests/unittests/flows/llm_flows/_test_examples.py +0 -142
  138. google/adk/tests/unittests/flows/llm_flows/test_agent_transfer.py +0 -311
  139. google/adk/tests/unittests/flows/llm_flows/test_functions_long_running.py +0 -244
  140. google/adk/tests/unittests/flows/llm_flows/test_functions_request_euc.py +0 -346
  141. google/adk/tests/unittests/flows/llm_flows/test_functions_sequential.py +0 -93
  142. google/adk/tests/unittests/flows/llm_flows/test_functions_simple.py +0 -258
  143. google/adk/tests/unittests/flows/llm_flows/test_identity.py +0 -66
  144. google/adk/tests/unittests/flows/llm_flows/test_instructions.py +0 -164
  145. google/adk/tests/unittests/flows/llm_flows/test_model_callbacks.py +0 -142
  146. google/adk/tests/unittests/flows/llm_flows/test_other_configs.py +0 -46
  147. google/adk/tests/unittests/flows/llm_flows/test_tool_callbacks.py +0 -269
  148. google/adk/tests/unittests/models/__init__.py +0 -14
  149. google/adk/tests/unittests/models/test_google_llm.py +0 -224
  150. google/adk/tests/unittests/models/test_litellm.py +0 -804
  151. google/adk/tests/unittests/models/test_models.py +0 -60
  152. google/adk/tests/unittests/sessions/__init__.py +0 -14
  153. google/adk/tests/unittests/sessions/test_session_service.py +0 -227
  154. google/adk/tests/unittests/sessions/test_vertex_ai_session_service.py +0 -246
  155. google/adk/tests/unittests/streaming/__init__.py +0 -14
  156. google/adk/tests/unittests/streaming/test_streaming.py +0 -50
  157. google/adk/tests/unittests/tools/__init__.py +0 -14
  158. google/adk/tests/unittests/tools/apihub_tool/clients/test_apihub_client.py +0 -499
  159. google/adk/tests/unittests/tools/apihub_tool/test_apihub_toolset.py +0 -204
  160. google/adk/tests/unittests/tools/application_integration_tool/clients/test_connections_client.py +0 -600
  161. google/adk/tests/unittests/tools/application_integration_tool/clients/test_integration_client.py +0 -630
  162. google/adk/tests/unittests/tools/application_integration_tool/test_application_integration_toolset.py +0 -345
  163. google/adk/tests/unittests/tools/google_api_tool/__init__.py +0 -13
  164. google/adk/tests/unittests/tools/google_api_tool/test_googleapi_to_openapi_converter.py +0 -657
  165. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_auto_auth_credential_exchanger.py +0 -145
  166. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_base_auth_credential_exchanger.py +0 -68
  167. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_oauth2_exchanger.py +0 -153
  168. google/adk/tests/unittests/tools/openapi_tool/auth/credential_exchangers/test_service_account_exchanger.py +0 -196
  169. google/adk/tests/unittests/tools/openapi_tool/auth/test_auth_helper.py +0 -573
  170. google/adk/tests/unittests/tools/openapi_tool/common/test_common.py +0 -436
  171. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test.yaml +0 -1367
  172. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_spec_parser.py +0 -628
  173. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_openapi_toolset.py +0 -139
  174. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_operation_parser.py +0 -406
  175. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py +0 -966
  176. google/adk/tests/unittests/tools/openapi_tool/openapi_spec_parser/test_tool_auth_handler.py +0 -201
  177. google/adk/tests/unittests/tools/retrieval/__init__.py +0 -14
  178. google/adk/tests/unittests/tools/retrieval/test_vertex_ai_rag_retrieval.py +0 -147
  179. google/adk/tests/unittests/tools/test_agent_tool.py +0 -167
  180. google/adk/tests/unittests/tools/test_base_tool.py +0 -141
  181. google/adk/tests/unittests/tools/test_build_function_declaration.py +0 -277
  182. google/adk/tests/unittests/utils.py +0 -304
  183. google_adk-0.0.3.dist-info/RECORD +0 -340
  184. {google_adk-0.0.3.dist-info → google_adk-0.0.4.dist-info}/entry_points.txt +0 -0
@@ -1,966 +0,0 @@
1
- # Copyright 2025 Google LLC
2
- #
3
- # Licensed under the Apache License, Version 2.0 (the "License");
4
- # you may not use this file except in compliance with the License.
5
- # You may obtain a copy of the License at
6
- #
7
- # http://www.apache.org/licenses/LICENSE-2.0
8
- #
9
- # Unless required by applicable law or agreed to in writing, software
10
- # distributed under the License is distributed on an "AS IS" BASIS,
11
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- # See the License for the specific language governing permissions and
13
- # limitations under the License.
14
-
15
-
16
- import json
17
- from unittest.mock import MagicMock
18
- from unittest.mock import patch
19
-
20
- from fastapi.openapi.models import MediaType
21
- from fastapi.openapi.models import Operation
22
- from fastapi.openapi.models import Parameter as OpenAPIParameter
23
- from fastapi.openapi.models import RequestBody
24
- from fastapi.openapi.models import Schema as OpenAPISchema
25
- from google.adk.sessions.state import State
26
- from google.adk.tools.openapi_tool.auth.auth_helpers import token_to_scheme_credential
27
- from google.adk.tools.openapi_tool.common.common import ApiParameter
28
- from google.adk.tools.openapi_tool.openapi_spec_parser.openapi_spec_parser import OperationEndpoint
29
- from google.adk.tools.openapi_tool.openapi_spec_parser.operation_parser import OperationParser
30
- from google.adk.tools.openapi_tool.openapi_spec_parser.rest_api_tool import RestApiTool
31
- from google.adk.tools.openapi_tool.openapi_spec_parser.rest_api_tool import snake_to_lower_camel
32
- from google.adk.tools.openapi_tool.openapi_spec_parser.rest_api_tool import to_gemini_schema
33
- from google.adk.tools.tool_context import ToolContext
34
- from google.genai.types import FunctionDeclaration
35
- from google.genai.types import Schema
36
- from google.genai.types import Type
37
- import pytest
38
-
39
-
40
- class TestRestApiTool:
41
-
42
- @pytest.fixture
43
- def mock_tool_context(self):
44
- """Fixture for a mock OperationParser."""
45
- mock_context = MagicMock(spec=ToolContext)
46
- mock_context.state = State({}, {})
47
- mock_context.get_auth_response.return_value = {}
48
- mock_context.request_credential.return_value = {}
49
- return mock_context
50
-
51
- @pytest.fixture
52
- def mock_operation_parser(self):
53
- """Fixture for a mock OperationParser."""
54
- mock_parser = MagicMock(spec=OperationParser)
55
- mock_parser.get_function_name.return_value = "mock_function_name"
56
- mock_parser.get_json_schema.return_value = {}
57
- mock_parser.get_parameters.return_value = []
58
- mock_parser.get_return_type_hint.return_value = "str"
59
- mock_parser.get_pydoc_string.return_value = "Mock docstring"
60
- mock_parser.get_signature_parameters.return_value = []
61
- mock_parser.get_return_type_value.return_value = str
62
- mock_parser.get_annotations.return_value = {}
63
- return mock_parser
64
-
65
- @pytest.fixture
66
- def sample_endpiont(self):
67
- return OperationEndpoint(
68
- base_url="https://example.com", path="/test", method="GET"
69
- )
70
-
71
- @pytest.fixture
72
- def sample_operation(self):
73
- return Operation(
74
- operationId="testOperation",
75
- description="Test operation",
76
- parameters=[],
77
- requestBody=RequestBody(
78
- content={
79
- "application/json": MediaType(
80
- schema=OpenAPISchema(
81
- type="object",
82
- properties={
83
- "testBodyParam": OpenAPISchema(type="string")
84
- },
85
- )
86
- )
87
- }
88
- ),
89
- )
90
-
91
- @pytest.fixture
92
- def sample_api_parameters(self):
93
- return [
94
- ApiParameter(
95
- original_name="test_param",
96
- py_name="test_param",
97
- param_location="query",
98
- param_schema=OpenAPISchema(type="string"),
99
- is_required=True,
100
- ),
101
- ApiParameter(
102
- original_name="",
103
- py_name="test_body_param",
104
- param_location="body",
105
- param_schema=OpenAPISchema(type="string"),
106
- is_required=True,
107
- ),
108
- ]
109
-
110
- @pytest.fixture
111
- def sample_return_parameter(self):
112
- return ApiParameter(
113
- original_name="test_param",
114
- py_name="test_param",
115
- param_location="query",
116
- param_schema=OpenAPISchema(type="string"),
117
- is_required=True,
118
- )
119
-
120
- @pytest.fixture
121
- def sample_auth_scheme(self):
122
- scheme, _ = token_to_scheme_credential(
123
- "apikey", "header", "", "sample_auth_credential_internal_test"
124
- )
125
- return scheme
126
-
127
- @pytest.fixture
128
- def sample_auth_credential(self):
129
- _, credential = token_to_scheme_credential(
130
- "apikey", "header", "", "sample_auth_credential_internal_test"
131
- )
132
- return credential
133
-
134
- def test_init(
135
- self,
136
- sample_endpiont,
137
- sample_operation,
138
- sample_auth_scheme,
139
- sample_auth_credential,
140
- ):
141
- tool = RestApiTool(
142
- name="test_tool",
143
- description="Test Tool",
144
- endpoint=sample_endpiont,
145
- operation=sample_operation,
146
- auth_scheme=sample_auth_scheme,
147
- auth_credential=sample_auth_credential,
148
- )
149
- assert tool.name == "test_tool"
150
- assert tool.description == "Test Tool"
151
- assert tool.endpoint == sample_endpiont
152
- assert tool.operation == sample_operation
153
- assert tool.auth_credential == sample_auth_credential
154
- assert tool.auth_scheme == sample_auth_scheme
155
- assert tool.credential_exchanger is not None
156
-
157
- def test_from_parsed_operation_str(
158
- self,
159
- sample_endpiont,
160
- sample_api_parameters,
161
- sample_return_parameter,
162
- sample_operation,
163
- ):
164
- parsed_operation_str = json.dumps({
165
- "name": "test_operation",
166
- "description": "Test Description",
167
- "endpoint": sample_endpiont.model_dump(),
168
- "operation": sample_operation.model_dump(),
169
- "auth_scheme": None,
170
- "auth_credential": None,
171
- "parameters": [p.model_dump() for p in sample_api_parameters],
172
- "return_value": sample_return_parameter.model_dump(),
173
- })
174
-
175
- tool = RestApiTool.from_parsed_operation_str(parsed_operation_str)
176
- assert tool.name == "test_operation"
177
-
178
- def test_get_declaration(
179
- self, sample_endpiont, sample_operation, mock_operation_parser
180
- ):
181
- tool = RestApiTool(
182
- name="test_tool",
183
- description="Test description",
184
- endpoint=sample_endpiont,
185
- operation=sample_operation,
186
- should_parse_operation=False,
187
- )
188
- tool._operation_parser = mock_operation_parser
189
-
190
- declaration = tool._get_declaration()
191
- assert isinstance(declaration, FunctionDeclaration)
192
- assert declaration.name == "test_tool"
193
- assert declaration.description == "Test description"
194
- assert isinstance(declaration.parameters, Schema)
195
-
196
- @patch(
197
- "google.adk.tools.openapi_tool.openapi_spec_parser.rest_api_tool.requests.request"
198
- )
199
- def test_call_success(
200
- self,
201
- mock_request,
202
- mock_tool_context,
203
- sample_endpiont,
204
- sample_operation,
205
- sample_auth_scheme,
206
- sample_auth_credential,
207
- ):
208
- mock_response = MagicMock()
209
- mock_response.json.return_value = {"result": "success"}
210
- mock_request.return_value = mock_response
211
-
212
- tool = RestApiTool(
213
- name="test_tool",
214
- description="Test Tool",
215
- endpoint=sample_endpiont,
216
- operation=sample_operation,
217
- auth_scheme=sample_auth_scheme,
218
- auth_credential=sample_auth_credential,
219
- )
220
-
221
- # Call the method
222
- result = tool.call(args={}, tool_context=mock_tool_context)
223
-
224
- # Check the result
225
- assert result == {"result": "success"}
226
-
227
- @patch(
228
- "google.adk.tools.openapi_tool.openapi_spec_parser.rest_api_tool.requests.request"
229
- )
230
- def test_call_auth_pending(
231
- self,
232
- mock_request,
233
- sample_endpiont,
234
- sample_operation,
235
- sample_auth_scheme,
236
- sample_auth_credential,
237
- ):
238
-
239
- tool = RestApiTool(
240
- name="test_tool",
241
- description="Test Tool",
242
- endpoint=sample_endpiont,
243
- operation=sample_operation,
244
- auth_scheme=sample_auth_scheme,
245
- auth_credential=sample_auth_credential,
246
- )
247
- with patch(
248
- "google.adk.tools.openapi_tool.openapi_spec_parser.rest_api_tool.ToolAuthHandler.from_tool_context"
249
- ) as mock_from_tool_context:
250
- mock_tool_auth_handler_instance = MagicMock()
251
- mock_tool_auth_handler_instance.prepare_auth_credentials.return_value.state = (
252
- "pending"
253
- )
254
- mock_from_tool_context.return_value = mock_tool_auth_handler_instance
255
-
256
- response = tool.call(args={}, tool_context=None)
257
- assert response == {
258
- "pending": True,
259
- "message": "Needs your authorization to access your data.",
260
- }
261
-
262
- def test_prepare_request_params_query_body(
263
- self, sample_endpiont, sample_auth_credential, sample_auth_scheme
264
- ):
265
- # Create a mock Operation object
266
- mock_operation = Operation(
267
- operationId="test_op",
268
- parameters=[
269
- OpenAPIParameter(**{
270
- "name": "testQueryParam",
271
- "in": "query",
272
- "schema": OpenAPISchema(type="string"),
273
- })
274
- ],
275
- requestBody=RequestBody(
276
- content={
277
- "application/json": MediaType(
278
- schema=OpenAPISchema(
279
- type="object",
280
- properties={
281
- "param1": OpenAPISchema(type="string"),
282
- "param2": OpenAPISchema(type="integer"),
283
- },
284
- )
285
- )
286
- }
287
- ),
288
- )
289
-
290
- tool = RestApiTool(
291
- name="test_tool",
292
- description="test",
293
- endpoint=sample_endpiont,
294
- operation=mock_operation,
295
- auth_credential=sample_auth_credential,
296
- auth_scheme=sample_auth_scheme,
297
- )
298
-
299
- params = [
300
- ApiParameter(
301
- original_name="param1",
302
- py_name="param1",
303
- param_location="body",
304
- param_schema=OpenAPISchema(type="string"),
305
- ),
306
- ApiParameter(
307
- original_name="param2",
308
- py_name="param2",
309
- param_location="body",
310
- param_schema=OpenAPISchema(type="integer"),
311
- ),
312
- ApiParameter(
313
- original_name="testQueryParam",
314
- py_name="test_query_param",
315
- param_location="query",
316
- param_schema=OpenAPISchema(type="string"),
317
- ),
318
- ]
319
- kwargs = {
320
- "param1": "value1",
321
- "param2": 123,
322
- "test_query_param": "query_value",
323
- }
324
-
325
- request_params = tool._prepare_request_params(params, kwargs)
326
- assert request_params["method"] == "get"
327
- assert request_params["url"] == "https://example.com/test"
328
- assert request_params["json"] == {"param1": "value1", "param2": 123}
329
- assert request_params["params"] == {"testQueryParam": "query_value"}
330
-
331
- def test_prepare_request_params_array(
332
- self, sample_endpiont, sample_auth_scheme, sample_auth_credential
333
- ):
334
- mock_operation = Operation(
335
- operationId="test_op",
336
- requestBody=RequestBody(
337
- content={
338
- "application/json": MediaType(
339
- schema=OpenAPISchema(
340
- type="array", items=OpenAPISchema(type="string")
341
- )
342
- )
343
- }
344
- ),
345
- )
346
-
347
- tool = RestApiTool(
348
- name="test_tool",
349
- description="test",
350
- endpoint=sample_endpiont,
351
- operation=mock_operation,
352
- auth_credential=sample_auth_credential,
353
- auth_scheme=sample_auth_scheme,
354
- )
355
- params = [
356
- ApiParameter(
357
- original_name="array", # Match the parameter name
358
- py_name="array",
359
- param_location="body",
360
- param_schema=OpenAPISchema(
361
- type="array", items=OpenAPISchema(type="string")
362
- ),
363
- )
364
- ]
365
- kwargs = {"array": ["item1", "item2"]}
366
-
367
- request_params = tool._prepare_request_params(params, kwargs)
368
-
369
- assert request_params["json"] == ["item1", "item2"]
370
-
371
- def test_prepare_request_params_string(
372
- self, sample_endpiont, sample_auth_credential, sample_auth_scheme
373
- ):
374
- mock_operation = Operation(
375
- operationId="test_op",
376
- requestBody=RequestBody(
377
- content={
378
- "text/plain": MediaType(schema=OpenAPISchema(type="string"))
379
- }
380
- ),
381
- )
382
- tool = RestApiTool(
383
- name="test_tool",
384
- description="Test Tool",
385
- endpoint=sample_endpiont,
386
- operation=mock_operation,
387
- auth_credential=sample_auth_credential,
388
- auth_scheme=sample_auth_scheme,
389
- )
390
- params = [
391
- ApiParameter(
392
- original_name="",
393
- py_name="input_string",
394
- param_location="body",
395
- param_schema=OpenAPISchema(type="string"),
396
- )
397
- ]
398
- kwargs = {"input_string": "test_value"}
399
-
400
- request_params = tool._prepare_request_params(params, kwargs)
401
-
402
- assert request_params["data"] == "test_value"
403
- assert request_params["headers"]["Content-Type"] == "text/plain"
404
-
405
- def test_prepare_request_params_form_data(
406
- self, sample_endpiont, sample_auth_scheme, sample_auth_credential
407
- ):
408
- mock_operation = Operation(
409
- operationId="test_op",
410
- requestBody=RequestBody(
411
- content={
412
- "application/x-www-form-urlencoded": MediaType(
413
- schema=OpenAPISchema(
414
- type="object",
415
- properties={"key1": OpenAPISchema(type="string")},
416
- )
417
- )
418
- }
419
- ),
420
- )
421
- tool = RestApiTool(
422
- name="test_tool",
423
- description="test",
424
- endpoint=sample_endpiont,
425
- operation=mock_operation,
426
- auth_credential=sample_auth_credential,
427
- auth_scheme=sample_auth_scheme,
428
- )
429
- params = [
430
- ApiParameter(
431
- original_name="key1",
432
- py_name="key1",
433
- param_location="body",
434
- param_schema=OpenAPISchema(type="string"),
435
- )
436
- ]
437
- kwargs = {"key1": "value1"}
438
-
439
- request_params = tool._prepare_request_params(params, kwargs)
440
-
441
- assert request_params["data"] == {"key1": "value1"}
442
- assert (
443
- request_params["headers"]["Content-Type"]
444
- == "application/x-www-form-urlencoded"
445
- )
446
-
447
- def test_prepare_request_params_multipart(
448
- self, sample_endpiont, sample_auth_credential, sample_auth_scheme
449
- ):
450
- mock_operation = Operation(
451
- operationId="test_op",
452
- requestBody=RequestBody(
453
- content={
454
- "multipart/form-data": MediaType(
455
- schema=OpenAPISchema(
456
- type="object",
457
- properties={
458
- "file1": OpenAPISchema(
459
- type="string", format="binary"
460
- )
461
- },
462
- )
463
- )
464
- }
465
- ),
466
- )
467
- tool = RestApiTool(
468
- name="test_tool",
469
- description="test",
470
- endpoint=sample_endpiont,
471
- operation=mock_operation,
472
- auth_credential=sample_auth_credential,
473
- auth_scheme=sample_auth_scheme,
474
- )
475
- params = [
476
- ApiParameter(
477
- original_name="file1",
478
- py_name="file1",
479
- param_location="body",
480
- param_schema=OpenAPISchema(type="string", format="binary"),
481
- )
482
- ]
483
- kwargs = {"file1": b"file_content"}
484
-
485
- request_params = tool._prepare_request_params(params, kwargs)
486
-
487
- assert request_params["files"] == {"file1": b"file_content"}
488
- assert request_params["headers"]["Content-Type"] == "multipart/form-data"
489
-
490
- def test_prepare_request_params_octet_stream(
491
- self, sample_endpiont, sample_auth_scheme, sample_auth_credential
492
- ):
493
- mock_operation = Operation(
494
- operationId="test_op",
495
- requestBody=RequestBody(
496
- content={
497
- "application/octet-stream": MediaType(
498
- schema=OpenAPISchema(type="string", format="binary")
499
- )
500
- }
501
- ),
502
- )
503
- tool = RestApiTool(
504
- name="test_tool",
505
- description="test",
506
- endpoint=sample_endpiont,
507
- operation=mock_operation,
508
- auth_credential=sample_auth_credential,
509
- auth_scheme=sample_auth_scheme,
510
- )
511
- params = [
512
- ApiParameter(
513
- original_name="",
514
- py_name="data",
515
- param_location="body",
516
- param_schema=OpenAPISchema(type="string", format="binary"),
517
- )
518
- ]
519
- kwargs = {"data": b"binary_data"}
520
-
521
- request_params = tool._prepare_request_params(params, kwargs)
522
-
523
- assert request_params["data"] == b"binary_data"
524
- assert (
525
- request_params["headers"]["Content-Type"] == "application/octet-stream"
526
- )
527
-
528
- def test_prepare_request_params_path_param(
529
- self, sample_endpiont, sample_auth_credential, sample_auth_scheme
530
- ):
531
- mock_operation = Operation(operationId="test_op")
532
- tool = RestApiTool(
533
- name="test_tool",
534
- description="Test Tool",
535
- endpoint=sample_endpiont,
536
- operation=mock_operation,
537
- auth_credential=sample_auth_credential,
538
- auth_scheme=sample_auth_scheme,
539
- )
540
- params = [
541
- ApiParameter(
542
- original_name="user_id",
543
- py_name="user_id",
544
- param_location="path",
545
- param_schema=OpenAPISchema(type="string"),
546
- )
547
- ]
548
- kwargs = {"user_id": "123"}
549
- endpoint_with_path = OperationEndpoint(
550
- base_url="https://example.com", path="/test/{user_id}", method="get"
551
- )
552
- tool.endpoint = endpoint_with_path
553
-
554
- request_params = tool._prepare_request_params(params, kwargs)
555
-
556
- assert (
557
- request_params["url"] == "https://example.com/test/123"
558
- ) # Path param replaced
559
-
560
- def test_prepare_request_params_header_param(
561
- self,
562
- sample_endpiont,
563
- sample_auth_credential,
564
- sample_auth_scheme,
565
- sample_operation,
566
- ):
567
- tool = RestApiTool(
568
- name="test_tool",
569
- description="Test Tool",
570
- endpoint=sample_endpiont,
571
- operation=sample_operation,
572
- auth_credential=sample_auth_credential,
573
- auth_scheme=sample_auth_scheme,
574
- )
575
- params = [
576
- ApiParameter(
577
- original_name="X-Custom-Header",
578
- py_name="x_custom_header",
579
- param_location="header",
580
- param_schema=OpenAPISchema(type="string"),
581
- )
582
- ]
583
- kwargs = {"x_custom_header": "header_value"}
584
-
585
- request_params = tool._prepare_request_params(params, kwargs)
586
-
587
- assert request_params["headers"]["X-Custom-Header"] == "header_value"
588
-
589
- def test_prepare_request_params_cookie_param(
590
- self,
591
- sample_endpiont,
592
- sample_auth_credential,
593
- sample_auth_scheme,
594
- sample_operation,
595
- ):
596
- tool = RestApiTool(
597
- name="test_tool",
598
- description="Test Tool",
599
- endpoint=sample_endpiont,
600
- operation=sample_operation,
601
- auth_credential=sample_auth_credential,
602
- auth_scheme=sample_auth_scheme,
603
- )
604
- params = [
605
- ApiParameter(
606
- original_name="session_id",
607
- py_name="session_id",
608
- param_location="cookie",
609
- param_schema=OpenAPISchema(type="string"),
610
- )
611
- ]
612
- kwargs = {"session_id": "cookie_value"}
613
-
614
- request_params = tool._prepare_request_params(params, kwargs)
615
-
616
- assert request_params["cookies"]["session_id"] == "cookie_value"
617
-
618
- def test_prepare_request_params_multiple_mime_types(
619
- self, sample_endpiont, sample_auth_credential, sample_auth_scheme
620
- ):
621
- # Test what happens when multiple mime types are specified. It should take
622
- # the first one.
623
- mock_operation = Operation(
624
- operationId="test_op",
625
- requestBody=RequestBody(
626
- content={
627
- "application/json": MediaType(
628
- schema=OpenAPISchema(type="string")
629
- ),
630
- "text/plain": MediaType(schema=OpenAPISchema(type="string")),
631
- }
632
- ),
633
- )
634
- tool = RestApiTool(
635
- name="test_tool",
636
- description="Test Tool",
637
- endpoint=sample_endpiont,
638
- operation=mock_operation,
639
- auth_credential=sample_auth_credential,
640
- auth_scheme=sample_auth_scheme,
641
- )
642
- params = [
643
- ApiParameter(
644
- original_name="",
645
- py_name="input",
646
- param_location="body",
647
- param_schema=OpenAPISchema(type="string"),
648
- )
649
- ]
650
- kwargs = {"input": "some_value"}
651
-
652
- request_params = tool._prepare_request_params(params, kwargs)
653
-
654
- assert request_params["headers"]["Content-Type"] == "application/json"
655
-
656
- def test_prepare_request_params_unknown_parameter(
657
- self,
658
- sample_endpiont,
659
- sample_auth_credential,
660
- sample_auth_scheme,
661
- sample_operation,
662
- ):
663
- tool = RestApiTool(
664
- name="test_tool",
665
- description="Test Tool",
666
- endpoint=sample_endpiont,
667
- operation=sample_operation,
668
- auth_credential=sample_auth_credential,
669
- auth_scheme=sample_auth_scheme,
670
- )
671
- params = [
672
- ApiParameter(
673
- original_name="known_param",
674
- py_name="known_param",
675
- param_location="query",
676
- param_schema=OpenAPISchema(type="string"),
677
- )
678
- ]
679
- kwargs = {"known_param": "value", "unknown_param": "unknown"}
680
-
681
- request_params = tool._prepare_request_params(params, kwargs)
682
-
683
- # Make sure unknown parameters are ignored and do not raise errors.
684
- assert "unknown_param" not in request_params["params"]
685
-
686
- def test_prepare_request_params_base_url_handling(
687
- self, sample_auth_credential, sample_auth_scheme, sample_operation
688
- ):
689
- # No base_url provided, should use path as is
690
- tool_no_base = RestApiTool(
691
- name="test_tool_no_base",
692
- description="Test Tool",
693
- endpoint=OperationEndpoint(base_url="", path="/no_base", method="get"),
694
- operation=sample_operation,
695
- auth_credential=sample_auth_credential,
696
- auth_scheme=sample_auth_scheme,
697
- )
698
- params = []
699
- kwargs = {}
700
-
701
- request_params_no_base = tool_no_base._prepare_request_params(
702
- params, kwargs
703
- )
704
- assert request_params_no_base["url"] == "/no_base"
705
-
706
- tool_trailing_slash = RestApiTool(
707
- name="test_tool",
708
- description="Test Tool",
709
- endpoint=OperationEndpoint(
710
- base_url="https://example.com/", path="/trailing", method="get"
711
- ),
712
- operation=sample_operation,
713
- auth_credential=sample_auth_credential,
714
- auth_scheme=sample_auth_scheme,
715
- )
716
-
717
- request_params_trailing = tool_trailing_slash._prepare_request_params(
718
- params, kwargs
719
- )
720
- assert request_params_trailing["url"] == "https://example.com/trailing"
721
-
722
- def test_prepare_request_params_no_unrecognized_query_parameter(
723
- self,
724
- sample_endpiont,
725
- sample_auth_credential,
726
- sample_auth_scheme,
727
- sample_operation,
728
- ):
729
- tool = RestApiTool(
730
- name="test_tool",
731
- description="Test Tool",
732
- endpoint=sample_endpiont,
733
- operation=sample_operation,
734
- auth_credential=sample_auth_credential,
735
- auth_scheme=sample_auth_scheme,
736
- )
737
- params = [
738
- ApiParameter(
739
- original_name="unrecognized_param",
740
- py_name="unrecognized_param",
741
- param_location="query",
742
- param_schema=OpenAPISchema(type="string"),
743
- )
744
- ]
745
- kwargs = {"unrecognized_param": None} # Explicitly passing None
746
- request_params = tool._prepare_request_params(params, kwargs)
747
-
748
- # Query param not in sample_operation. It should be ignored.
749
- assert "unrecognized_param" not in request_params["params"]
750
-
751
- def test_prepare_request_params_no_credential(
752
- self,
753
- sample_endpiont,
754
- sample_operation,
755
- ):
756
- tool = RestApiTool(
757
- name="test_tool",
758
- description="Test Tool",
759
- endpoint=sample_endpiont,
760
- operation=sample_operation,
761
- auth_credential=None,
762
- auth_scheme=None,
763
- )
764
- params = [
765
- ApiParameter(
766
- original_name="param_name",
767
- py_name="param_name",
768
- param_location="query",
769
- param_schema=OpenAPISchema(type="string"),
770
- )
771
- ]
772
- kwargs = {"param_name": "aaa", "empty_param": ""}
773
-
774
- request_params = tool._prepare_request_params(params, kwargs)
775
-
776
- assert "param_name" in request_params["params"]
777
- assert "empty_param" not in request_params["params"]
778
-
779
-
780
- class TestToGeminiSchema:
781
-
782
- def test_to_gemini_schema_none(self):
783
- assert to_gemini_schema(None) is None
784
-
785
- def test_to_gemini_schema_not_dict(self):
786
- with pytest.raises(TypeError, match="openapi_schema must be a dictionary"):
787
- to_gemini_schema("not a dict")
788
-
789
- def test_to_gemini_schema_empty_dict(self):
790
- result = to_gemini_schema({})
791
- assert isinstance(result, Schema)
792
- assert result.type == Type.OBJECT
793
- assert result.properties == {"dummy_DO_NOT_GENERATE": Schema(type="string")}
794
-
795
- def test_to_gemini_schema_dict_with_only_object_type(self):
796
- result = to_gemini_schema({"type": "object"})
797
- assert isinstance(result, Schema)
798
- assert result.type == Type.OBJECT
799
- assert result.properties == {"dummy_DO_NOT_GENERATE": Schema(type="string")}
800
-
801
- def test_to_gemini_schema_basic_types(self):
802
- openapi_schema = {
803
- "type": "object",
804
- "properties": {
805
- "name": {"type": "string"},
806
- "age": {"type": "integer"},
807
- "is_active": {"type": "boolean"},
808
- },
809
- }
810
- gemini_schema = to_gemini_schema(openapi_schema)
811
- assert isinstance(gemini_schema, Schema)
812
- assert gemini_schema.type == Type.OBJECT
813
- assert gemini_schema.properties["name"].type == Type.STRING
814
- assert gemini_schema.properties["age"].type == Type.INTEGER
815
- assert gemini_schema.properties["is_active"].type == Type.BOOLEAN
816
-
817
- def test_to_gemini_schema_nested_objects(self):
818
- openapi_schema = {
819
- "type": "object",
820
- "properties": {
821
- "address": {
822
- "type": "object",
823
- "properties": {
824
- "street": {"type": "string"},
825
- "city": {"type": "string"},
826
- },
827
- }
828
- },
829
- }
830
- gemini_schema = to_gemini_schema(openapi_schema)
831
- assert gemini_schema.properties["address"].type == Type.OBJECT
832
- assert (
833
- gemini_schema.properties["address"].properties["street"].type
834
- == Type.STRING
835
- )
836
- assert (
837
- gemini_schema.properties["address"].properties["city"].type
838
- == Type.STRING
839
- )
840
-
841
- def test_to_gemini_schema_array(self):
842
- openapi_schema = {
843
- "type": "array",
844
- "items": {"type": "string"},
845
- }
846
- gemini_schema = to_gemini_schema(openapi_schema)
847
- assert gemini_schema.type == Type.ARRAY
848
- assert gemini_schema.items.type == Type.STRING
849
-
850
- def test_to_gemini_schema_nested_array(self):
851
- openapi_schema = {
852
- "type": "array",
853
- "items": {
854
- "type": "object",
855
- "properties": {"name": {"type": "string"}},
856
- },
857
- }
858
- gemini_schema = to_gemini_schema(openapi_schema)
859
- assert gemini_schema.items.properties["name"].type == Type.STRING
860
-
861
- def test_to_gemini_schema_any_of(self):
862
- openapi_schema = {
863
- "anyOf": [{"type": "string"}, {"type": "integer"}],
864
- }
865
- gemini_schema = to_gemini_schema(openapi_schema)
866
- assert len(gemini_schema.any_of) == 2
867
- assert gemini_schema.any_of[0].type == Type.STRING
868
- assert gemini_schema.any_of[1].type == Type.INTEGER
869
-
870
- def test_to_gemini_schema_general_list(self):
871
- openapi_schema = {
872
- "type": "array",
873
- "properties": {
874
- "list_field": {"type": "array", "items": {"type": "string"}},
875
- },
876
- }
877
- gemini_schema = to_gemini_schema(openapi_schema)
878
- assert gemini_schema.properties["list_field"].type == Type.ARRAY
879
- assert gemini_schema.properties["list_field"].items.type == Type.STRING
880
-
881
- def test_to_gemini_schema_enum(self):
882
- openapi_schema = {"type": "string", "enum": ["a", "b", "c"]}
883
- gemini_schema = to_gemini_schema(openapi_schema)
884
- assert gemini_schema.enum == ["a", "b", "c"]
885
-
886
- def test_to_gemini_schema_required(self):
887
- openapi_schema = {
888
- "type": "object",
889
- "required": ["name"],
890
- "properties": {"name": {"type": "string"}},
891
- }
892
- gemini_schema = to_gemini_schema(openapi_schema)
893
- assert gemini_schema.required == ["name"]
894
-
895
- def test_to_gemini_schema_nested_dict(self):
896
- openapi_schema = {
897
- "type": "object",
898
- "properties": {"metadata": {"key1": "value1", "key2": 123}},
899
- }
900
- gemini_schema = to_gemini_schema(openapi_schema)
901
- # Since metadata is not properties nor item, it will call to_gemini_schema recursively.
902
- assert isinstance(gemini_schema.properties["metadata"], Schema)
903
- assert (
904
- gemini_schema.properties["metadata"].type == Type.OBJECT
905
- ) # add object type by default
906
- assert gemini_schema.properties["metadata"].properties == {
907
- "dummy_DO_NOT_GENERATE": Schema(type="string")
908
- }
909
-
910
- def test_to_gemini_schema_ignore_title_default_format(self):
911
- openapi_schema = {
912
- "type": "string",
913
- "title": "Test Title",
914
- "default": "default_value",
915
- "format": "date",
916
- }
917
- gemini_schema = to_gemini_schema(openapi_schema)
918
-
919
- assert gemini_schema.title is None
920
- assert gemini_schema.default is None
921
- assert gemini_schema.format is None
922
-
923
- def test_to_gemini_schema_property_ordering(self):
924
- openapi_schema = {
925
- "type": "object",
926
- "propertyOrdering": ["name", "age"],
927
- "properties": {
928
- "name": {"type": "string"},
929
- "age": {"type": "integer"},
930
- },
931
- }
932
-
933
- gemini_schema = to_gemini_schema(openapi_schema)
934
- assert gemini_schema.property_ordering == ["name", "age"]
935
-
936
- def test_to_gemini_schema_converts_property_dict(self):
937
- openapi_schema = {
938
- "properties": {
939
- "name": {"type": "string", "description": "The property key"},
940
- "value": {"type": "string", "description": "The property value"},
941
- },
942
- "type": "object",
943
- "description": "A single property entry in the Properties message.",
944
- }
945
- gemini_schema = to_gemini_schema(openapi_schema)
946
- assert gemini_schema.type == Type.OBJECT
947
- assert gemini_schema.properties["name"].type == Type.STRING
948
- assert gemini_schema.properties["value"].type == Type.STRING
949
-
950
- def test_to_gemini_schema_remove_unrecognized_fields(self):
951
- openapi_schema = {
952
- "type": "string",
953
- "description": "A single date string.",
954
- "format": "date",
955
- }
956
- gemini_schema = to_gemini_schema(openapi_schema)
957
- assert gemini_schema.type == Type.STRING
958
- assert not gemini_schema.format
959
-
960
-
961
- def test_snake_to_lower_camel():
962
- assert snake_to_lower_camel("single") == "single"
963
- assert snake_to_lower_camel("two_words") == "twoWords"
964
- assert snake_to_lower_camel("three_word_example") == "threeWordExample"
965
- assert not snake_to_lower_camel("")
966
- assert snake_to_lower_camel("alreadyCamelCase") == "alreadyCamelCase"