docs: Update ADK release analyzer to use Gemini 3 Pro with retry and improve file filtering

Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 861784867
This commit is contained in:
Xuan Yang
2026-01-27 10:31:03 -08:00
committed by Copybara-Service
parent 3bcd8f7f7a
commit 2155a35c51
3 changed files with 37 additions and 13 deletions
@@ -57,8 +57,25 @@ from google.adk import Agent
from google.adk.agents.loop_agent import LoopAgent from google.adk.agents.loop_agent import LoopAgent
from google.adk.agents.readonly_context import ReadonlyContext from google.adk.agents.readonly_context import ReadonlyContext
from google.adk.agents.sequential_agent import SequentialAgent from google.adk.agents.sequential_agent import SequentialAgent
from google.adk.models import Gemini
from google.adk.tools.exit_loop_tool import exit_loop from google.adk.tools.exit_loop_tool import exit_loop
from google.adk.tools.tool_context import ToolContext from google.adk.tools.tool_context import ToolContext
from google.genai import types
# Retry configuration for handling API rate limits and overload
_RETRY_OPTIONS = types.HttpRetryOptions(
initial_delay=10,
attempts=8,
exp_base=2,
max_delay=300,
http_status_codes=[429, 503],
)
# Use gemini-3-pro-preview for planning and summary (better quality)
GEMINI_PRO_WITH_RETRY = Gemini(
model="gemini-3-pro-preview",
retry_options=_RETRY_OPTIONS,
)
# Maximum number of files per analysis group to avoid context overflow # Maximum number of files per analysis group to avoid context overflow
MAX_FILES_PER_GROUP = 5 MAX_FILES_PER_GROUP = 5
@@ -249,7 +266,7 @@ def get_release_context(tool_context: ToolContext) -> dict[str, Any]:
# ============================================================================= # =============================================================================
planner_agent = Agent( planner_agent = Agent(
model="gemini-2.5-pro", model=GEMINI_PRO_WITH_RETRY,
name="release_planner", name="release_planner",
description=( description=(
"Plans the analysis by fetching release info and organizing files into" "Plans the analysis by fetching release info and organizing files into"
@@ -272,12 +289,12 @@ efficient processing.
3. Call `get_changed_files_summary` to get the list of changed files WITHOUT 3. Call `get_changed_files_summary` to get the list of changed files WITHOUT
the full patches (to save context space). the full patches (to save context space).
- **IMPORTANT**: Pass `local_repo_path="{LOCAL_REPOS_DIR_PATH}/{CODE_REPO}"` - **IMPORTANT**: Pass these parameters:
to use local git and avoid GitHub API's 300-file limit. - `local_repo_path="{LOCAL_REPOS_DIR_PATH}/{CODE_REPO}"` to avoid 300-file limit
- `path_filter="src/google/adk/"` to only get ADK source files (reduces token usage)
4. Filter and organize the files: 4. Further filter the returned files:
- **INCLUDE** only files in `src/google/adk/` directory - **EXCLUDE** test files and `__init__.py` files
- **EXCLUDE** test files, `__init__.py`, and files outside src/
- **IMPORTANT**: Do NOT exclude any file just because it has few changes. - **IMPORTANT**: Do NOT exclude any file just because it has few changes.
Even single-line changes to public APIs need documentation updates. Even single-line changes to public APIs need documentation updates.
- **PRIORITIZE** by importance: - **PRIORITIZE** by importance:
@@ -423,7 +440,7 @@ files and finding related documentation that needs updating.
file_group_analyzer = Agent( file_group_analyzer = Agent(
model="gemini-2.5-pro", model=GEMINI_PRO_WITH_RETRY,
name="file_group_analyzer", name="file_group_analyzer",
description=( description=(
"Analyzes a group of changed files and generates recommendations." "Analyzes a group of changed files and generates recommendations."
@@ -507,7 +524,7 @@ Present a summary of:
summary_agent = Agent( summary_agent = Agent(
model="gemini-2.5-pro", model=GEMINI_PRO_WITH_RETRY,
name="summary_agent", name="summary_agent",
description="Compiles recommendations and creates the GitHub issue.", description="Compiles recommendations and creates the GitHub issue.",
instruction=summary_instruction, instruction=summary_instruction,
@@ -542,7 +559,7 @@ analysis_pipeline = SequentialAgent(
# ============================================================================= # =============================================================================
root_agent = Agent( root_agent = Agent(
model="gemini-2.5-pro", model=GEMINI_PRO_WITH_RETRY,
name="adk_release_analyzer", name="adk_release_analyzer",
description=( description=(
"Analyzes ADK Python releases and generates documentation update" "Analyzes ADK Python releases and generates documentation update"
@@ -605,6 +605,7 @@ def get_changed_files_summary(
start_tag: str, start_tag: str,
end_tag: str, end_tag: str,
local_repo_path: Optional[str] = None, local_repo_path: Optional[str] = None,
path_filter: Optional[str] = None,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""Gets a summary of changed files between two releases without patches. """Gets a summary of changed files between two releases without patches.
@@ -620,6 +621,9 @@ def get_changed_files_summary(
local_repo_path: Optional absolute path to local git repo. If provided local_repo_path: Optional absolute path to local git repo. If provided
and valid, uses git diff instead of GitHub API to get complete and valid, uses git diff instead of GitHub API to get complete
file list (avoids 300-file limit). file list (avoids 300-file limit).
path_filter: Optional path prefix to filter files. Only files whose
path starts with this prefix will be included. Example:
"src/google/adk/" to only include ADK source files.
Returns: Returns:
A dictionary containing the status and a summary of changed files. A dictionary containing the status and a summary of changed files.
@@ -627,7 +631,7 @@ def get_changed_files_summary(
# Use local git if valid path is provided (avoids GitHub API 300-file limit) # Use local git if valid path is provided (avoids GitHub API 300-file limit)
if local_repo_path and os.path.isdir(os.path.join(local_repo_path, ".git")): if local_repo_path and os.path.isdir(os.path.join(local_repo_path, ".git")):
return _get_changed_files_from_local_git( return _get_changed_files_from_local_git(
local_repo_path, start_tag, end_tag, repo_owner, repo_name local_repo_path, start_tag, end_tag, repo_owner, repo_name, path_filter
) )
# Fall back to GitHub API (limited to 300 files) # Fall back to GitHub API (limited to 300 files)
@@ -689,6 +693,7 @@ def _get_changed_files_from_local_git(
end_tag: str, end_tag: str,
repo_owner: str, repo_owner: str,
repo_name: str, repo_name: str,
path_filter: Optional[str] = None,
) -> Dict[str, Any]: ) -> Dict[str, Any]:
"""Gets changed files using local git commands (no file limit). """Gets changed files using local git commands (no file limit).
@@ -698,6 +703,7 @@ def _get_changed_files_from_local_git(
end_tag: The newer tag (head) for the comparison. end_tag: The newer tag (head) for the comparison.
repo_owner: Repository owner for compare URL. repo_owner: Repository owner for compare URL.
repo_name: Repository name for compare URL. repo_name: Repository name for compare URL.
path_filter: Optional path prefix to filter files.
Returns: Returns:
A dictionary containing the status and a summary of changed files. A dictionary containing the status and a summary of changed files.
@@ -766,6 +772,10 @@ def _get_changed_files_from_local_git(
status_code = parts[0][0] # First char is the status status_code = parts[0][0] # First char is the status
filename = parts[-1] # Last part is filename (handles renames) filename = parts[-1] # Last part is filename (handles renames)
# Apply path filter if specified
if path_filter and not filename.startswith(path_filter):
continue
stats = file_stats.get( stats = file_stats.get(
filename, filename,
{ {
-3
View File
@@ -701,7 +701,6 @@ def cli_eval(
separated list of eval names and then add that as a suffix to the eval set separated list of eval names and then add that as a suffix to the eval set
file name, demarcated by a `:`. file name, demarcated by a `:`.
\b
For example, we have `sample_eval_set_file.json` file that has following the For example, we have `sample_eval_set_file.json` file that has following the
eval cases: eval cases:
sample_eval_set_file.json: sample_eval_set_file.json:
@@ -722,7 +721,6 @@ def cli_eval(
separated list of eval names and then add that as a suffix to the eval set separated list of eval names and then add that as a suffix to the eval set
file name, demarcated by a `:`. file name, demarcated by a `:`.
\b
For example, we have `sample_eval_set_id` that has following the eval cases: For example, we have `sample_eval_set_id` that has following the eval cases:
sample_eval_set_id: sample_eval_set_id:
|....... eval_1 |....... eval_1
@@ -731,7 +729,6 @@ def cli_eval(
|....... eval_4 |....... eval_4
|....... eval_5 |....... eval_5
\b
If we did: If we did:
sample_eval_set_id:eval_1,eval_2,eval_3 sample_eval_set_id:eval_1,eval_2,eval_3