1+ import os
2+ import json
3+ import urllib .request
4+ import sys
5+
6+ def get_gemini_response (api_key , prompt ):
7+ # Using the stable Gemini 2.5 Flash
8+ url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key={ api_key } "
9+ headers = {'Content-Type' : 'application/json' }
10+ data = {
11+ "contents" : [{
12+ "parts" : [{"text" : prompt }]
13+ }],
14+ "generationConfig" : {
15+ "response_mime_type" : "application/json"
16+ }
17+ }
18+
19+ req = urllib .request .Request (url , data = json .dumps (data ).encode ('utf-8' ), headers = headers )
20+ try :
21+ with urllib .request .urlopen (req ) as response :
22+ res_data = json .loads (response .read ().decode ('utf-8' ))
23+ return res_data ['candidates' ][0 ]['content' ]['parts' ][0 ]['text' ]
24+ except urllib .error .HTTPError as e :
25+ print (f"Gemini API Error ({ e .code } ): { e .reason } " , file = sys .stderr )
26+ try :
27+ error_body = e .read ().decode ('utf-8' )
28+ print (f"Error details: { error_body } " , file = sys .stderr )
29+ except :
30+ pass
31+ return None
32+ except Exception as e :
33+ print (f"Error calling Gemini API: { e } " , file = sys .stderr )
34+ return None
35+
36+ def main ():
37+ api_key = os .getenv ("GEMINI_API_KEY" )
38+ issue_title = os .getenv ("ISSUE_TITLE" )
39+ issue_body = os .getenv ("ISSUE_BODY" )
40+
41+ if not api_key :
42+ print ("GEMINI_API_KEY not found" , file = sys .stderr )
43+ sys .exit (1 )
44+
45+ if not issue_title and not issue_body :
46+ print ("Error: ISSUE_TITLE and ISSUE_BODY are both empty. Triage skipped." , file = sys .stderr )
47+ sys .exit (0 ) # Exit gracefully so the workflow doesn't just fail without a reason
48+
49+ prompt = f"""
50+ You are an expert software engineer and triage assistant.
51+ Analyze the following GitHub Issue details and suggest appropriate labels.
52+
53+ Issue Title: { issue_title }
54+ Issue Description: { issue_body }
55+
56+ Triage Criteria:
57+ - Severity:
58+ - priority: p0: Critical issues, crashes, security vulnerabilities (specifically if it mentions "crash" or "exception").
59+ - priority: p1: Important issues that block release.
60+ - priority: p2: Normal priority bugs or improvements.
61+ - priority: p3: Minor enhancements or non-critical fixes.
62+ - priority: p4: Low priority, nice-to-have eventually.
63+
64+ Return a JSON object with a 'labels' key containing an array of suggested label names.
65+ The response MUST be valid JSON.
66+ Example: {{"labels": ["priority: p2", "type: bug"]}}
67+ """
68+
69+ response_text = get_gemini_response (api_key , prompt )
70+ if response_text :
71+ try :
72+ # Clean up response text in case it has markdown wrapping
73+ if response_text .startswith ("```json" ):
74+ response_text = response_text .replace ("```json" , "" , 1 ).replace ("```" , "" , 1 ).strip ()
75+
76+ result = json .loads (response_text )
77+ labels = result .get ("labels" , [])
78+ # Print labels as a comma-separated string for GitHub Actions
79+ print ("," .join (labels ))
80+ except Exception as e :
81+ print (f"Error parsing Gemini response: { e } " , file = sys .stderr )
82+ print (f"Raw response: { response_text } " , file = sys .stderr )
83+ sys .exit (1 )
84+ else :
85+ sys .exit (1 )
86+
87+ if __name__ == "__main__" :
88+ main ()
0 commit comments