2828from .code_execution_utils import CodeExecutionResult
2929from .code_execution_utils import File
3030
31- logger = logging .getLogger (' google_adk.' + __name__ )
31+ logger = logging .getLogger (" google_adk." + __name__ )
3232
3333
3434class AgentEngineSandboxCodeExecutor (BaseCodeExecutor ):
@@ -63,8 +63,8 @@ def __init__(
6363 **data: Additional keyword arguments to be passed to the base class.
6464 """
6565 super ().__init__ (** data )
66- sandbox_resource_name_pattern = r' ^projects/([a-zA-Z0-9-_]+)/locations/([a-zA-Z0-9-_]+)/reasoningEngines/(\d+)/sandboxEnvironments/(\d+)$'
67- agent_engine_resource_name_pattern = r' ^projects/([a-zA-Z0-9-_]+)/locations/([a-zA-Z0-9-_]+)/reasoningEngines/(\d+)$'
66+ sandbox_resource_name_pattern = r" ^projects/([a-zA-Z0-9-_]+)/locations/([a-zA-Z0-9-_]+)/reasoningEngines/(\d+)/sandboxEnvironments/(\d+)$"
67+ agent_engine_resource_name_pattern = r" ^projects/([a-zA-Z0-9-_]+)/locations/([a-zA-Z0-9-_]+)/reasoningEngines/(\d+)$"
6868
6969 if sandbox_resource_name is not None :
7070 self .sandbox_resource_name = sandbox_resource_name
@@ -84,17 +84,17 @@ def __init__(
8484 # @TODO - Add TTL for sandbox creation after it is available
8585 # in SDK.
8686 operation = self ._get_api_client ().agent_engines .sandboxes .create (
87- spec = {' code_execution_environment' : {}},
87+ spec = {" code_execution_environment" : {}},
8888 name = agent_engine_resource_name ,
8989 config = types .CreateAgentEngineSandboxConfig (
90- display_name = ' default_sandbox'
90+ display_name = " default_sandbox"
9191 ),
9292 )
9393 self .sandbox_resource_name = operation .response .name
9494 else :
9595 raise ValueError (
96- ' Either sandbox_resource_name or agent_engine_resource_name must be'
97- ' set.'
96+ " Either sandbox_resource_name or agent_engine_resource_name must be"
97+ " set."
9898 )
9999
100100 @override
@@ -105,14 +105,14 @@ def execute_code(
105105 ) -> CodeExecutionResult :
106106 # Execute the code.
107107 input_data = {
108- ' code' : code_execution_input .code ,
108+ " code" : code_execution_input .code ,
109109 }
110110 if code_execution_input .input_files :
111- input_data [' files' ] = [
111+ input_data [" files" ] = [
112112 {
113- ' name' : f .name ,
114- 'contents' : f .content ,
115- 'mimeType' : f .mime_type ,
113+ " name" : f .name ,
114+ "content" : f .content ,
115+ "mime_type" : f .mime_type ,
116116 }
117117 for f in code_execution_input .input_files
118118 ]
@@ -123,27 +123,39 @@ def execute_code(
123123 input_data = input_data ,
124124 )
125125 )
126- logger .debug (' Executed code:\n ```\n %s\n ```' , code_execution_input .code )
126+ logger .debug (" Executed code:\n ```\n %s\n ```" , code_execution_input .code )
127127 saved_files = []
128- stdout = ''
129- stderr = ''
128+ stdout = ""
129+ stderr = ""
130130 for output in code_execution_response .outputs :
131- if output .mime_type == ' application/json' and (
131+ if output .mime_type == " application/json" and (
132132 output .metadata is None
133133 or output .metadata .attributes is None
134- or ' file_name' not in output .metadata .attributes
134+ or " file_name" not in output .metadata .attributes
135135 ):
136- json_output_data = json .loads (output .data .decode ('utf-8' ))
137- stdout = json_output_data .get ('stdout' , '' )
138- stderr = json_output_data .get ('stderr' , '' )
136+ json_output_data = json .loads (output .data .decode ("utf-8" ))
137+ if isinstance (json_output_data , dict ):
138+ # Primary fields returned by the API are msg_out/msg_err.
139+ # Fall back to stdout/stderr for backward compatibility.
140+ stdout = json_output_data .get (
141+ "msg_out" , json_output_data .get ("stdout" , "" )
142+ )
143+ stderr = json_output_data .get (
144+ "msg_err" , json_output_data .get ("stderr" , "" )
145+ )
146+ else :
147+ logger .warning (
148+ "Received non-dict JSON output from sandbox: %s" ,
149+ json_output_data ,
150+ )
139151 else :
140- file_name = ''
152+ file_name = ""
141153 if (
142154 output .metadata is not None
143155 and output .metadata .attributes is not None
144156 ):
145- file_name = output .metadata .attributes .get (' file_name' , b'' ).decode (
146- ' utf-8'
157+ file_name = output .metadata .attributes .get (" file_name" , b"" ).decode (
158+ " utf-8"
147159 )
148160 mime_type = output .mime_type
149161 if not mime_type :
@@ -183,6 +195,6 @@ def _get_project_id_and_location_from_resource_name(
183195 match = re .fullmatch (pattern , resource_name )
184196
185197 if not match :
186- raise ValueError (f' resource name { resource_name } is not valid.' )
198+ raise ValueError (f" resource name { resource_name } is not valid." )
187199
188200 return match .groups ()[0 ], match .groups ()[1 ]
0 commit comments