10000 chore: Triaging agent improvements & github workflow · devevignesh/adk-python@131957c · GitHub
[go: up one dir, main page]

Skip to content

Commit 131957c

Browse files
selcukguncopybara-github
authored andcommitted
chore: Triaging agent improvements & github workflow
* modified list issues to only return unlabelled open issues * added github workflow to run on schedule and issue open/reopen * interactive/workflow modes * readme document PiperOrigin-RevId: 771152306
1 parent 8e28587 commit 131957c

File tree

5 files changed

+360
-44
lines changed

5 files changed

+360
-44
lines changed

.github/workflows/triage.yml

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
name: ADK Issue Triaging Agent
2+
3+
on:
4+
issues:
5+
types: [opened, reopened]
6+
schedule:
7+
- cron: '0 */6 * * *' # every 6h
8+
9+
jobs:
10+
agent-triage-issues:
11+
runs-on: ubuntu-latest
12+
permissions:
13+
issues: write
14+
contents: read
15+
16+
steps:
17+
- name: Checkout repository
18+
uses: actions/checkout@v4
19+
20+
- name: Set up Python
21+
uses: actions/setup-python@v5
22+
with:
23+
python-version: '3.11'
24+
25+
- name: Install dependencies
26+
run: |
27+
python -m pip install --upgrade pip
28+
pip install requests google-adk
29+
30+
- name: Run Triaging Script
31+
env:
32+
GITHUB_TOKEN: ${{ secrets.ADK_TRIAGE_AGENT }}
33+
GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }}
34+
GOOGLE_GENAI_USE_VERTEXAI: 0
35+
OWNER: 'google'
36+
REPO: 'adk-python'
37+
INTERACTIVE: 0
38+
EVENT_NAME: ${{ github.event_name }} # 'issues', 'schedule', etc.
39+
ISSUE_NUMBER: ${{ github.event.issue.number }}
40+
ISSUE_TITLE: ${{ github.event.issue.title }}
41+
ISSUE_BODY: ${{ github.event.issue.body }}
42+
ISSUE_COUNT_TO_PROCESS: '3' # Process 3 issues at a time on schedule
43+
run: python contributing/samples/adk_triaging_agent/main.py
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# ADK Issue Triaging Assistant
2+
3+
The ADK Issue Triaging Assistant is a Python-based agent designed to help manage and triage GitHub issues for the `google/adk-python` repository. It uses a large language model to analyze new and unlabelled issues, recommend appropriate labels based on a predefined set of rules, and apply them.
4+
5+
This agent can be operated in two distinct modes: an interactive mode for local use or as a fully automated GitHub Actions workflow.
6+
7+
---
8+
9+
## Interactive Mode
10+
11+
This mode allows you to run the agent locally to review its recommendations in real-time before any changes are made to your repository's issues.
12+
13+
### Features
14+
* **Web Interface**: The agent's interactive mode can be rendered in a web browser using the ADK's `adk web` command.
15+
* **User Approval**: In interactive mode, the agent is instructed to ask for your confirmation before applying a label to a GitHub issue.
16+
17+
### Running in Interactive Mode
18+
To run the agent in interactive mode, first set the required environment variables. Then, execute the following command in your terminal:
19+
20+
```bash
21+
adk web
22+
```
23+
This will start a local server and provide a URL to access the agent's web interface in your browser.
24+
25+
---
26+
27+
## GitHub Workflow Mode
28+
29+
For automated, hands-off issue triaging, the agent can be integrated directly into your repository's CI/CD pipeline using a GitHub Actions workflow.
30+
31+
### Workflow Triggers
32+
The GitHub workflow is configured to run on specific triggers:
33+
34+
1. **Issue Events**: The workflow executes automatically whenever a new issue is `opened` or an existing one is `reopened`.
35+
36+
2. **Scheduled Runs**: The workflow also runs on a recurring schedule (every 6 hours) to process any unlabelled issues that may have been missed.
37+
38+
### Automated Labeling
39+
When running as part of the GitHub workflow, the agent operates non-interactively. It identifies the best label and applies it directly without requiring user approval. This behavior is configured by setting the `INTERACTIVE` environment variable to `0` in the workflow file.
40+
41+
### Workflow Configuration
42+
The workflow is defined in a YAML file (`.github/workflows/triage.yml`). This file contains the steps to check out the code, set up the Python environment, install dependencies, and run the triaging script with the necessary environment variables and secrets.
43+
44+
---
45+
46+
## Setup and Configuration
47+
48+
Whether running in interactive or workflow mode, the agent requires the following setup.
49+
50+
### Dependencies
51+
The agent requires the following Python libraries.
52+
53+
```bash
54+
pip install --upgrade pip
55+
pip install google-adk requests
56+
```
57+
58+
### Environment Variables
59+
The following environment variables are required for the agent to connect to the necessary services.
60+
61+
* `GITHUB_TOKEN`: **(Required)** A GitHub Personal Access Token with `issues:write` permissions. Needed for both interactive and workflow modes.
62+
* `GOOGLE_API_KEY`: **(Required)** Your API key for the Gemini API. Needed for both interactive and workflow modes.
63+
* `OWNER`: The GitHub organization or username that owns the repository (e.g., `google`). Needed for both modes.
64+
* `REPO`: The name of the GitHub repository (e.g., `adk-python`). Needed for both modes.
65+
* `INTERACTIVE`: Controls the agent's interaction mode. For the automated workflow, this is set to `0`. For interactive mode, it should be set to `1` or left unset.
66+
67+
For local execution in interactive mode, you can place these variables in a `.env` file in the project's root directory. For the GitHub workflow, they should be configured as repository secrets.
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

