- Notifications
You must be signed in to change notification settings - Fork32
test(toolbox-adk): Implement parity integration tests#458
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Open
anubhav756 wants to merge1 commit intofeature/tool_wrapperChoose a base branch fromfeature/e2e_testing
base:feature/tool_wrapper
Could not load branches
Branch not found:{{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline, and old review comments may become outdated.
+791 −7
Open
Changes fromall commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Jump to file
Failed to load files.
Loading
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| -e ../toolbox-core | ||
| google-auth==2.43.0 | ||
| google-auth-oauthlib==1.2.1 | ||
| google-adk==1.20.0 | ||
| typing-extensions==4.12.2 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -12,6 +12,7 @@ | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| import logging | ||
| from typing import Any, Awaitable, Callable, Dict, Optional, cast | ||
| import toolbox_core | ||
| @@ -146,6 +147,12 @@ async def run_async( | ||
| ctx.error = e | ||
| if "credential" in str(e).lower() or isinstance(e, ValueError): | ||
| raise e | ||
| logging.warning( | ||
| f"Unexpected error in get_auth_response during 3LO retrieval: {e}. " | ||
Contributor There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. nit:3LO will be unclear for most users | ||
| "Falling back to request_credential.", | ||
| exc_info=True | ||
| ) | ||
| # Fallback to request logic | ||
| ctx_any = cast(Any, tool_context) | ||
| ctx_any.request_credential(auth_config_adk) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,169 @@ | ||
| # Copyright 2025 Google LLC | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
| """Contains pytest fixtures that are accessible from all | ||
| files present in the same directory.""" | ||
| from __future__ import annotations | ||
| import os | ||
| import platform | ||
| import subprocess | ||
| import tempfile | ||
| import time | ||
| from typing import Generator | ||
| import google | ||
| import pytest_asyncio | ||
| from google.auth import compute_engine | ||
| from google.cloud import secretmanager, storage | ||
| #### Define Utility Functions | ||
| def get_env_var(key: str) -> str: | ||
| """Gets environment variables.""" | ||
| value = os.environ.get(key) | ||
| if value is None: | ||
| raise ValueError(f"Must set env var {key}") | ||
| return value | ||
| def access_secret_version( | ||
| project_id: str, secret_id: str, version_id: str = "latest" | ||
| ) -> str: | ||
| """Accesses the payload of a given secret version from Secret Manager.""" | ||
| client = secretmanager.SecretManagerServiceClient() | ||
| name = f"projects/{project_id}/secrets/{secret_id}/versions/{version_id}" | ||
| response = client.access_secret_version(request={"name": name}) | ||
| return response.payload.data.decode("UTF-8") | ||
| def create_tmpfile(content: str) -> str: | ||
| """Creates a temporary file with the given content.""" | ||
| with tempfile.NamedTemporaryFile(delete=False, mode="w") as tmpfile: | ||
| tmpfile.write(content) | ||
| return tmpfile.name | ||
| def download_blob( | ||
| bucket_name: str, source_blob_name: str, destination_file_name: str | ||
| ) -> None: | ||
| """Downloads a blob from a GCS bucket.""" | ||
| storage_client = storage.Client() | ||
| bucket = storage_client.bucket(bucket_name) | ||
| blob = bucket.blob(source_blob_name) | ||
| blob.download_to_filename(destination_file_name) | ||
| print(f"Blob {source_blob_name} downloaded to {destination_file_name}.") | ||
| def get_toolbox_binary_url(toolbox_version: str) -> str: | ||
| """Constructs the GCS path to the toolbox binary.""" | ||
| os_system = platform.system().lower() | ||
| arch = ( | ||
| "arm64" if os_system == "darwin" and platform.machine() == "arm64" else "amd64" | ||
| ) | ||
| return f"v{toolbox_version}/{os_system}/{arch}/toolbox" | ||
| def get_auth_token(client_id: str) -> str: | ||
| """Retrieves an authentication token""" | ||
| request = google.auth.transport.requests.Request() | ||
| credentials = compute_engine.IDTokenCredentials( | ||
| request=request, | ||
| target_audience=client_id, | ||
| use_metadata_identity_endpoint=True, | ||
| ) | ||
| if not credentials.valid: | ||
| credentials.refresh(request) | ||
| return credentials.token | ||
| #### Define Fixtures | ||
| @pytest_asyncio.fixture(scope="session") | ||
| def project_id() -> str: | ||
| return get_env_var("GOOGLE_CLOUD_PROJECT") | ||
| @pytest_asyncio.fixture(scope="session") | ||
| def toolbox_version() -> str: | ||
| return get_env_var("TOOLBOX_VERSION") | ||
| @pytest_asyncio.fixture(scope="session") | ||
| def tools_file_path(project_id: str) -> Generator[str]: | ||
| """Provides a temporary file path containing the tools manifest.""" | ||
| tools_manifest = access_secret_version( | ||
| project_id=project_id, | ||
| secret_id="sdk_testing_tools", | ||
| version_id=os.environ.get("TOOLBOX_MANIFEST_VERSION", "latest"), | ||
| ) | ||
| tools_file_path = create_tmpfile(tools_manifest) | ||
| yield tools_file_path | ||
| os.remove(tools_file_path) | ||
| @pytest_asyncio.fixture(scope="session") | ||
| def auth_token1(project_id: str) -> str: | ||
| client_id = access_secret_version( | ||
| project_id=project_id, secret_id="sdk_testing_client1" | ||
| ) | ||
| return get_auth_token(client_id) | ||
| @pytest_asyncio.fixture(scope="session") | ||
| def auth_token2(project_id: str) -> str: | ||
| client_id = access_secret_version( | ||
| project_id=project_id, secret_id="sdk_testing_client2" | ||
| ) | ||
| return get_auth_token(client_id) | ||
| @pytest_asyncio.fixture(scope="session") | ||
| def toolbox_server(toolbox_version: str, tools_file_path: str) -> Generator[None]: | ||
| """Starts the toolbox server as a subprocess.""" | ||
| print("Downloading toolbox binary from gcs bucket...") | ||
| source_blob_name = get_toolbox_binary_url(toolbox_version) | ||
| download_blob("genai-toolbox", source_blob_name, "toolbox") | ||
| print("Toolbox binary downloaded successfully.") | ||
| try: | ||
| print("Opening toolbox server process...") | ||
| # Make toolbox executable | ||
| os.chmod("toolbox", 0o700) | ||
| # Run toolbox binary | ||
| toolbox_server = subprocess.Popen( | ||
| ["./toolbox", "--tools-file", tools_file_path] | ||
| ) | ||
| # Wait for server to start | ||
| # Retry logic with a timeout | ||
| for _ in range(5): # retries | ||
| time.sleep(2) | ||
| print("Checking if toolbox is successfully started...") | ||
| if toolbox_server.poll() is None: | ||
| print("Toolbox server started successfully.") | ||
| break | ||
| else: | ||
| raise RuntimeError("Toolbox server failed to start after 5 retries.") | ||
| except subprocess.CalledProcessError as e: | ||
| print(e.stderr.decode("utf-8")) | ||
| print(e.stdout.decode("utf-8")) | ||
| raise RuntimeError(f"{e}\n\n{e.stderr.decode('utf-8')}") from e | ||
| yield | ||
| # Clean up toolbox server | ||
| toolbox_server.terminate() | ||
| toolbox_server.wait(timeout=5) |
Oops, something went wrong.
Uh oh!
There was an error while loading.Please reload this page.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.