Skip to content

Can't deploy adk with VertexAiCodeExecutor #1730

Open
@KSonPham

Description

@KSonPham

** 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:

  1. 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.",
)
  1. init vertexai:
vertexai.init(
project=PROJECT_ID,
location="europe-west1",
staging_bucket=STAGING_BUCKET,
)
  1. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent_enginecoreIssues related to the core interface and implementation

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions