Skip to content

Commit 1c445eb

Browse files
authored
Merge branch 'main' into feat/function-tools-enum-support
2 parents 3e2fcb0 + d767fcc commit 1c445eb

7 files changed

Lines changed: 72 additions & 86 deletions

File tree

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ We welcome contributions from the community! Whether it's bug reports, feature r
155155

156156
If you are to develop agent via vibe coding the [llms.txt](./llms.txt) and the [llms-full.txt](./llms-full.txt) can be used as context to LLM. While the former one is a summarized one and the later one has the full information in case your LLM has big enough context window.
157157

158+
## Community Events
159+
160+
- [Completed] ADK's 1st community meeting on Wednesday, October 15, 2025. Remember to [join our group](https://groups.google.com/g/adk-community) to get access to the [recording](https://drive.google.com/file/d/1rpXDq5NSH8-MyMeYI6_5pZ3Lhn0X9BQf/view), and [deck](https://docs.google.com/presentation/d/1_b8LG4xaiadbUUDzyNiapSFyxanc9ZgFdw7JQ6zmZ9Q/edit?slide=id.g384e60cdaca_0_658&resourcekey=0-tjFFv0VBQhpXBPCkZr0NOg#slide=id.g384e60cdaca_0_658).
161+
158162
## 📄 License
159163

160164
This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE) file for details.

contributing/samples/adk_agent_builder_assistant/instruction_embedded.template

Lines changed: 25 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,12 @@ Always reference this schema when creating configurations to ensure compliance.
120120
- **`agent_class` field**:
121121
* Always declare `agent_class` explicitly for every agent block (the loader defaults to `LlmAgent`, but we require clarity)
122122
* Use `agent_class: LlmAgent` when the agent talks directly to an LLM
123+
- **🚫 Workflow agent field ban**: Workflow orchestrators (`SequentialAgent`,
124+
`ParallelAgent`, `LoopAgent`, etc.) must NEVER include `model`, `instruction`,
125+
or `tools`. Only `LlmAgent` definitions—whether they are root agents or
126+
sub-agents—may declare those fields
127+
- **Root agent requirement**: The root configuration must always remain an
128+
`LlmAgent`. Never convert the root agent into a workflow agent.
123129
- **Workflow agent tool rule**: See **ADK Agent Types and Model Field Rules** for tool restrictions on workflow orchestrators; attach tools to their `LlmAgent` sub-agents.
124130
- **Sub-agent placement**: Place ALL sub-agent YAML files in the main project folder, NOT in `sub_agents/` subfolder
125131
- Tool paths use format: `project_name.tools.module.function_name` (must start with project folder name, no `.py` extension, all dots)
@@ -236,44 +242,25 @@ tools:
236242

237243
### ADK Knowledge and Research Tools
238244

