docs: Update ADK triaging agent to only triage planned issues

It also enables the ADK triaging agent to run periodically on planned but not triaged issues.

Co-authored-by: Xuan Yang <xygoogle@google.com>
PiperOrigin-RevId: 834489103
This commit is contained in:
Xuan Yang
2025-11-19 16:26:23 -08:00
committed by Copybara-Service
parent f13a11e1dc
commit 679d543f8e
3 changed files with 73 additions and 13 deletions
+6 -1
View File
@@ -2,11 +2,16 @@ name: ADK Issue Triaging Agent
on:
issues:
types: [opened, reopened]
types: [labeled]
schedule:
# Run every 6 hours to triage planned but not triaged issues
- cron: '0 */6 * * *'
jobs:
agent-triage-issues:
runs-on: ubuntu-latest
# Only run if labeled with "planned" or if it's a scheduled run
if: github.event_name == 'schedule' || github.event.label.name == 'planned'
permissions:
issues: write
contents: read
@@ -110,6 +110,42 @@ def list_unlabeled_issues(issue_count: int) -> dict[str, Any]:
return {"status": "success", "issues": unlabeled_issues}
def list_planned_untriaged_issues(issue_count: int) -> dict[str, Any]:
"""List planned issues without component labels (e.g., core, tools, etc.).
Args:
issue_count: number of issues to return
Returns:
The status of this request, with a list of issues when successful.
"""
url = f"{GITHUB_BASE_URL}/search/issues"
query = f"repo:{OWNER}/{REPO} is:open is:issue label:planned"
params = {
"q": query,
"sort": "created",
"order": "desc",
"per_page": issue_count,
"page": 1,
}
try:
response = get_request(url, params)
except requests.exceptions.RequestException as e:
return error_response(f"Error: {e}")
issues = response.get("items", [])
# Filter out issues that already have component labels
component_labels = set(LABEL_TO_OWNER.keys())
untriaged_issues = []
for issue in issues:
issue_labels = {label["name"] for label in issue.get("labels", [])}
# If the issue only has "planned" but no component labels, it's untriaged
if not (issue_labels & component_labels):
untriaged_issues.append(issue)
return {"status": "success", "issues": untriaged_issues}
def add_label_and_owner_to_issue(
issue_number: int, label: str
) -> dict[str, Any]:
@@ -241,6 +277,7 @@ root_agent = Agent(
""",
tools=[
list_unlabeled_issues,
list_planned_untriaged_issues,
add_label_and_owner_to_issue,
change_issue_type,
],
+30 -12
View File
@@ -16,6 +16,7 @@ import asyncio
import time
from adk_triaging_agent import agent
from adk_triaging_agent.agent import LABEL_TO_OWNER
from adk_triaging_agent.settings import EVENT_NAME
from adk_triaging_agent.settings import GITHUB_BASE_URL
from adk_triaging_agent.settings import ISSUE_BODY
@@ -37,21 +38,32 @@ USER_ID = "adk_triage_user"
async def fetch_specific_issue_details(issue_number: int):
"""Fetches details for a single issue if it's unlabelled."""
"""Fetches details for a single issue if it needs triaging."""
url = f"{GITHUB_BASE_URL}/repos/{OWNER}/{REPO}/issues/{issue_number}"
print(f"Fetching details for specific issue: {url}")
try:
issue_data = get_request(url)
if not issue_data.get("labels", None):
print(f"Issue #{issue_number} is unlabelled. Proceeding.")
labels = issue_data.get("labels", [])
label_names = {label["name"] for label in labels}
# Check if issue has "planned" label but no component labels
component_labels = set(LABEL_TO_OWNER.keys())
has_planned = "planned" in label_names
has_component = bool(label_names & component_labels)
if has_planned and not has_component:
print(f"Issue #{issue_number} is planned but not triaged. Proceeding.")
return {
"number": issue_data["number"],
"title": issue_data["title"],
"body": issue_data.get("body", ""),
}
else:
print(f"Issue #{issue_number} is already labelled. Skipping.")
print(
f"Issue #{issue_number} is already triaged or doesn't have"
" 'planned' label. Skipping."
)
return None
except requests.exceptions.RequestException as e:
print(f"Error fetching issue #{issue_number}: {e}")
@@ -108,26 +120,32 @@ async def main():
specific_issue = await fetch_specific_issue_details(issue_number)
if specific_issue is None:
print(
f"No unlabelled issue details found for #{issue_number} or an error"
" occurred. Skipping agent interaction."
f"No issue details found for #{issue_number} that needs triaging,"
" or an error occurred. Skipping agent interaction."
)
return
issue_title = ISSUE_TITLE or specific_issue["title"]
issue_body = ISSUE_BODY or specific_issue["body"]
prompt = (
f"A new GitHub issue #{issue_number} has been opened or"
f' reopened. Title: "{issue_title}"\nBody:'
f"A GitHub issue #{issue_number} has been labeled as 'planned'."
f' Title: "{issue_title}"\nBody:'
f' "{issue_body}"\n\nBased on the rules, recommend an'
" appropriate label and its justification."
" Then, use the 'add_label_to_issue' tool to apply the label "
"directly to this issue. Only label it, do not"
" appropriate component label and its justification."
" Then, use the 'add_label_and_owner_to_issue' tool to apply the"
" label directly to this issue. Only label it, do not"
" process any other issues."
)
else:
print(f"EVENT: Processing batch of issues (event: {EVENT_NAME}).")
issue_count = parse_number_string(ISSUE_COUNT_TO_PROCESS, default_value=3)
prompt = f"Please triage the most recent {issue_count} issues."
prompt = (
"Please use the 'list_planned_untriaged_issues' tool to find the"
f" most recent {issue_count} planned issues that haven't been"
" triaged yet (i.e., issues with 'planned' label but no component"
" labels like 'core', 'tools', etc.). Then triage each of them by"
" applying appropriate component labels."
)
response = await call_agent_async(runner, USER_ID, session.id, prompt)
print(f"<<<< Agent Final Output: {response}\n")