contributing/samples/adk_triaging_agent/agent.py

Lines changed: 71 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -13,61 +13,73 @@
1313
# limitations under the License.
1414

1515
import os
16-
import random
17-
import time
1816

1917
from google.adk import Agent
20-
from google.adk.tools.tool_context import ToolContext
21-
from google.genai import types
2218
import requests
2319

24-
# Read the PAT from the environment variable
25-
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") # Ensure you've set this in your shell
20+
GITHUB_TOKEN = os.getenv("GITHUB_TOKEN")
2621
if not GITHUB_TOKEN:
2722
raise ValueError("GITHUB_TOKEN environment variable not set")
2823

29-
# Repository information
30-
OWNER = "google"
31-
REPO = "adk-python"
24+
OWNER = os.getenv("OWNER", "google")
25+
REPO = os.getenv("REPO", "adk-python")
26+
BOT_LABEL = os.getenv("BOT_LABEL", "bot_triaged")
3227

33-
# Base URL for the GitHub API
3428
BASE_URL = "https://api.github.com"
3529

36-
# Headers including the Authorization header
3730
headers = {
3831
"Authorization": f"token {GITHUB_TOKEN}",
3932
"Accept": "application/vnd.github.v3+json",
4033
}
4134

35+
ALLOWED_LABELS = [
36+
"documentation",
37+
"services",
38+
"question",
39+
"tools",
40+
"eval",
41+
"live",
42+
"models",
43+
"tracing",
44+
"core",
45+
"web",
46+
]
4247

43-
def list_issues(per_page: int):
48+
49+
def is_interactive():
50+
return os.environ.get("INTERACTIVE", "1").lower() in ["true", "1"]
51+
52+
53+
def list_issues(issue_count: int):
4454
"""
4555
Generator to list all issues for the repository by handling pagination.
4656
4757
Args:
48-
per_page: number of pages to return per page.
58+
issue_count: number of issues to return
4959
5060
"""
51-
state = "open"
52-
# only process the 1st page for testing for now< E41F /span>
53-
page = 1
54-
results = []
55-
url = ( # :contentReference[oaicite:16]{index=16}
56-
f"{BASE_URL}/repos/{OWNER}/{REPO}/issues"
57-
)
58-
# Warning: let's only handle max 10 issues at a time to avoid bad results
59-
params = {"state": state, "per_page": per_page, "page": page}
60-
response = requests.get(url, headers=headers, params=params)
61-
response.raise_for_status() # :contentReference[oaicite:17]{index=17}
62-
issues = response.json()
61+
query = f"repo:{OWNER}/{REPO} is:open is:issue no:label"
62+
63+
unlabelled_issues = []
64+
url = f"{BASE_URL}/search/issues"
65+
66+
params = {
67+
"q": query,
68+
"sort": "created",
69+
"order": "desc",
70+
"per_page": issue_count,
71+
"page": 1,
72+
}
73+
response = requests.get(url, headers=headers, params=params, timeout=60)
74+
response.raise_for_status()
75+
json_response = response.json()
76+
issues = json_response.get("items", None)
6377
if not issues:
6478
return []
6579
for issue in issues:
66-
# Skip pull requests (issues API returns PRs as well)
67-
if "pull_request" in issue:
68-
continue
69-
results.append(issue)
70-
return results
80+
if not issue.get("labels", None) or len(issue["labels"]) == 0:
81+
unlabelled_issues.append(issue)
82+
return unlabelled_issues
7183