239-
#### Remote Semantic Search
240-
- **adk_knowledge_agent**: Search ADK knowledge base for ADK examples, patterns, and documentation
241-
242-
#### Web-based Research
243-
- **google_search_agent**: Search web for ADK examples, patterns, and documentation (returns full page content as results)
244-
- **url_context_agent**: Fetch content from specific URLs when mentioned in search results or user queries (use only when specific URLs need additional fetching)
245-
246-
#### Local ADK Source Search
247-
- **search_adk_source**: Search ADK source code using regex patterns for precise code lookups
248-
* Use for finding class definitions: `"class FunctionTool"`
249-
* Use for constructor signatures: `"def __init__.*FunctionTool"`
250-
* Use for method definitions: `"def method_name"`
251-
* Returns matches with file paths, line numbers, and context
252-
* Follow up with **read_files** to get complete file contents
253-
254-
**Research Workflow for ADK Questions:**
255-
Mainly rely on **adk_knowledge_agent** for ADK questions. Use other tools only when the knowledge agent doesn't have enough information.
256-
257-
1. **search_adk_source** - Find specific code patterns with regex
258-
2. **read_files** - Read complete source files for detailed analysis
259-
3. **google_search_agent** - Find external examples and documentation
260-
4. **url_context_agent** - Fetch specific GitHub files or documentation pages
261-
262-
### When to Use Research Tools
263-
**ALWAYS use research tools when:**
264-
1. **User asks ADK questions**: Any questions about ADK concepts, APIs, usage patterns, or troubleshooting
265-
2. **Unfamiliar ADK features**: When user requests features you're not certain about
266-
3. **Agent type clarification**: When unsure about agent types, their capabilities, or configuration
267-
4. **Best practices**: When user asks for examples or best practices
268-
5. **Error troubleshooting**: When helping debug ADK-related issues
269-
6. **Agent building uncertainty**: When unsure how to create agents or what's the best practice
270-
7. **Architecture decisions**: When evaluating different approaches or patterns for agent design
271-
272-
**Research Tool Usage Patterns:**
273-
274-
**Default Research Tool:**
275-
Use **adk_knowledge_agent** as the primary research tool for ADK questions.
276-
Use other tools only when the knowledge agent doesn't have enough information.
245+
**Default research tool**: Use `adk_knowledge_agent` first for ADK concepts, APIs,
246+
examples, and troubleshooting. Switch to the tools below only when the
247+
knowledge agent lacks the needed information.
248+
249+
- `search_adk_source`: Regex search across ADK source for classes, methods, and
250+
signatures; follow up with `read_files` for full context.
251+
- `google_search_agent`: Broader web search for ADK-related examples or docs.
252+
- `url_context_agent`: Fetch content from specific URLs returned by search
253+
results.
254+
255+
**Trigger research when** users ask ADK questions, request unfamiliar features,
256+
need agent-type clarification, want best practices, hit errors, express
257+
uncertainty about architecture, or you otherwise need authoritative guidance.
258+
259+
**Recommended research sequence** (stop once you have enough information):
260+
1. `adk_knowledge_agent`
261+
2. `search_adk_source` → `read_files`
262+
3. `google_search_agent`
263+
4. `url_context_agent`
277264

278265
**For ADK Code Questions (NEW - Preferred Method):**
279266
1. **search_adk_source** - Find exact code patterns:

contributing/samples/adk_pr_triaging_agent/agent.py

Lines changed: 21 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -27,18 +27,18 @@
2727
from google.adk import Agent
2828
import requests
2929

30-
LABEL_TO_OWNER = {
31-
"documentation": "polong-lin",
32-
"services": "DeanChensj",
33-
"tools": "seanzhou1023",
34-
"mcp": "seanzhou1023",
35-
"eval": "ankursharmas",
36-
"live": "hangfei",
37-
"models": "genquan9",
38-
"tracing": "Jacksunwei",
39-
"core": "Jacksunwei",
40-
"web": "wyf7107",
41-
}
30+
ALLOWED_LABELS = [
31+
"documentation",
32+
"services",
33+
"tools",
34+
"mcp",
35+
"eval",
36+
"live",
37+
"models",
38+
"tracing",
39+
"core",
40+
"web",
41+
]
4242

