Skip to content

Commit 644f68c

Browse files
authored
Merge branch 'main' into feat/function-tools-enum-support
2 parents d9a2dae + 9dce06f commit 644f68c

87 files changed

Lines changed: 4232 additions & 1726 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/pr-triage.yml

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,16 @@ name: ADK Pull Request Triaging Agent
33
on:
44
pull_request_target:
55
types: [opened, reopened, edited]
6+
workflow_dispatch:
7+
inputs:
8+
pr_number:
9+
description: 'The Pull Request number to triage'
10+
required: true
11+
type: 'string'
612

713
jobs:
814
agent-triage-pull-request:
9-
if: "!contains(github.event.pull_request.labels.*.name, 'bot triaged') && !contains(github.event.pull_request.labels.*.name, 'google-contributor')"
15+
if: github.event_name == 'workflow_dispatch' || !contains(github.event.pull_request.labels.*.name, 'google-contributor')
1016
runs-on: ubuntu-latest
1117
permissions:
1218
pull-requests: write
@@ -33,7 +39,7 @@ jobs:
3339
GOOGLE_GENAI_USE_VERTEXAI: 0
3440
OWNER: 'google'
3541
REPO: 'adk-python'
36-
PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }}
42+
PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number || github.event.inputs.pr_number }}
3743
INTERACTIVE: ${{ vars.PR_TRIAGE_INTERACTIVE }}
3844
PYTHONPATH: contributing/samples
3945
run: python -m adk_pr_triaging_agent.main

.github/workflows/triage.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ name: ADK Issue Triaging Agent
33
on:
44
issues:
55
types: [opened, reopened]
6-
schedule:
7-
- cron: '0 */6 * * *' # every 6h
86

