返回提交历史
Modified
etc/tool/copilot.py
+160
-66
XFEstudio/gpt4free
update etc/tool/copilot.py
294b25de
代码差异
1 个文件
+160
-66
@@ -20,6 +20,16 @@ GITHUB_REPOSITORY = os.getenv('GITHUB_REPOSITORY')
20
20
G4F_PROVIDER = os.getenv('G4F_PROVIDER')
21
21
G4F_MODEL = os.getenv('G4F_MODEL') or g4f.models.gpt_4
22
22
23
def get_github_token():
24
token = os.getenv('GITHUB_TOKEN')
25
if not token:
26
raise ValueError("GITHUB_TOKEN environment variable is not set")
27
print(f"Token length: {len(token)}")
28
print(f"Token (masked): {'*' * (len(token) - 4) + token[-4:]}")
29
if len(token) != 40 or not token.isalnum():
30
raise ValueError("GITHUB_TOKEN appears to be invalid (should be 40 alphanumeric characters)")
31
return token
32
23
33
def get_pr_details(github: Github) -> PullRequest:
24
34
"""
25
35
Retrieves the details of the pull request from GitHub.
@@ -30,15 +40,24 @@ def get_pr_details(github: Github) -> PullRequest:
30
40
Returns:
31
41
PullRequest: An object representing the pull request.
32
42
"""
33
with open('./pr_number', 'r') as file:
34
pr_number = file.read().strip()
43
pr_number = os.getenv('PR_NUMBER')
35
44
if not pr_number:
36
return
37
38
repo = github.get_repo(GITHUB_REPOSITORY)
39
pull = repo.get_pull(int(pr_number))
45
print("PR_NUMBER environment variable is not set.")
46
return None
40
47
41
return pull
48
try:
49
print(f"Attempting to get repo: {GITHUB_REPOSITORY}")
50
repo = github.get_repo(GITHUB_REPOSITORY)
51
print(f"Successfully got repo: {repo.full_name}")
52
53
print(f"Attempting to get pull request: {pr_number}")
54
pull = repo.get_pull(int(pr_number))
55
print(f"Successfully got pull request: #{pull.number}")
56
57
return pull
58
except Exception as e:
59
print(f"Error in get_pr_details: {e}")
60
return None
42
61
43
62
def get_diff(diff_url: str) -> str:
44
63
"""
@@ -99,15 +118,36 @@ def get_ai_response(prompt: str, as_json: bool = True) -> Union[dict, str]:
99
118
Returns:
100
119
Union[dict, str]: The parsed response from g4f, either as a dictionary or a string.
101
120
"""
102
response = g4f.ChatCompletion.create(
103
G4F_MODEL,
104
[{'role': 'user', 'content': prompt}],
105
G4F_PROVIDER,
106
ignore_stream_and_auth=True
107
)
108
return read_json(response) if as_json else read_text(response)
109
110
def analyze_code(pull: PullRequest, diff: str)-> list[dict]:
121
max_retries = 5
122
providers = [None, 'Chatgpt4Online', 'OpenaiChat', 'Bing', 'Ai4Chat', 'NexraChatGPT']
123
124
for provider in providers:
125
for _ in range(max_retries):
126
try:
127
response = g4f.chat.completions.create(
128
G4F_MODEL,
129
[{'role': 'user', 'content': prompt}],
130
provider,
131
ignore_stream_and_auth=True
132
)
133
if as_json:
134
parsed_response = read_json(response)
135
if parsed_response and 'reviews' in parsed_response:
136
return parsed_response
137
else:
138
parsed_response = read_text(response)
139
if parsed_response.strip():
140
return parsed_response
141
except Exception as e:
142
print(f"Error with provider {provider}: {e}")
143
144
# If all retries and providers fail, return a default response
145
if as_json:
146
return {"reviews": []}
147
else:
148
return "AI Code Review: Unable to generate a detailed response. Please review the changes manually."
149
150
def analyze_code(pull: PullRequest, diff: str) -> list[dict]:
111
151
"""
112
152
Analyzes the code changes in the pull request.
113
153
@@ -123,28 +163,34 @@ def analyze_code(pull: PullRequest, diff: str)-> list[dict]:
123
163
current_file_path = None
124
164
offset_line = 0
125
165
126
for line in diff.split('\n'):
127
if line.startswith('+++ b/'):
128
current_file_path = line[6:]
129
changed_lines = []
130
elif line.startswith('@@'):
131
match = re.search(r'\+([0-9]+?),', line)
132
if match:
133
offset_line = int(match.group(1))
134
elif current_file_path:
135
if (line.startswith('\\') or line.startswith('diff')) and changed_lines:
136
prompt = create_analyze_prompt(changed_lines, pull, current_file_path)
137
response = get_ai_response(prompt)
138
for review in response.get('reviews', []):
139
review['path'] = current_file_path
140
comments.append(review)
141
current_file_path = None
142
elif line.startswith('-'):
143
changed_lines.append(line)
144
else:
145
changed_lines.append(f"{offset_line}:{line}")
146
offset_line += 1
147
166
try:
167
for line in diff.split('\n'):
168
if line.startswith('+++ b/'):
169
current_file_path = line[6:]
170
changed_lines = []
171
elif line.startswith('@@'):
172
match = re.search(r'\+([0-9]+?),', line)
173
if match:
174
offset_line = int(match.group(1))
175
elif current_file_path:
176
if (line.startswith('\\') or line.startswith('diff')) and changed_lines:
177
prompt = create_analyze_prompt(changed_lines, pull, current_file_path)
178
response = get_ai_response(prompt)
179
for review in response.get('reviews', []):
180
review['path'] = current_file_path
181
comments.append(review)
182
current_file_path = None
183
elif line.startswith('-'):
184
changed_lines.append(line)
185
else:
186
changed_lines.append(f"{offset_line}:{line}")
187
offset_line += 1
188
except Exception as e:
189
print(f"Error in analyze_code: {e}")
190
191
if not comments:
192
print("No comments generated by analyze_code")
193
148
194
return comments
149
195
150
196
def create_analyze_prompt(changed_lines: list[str], pull: PullRequest, file_path: str):
@@ -194,57 +240,105 @@ def create_review_prompt(pull: PullRequest, diff: str):
194
240
Returns:
195
241
str: The generated prompt for review.
196
242
"""
243
description = pull.body if pull.body else "No description provided."
197
244
return f"""Your task is to review a pull request. Instructions:
198
245
- Write in name of g4f copilot. Don't use placeholder.
199
246
- Write the review in GitHub Markdown format.
200
247
- Thank the author for contributing to the project.
248
- If no issues are found, still provide a brief summary of the changes.
201
249
202
Pull request author: {pull.user.name}
203
Pull request title: {pull.title}
250
Pull request author: {pull.user.name or "Unknown"}
251
Pull request title: {pull.title or "Untitled Pull Request"}
204
252
Pull request description:
205
253
---
206
{pull.body}
254
{description}
207
255
---
208
256
209
257
Diff:
210
258
```diff
211
259
{diff}
212
260
```
261
262
Please provide a comprehensive review of the changes, highlighting any potential issues or improvements, or summarizing the changes if no issues are found.
213
263
"""
214
264
215
265
def main():
216
266
try:
217
github = Github(GITHUB_TOKEN)
267
github_token = get_github_token()
268
except ValueError as e:
269
print(f"Error: {str(e)}")
270
return
271
272
if not GITHUB_REPOSITORY or not os.getenv('PR_NUMBER'):
273
print("Error: GITHUB_REPOSITORY or PR_NUMBER environment variables are not set.")
274
return
275
276
print(f"GITHUB_REPOSITORY: {GITHUB_REPOSITORY}")
277
print(f"PR_NUMBER: {os.getenv('PR_NUMBER')}")
278
print("GITHUB_TOKEN is set")
279
280
try:
281
github = Github(github_token)
282
283
# Test GitHub connection
284
print("Testing GitHub connection...")
285
try:
286
user = github.get_user()
287
print(f"Successfully authenticated as: {user.login}")
288
except Exception as e:
289
print(f"Error authenticating: {str(e)}")
290
print(f"Error type: {type(e).__name__}")
291
print(f"Error args: {e.args}")
292
return
293
294
# If connection is successful, proceed with PR details
218
295
pull = get_pr_details(github)
219
296
if not pull:
220
print(f"No PR number found")
221
exit()
297
print(f"No PR number found or invalid PR number")
298
return
299
print(f"Successfully fetched PR #{pull.number}")
222
300
if pull.get_reviews().totalCount > 0 or pull.get_issue_comments().totalCount > 0:
223
301
print(f"Has already a review")
224
exit()
302
return
303
225
304
diff = get_diff(pull.diff_url)
305
review = "AI Code Review: Unable to generate a detailed response."
306
comments = []
307
308
try:
309
review = get_ai_response(create_review_prompt(pull, diff), False)
310
comments = analyze_code(pull, diff)
311
except Exception as analysis_error:
312
print(f"Error during analysis: {analysis_error}")
313
review += f" Error during analysis: {str(analysis_error)[:200]}"
314
315
print("Comments:", comments)
316
317
review_body = review if review and review.strip() else "AI Code Review"
318
if not review_body.strip():
319
review_body = "AI Code Review: No specific issues found."
320
321
try:
322
if comments:
323
pull.create_review(body=review_body, comments=comments, event='COMMENT')
324
else:
325
pull.create_review(body=review_body, event='COMMENT')
326
print("Review posted successfully")
327
except Exception as post_error:
328
print(f"Error posting review: {post_error}")
329
error_message = f"AI Code Review: An error occurred while posting the review. Error: {str(post_error)[:200]}. Please review the changes manually."
330
pull.create_issue_comment(body=error_message)
331
226
332
except Exception as e:
227
print(f"Error get details: {e.__class__.__name__}: {e}")
228
exit(1)
229
try:
230
review = get_ai_response(create_review_prompt(pull, diff), False)
231
except Exception as e:
232
print(f"Error create review: {e}")
233
exit(1)
234
try:
235
comments = analyze_code(pull, diff)
236
except Exception as e:
237
print(f"Error analyze: {e}")
238
exit(1)
239
print("Comments:", comments)
240
try:
241
if comments:
242
pull.create_review(body=review, comments=comments)
243
else:
244
pull.create_issue_comment(body=review)
245
except Exception as e:
246
print(f"Error posting review: {e}")
247
exit(1)
333
print(f"Unexpected error in main: {e.__class__.__name__}: {e}")
334
try:
335
if 'pull' in locals():
336
error_message = f"AI Code Review: An error occurred while processing this pull request. Error: {str(e)[:200]}. Please review the changes manually."
337
pull.create_issue_comment(body=error_message)
338
else:
339
print("Unable to post error message: Pull request object not available")
340
except Exception as post_error:
341
print(f"Failed to post error message to pull request: {post_error}")
248
342
249
343
if __name__ == "__main__":
250
344
main()