Open
Description
** Please make sure you read the contribution guide and file the issues in the right place. **
Contribution guide.
Describe the bug
I want the agent to visualize a chart generated by Python and display it in the agent space or locak adk web. I discovered that this only works when using VertexAiCodeExecutor(). However, deploying the model with this executor to the Agent Engine fails.
To Reproduce
Steps to reproduce the behavior:
- Define agent:
code_agent = Agent(
name=AGENT_NAME,
model=GEMINI_MODEL,
code_executor=VertexAiCodeExecutor()
instruction="""You are a calculator agent.
When given a mathematical expression, write and execute Python code to calculate the result. Also do data visuallization.
""",
description="Executes Python code to perform calculations and do data visualization.",
)
- init vertexai:
vertexai.init(
project=PROJECT_ID,
location="europe-west1",
staging_bucket=STAGING_BUCKET,
)
- create agent engine
from vertexai import agent_engines
remote_app = agent_engines.create(
agent_engine=code_agent,
display_name="code_agent",
description="Agent that can code and visualize data",
requirements=[
"google-cloud-aiplatform[adk,agent_engines]",
"google-genai==1.20.0",
"google-cloud-aiplatform==1.98",
"matplotlib",
"numpy",
]
)
Expected behavior
RecursionError Traceback (most recent call last)
Cell In[8], line 13
5 vertexai.init(
6 project=PROJECT_ID,
7 location="europe-west1",
8 staging_bucket=STAGING_BUCKET,
9 )
11 from vertexai import agent_engines
---> 13 remote_app = agent_engines.create(
14 agent_engine=code_agent,
15 display_name="code_agent",
16 description="Agent that can code and visualize data",
17 requirements=[
18 "google-cloud-aiplatform[adk,agent_engines]",
19 "google-genai==1.20.0",
20 "google-cloud-aiplatform==1.98",
21 "matplotlib",
22 "numpy",
23 ]
24 )
File ~/adk/venv/lib/python3.13/site-packages/vertexai/agent_engines/__init__.py:148, in create(agent_engine, requirements, display_name, description, gcs_dir_name, extra_packages, env_vars)
62 def create(
63 agent_engine: Optional[Union[Queryable, OperationRegistrable]] = None,
64 *,
(...) 72 ] = None,
73 ) -> AgentEngine:
74 """Creates a new Agent Engine.
75
76 The Agent Engine will be an instance of the `agent_engine` that
(...) 146 nonexistent file.
147 """
--> 148 return AgentEngine.create(
149 agent_engine=agent_engine,
150 requirements=requirements,
151 display_name=display_name,
152 description=description,
153 gcs_dir_name=gcs_dir_name,
154 extra_packages=extra_packages,
155 env_vars=env_vars,
156 )
File ~/adk/venv/lib/python3.13/site-packages/vertexai/agent_engines/_agent_engines.py:413, in AgentEngine.create(cls, agent_engine, requirements, display_name, description, gcs_dir_name, extra_packages, env_vars)
411 staging_bucket = initializer.global_config.staging_bucket
412 if agent_engine is not None:
--> 413 agent_engine = _validate_agent_engine_or_raise(agent_engine)
414 _validate_staging_bucket_or_raise(staging_bucket)
415 if agent_engine is None:
File ~/adk/venv/lib/python3.13/site-packages/vertexai/agent_engines/_agent_engines.py:854, in _validate_agent_engine_or_raise(agent_engine)
846 raise ValueError(
847 "Invalid register_operations signature. This might be due to a "
848 "missing `self` argument in the "
849 "agent_engine.register_operations method."
850 ) from err
852 if isinstance(agent_engine, Cloneable):
853 # Avoid undeployable states.
--> 854 agent_engine = agent_engine.clone()
855 return agent_engine
File ~/adk/venv/lib/python3.13/site-packages/vertexai/preview/reasoning_engines/templates/adk.py:409, in AdkApp.clone(self)
405 """Returns a clone of the ADK application."""
406 import copy
408 return AdkApp(
--> 409 agent=copy.deepcopy(self._tmpl_attrs.get("agent")),
410 enable_tracing=self._tmpl_attrs.get("enable_tracing"),
411 session_service_builder=self._tmpl_attrs.get("session_service_builder"),
412 artifact_service_builder=self._tmpl_attrs.get("artifact_service_builder"),
413 env_vars=self._tmpl_attrs.get("env_vars"),
414 )
File /opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/copy.py:144, in deepcopy(x, memo, _nil)
142 copier = getattr(x, "__deepcopy__", None)
143 if copier is not None:
--> 144 y = copier(memo)
145 else:
146 reductor = dispatch_table.get(cls)
File ~/adk/venv/lib/python3.13/site-packages/pydantic/main.py:940, in BaseModel.__deepcopy__(self, memo)
938 cls = type(self)
939 m = cls.__new__(cls)
--> 940 _object_setattr(m, '__dict__', deepcopy(self.__dict__, memo=memo))
941 _object_setattr(m, '__pydantic_extra__', deepcopy(self.__pydantic_extra__, memo=memo))
942 # This next line doesn't need a deepcopy because __pydantic_fields_set__ is a set[str],
943 # and attempting a deepcopy would be marginally slower.
File /opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/copy.py:137, in deepcopy(x, memo, _nil)
135 copier = _deepcopy_dispatch.get(cls)
136 if copier is not None:
--> 137 y = copier(x, memo)
138 else:
139 if issubclass(cls, type):
File /opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/copy.py:222, in _deepcopy_dict(x, memo, deepcopy)
220 memo[id(x)] = y
221 for key, value in x.items():
--> 222 y[deepcopy(key, memo)] = deepcopy(value, memo)
223 return y
File /opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/copy.py:144, in deepcopy(x, memo, _nil)
142 copier = getattr(x, "__deepcopy__", None)
143 if copier is not None:
--> 144 y = copier(memo)
145 else:
146 reductor = dispatch_table.get(cls)
File ~/adk/venv/lib/python3.13/site-packages/pydantic/main.py:952, in BaseModel.__deepcopy__(self, memo)
947 _object_setattr(m, '__pydantic_private__', None)
948 else:
949 _object_setattr(
950 m,
951 '__pydantic_private__',
--> 952 deepcopy({k: v for k, v in self.__pydantic_private__.items() if v is not PydanticUndefined}, memo=memo),
953 )
955 return m
File /opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/copy.py:137, in deepcopy(x, memo, _nil)
135 copier = _deepcopy_dispatch.get(cls)
136 if copier is not None:
--> 137 y = copier(x, memo)
138 else:
139 if issubclass(cls, type):
File /opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/copy.py:222, in _deepcopy_dict(x, memo, deepcopy)
220 memo[id(x)] = y
221 for key, value in x.items():
--> 222 y[deepcopy(key, memo)] = deepcopy(value, memo)
223 return y
File /opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/copy.py:163, in deepcopy(x, memo, _nil)
161 y = x
162 else:
--> 163 y = _reconstruct(x, memo, *rv)
165 # If is its own copy, don't memoize.
166 if y is not x:
File /opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/copy.py:260, in _reconstruct(x, memo, func, args, state, listiter, dictiter, deepcopy)
258 if state is not None:
259 if deep:
--> 260 state = deepcopy(state, memo)
261 if hasattr(y, '__setstate__'):
262 y.__setstate__(state)
File /opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/copy.py:137, in deepcopy(x, memo, _nil)
135 copier = _deepcopy_dispatch.get(cls)
136 if copier is not None:
--> 137 y = copier(x, memo)
138 else:
139 if issubclass(cls, type):
File /opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/copy.py:222, in _deepcopy_dict(x, memo, deepcopy)
220 memo[id(x)] = y
221 for key, value in x.items():
--> 222 y[deepcopy(key, memo)] = deepcopy(value, memo)
223 return y
File /opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/copy.py:163, in deepcopy(x, memo, _nil)
161 y = x
162 else:
--> 163 y = _reconstruct(x, memo, *rv)
165 # If is its own copy, don't memoize.
166 if y is not x:
File /opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/copy.py:260, in _reconstruct(x, memo, func, args, state, listiter, dictiter, deepcopy)
258 if state is not None:
259 if deep:
--> 260 state = deepcopy(state, memo)
261 if hasattr(y, '__setstate__'):
262 y.__setstate__(state)
File /opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/copy.py:137, in deepcopy(x, memo, _nil)
135 copier = _deepcopy_dispatch.get(cls)
136 if copier is not None:
--> 137 y = copier(x, memo)
138 else:
139 if issubclass(cls, type):
File /opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/copy.py:222, in _deepcopy_dict(x, memo, deepcopy)
220 memo[id(x)] = y
221 for key, value in x.items():
--> 222 y[deepcopy(key, memo)] = deepcopy(value, memo)
223 return y
File /opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/copy.py:137, in deepcopy(x, memo, _nil)
135 copier = _deepcopy_dispatch.get(cls)
136 if copier is not None:
--> 137 y = copier(x, memo)
138 else:
139 if issubclass(cls, type):
File /opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/copy.py:222, in _deepcopy_dict(x, memo, deepcopy)
220 memo[id(x)] = y
221 for key, value in x.items():
--> 222 y[deepcopy(key, memo)] = deepcopy(value, memo)
223 return y
File /opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/copy.py:163, in deepcopy(x, memo, _nil)
161 y = x
162 else:
--> 163 y = _reconstruct(x, memo, *rv)
165 # If is its own copy, don't memoize.
166 if y is not x:
File /opt/homebrew/Cellar/python@3.13/3.13.5/Frameworks/Python.framework/Versions/3.13/lib/python3.13/copy.py:261, in _reconstruct(x, memo, func, args, state, listiter, dictiter, deepcopy)
259 if deep:
260 state = deepcopy(state, memo)
--> 261 if hasattr(y, '__setstate__'):
262 y.__setstate__(state)
263 else:
File ~/adk/venv/lib/python3.13/site-packages/google/cloud/aiplatform/utils/__init__.py:494, in ClientWithOverride.WrappedClient.__getattr__(self, name)
490 def __getattr__(self, name: str) -> Any:
491 """Instantiates client and returns attribute of the client."""
493 kwargs = dict(
--> 494 credentials=self._credentials,
495 client_options=self._client_options,
496 client_info=self._client_info,
497 )
499 if self._api_transport is not None:
500 kwargs["transport"] = self._api_transport
File ~/adk/venv/lib/python3.13/site-packages/google/cloud/aiplatform/utils/__init__.py:494, in ClientWithOverride.WrappedClient.__getattr__(self, name)
490 def __getattr__(self, name: str) -> Any:
491 """Instantiates client and returns attribute of the client."""
493 kwargs = dict(
--> 494 credentials=self._credentials,
495 client_options=self._client_options,
496 client_info=self._client_info,
497 )
499 if self._api_transport is not None:
500 kwargs["transport"] = self._api_transport
[... skipping similar frames: ClientWithOverride.WrappedClient.__getattr__ at line 494 (2950 times)]
File ~/adk/venv/lib/python3.13/site-packages/google/cloud/aiplatform/utils/__init__.py:494, in ClientWithOverride.WrappedClient.__getattr__(self, name)
490 def __getattr__(self, name: str) -> Any:
491 """Instantiates client and returns attribute of the client."""
493 kwargs = dict(
--> 494 credentials=self._credentials,
495 client_options=self._client_options,
496 client_info=self._client_info,
497 )
499 if self._api_transport is not None:
500 kwargs["transport"] = self._api_transport
RecursionError: maximum recursion depth exceeded
Screenshots
If applicable, add screenshots to help explain your problem.
Desktop (please complete the following information):
- OS: MacOS
- Python version(python -V): 3.13
- ADK version(pip show google-adk): 1.4.2
Model Information:
For example, which model is being used.
Additional context
If there's a way model can show the visualized images using BuiltInCodeExecutor(), please tell me