4343
CONTRIBUTING_MD = read_file(
4444
Path(__file__).resolve().parents[3] / "CONTRIBUTING.md"
@@ -158,19 +158,19 @@ def get_pull_request_details(pr_number: int) -> str:
158158
return error_response(str(e))
159159

160160

161-
def add_label_and_reviewer_to_pr(pr_number: int, label: str) -> dict[str, Any]:
162-
"""Adds a specified label and requests a review from a mapped reviewer on a PR.
161+
def add_label_to_pr(pr_number: int, label: str) -> dict[str, Any]:
162+
"""Adds a specified label on a pull request.
163163
164164
Args:
165165
pr_number: the number of the Github pull request
166166
label: the label to add
167167
168168
Returns:
169-
The the status of this request, with the applied label and assigned
170-
reviewer when successful.
169+
The the status of this request, with the applied label and response when
170+
successful.
171171
"""
172-
print(f"Attempting to add label '{label}' and a reviewer to PR #{pr_number}")
173-
if label not in LABEL_TO_OWNER:
172+
print(f"Attempting to add label '{label}' to PR #{pr_number}")
173+
if label not in ALLOWED_LABELS:
174174
return error_response(
175175
f"Error: Label '{label}' is not an allowed label. Will not apply."
176176
)
@@ -186,31 +186,10 @@ def add_label_and_reviewer_to_pr(pr_number: int, label: str) -> dict[str, Any]:
186186
except requests.exceptions.RequestException as e:
187187
return error_response(f"Error: {e}")
188188

189-
owner = LABEL_TO_OWNER.get(label, None)
190-
if not owner:
191-
return {
192-
"status": "warning",
193-
"message": (
194-
f"{response}\n\nLabel '{label}' does not have an owner. Will not"
195-
" assign."
196-
),
197-
"applied_label": label,
198-
}
199-
reviewer_url = f"{GITHUB_BASE_URL}/repos/{OWNER}/{REPO}/pulls/{pr_number}/requested_reviewers"
200-
reviewer_payload = {"reviewers": [owner]}
201-
try:
202-
post_request(reviewer_url, reviewer_payload)
203-
except requests.exceptions.RequestException as e:
204-
return {
205-
"status": "warning",
206-
"message": f"Reviewer not assigned: {e}",
207-
"applied_label": label,
208-
}
209-
210189
return {
211190
"status": "success",
212191
"applied_label": label,
213-
"assigned_reviewer": owner,
192+
"response": response,
214193
}
215194

216195

@@ -252,7 +231,6 @@ def add_comment_to_pr(pr_number: int, comment: str) -> dict[str, Any]:
252231
Your core responsibility includes:
253232
- Get the pull request details.
254233
- Add a label to the pull request.
255-
- Assign a reviewer to the pull request.
256234
- Check if the pull request is following the contribution guidelines.
257235
- Add a comment to the pull request if it's not following the guidelines.
258236
@@ -303,7 +281,7 @@ def add_comment_to_pr(pr_number: int, comment: str) -> dict[str, Any]:
303281
- Skip the PR (i.e. do not label or comment) if any of the following is true:
304282
- the PR is closed
305283
- 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.
284+
- the PR is already labelled with the above labels (e.g. "documentation", "services", "tools", etc.).
307285
- Check if the PR is following the contribution guidelines.
308286
- 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).
309287
- If it's following the guidelines, recommend or add a label to the PR.
@@ -312,12 +290,11 @@ def add_comment_to_pr(pr_number: int, comment: str) -> dict[str, Any]:
312290
Present the followings in an easy to read format highlighting PR number and your label.
313291
- The PR summary in a few sentence
314292
- The label you recommended or added with the justification
315-
- The owner of the label if you assigned a reviewer to the PR
316293
- The comment you recommended or added to the PR with the justification
317294
""",
318295
tools=[
319296
get_pull_request_details,
320-
add_label_and_reviewer_to_pr,
297+
add_label_to_pr,
321298
add_comment_to_pr,
322299
],
323300
)

