Skip to content

Commit 1f19fc7

Browse files
authored
Merge branch 'main' into feat/function-tools-enum-support
2 parents 644f68c + 6a5eac0 commit 1f19fc7

33 files changed

Lines changed: 1067 additions & 539 deletions
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# OAuth Sample
2+
3+
## Introduction
4+
5+
This sample data science agent uses Agent Engine Code Execution Sandbox to execute LLM generated code.
6+
7+
8+
## How to use
9+
10+
* 1. Follow https://cloud.google.com/vertex-ai/generative-ai/docs/agent-engine/code-execution/overview to create a code execution sandbox environment.
11+
12+
* 2. Replace the SANDBOX_RESOURCE_NAME with the one you just created. If you dont want to create a new sandbox environment directly, the Agent Engine Code Execution Sandbox will create one for you by default using the AGENT_ENGINE_RESOURCE_NAME you specified, however, please ensure to clean up sandboxes after use, otherwise, it will consume quotas.
13+
14+
15+
## Sample prompt
16+
17+
* Can you write a function that calculates the sum from 1 to 100.
18+
* The dataset is given as below. Store,Date,Weekly_Sales,Holiday_Flag,Temperature,Fuel_Price,CPI,Unemployment Store 1,2023-06-01,1000,0,70,3.0,200,5 Store 2,2023-06-02,1200,1,80,3.5,210,6 Store 3,2023-06-03,1400,0,90,4.0,220,7 Store 4,2023-06-04,1600,1,70,4.5,230,8 Store 5,2023-06-05,1800,0,80,5.0,240,9 Store 6,2023-06-06,2000,1,90,5.5,250,10 Store 7,2023-06-07,2200,0,90,6.0,260,11 Plot a scatter plot showcasing the relationship between Weekly Sales and Temperature for each store, distinguishing stores with a Holiday Flag.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from . import agent
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
# Copyright 2025 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Data science agent."""
16+
17+
from google.adk.agents.llm_agent import Agent
18+
from google.adk.code_executors.agent_engine_sandbox_code_executor import AgentEngineSandboxCodeExecutor
19+
20+
21+
def base_system_instruction():
22+
"""Returns: data science agent system instruction."""
23+
24+
return """
25+
# Guidelines
26+
27+
**Objective:** Assist the user in achieving their data analysis goals within the context of a Python Colab notebook, **with emphasis on avoiding assumptions and ensuring accuracy.** Reaching that goal can involve multiple steps. When you need to generate code, you **don't** need to solve the goal in one go. Only generate the next step at a time.
28+
29+
**Code Execution:** All code snippets provided will be executed within the Colab environment.
30+
31+
**Statefulness:** All code snippets are executed and the variables stays in the environment. You NEVER need to re-initialize variables. You NEVER need to reload files. You NEVER need to re-import libraries.
32+
33+
**Output Visibility:** Always print the output of code execution to visualize results, especially for data exploration and analysis. For example:
34+
- To look a the shape of a pandas.DataFrame do:
35+
```tool_code
36+
print(df.shape)
37+
```
38+
The output will be presented to you as:
39+
```tool_outputs
40+
(49, 7)
41+
42+
```
43+
- To display the result of a numerical computation:
44+
```tool_code
45+
x = 10 ** 9 - 12 ** 5
46+
print(f'{{x=}}')
47+
```
48+
The output will be presented to you as:
49+
```tool_outputs
50+
x=999751168
51+
52+
```
53+
- You **never** generate ```tool_outputs yourself.
54+
- You can then use this output to decide on next steps.
55+
- Print just variables (e.g., `print(f'{{variable=}}')`.
56+
57+
**No Assumptions:** **Crucially, avoid making assumptions about the nature of the data or column names.** Base findings solely on the data itself. Always use the information obtained from `explore_df` to guide your analysis.
58+
59+
**Available files:** Only use the files that are available as specified in the list of available files.
60+
61+
**Data in prompt:** Some queries contain the input data directly in the prompt. You have to parse that data into a pandas DataFrame. ALWAYS parse all the data. NEVER edit the data that are given to you.
62+
63+
**Answerability:** Some queries may not be answerable with the available data. In those cases, inform the user why you cannot process their query and suggest what type of data would be needed to fulfill their request.
64+
65+
"""
66+
67+
68+
root_agent = Agent(
69+
model="gemini-2.0-flash-001",
70+
name="agent_engine_code_execution_agent",
71+
instruction=base_system_instruction() + """
72+
73+
74+
You need to assist the user with their queries by looking at the data and the context in the conversation.
75+
You final answer should summarize the code and code execution relevant to the user query.
76+
77+
You should include all pieces of data to answer the user query, such as the table from code execution results.
78+
If you cannot answer the question directly, you should follow the guidelines above to generate the next step.
79+
If the question can be answered directly with writing any code, you should do that.
80+
If you doesn't have enough data to answer the question, you should ask for clarification from the user.
81+
82+
You should NEVER install any package on your own like `pip install ...`.
83+
When plotting trends, you should make sure to sort and order the data by the x-axis.
84+
85+
86+
""",
87+
code_executor=AgentEngineSandboxCodeExecutor(
88+
# Replace with your sandbox resource name if you already have one.
89+
sandbox_resource_name="SANDBOX_RESOURCE_NAME",
90+
# "projects/vertex-agent-loadtest/locations/us-central1/reasoningEngines/6842889780301135872/sandboxEnvironments/6545148628569161728",
91+
# Replace with agent engine resource name used for creating sandbox if
92+
# sandbox_resource_name is not set.
93+
agent_engine_resource_name="AGENT_ENGINE_RESOURCE_NAME",
94+
),
95+
)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ dependencies = [
3232
"click>=8.1.8, <9.0.0", # For CLI tools
3333
"fastapi>=0.115.0, <1.0.0", # FastAPI framework
3434
"google-api-python-client>=2.157.0, <3.0.0", # Google API client discovery
35-
"google-cloud-aiplatform[agent_engines]>=1.112.0, <2.0.0",# For VertexAI integrations, e.g. example store.
35+
"google-cloud-aiplatform[agent_engines]>=1.121.0, <2.0.0",# For VertexAI integrations, e.g. example store.
3636
"google-cloud-bigtable>=2.32.0", # For Bigtable database
3737
"google-cloud-discoveryengine>=0.13.12, <0.14.0", # For Discovery Engine Search Tool
3838
"google-cloud-secret-manager>=2.22.0, <3.0.0", # Fetching secrets in RestAPI Tool

src/google/adk/cli/adk_web_server.py

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,12 @@ async def get_runner_async(self, app_name: str) -> Runner:
486486
self.runner_dict[app_name] = runner
487487
return runner
488488

489+
def _get_root_agent(self, agent_or_app: BaseAgent | App) -> BaseAgent:
490+
"""Extract root agent from either a BaseAgent or App object."""
491+
if isinstance(agent_or_app, App):
492+
return agent_or_app.root_agent
493+
return agent_or_app
494+
489495
def _create_runner(self, agentic_app: App) -> Runner:
490496
"""Create a runner with common services."""
491497
return Runner(
@@ -741,7 +747,7 @@ async def create_session_with_id(
741747
is not None
742748
):
743749
raise HTTPException(
744-
status_code=400, detail=f"Session already exists: {session_id}"
750+
status_code=409, detail=f"Session already exists: {session_id}"
745751
)
746752
session = await self.session_service.create_session(
747753
app_name=app_name, user_id=user_id, state=state, session_id=session_id
@@ -763,6 +769,13 @@ async def create_session(
763769
app_name=app_name, user_id=user_id
764770
)
765771

772+
if req.session_id and await self.session_service.get_session(
773+
app_name=app_name, user_id=user_id, session_id=req.session_id
774+
):
775+
raise HTTPException(
776+
status_code=409, detail=f"Session already exists: {req.session_id}"
777+
)
778+
766779
session = await self.session_service.create_session(
767780
app_name=app_name,
768781
user_id=user_id,
@@ -926,9 +939,8 @@ async def add_session_to_eval_set(
926939

927940
# Populate the session with initial session state.
928941
agent_or_app = self.agent_loader.load_agent(app_name)
929-
if isinstance(agent_or_app, App):
930-
agent_or_app = agent_or_app.root_agent
931-
initial_session_state = create_empty_state(agent_or_app)
942+
root_agent = self._get_root_agent(agent_or_app)
943+
initial_session_state = create_empty_state(root_agent)
932944

933945
new_eval_case = EvalCase(
934946
eval_id=req.eval_id,
@@ -1089,7 +1101,8 @@ async def run_eval(
10891101
status_code=400, detail=f"Eval set `{eval_set_id}` not found."
10901102
)
10911103

1092-
root_agent = self.agent_loader.load_agent(app_name)
1104+
agent_or_app = self.agent_loader.load_agent(app_name)
1105+
root_agent = self._get_root_agent(agent_or_app)
10931106

10941107
eval_case_results = []
10951108

@@ -1430,13 +1443,7 @@ async def get_event_graph(
14301443
function_calls = event.get_function_calls()
14311444
function_responses = event.get_function_responses()
14321445
agent_or_app = self.agent_loader.load_agent(app_name)
1433-
# The loader may return an App; unwrap to its root agent so the graph builder
1434-
# receives a BaseAgent instance.
1435-
root_agent = (
1436-
agent_or_app.root_agent
1437-
if isinstance(agent_or_app, App)
1438-
else agent_or_app
1439-
)
1446+
root_agent = self._get_root_agent(agent_or_app)
14401447
dot_graph = None
14411448
if function_calls:
14421449
function_call_highlights = []

src/google/adk/code_executors/__init__.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
from __future__ import annotations
16+
1517
import logging
1618

1719
from .base_code_executor import BaseCodeExecutor
@@ -29,6 +31,7 @@
2931
'VertexAiCodeExecutor',
3032
'ContainerCodeExecutor',
3133
'GkeCodeExecutor',
34+
'AgentEngineSandboxCodeExecutor',
3235
]
3336

3437

@@ -63,4 +66,14 @@ def __getattr__(name: str):
6366
'GkeCodeExecutor requires additional dependencies. '
6467
'Please install with: pip install "google-adk[extensions]"'
6568
) from e
69+
elif name == 'AgentEngineSandboxCodeExecutor':
70+
try:
71+
from .agent_engine_sandbox_code_executor import AgentEngineSandboxCodeExecutor
72+
73+
return AgentEngineSandboxCodeExecutor
74+
except ImportError as e:
75+
raise ImportError(
76+
'AgentEngineSandboxCodeExecutor requires additional dependencies. '
77+
'Please install with: pip install "google-adk[extensions]"'
78+
) from e
6679
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")

0 commit comments

Comments
 (0)