Skip to content

Commit a2c6a8a

Browse files
lizzijcopybara-github
authored andcommitted
feat: Expose the Python code run by the code interpreter in the logs
Co-authored-by: Eliza Huang <heliza@google.com> PiperOrigin-RevId: 826521391
1 parent 546c2a6 commit a2c6a8a

File tree

7 files changed

+59
-1
lines changed

7 files changed

+59
-1
lines changed

src/google/adk/code_executors/agent_engine_sandbox_code_executor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ def execute_code(
125125
input_data=input_data,
126126
)
127127
)
128+
logger.debug('Executed code:\n```\n%s\n```', code_execution_input.code)
128129
saved_files = []
129130
stdout = ''
130131
stderr = ''

src/google/adk/code_executors/container_code_executor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ def execute_code(
131131
['python3', '-c', code_execution_input.code],
132132
demux=True,
133133
)
134+
logger.debug('Executed code:\n```\n%s\n```', code_execution_input.code)
134135

135136
if exec_result.output and exec_result.output[0]:
136137
output = exec_result.output[0].decode('utf-8')

src/google/adk/code_executors/gke_code_executor.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ def execute_code(
162162
logger.info(
163163
f"Submitted Job '{job_name}' to namespace '{self.namespace}'."
164164
)
165+
logger.debug("Executing code:\n```\n%s\n```", code_execution_input.code)
165166
return self._watch_job_completion(job_name)
166167

167168
except ApiException as e:

src/google/adk/code_executors/unsafe_local_code_executor.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
from contextlib import redirect_stdout
1818
import io
19+
import logging
1920
import re
2021
from typing import Any
2122

@@ -27,6 +28,8 @@
2728
from .code_execution_utils import CodeExecutionInput
2829
from .code_execution_utils import CodeExecutionResult
2930

31+
logger = logging.getLogger('google_adk.' + __name__)
32+
3033

3134
def _prepare_globals(code: str, globals_: dict[str, Any]) -> None:
3235
"""Prepare globals for code execution, injecting __name__ if needed."""
@@ -60,6 +63,7 @@ def execute_code(
6063
invocation_context: InvocationContext,
6164
code_execution_input: CodeExecutionInput,
6265
) -> CodeExecutionResult:
66+
logger.debug('Executing code:\n```\n%s\n```', code_execution_input.code)
6367
# Execute the code.
6468
output = ''
6569
error = ''

src/google/adk/code_executors/vertex_ai_code_executor.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ def execute_code(
152152
code_execution_input.input_files,
153153
code_execution_input.execution_id,
154154
)
155+
logger.debug('Executed code:\n```\n%s\n```', code_execution_input.code)
155156

156157
# Save output file as artifacts.
157158
saved_files = []
@@ -187,11 +188,13 @@ def execute_code(
187188
)
188189

189190
# Collect the final result.
190-
return CodeExecutionResult(
191+
result = CodeExecutionResult(
191192
stdout=code_execution_result.get('execution_result', ''),
192193
stderr=code_execution_result.get('execution_error', ''),
193194
output_files=saved_files,
194195
)
196+
logger.debug('Code execution result: %s', result)
197+
return result
195198

196199
def _execute_code_interpreter(
197200
self,

src/google/adk/flows/llm_flows/_code_execution.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import copy
2121
import dataclasses
2222
import datetime
23+
import logging
2324
import os
2425
import re
2526
from typing import AsyncGenerator
@@ -47,6 +48,8 @@
4748
if TYPE_CHECKING:
4849
from ...models.llm_request import LlmRequest
4950

51+
logger = logging.getLogger('google_adk.' + __name__)
52+
5053

5154
@dataclasses.dataclass
5255
class DataFileUtil:
@@ -245,6 +248,7 @@ async def _run_pre_processor(
245248
),
246249
),
247250
)
251+
logger.debug('Executed code:\n```\n%s\n```', code_str)
248252
# Update the processing results to code executor context.
249253
code_executor_context.update_code_execution_result(
250254
invocation_context.invocation_id,
@@ -352,6 +356,7 @@ async def _run_post_processor(
352356
),
353357
),
354358
)
359+
logger.debug('Executed code:\n```\n%s\n```', code_str)
355360
code_executor_context.update_code_execution_result(
356361
invocation_context.invocation_id,
357362
code_str,

tests/unittests/flows/llm_flows/test_code_execution.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
from unittest.mock import patch
2121

2222
from google.adk.agents.llm_agent import Agent
23+
from google.adk.code_executors.base_code_executor import BaseCodeExecutor
2324
from google.adk.code_executors.built_in_code_executor import BuiltInCodeExecutor
25+
from google.adk.code_executors.code_execution_utils import CodeExecutionResult
2426
from google.adk.flows.llm_flows._code_execution import response_processor
2527
from google.adk.models.llm_response import LlmResponse
2628
from google.genai import types
@@ -105,3 +107,44 @@ async def test_builtin_code_executor_image_artifact_creation(mock_datetime):
105107
== f'Saved as artifact: {expected_filename2}. '
106108
)
107109
assert not llm_response.content.parts[2].inline_data
110+
111+
112+
@pytest.mark.asyncio
113+
@patch('google.adk.flows.llm_flows._code_execution.logger')
114+
async def test_logs_executed_code(mock_logger):
115+
"""Test that the response processor logs the code it executes."""
116+
mock_code_executor = MagicMock(spec=BaseCodeExecutor)
117+
mock_code_executor.code_block_delimiters = [('```python\n', '\n```')]
118+
mock_code_executor.error_retry_attempts = 2
119+
mock_code_executor.stateful = False
120+
mock_code_executor.execute_code.return_value = CodeExecutionResult(
121+
stdout='hello'
122+
)
123+
124+
agent = Agent(name='test_agent', code_executor=mock_code_executor)
125+
invocation_context = await testing_utils.create_invocation_context(
126+
agent=agent, user_content='test message'
127+
)
128+
invocation_context.artifact_service = MagicMock()
129+
invocation_context.artifact_service.save_artifact = AsyncMock()
130+
131+
llm_response = LlmResponse(
132+
content=types.Content(
133+
parts=[
134+
types.Part(text='Here is some code:'),
135+
types.Part(text='```python\nprint("hello")\n```'),
136+
]
137+
)
138+
)
139+
140+
_ = [
141+
event
142+
async for event in response_processor.run_async(
143+
invocation_context, llm_response
144+
)
145+
]
146+
147+
mock_code_executor.execute_code.assert_called_once()
148+
mock_logger.debug.assert_called_once_with(
149+
'Executed code:\n```\n%s\n```', 'print("hello")'
150+
)

0 commit comments

Comments
 (0)