Skip to content

Commit 38eaac5

Browse files
committed
Make OpenAPI tool async
1 parent 1979dcf commit 38eaac5

File tree

5 files changed

+26
-17
lines changed

5 files changed

+26
-17
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ dependencies = [
4040
"google-cloud-storage>=2.18.0, <3.0.0", # For GCS Artifact service
4141
"google-genai>=1.21.1, <2.0.0", # Google GenAI SDK
4242
"graphviz>=0.20.2, <1.0.0", # Graphviz for graph rendering
43+
"httpx>=0.27.0, <1.0.0", # HTTP client library
4344
"mcp>=1.8.0, <2.0.0;python_version>='3.10'", # For MCP Toolset
4445
"opentelemetry-api>=1.31.0, <2.0.0", # OpenTelemetry
4546
"opentelemetry-exporter-gcp-trace>=1.9.0, <2.0.0",

src/google/adk/tools/openapi_tool/auth/auth_helpers.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626
from fastapi.openapi.models import OAuth2
2727
from fastapi.openapi.models import OpenIdConnect
2828
from fastapi.openapi.models import Schema
29+
import httpx
2930
from pydantic import BaseModel
3031
from pydantic import ValidationError
31-
import requests
3232

3333
from ....auth.auth_credential import AuthCredential
3434
from ....auth.auth_credential import AuthCredentialTypes
@@ -287,14 +287,14 @@ def openid_url_to_scheme_credential(
287287
Raises:
288288
ValueError: If the OpenID URL is invalid, fetching fails, or required
289289
fields are missing.
290-
requests.exceptions.RequestException: If there's an error during the
290+
httpx.HTTPStatusError or httpx.RequestError: If there's an error during the
291291
HTTP request.
292292
"""
293293
try:
294-
response = requests.get(openid_url, timeout=10)
294+
response = httpx.get(openid_url, timeout=10)
295295
response.raise_for_status()
296296
config_dict = response.json()
297-
except requests.exceptions.RequestException as e:
297+
except httpx.RequestError as e:
298298
raise ValueError(
299299
f"Failed to fetch OpenID configuration from {openid_url}: {e}"
300300
) from e

src/google/adk/tools/openapi_tool/openapi_spec_parser/rest_api_tool.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
from fastapi.openapi.models import Operation
2626
from google.genai.types import FunctionDeclaration
27-
import requests
27+
import httpx
2828
from typing_extensions import override
2929

3030
from ....auth.auth_credential import AuthCredential
@@ -240,7 +240,7 @@ def _prepare_request_params(
240240
241241
Returns:
242242
A dictionary containing the request parameters for the API call. This
243-
initializes a requests.request() call.
243+
initializes an httpx.AsyncClient.request() call.
244244
245245
Example:
246246
self._prepare_request_params({"input_id": "test-id"})
@@ -395,13 +395,13 @@ async def call(
395395

396396
# Got all parameters. Call the API.
397397
request_params = self._prepare_request_params(api_params, api_args)
398-
response = requests.request(**request_params)
398+
response = await _request(**request_params)
399399

400400
# Parse API response
401401
try:
402-
response.raise_for_status() # Raise HTTPError for bad responses
402+
response.raise_for_status() # Raise HTTPStatusError for bad responses
403403
return response.json() # Try to decode JSON
404-
except requests.exceptions.HTTPError:
404+
except httpx.HTTPStatusError:
405405
error_details = response.content.decode("utf-8")
406406
return {
407407
"error": (
@@ -427,3 +427,8 @@ def __repr__(self):
427427
f' auth_scheme="{self.auth_scheme}",'
428428
f' auth_credential="{self.auth_credential}")'
429429
)
430+
431+
432+
async def _request(**request_params) -> httpx.Response:
433+
async with httpx.AsyncClient() as client:
434+
return await client.request(**request_params)

tests/unittests/tools/openapi_tool/auth/test_auth_helper.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@
3636
from google.adk.tools.openapi_tool.auth.auth_helpers import service_account_dict_to_scheme_credential
3737
from google.adk.tools.openapi_tool.auth.auth_helpers import service_account_scheme_credential
3838
from google.adk.tools.openapi_tool.auth.auth_helpers import token_to_scheme_credential
39+
import httpx
3940
import pytest
40-
import requests
4141

4242

4343
def test_token_to_scheme_credential_api_key_header():
@@ -272,7 +272,7 @@ def test_openid_dict_to_scheme_credential_missing_credential_fields():
272272
openid_dict_to_scheme_credential(config_dict, scopes, credential_dict)
273273

274274

275-
@patch("requests.get")
275+
@patch("httpx.get")
276276
def test_openid_url_to_scheme_credential(mock_get):
277277
mock_response = {
278278
"authorization_endpoint": "auth_url",
@@ -303,7 +303,7 @@ def test_openid_url_to_scheme_credential(mock_get):
303303
mock_get.assert_called_once_with("openid_url", timeout=10)
304304

305305

306-
@patch("requests.get")
306+
@patch("httpx.get")
307307
def test_openid_url_to_scheme_credential_no_openid_url(mock_get):
308308
mock_response = {
309309
"authorization_endpoint": "auth_url",
@@ -326,9 +326,11 @@ def test_openid_url_to_scheme_credential_no_openid_url(mock_get):
326326
assert scheme.openIdConnectUrl == "openid_url"
327327

328328

329-
@patch("requests.get")
329+
@patch("httpx.get")
330330
def test_openid_url_to_scheme_credential_request_exception(mock_get):
331-
mock_get.side_effect = requests.exceptions.RequestException("Test Error")
331+
mock_get.side_effect = httpx.HTTPStatusError(
332+
"Test Error", request=None, response=None
333+
)
332334
credential_dict = {"client_id": "client_id", "client_secret": "client_secret"}
333335

334336
with pytest.raises(
@@ -337,7 +339,7 @@ def test_openid_url_to_scheme_credential_request_exception(mock_get):
337339
openid_url_to_scheme_credential("openid_url", [], credential_dict)
338340

339341

340-
@patch("requests.get")
342+
@patch("httpx.get")
341343
def test_openid_url_to_scheme_credential_invalid_json(mock_get):
342344
mock_get.return_value.json.side_effect = ValueError("Invalid JSON")
343345
mock_get.return_value.raise_for_status.return_value = None

tests/unittests/tools/openapi_tool/openapi_spec_parser/test_rest_api_tool.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
from google.adk.tools.tool_context import ToolContext
3434
from google.genai.types import FunctionDeclaration
3535
from google.genai.types import Schema
36+
import httpx
3637
import pytest
3738

3839

@@ -193,7 +194,7 @@ def test_get_declaration(
193194
assert isinstance(declaration.parameters, Schema)
194195

195196
@patch(
196-
"google.adk.tools.openapi_tool.openapi_spec_parser.rest_api_tool.requests.request"
197+
"google.adk.tools.openapi_tool.openapi_spec_parser.rest_api_tool._request"
197198
)
198199
@pytest.mark.asyncio
199200
async def test_call_success(
@@ -225,7 +226,7 @@ async def test_call_success(
225226
assert result == {"result": "success"}
226227

227228
@patch(
228-
"google.adk.tools.openapi_tool.openapi_spec_parser.rest_api_tool.requests.request"
229+
"google.adk.tools.openapi_tool.openapi_spec_parser.rest_api_tool._request"
229230
)
230231
@pytest.mark.asyncio
231232
async def test_call_auth_pending(

0 commit comments

Comments
 (0)