Skip to content

Commit 5346a08

Browse files
vertex-sdk-botcopybara-github
authored andcommitted
feat: GenAI SDK client - Support agent engine sandbox http request in genai sdk
PiperOrigin-RevId: 816865842
1 parent d4e211d commit 5346a08

File tree

2 files changed

+113
-0
lines changed

2 files changed

+113
-0
lines changed
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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+
# pylint: disable=protected-access,bad-continuation,missing-function-docstring
16+
17+
from tests.unit.vertexai.genai.replays import pytest_helper
18+
19+
20+
def test_send_command_sandbox(client):
21+
# agent_engine = client.agent_engines.create()
22+
# assert isinstance(agent_engine, types.AgentEngine)
23+
# assert isinstance(agent_engine.api_resource, types.ReasoningEngine)
24+
# agent_engine_name = "projects/254005681254/locations/us-central1/reasoningEngines/2112984271655272448"
25+
# operation = client.agent_engines.sandboxes.create(
26+
# # name=agent_engine.api_resource.name,
27+
# name=agent_engine_name,
28+
# spec={
29+
# "code_execution_environment": {
30+
# "machineConfig": "MACHINE_CONFIG_VCPU4_RAM4GIB"
31+
# }
32+
# },
33+
# config=types.CreateAgentEngineSandboxConfig(display_name="test_sandbox"),
34+
# )
35+
# assert isinstance(operation, types.AgentEngineSandboxOperation)
36+
37+
client._api_client.project = None
38+
client._api_client.location = None
39+
client._api_client.api_version = None
40+
token = client.agent_engines.sandboxes.generate_access_token(
41+
service_account_email="sign-verify-jwt@mariner-proxy.iam.gserviceaccount.com",
42+
sandbox_id="tenghuil-manual-sandbox-new",
43+
)
44+
response = client.agent_engines.sandboxes.send_command(
45+
http_method="GET",
46+
path="/",
47+
query_params={},
48+
access_token=token,
49+
headers={},
50+
request_dict={},
51+
sandbox_environment=None,
52+
)
53+
# assert token == "xxx"
54+
assert response.body == "Hello World"
55+
# assert response.outputs[0].mime_type == "application/json"
56+
57+
58+
pytestmark = pytest_helper.setup(
59+
file=__file__,
60+
globals_for_file=globals(),
61+
test_method="agent_engines.sandboxes.send_command",
62+
)

vertexai/_genai/sandboxes.py

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,16 @@
1919
import json
2020
import logging
2121
import mimetypes
22+
import random
23+
import time
2224
from typing import Any, Iterator, Optional, Union
2325
from urllib.parse import urlencode
2426

27+
from google import genai
28+
from google.cloud import iam_credentials_v1
2529
from google.genai import _api_module
2630
from google.genai import _common
31+
from google.genai import types as genai_types
2732
from google.genai._common import get_value_by_path as getv
2833
from google.genai._common import set_value_by_path as setv
2934
from google.genai.pagers import Pager
@@ -704,6 +709,52 @@ def delete(
704709
"""
705710
return self._delete(name=name, config=config)
706711

712+
def generate_access_token(
713+
self,
714+
service_account_email: str,
715+
sandbox_id: str,
716+
port: str = "8080",
717+
timeout: int = 3600,
718+
) -> str:
719+
"""Signs a JWT with a Google Cloud service account."""
720+
client = iam_credentials_v1.IAMCredentialsClient()
721+
name = f"projects/-/serviceAccounts/{service_account_email}"
722+
custom_claims = {"port": port, "sandbox_id": sandbox_id}
723+
payload = {
724+
"iat": int(time.time()),
725+
"exp": int(time.time()) + timeout,
726+
"iss": service_account_email,
727+
"nonce": random.randint(1, 1000000000),
728+
"aud": "vmaas-proxy-api", # default audience for sandbox proxy
729+
**custom_claims,
730+
}
731+
request = iam_credentials_v1.SignJwtRequest(
732+
name=name,
733+
payload=json.dumps(payload),
734+
)
735+
response = client.sign_jwt(request=request)
736+
return response.signed_jwt
737+
738+
def send_command(
739+
self,
740+
http_method: str,
741+
path: str,
742+
query_params: Any,
743+
access_token: str,
744+
headers: dict[str, str],
745+
request_dict: dict[str, object],
746+
sandbox_environment: Optional[types.SandboxEnvironment] = None,
747+
) -> str | None:
748+
"""Sends a command to the sandbox."""
749+
# TODO(tenghuil): Get connection info from sandbox environment when it's ready.
750+
endpoint = "https://test-us-central1.autopush-sandbox.vertexai.goog/" + path
751+
752+
headers = {"Authorization": f"Bearer {access_token}"}
753+
http_options = genai_types.HttpOptions(headers=headers, base_url=endpoint)
754+
http_client = genai.Client(vertexai=True, http_options=http_options)
755+
response = http_client._api_client.request(http_method, path, request_dict)
756+
return response
757+
707758

708759
class AsyncSandboxes(_api_module.BaseModule):
709760

0 commit comments

Comments
 (0)