Skip to content

Commit 3418640

Browse files
vertex-sdk-botcopybara-github
authored andcommitted
feat: GenAI SDK client - Update input handling inside code execution sandbox
FUTURE_COPYBARA_INTEGRATE_REVIEW=#5883 from googleapis:release-please--branches--main 048acfd PiperOrigin-RevId: 816441862
1 parent ffe9cde commit 3418640

File tree

2 files changed

+87
-9
lines changed

2 files changed

+87
-9
lines changed

tests/unit/vertexai/genai/replays/test_execute_code_agent_engine_sandbox.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,30 @@ def test_execute_code_sandbox(client):
3333
config=types.CreateAgentEngineSandboxConfig(display_name="test_sandbox"),
3434
)
3535
assert isinstance(operation, types.AgentEngineSandboxOperation)
36+
37+
code = """
38+
with open("test.txt", "r") as input:
39+
with open("output.txt", "w") as output_txt:
40+
for line in input:
41+
output_txt.write(line)
42+
"""
3643
input_data = {
37-
"language": "python",
38-
"code": 'with open("hello.txt","w") as file:\n file.write("Hello, world!")',
44+
"code": code,
45+
"files": [
46+
{
47+
"name": "test.txt",
48+
"mimeType": "text/plain",
49+
"content": b"Hello, world!",
50+
}
51+
],
3952
}
4053
response = client.agent_engines.sandboxes.execute_code(
4154
name=operation.response.name,
4255
input_data=input_data,
4356
)
4457
assert response.outputs[0].mime_type == "application/json"
58+
assert response.outputs[1].data == b"Hello, world!"
59+
assert response.outputs[1].metadata.attributes.get("file_name") == b"output.txt"
4560

4661

4762
pytestmark = pytest_helper.setup(

vertexai/_genai/sandboxes.py

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import functools
2020
import json
2121
import logging
22+
import mimetypes
2223
from typing import Any, Iterator, Optional, Union
2324
from urllib.parse import urlencode
2425

@@ -528,6 +529,22 @@ def _get_sandbox_operation(
528529
self._api_client._verify_response(return_value)
529530
return return_value
530531

532+
_NEEDED_BASE64_ENCODING_MIME_TYPES = [
533+
"image/jpeg",
534+
"image/png",
535+
"image/gif",
536+
"image/bmp",
537+
"image/webp",
538+
"audio/mpeg",
539+
"audio/wav",
540+
"audio/aac",
541+
"audio/ogg",
542+
"video/mp4",
543+
"video/webm",
544+
"video/ogg",
545+
"video/mpeg",
546+
]
547+
531548
def create(
532549
self,
533550
*,
@@ -618,20 +635,66 @@ def execute_code(
618635
Returns:
619636
ExecuteSandboxEnvironmentResponse: The response from executing the code.
620637
"""
621-
json_string = json.dumps(input_data)
622-
623-
base64_bytes = base64.b64encode(json_string.encode("utf-8"))
624-
base64_string = base64_bytes.decode("utf-8")
638+
input_chunks = []
639+
640+
if input_data.get("code") is not None:
641+
code = input_data.get("code", "")
642+
json_code = json.dumps({"code": code}).encode("utf-8")
643+
input_chunks.append(
644+
types.Chunk(
645+
mime_type="application/json",
646+
data=json_code,
647+
)
648+
)
625649

626-
# Only single JSON input is supported for now.
627-
inputs = [{"mime_type": "application/json", "data": base64_string}]
650+
for file in input_data.get("files", []):
651+
file_name = file.get("name", "")
652+
mime_type = file.get("mimeType", "")
653+
if mime_type is None:
654+
mime_type, _ = mimetypes.guess_type(file_name)
655+
if mime_type in self._NEEDED_BASE64_ENCODING_MIME_TYPES:
656+
base64_bytes = base64.b64encode(file.get("content", b""))
657+
content = base64_bytes.decode("utf-8")
658+
else:
659+
content = file.get("content", b"")
660+
input_chunks.append(
661+
types.Chunk(
662+
mime_type=mime_type,
663+
data=content,
664+
metadata={"attributes": {"file_name": file_name.encode("utf-8")}},
665+
)
666+
)
628667

629668
response = self._execute_code(
630669
name=name,
631-
inputs=inputs,
670+
inputs=input_chunks,
632671
config=config,
633672
)
634673

674+
output_chunks = []
675+
for output in response.outputs:
676+
if output.mime_type != "application/json":
677+
mime_type = output.mime_type
678+
# if mime_type is not available, try to guess the mime_type from the file_name.
679+
if (
680+
mime_type is None
681+
and output.metadata is not None
682+
and output.metadata.attributes is not None
683+
):
684+
file_name = output.metadata.attributes.get("file_name", b"").decode(
685+
"utf-8"
686+
)
687+
mime_type, _ = mimetypes.guess_type(file_name)
688+
output.mime_type = mime_type
689+
690+
# if the mime_type is in the list of mime_types that need base64 encoding,
691+
# decode the data.
692+
if mime_type in self._NEEDED_BASE64_ENCODING_MIME_TYPES:
693+
output.data = base64.b64decode(output.data)
694+
output_chunks.append(output)
695+
696+
response = types.ExecuteSandboxEnvironmentResponse(outputs=output_chunks)
697+
635698
return response
636699

637700
def get(

0 commit comments

Comments
 (0)