src/google/adk/a2a/converters/request_converter.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,16 @@ def convert_a2a_request_to_agent_run_request(
106106
if not request.message:
107107
raise ValueError('Request message cannot be None')
108108

109+
custom_metadata = {}
110+
if request.metadata:
111+
custom_metadata['a2a_metadata'] = request.metadata
112+
109113
return AgentRunRequest(
110114
user_id=_get_user_id(request),
111115
session_id=request.context_id,
112116
new_message=genai_types.Content(
113117
role='user',
114118
parts=[part_converter(part) for part in request.message.parts],
115119
),
116-
run_config=RunConfig(),
120+
run_config=RunConfig(custom_metadata=custom_metadata),
117121
)

src/google/adk/agents/run_config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from enum import Enum
1818
import logging
1919
import sys
20+
from typing import Any
2021
from typing import Optional
2122

2223
from google.genai import types
@@ -118,6 +119,9 @@ class RunConfig(BaseModel):
118119
- Less than or equal to 0: This allows for unbounded number of llm calls.
119120
"""
120121

122+
custom_metadata: Optional[dict[str, Any]] = None
123+
"""Custom metadata for the current invocation."""
124+
121125
@field_validator('max_llm_calls', mode='after')
122126
@classmethod
123127
def validate_max_llm_calls(cls, value: int) -> int:

src/google/adk/evaluation/local_eval_service.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,10 +184,10 @@ async def run_evaluation(inference_result):
184184
async def _evaluate_single_inference_result(
185185
self, inference_result: InferenceResult, evaluate_config: EvaluateConfig
186186
) -> tuple[InferenceResult, EvalCaseResult]:
187-
"""Returns EvalCaseResult for the given inference result.
187+
"""Returns the inference result and its corresponding EvalCaseResult.
188188
189189
A single inference result can have multiple invocations. For each
190-
invocaiton, this method evaluates the metrics present in evaluate config.
190+
invocation, this method evaluates the metrics present in evaluate config.
191191
192192
The EvalCaseResult contains scores for each metric per invocation and the
193193
overall score.
@@ -432,9 +432,10 @@ async def _perform_inference_sigle_eval_item(
432432
# We intentionally catch the Exception as we don't failures to affect
433433
# other inferences.
434434
logger.error(
435-
'Inference failed for eval case `%s` with error %s',
435+
'Inference failed for eval case `%s` with error %s.',
436436
eval_case.eval_id,
437437
e,
438+
exc_info=True,
438439
)
439440
inference_result.status = InferenceStatus.FAILURE
440441
inference_result.error_message = str(e)

tests/unittests/a2a/converters/test_request_converter.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ def test_convert_a2a_request_basic(self):
165165
request.message = mock_message
166166
request.context_id = "test_context_123"
167167
request.call_context = mock_call_context
168+
request.metadata = {"test_key": "test_value"}
168169

169170
# Create proper genai_types.Part objects instead of mocks
170171
mock_genai_part1 = genai_types.Part(text="test part 1")
@@ -185,6 +186,9 @@ def test_convert_a2a_request_basic(self):
185186
assert result.new_message.role == "user"
186187
assert result.new_message.parts == [mock_genai_part1, mock_genai_part2]
187188
assert isinstance(result.run_config, RunConfig)
189+
assert result.run_config.custom_metadata == {
190+
"a2a_metadata": {"test_key": "test_value"}
191+
}
188192

189193
# Verify calls
190194
assert mock_convert_part.call_count == 2
@@ -212,6 +216,7 @@ def test_convert_a2a_request_empty_parts(self):
212216
request.message = mock_message
213217
request.context_id = "test_context_123"
214218
request.call_context = None
219+
request.metadata = {}
215220

216221
# Act
217222
result = convert_a2a_request_to_agent_run_request(
@@ -241,6 +246,7 @@ def test_convert_a2a_request_none_context_id(self):
241246
request.message = mock_message
242247
request.context_id = None
243248
request.call_context = None
249+
request.metadata = {}
244250

245251
# Create proper genai_types.Part object instead of mock
246252
mock_genai_part = genai_types.Part(text="test part")
@@ -272,6 +278,7 @@ def test_convert_a2a_request_no_auth(self):
272278
request.message = mock_message
273279
request.context_id = "session_123"
274280
request.call_context = None
281+
request.metadata = {}
275282

276283
# Create proper genai_types.Part object instead of mock
277284
mock_genai_part = genai_types.Part(text="test part")
@@ -313,6 +320,7 @@ def test_end_to_end_conversion_with_auth_user(self):
313320
request.call_context = mock_call_context
314321
request.message = mock_message
315322
request.context_id = "mysession"
323+
request.metadata = {}
316324

317325
# Create proper genai_types.Part object instead of mock
318326
mock_genai_part = genai_types.Part(text="test part")
@@ -344,6 +352,7 @@ def test_end_to_end_conversion_with_fallback_user(self):
344352
request.call_context = None
345353
request.message = mock_message
346354
request.context_id = "test_session_456"
355+
request.metadata = {}
347356

348357
# Create proper genai_types.Part object instead of mock
349358
mock_genai_part = genai_types.Part(text="test part")

0 commit comments

Comments
 (0)