7284

7385
def add_label_to_issue(issue_number: str, label: str):
@@ -78,41 +90,56 @@ def add_label_to_issue(issue_number: str, label: str):
7890
issue_number: issue number of the Github issue, in string foramt.
7991
label: label to assign
8092
"""
93+
print(f"Attempting to add label '{label}' to issue #{issue_number}")
94+
if label not in ALLOWED_LABELS:
95+
error_message = (
96+
f"Error: Label '{label}' is not an allowed label. Will not apply."
97+
)
98+
print(error_message)
99+
return {"status": "error", "message": error_message, "applied_label": None}
100+
81101
url = f"{BASE_URL}/repos/{OWNER}/{REPO}/issues/{issue_number}/labels"
82-
payload = [label]
83-
response = requests.post(url, headers=headers, json=payload)
102+
payload = [label, BOT_LABEL]
103+
response = requests.post(url, headers=headers, json=payload, timeout=60)
84104
response.raise_for_status()
85105
return response.json()
86106

87107

108+
approval_instruction = (
109+
"Only label them when the user approves the labeling!"
110+
if is_interactive()
111+
else (
112+
"Do not ask for user approval for labeling! If you can't find a"
113+
" appropriate labels for the issue, do not label it."
114+
)
115+
)
116+
88117
root_agent = Agent(
89118
model="gemini-2.5-pro-preview-05-06",
90119
name="adk_triaging_assistant",
91120
description="Triage ADK issues.",
92-
instruction="""
93-
You are a Github adk-python repo triaging bot. You will help get issues, and label them.
121+
instruction=f"""
122+
You are a Github adk-python repo triaging bot. You will help get issues, and recommend a label.
123+
IMPORTANT: {approval_instruction}
94124
Here are the rules for labeling:
95125
- If the user is asking about documentation-related questions, label it with "documentation".
96126
- If it's about session, memory services, label it with "services"
97-
- If it's about UI/web, label it with "question"
127+
- If it's about UI/web, label it with "web"
128+
- If the user is asking about a question, label it with "question"
98129
- If it's related to tools, label it with "tools"
99130
- If it's about agent evalaution, then label it with "eval".
100131
- If it's about streaming/live, label it with "live".
101132
- If it's about model support(non-Gemini, like Litellm, Ollama, OpenAI models), label it with "models".
102133
- If it's about tracing, label it with "tracing".
103134
- If it's agent orchestration, agent definition, label it with "core".
104-
- If you can't find a appropriate labels for the issue, return the issues to user to decide.
135+
- If you can't find a appropriate labels for the issue, follow the previous instruction that starts with "IMPORTANT:".
136+
137+
Present the followings in an easy to read format highlighting issue number and your label.
138+
- the issue summary in a few sentence
139+
- your label recommendation and justification
105140
""",
106141
tools=[
107142
list_issues,
108143
add_label_to_issue,
109144
],
110-
generate_content_config=types.GenerateContentConfig(
111-
safety_settings=[
112-
types.SafetySetting( # avoid false alarm about rolling dice.
113-
category=types.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,
114-
threshold=types.HarmBlockThreshold.OFF,
115-
),
116-
]
117-
),
118145
)

0 commit comments

Comments
 (0)
0