97
jobs:
108
agent-triage-issues:

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ Meeting link:
4343
[Join the call](http://meet.google.com/gjm-gfim-ctz)
4444

4545
Add to your calendar
46-
[Event calendar invite](https://calendar.google.com/calendar/event?action=TEMPLATE&tmeid=MDUydWo1dHV1dHFtNzJuM3E0bmEyMW12ZnZfMjAyNTEwMTVUMTYzMDAwWiBjXzljNWVjODhhMmQyYWU5YjY5Mzk4ODU1MGZkNDA5MjVmYjgxYjM4MTI1NGNjYTgzNmRkMjMwNzRiMjNmYzcyZDVAZw&tmsrc=c_9c5ec88a2d2ae9b693988550fd40925fb81b381254cca836dd23074b23fc72d5%40group.calendar.google.com), [.ics file](https://calendar.google.com/calendar/ical/c_9c5ec88a2d2ae9b693988550fd40925fb81b381254cca836dd23074b23fc72d5%40group.calendar.google.com/public/basic.ics), [ADK community calendar](https://calendar.google.com/calendar/embed?src=c_9c5ec88a2d2ae9b693988550fd40925fb81b381254cca836dd23074b23fc72d5%40group.calendar.google.com&ctz=America%2FLos_Angeles)
46+
[Event calendar invite](https://calendar.google.com/calendar/event?action=TEMPLATE&tmeid=MDUydWo1dHV1dHFtNzJuM3E0bmEyMW12ZnZfMjAyNTEwMTVUMTYzMDAwWiBjXzljNWVjODhhMmQyYWU5YjY5Mzk4ODU1MGZkNDA5MjVmYjgxYjM4MTI1NGNjYTgzNmRkMjMwNzRiMjNmYzcyZDVAZw&tmsrc=c_9c5ec88a2d2ae9b693988550fd40925fb81b381254cca836dd23074b23fc72d5%40group.calendar.google.com), [.ics file](https://calendar.google.com/calendar/ical/c_9c5ec88a2d2ae9b693988550fd40925fb81b381254cca836dd23074b23fc72d5%40group.calendar.google.com/public/basic.ics), [ADK community calendar](https://calendar.google.com/calendar/embed?src=c_9c5ec88a2d2ae9b693988550fd40925fb81b381254cca836dd23074b23fc72d5%40group.calendar.google.com&ctz=America%2FLos_Angeles), [ADK Community Call RSVP](https://google.qualtrics.com/jfe/form/SV_3K0RJZ64H1BexqS)
4747

4848
Agenda:
4949
[Julia] ADK Roadmap

contributing/samples/adk_agent_builder_assistant/instruction_embedded.template

Lines changed: 57 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ Always reference this schema when creating configurations to ensure compliance.
6464
- **MANDATORY CONFIRMATION**: Say "Please confirm what model you want to use" - do NOT assume or suggest defaults
6565
- **EXAMPLES**: "gemini-2.5-flash", "gemini-2.5-pro", etc.
6666
- **RATIONALE**: Only LlmAgent requires model specification; workflow agents do not
67-
- **DEFAULT ONLY**: Use "{default_model}" only if user explicitly says "use default" or similar
67+
- **DEFAULT MODEL**: If user says "use default" or "proceed with default model", use: {default_model}
68+
* This is the actual model name, NOT the literal string "default"
69+
* The default model for this session is: {default_model}
6870
- **WORKFLOW**: Complete all Discovery steps (including this model selection) → Then proceed to Design Phase with model already chosen
6971

7072
### 2. Design Phase
@@ -127,6 +129,43 @@ Always reference this schema when creating configurations to ensure compliance.
127129
* **Remember**: Always extract just the folder name after the last slash/separator
128130
- No function declarations in YAML (handled automatically by ADK)
129131

132+
**🚨 CRITICAL: Built-in Tools vs Custom Tools**
133+
134+
**ADK Built-in Tools** (use directly, NO custom Python file needed):
135+
- **Naming**: Use simple name WITHOUT dots (e.g., `google_search`, NOT `google.adk.tools.google_search`)
136+
- **No custom code**: Do NOT create Python files for built-in tools
137+
- **Available built-in tools**:
138+
* `google_search` - Google Search tool
139+
* `enterprise_web_search` - Enterprise web search
140+
* `google_maps_grounding` - Google Maps grounding
141+
* `url_context` - URL context fetching
142+
* `VertexAiSearchTool` - Vertex AI Search (class name)
143+
* `exit_loop` - Exit loop control
144+
* `get_user_choice` - User choice interaction
145+
* `load_artifacts` - Load artifacts
146+
* `load_memory` - Load memory
147+
* `preload_memory` - Preload memory
148+
* `transfer_to_agent` - Transfer to another agent
149+
150+
**Example - Built-in Tool Usage (CORRECT):**
151+
```yaml
152+
tools:
153+
- name: google_search
154+
- name: url_context
155+
```
156+
157+
**Example - Built-in Tool Usage (WRONG):**
158+
```yaml
159+
tools:
160+
- name: cb.tools.google_search_tool.google_search_tool # ❌ WRONG - treating built-in as custom
161+
```
162+
**DO NOT create Python files like `tools/google_search_tool.py` for built-in tools!**
163+
164+
**Custom Tools** (require Python implementation):
165+
- **Naming**: Use dotted path: `{{project_folder_name}}.tools.{{module_name}}.{{function_name}}`
166+
- **Require Python file**: Must create actual Python file in `tools/` directory
167+
- **Example**: `my_project.tools.dice_tool.roll_dice` → requires `tools/dice_tool.py` with `roll_dice()` function
168+
130169
**TOOL IMPLEMENTATION STRATEGY:**
131170
- **For simple/obvious tools**: Implement them directly with actual working code
132171
* Example: dice rolling, prime checking, basic math, file operations
@@ -286,11 +325,11 @@ from typing import Optional
286325
from google.genai import types
287326
from google.adk.agents.callback_context import CallbackContext
288327

289-
def content_filter_callback(context: CallbackContext) -> Optional[types.Content]:
328+
def content_filter_callback(callback_context: CallbackContext) -> Optional[types.Content]:
290329
"""After agent callback to filter sensitive content."""
291-
# Access the response content through context
292-
if hasattr(context, 'response') and context.response:
293-
response_text = str(context.response)
330+
# Access the response content through callback_context
331+
if hasattr(callback_context, 'response') and callback_context.response:
332+
response_text = str(callback_context.response)
294333
if "confidential" in response_text.lower():
295334
filtered_text = response_text.replace("confidential", "[FILTERED]")
296335
return types.Content(parts=[types.Part(text=filtered_text)])
@@ -306,12 +345,12 @@ from google.adk.models.llm_request import LlmRequest
306345
from google.adk.models.llm_response import LlmResponse
307346
from google.adk.agents.callback_context import CallbackContext
308347

309-
def log_model_request(context: CallbackContext, request: LlmRequest) -> Optional[LlmResponse]:
348+
def log_model_request(callback_context: CallbackContext, request: LlmRequest) -> Optional[LlmResponse]:
310349
"""Before model callback to log requests."""
311350
print(f"Model request: {{request.contents}}")
312351
return None # Return None to proceed with original request
313352

314-
def modify_model_response(context: CallbackContext, response: LlmResponse) -> Optional[LlmResponse]:
353+
def modify_model_response(callback_context: CallbackContext, response: LlmResponse) -> Optional[LlmResponse]:
315354
"""After model callback to modify response."""
316355
# Modify response if needed
317356
return response # Return modified response or None for original
@@ -325,25 +364,25 @@ from typing import Any, Dict, Optional
325364
from google.adk.tools.base_tool import BaseTool
326365
from google.adk.tools.tool_context import ToolContext
327366

328-
def validate_tool_input(tool: BaseTool, args: Dict[str, Any], context: ToolContext) -> Optional[Dict]:
367+
def validate_tool_input(tool: BaseTool, tool_args: Dict[str, Any], tool_context: ToolContext) -> Optional[Dict]:
329368
"""Before tool callback to validate input."""
330369
# Validate or modify tool arguments
331-
if "unsafe_param" in args:
332-
del args["unsafe_param"]
333-
return args # Return modified args or None for original
370+
if "unsafe_param" in tool_args:
371+
del tool_args["unsafe_param"]
372+
return tool_args # Return modified args or None for original
334373

335-
def log_tool_result(tool: BaseTool, args: Dict[str, Any], context: ToolContext, result: Dict) -> Optional[Dict]:
374+
def log_tool_result(tool: BaseTool, tool_args: Dict[str, Any], tool_context: ToolContext, result: Dict) -> Optional[Dict]:
336375
"""After tool callback to log results."""
337376
print(f"Tool {{tool.name}} executed with result: {{result}}")
338377
return None # Return None to keep original result
339378
```
340379

341380
## Callback Signature Summary:
342-
- **Agent Callbacks**: `(CallbackContext) -> Optional[types.Content]`
343-
- **Before Model**: `(CallbackContext, LlmRequest) -> Optional[LlmResponse]`
344-
- **After Model**: `(CallbackContext, LlmResponse) -> Optional[LlmResponse]`
345-
- **Before Tool**: `(BaseTool, Dict[str, Any], ToolContext) -> Optional[Dict]`
346-
- **After Tool**: `(BaseTool, Dict[str, Any], ToolContext, Dict) -> Optional[Dict]`
381+
- **Agent Callbacks**: `(callback_context: CallbackContext) -> Optional[types.Content]`
382+
- **Before Model**: `(callback_context: CallbackContext, request: LlmRequest) -> Optional[LlmResponse]`
383+
- **After Model**: `(callback_context: CallbackContext, response: LlmResponse) -> Optional[LlmResponse]`
384+
- **Before Tool**: `(tool: BaseTool, tool_args: Dict[str, Any], tool_context: ToolContext) -> Optional[Dict]`
385+
- **After Tool**: `(tool: BaseTool, tool_args: Dict[str, Any], tool_context: ToolContext, result: Dict) -> Optional[Dict]`
347386

348387
## Important ADK Requirements
349388

@@ -369,7 +408,7 @@ def log_tool_result(tool: BaseTool, args: Dict[str, Any], context: ToolContext,
369408
**ADK AgentConfig Schema Compliance:**
370409
- Always reference the embedded ADK AgentConfig schema to verify field requirements
371410
- **MODEL FIELD RULES**:
372-
* **LlmAgent**: `model` field is REQUIRED (unless inherited from ancestor) - Ask user for preference only when LlmAgent is needed, use "{default_model}" if not specified
411+
* **LlmAgent**: `model` field is REQUIRED (unless inherited from ancestor) - Ask user for preference only when LlmAgent is needed, use {default_model} if user says to use default
373412
* **Workflow Agents**: `model` field is FORBIDDEN - Remove model field entirely for Sequential/Parallel/Loop agents
374413
- Optional fields: description, instruction, tools, sub_agents as defined in ADK AgentConfig schema
375414

@@ -404,7 +443,6 @@ def log_tool_result(tool: BaseTool, args: Dict[str, Any], context: ToolContext,
404443
2. No redundant suggest_file_path calls for pre-approved paths
405444
3. Generated configurations pass schema validation (automatically checked)
406445
4. Follow ADK naming and organizational conventions
407-
5. Be immediately testable with `adk run [root_directory]` or via `adk web` interface
408446
6. Include clear, actionable instructions for each agent
409447
7. Use appropriate tools for intended functionality
410448

@@ -424,14 +462,3 @@ def log_tool_result(tool: BaseTool, args: Dict[str, Any], context: ToolContext,
424462
6. **Focus on collaboration** - Ensure user gets exactly what they need with clear understanding
425463

426464
**This workflow eliminates inefficiencies and ensures users get well-organized, predictable file structures in their chosen location.**
427-
428-
## Running Generated Agents
429-
430-
**Correct ADK Commands:**
431-
- `adk run [project_directory]` - Run agent from project directory (e.g., `adk run config_agents/roll_and_check`)
432-
- `adk web [parent_directory]` - Start web interface, then select agent from dropdown menu (e.g., `adk web config_agents`)
433-
- **Key Rule**: Always use the project directory for `adk run`, and parent directory for `adk web`
434-
435-
**Incorrect Commands to Avoid:**
436-
- `adk run [project_directory]/root_agent.yaml` - Do NOT specify the YAML file directly
437-
- `adk web` without parent directory - Must specify the parent folder containing the agent projects

contributing/samples/adk_agent_builder_assistant/tools/cleanup_unused_files.py

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -14,44 +14,53 @@
1414

1515
"""Cleanup unused files tool for Agent Builder Assistant."""
1616

17-
from pathlib import Path
1817
from typing import Any
1918
from typing import Dict
2019
from typing import List
2120
from typing import Optional
2221

22+
from google.adk.tools.tool_context import ToolContext
23+
24+
from ..utils.resolve_root_directory import resolve_file_path
25+
from ..utils.resolve_root_directory import resolve_file_paths
26+
2327

2428
async def cleanup_unused_files(
25-
root_directory: str,
2629
used_files: List[str],
30+
tool_context: ToolContext,
2731
file_patterns: Optional[List[str]] = None,
2832
exclude_patterns: Optional[List[str]] = None,
2933
) -> Dict[str, Any]:
3034
"""Identify and optionally delete unused files in project directories.
3135
3236
This tool helps clean up unused tool files when agent configurations change.
3337
It identifies files that match patterns but aren't referenced in used_files
34-
list.
38+
list. Paths are resolved automatically using the tool context.
3539
3640
Args:
37-
root_directory: Root directory to scan for unused files
3841
used_files: List of file paths currently in use (should not be deleted)
42+
tool_context: Tool execution context (provides session state)
3943
file_patterns: List of glob patterns to match files (default: ["*.py"])
4044
exclude_patterns: List of patterns to exclude (default: ["__init__.py"])
4145
4246
Returns:
4347
Dict containing cleanup results:
4448
- success: bool indicating if scan succeeded
45-
- root_directory: absolute path to scanned directory
4649
- unused_files: list of unused files found
4750
- deleted_files: list of files actually deleted
4851
- backup_files: list of backup files created
4952
- errors: list of error messages
5053
- total_freed_space: total bytes freed by deletions
5154
"""
55+
session_state = tool_context.state
56+
root_path = resolve_file_path(".", session_state)
57+
5258
try:
53-
root_path = Path(root_directory).resolve()
54-
used_files_set = {Path(f).resolve() for f in used_files}
59+
root_path = root_path.resolve()
60+
resolved_used_files = {
61+
path.resolve()
62+
for path in resolve_file_paths(used_files or [], session_state)
63+
}
5564

5665
# Set defaults
5766
if file_patterns is None:
@@ -61,7 +70,6 @@ async def cleanup_unused_files(
6170

6271
result = {
6372
"success": False,
64-
"root_directory": str(root_path),
6573
"unused_files": [],
6674
"deleted_files": [],
6775
"backup_files": [],
@@ -85,7 +93,7 @@ async def cleanup_unused_files(
8593
# Identify unused files
8694
unused_files = []
8795
for file_path in all_files:
88-
if file_path not in used_files_set:
96+
if file_path.resolve() not in resolved_used_files:
8997
unused_files.append(file_path)
9098

9199
result["unused_files"] = [str(f) for f in unused_files]
@@ -99,7 +107,6 @@ async def cleanup_unused_files(
99107
except Exception as e:
100108
return {
101109
"success": False,
102-
"root_directory": root_directory,
103110
"unused_files": [],
104111
"deleted_files": [],
105112
"backup_files": [],

contributing/samples/adk_pr_triaging_agent/agent.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,10 @@ def get_pull_request_details(pr_number: int) -> str:
6969
repository(owner: $owner, name: $repo) {
7070
pullRequest(number: $prNumber) {
7171
id
72+
number
7273
title
7374
body
75+
state
7476
author {
7577
login
7678
}
@@ -298,7 +300,10 @@ def add_comment_to_pr(pr_number: int, comment: str) -> dict[str, Any]:
298300
# 4. Steps
299301
When you are given a PR, here are the steps you should take:
300302
- Call the `get_pull_request_details` tool to get the details of the PR.
301-
- Skip the PR (i.e. do not label or comment) if the PR is closed or is labeled with "{BOT_LABEL}" or "google-contributior".
303+
- Skip the PR (i.e. do not label or comment) if any of the following is true:
304+
- the PR is closed
305+
- the PR is labeled with "google-contributior"
306+
- the PR is already labelled with the above labels (e.g. "documentation", "services", "tools", etc.) and has a reviewer assigned.
302307
- Check if the PR is following the contribution guidelines.
303308
- If it's not following the guidelines, recommend or add a comment to the PR that points to the contribution guidelines (https://github.com/google/adk-python/blob/main/CONTRIBUTING.md).
304309
- If it's following the guidelines, recommend or add a label to the PR.

contributing/samples/adk_pr_triaging_agent/main.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
# limitations under the License.
1414

1515
import asyncio
16+
import logging
1617
import time
1718

1819
from adk_pr_triaging_agent import agent
@@ -21,11 +22,14 @@
2122
from adk_pr_triaging_agent.settings import REPO
2223
from adk_pr_triaging_agent.utils import call_agent_async
2324
from adk_pr_triaging_agent.utils import parse_number_string
25+
from google.adk.cli.utils import logs
2426
from google.adk.runners import InMemoryRunner
2527

2628
APP_NAME = "adk_pr_triaging_app"
2729
USER_ID = "adk_pr_triaging_user"
2830

31+
logs.setup_adk_logger(level=logging.DEBUG)
32+
2933

3034
async def main():
3135
runner = InMemoryRunner(

contributing/samples/built_in_multi_tools/agent.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717

1818
from dotenv import load_dotenv
1919
from google.adk import Agent
20-
from google.adk.tools.google_search_tool import google_search
20+
from google.adk.tools.google_search_tool import GoogleSearchTool
2121
from google.adk.tools.tool_context import ToolContext
2222
from google.adk.tools.vertex_ai_search_tool import VertexAiSearchTool
2323

@@ -57,7 +57,9 @@ def roll_die(sides: int, tool_context: ToolContext) -> int:
5757
""",
5858
tools=[
5959
roll_die,
60-
VertexAiSearchTool(data_store_id=VERTEXAI_DATASTORE_ID),
61-
google_search,
60+
VertexAiSearchTool(
61+
data_store_id=VERTEXAI_DATASTORE_ID, bypass_multi_tools_limit=True
62+
),
63+
GoogleSearchTool(bypass_multi_tools_limit=True),
6264
],
6365
)

0 commit comments

Comments
 (0)