mirror of
https://github.com/xtekky/gpt4free.git
synced 2025-12-06 02:30:41 -08:00
Add copilot github action
This commit is contained in:
parent
b955620aeb
commit
7f4d9ba82b
2 changed files with 248 additions and 0 deletions
33
.github/workflows/copilot.yml
vendored
Normal file
33
.github/workflows/copilot.yml
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
name: AI Code Reviewer
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types:
|
||||
- opened
|
||||
- synchronize
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Repo
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: "3.x"
|
||||
cache: 'pip'
|
||||
- name: Install Requirements
|
||||
run: pip install -r requirements.txt
|
||||
- name: Install PyGithub
|
||||
run: pip install PyGithub
|
||||
- name: AI Code Review
|
||||
run: python -m etc.tool.copilot
|
||||
215
etc/tool/copilot.py
Normal file
215
etc/tool/copilot.py
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.append(str(Path(__file__).parent.parent.parent))
|
||||
|
||||
import g4f
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import requests
|
||||
from typing import Union
|
||||
from github import Github
|
||||
from github.PullRequest import PullRequest
|
||||
|
||||
g4f.debug.logging = True
|
||||
g4f.debug.version_check = False
|
||||
|
||||
GITHUB_TOKEN = os.getenv('GITHUB_TOKEN')
|
||||
G4F_PROVIDER = os.getenv('G4F_PROVIDER') or g4f.Provider.OpenaiChat
|
||||
G4F_MODEL = os.getenv('G4F_MODEL') or g4f.models.gpt_4
|
||||
|
||||
def get_pr_details(github: Github) -> PullRequest:
|
||||
"""
|
||||
Rteurns the details of the pull request from GitHub.
|
||||
|
||||
Returns:
|
||||
PullRequest: A PullRequest instance.
|
||||
"""
|
||||
with open(os.getenv('GITHUB_EVENT_PATH', ''), 'r') as file:
|
||||
data = json.load(file)
|
||||
|
||||
repo = github.get_repo(f"{data['repository']['owner']['login']}/{data['repository']['name']}")
|
||||
pull = repo.get_pull(data['number'])
|
||||
|
||||
return pull
|
||||
|
||||
def get_diff(diff_url: str) -> str:
|
||||
"""
|
||||
Fetches the diff of the pull request.
|
||||
|
||||
Args:
|
||||
pull (PullRequest): Pull request.
|
||||
|
||||
Returns:
|
||||
str or None: The diff of the pull request or None if not available.
|
||||
"""
|
||||
response = requests.get(diff_url)
|
||||
response.raise_for_status()
|
||||
return response.text
|
||||
|
||||
def read_json(text: str) -> dict:
|
||||
match = re.search(r"```(json|)\n(?P<code>[\S\s]+?)\n```", text)
|
||||
if match:
|
||||
text = match.group("code")
|
||||
try:
|
||||
return json.loads(text.strip())
|
||||
except json.JSONDecodeError:
|
||||
print("No valid json:", text)
|
||||
return {}
|
||||
|
||||
def read_text(text: str) -> dict:
|
||||
match = re.search(r"```(markdown|)\n(?P<text>[\S\s]+?)\n```", text)
|
||||
if match:
|
||||
return match.group("text")
|
||||
return text
|
||||
|
||||
def get_ai_response(prompt, as_json: bool = True) -> Union[dict, str]:
|
||||
"""
|
||||
Gets a response from g4f API based on the prompt.
|
||||
|
||||
Args:
|
||||
prompt (str): The prompt to send to g4f.
|
||||
|
||||
Returns:
|
||||
dict: The parsed response from g4f.
|
||||
"""
|
||||
response = g4f.ChatCompletion.create(
|
||||
G4F_MODEL,
|
||||
[{'role': 'user', 'content': prompt}],
|
||||
G4F_PROVIDER,
|
||||
ignore_stream_and_auth=True
|
||||
)
|
||||
if as_json:
|
||||
return read_json(response)
|
||||
return read_text(response)
|
||||
|
||||
def analyze_code(pull: PullRequest, diff: str)-> list:
|
||||
"""
|
||||
Analyzes the code changes in the pull request.
|
||||
|
||||
Args:
|
||||
diff (str): The diff of the pull request.
|
||||
pr_details (dict): Details of the pull request.
|
||||
|
||||
Returns:
|
||||
list: List of comments generated by the analysis.
|
||||
"""
|
||||
comments = []
|
||||
changed_lines = []
|
||||
current_file_path = None
|
||||
offset_line = 0
|
||||
|
||||
for line in diff.split('\n'):
|
||||
if line.startswith('+++ b/'):
|
||||
current_file_path = line[6:]
|
||||
elif line.startswith('@@'):
|
||||
match = re.search(r'\+([0-9]+?),', line)
|
||||
if match:
|
||||
offset_line = int(match.group(1))
|
||||
elif current_file_path:
|
||||
if line.startswith('\\') or line.startswith('diff'):
|
||||
prompt = create_prompt(changed_lines, pull, current_file_path, offset_line)
|
||||
response = get_ai_response(prompt)
|
||||
for review in response.get('reviews', []):
|
||||
review['path'] = current_file_path
|
||||
comments.append(review)
|
||||
changed_lines = []
|
||||
current_file_path = None
|
||||
elif not line.startswith('-'):
|
||||
changed_lines.append(line)
|
||||
|
||||
return comments
|
||||
|
||||
def create_prompt(changed_lines: list, pull: PullRequest, file_path: str, offset_line: int):
|
||||
"""
|
||||
Creates a prompt for the g4f model.
|
||||
|
||||
Args:
|
||||
diff (str): The line of code to analyze.
|
||||
pr_details (dict): Details of the pull request.
|
||||
|
||||
Returns:
|
||||
str: The generated prompt.
|
||||
"""
|
||||
code = "\n".join([f"{offset_line+idx}:{line}" for idx, line in enumerate(changed_lines)])
|
||||
print("Code:", code)
|
||||
example = '{"reviews": [{"line": <line_number>, "body": "<review comment>"}]}'
|
||||
return f"""Your task is to review pull requests. Instructions:
|
||||
- Provide the response in following JSON format: {example}
|
||||
- Do not give positive comments or compliments.
|
||||
- Provide comments and suggestions ONLY if there is something to improve, otherwise "reviews" should be an empty array.
|
||||
- Write the comment in GitHub Markdown format.
|
||||
- Use the given description only for the overall context and only comment the code.
|
||||
- IMPORTANT: NEVER suggest adding comments to the code.
|
||||
|
||||
Review the following code diff in the file "{file_path}" and take the pull request title and description into account when writing the response.
|
||||
|
||||
Pull request title: {pull.title}
|
||||
Pull request description:
|
||||
---
|
||||
{pull.body}
|
||||
---
|
||||
|
||||
Each line is prefixed by its number. Code to review:
|
||||
```
|
||||
{code}
|
||||
```
|
||||
"""
|
||||
|
||||
def create_review_prompt(pull: PullRequest, diff: str):
|
||||
"""
|
||||
Creates a prompt to create a review.
|
||||
|
||||
Args:
|
||||
diff (str): The line of code to analyze.
|
||||
|
||||
Returns:
|
||||
str: The generated prompt.
|
||||
"""
|
||||
return f"""Your task is to review a pull request. Instructions:
|
||||
- Your name / you are copilot.
|
||||
- Write the review in GitHub Markdown format.
|
||||
- Thank the author for contributing to the project.
|
||||
- Point out that you might leave a few comments on the files.
|
||||
|
||||
Pull request author: {pull.user.name}
|
||||
Pull request title: {pull.title}
|
||||
Pull request description:
|
||||
---
|
||||
{pull.body}
|
||||
---
|
||||
|
||||
Diff:
|
||||
```diff
|
||||
{diff}
|
||||
```
|
||||
"""
|
||||
|
||||
def main():
|
||||
try:
|
||||
github = Github(GITHUB_TOKEN)
|
||||
pull = get_pr_details(github)
|
||||
diff = get_diff(pull.diff_url)
|
||||
except Exception as e:
|
||||
print(f"Error get details: {e}")
|
||||
exit(1)
|
||||
try:
|
||||
review = get_ai_response(create_review_prompt(pull, diff), False)
|
||||
except Exception as e:
|
||||
print(f"Error create review: {e}")
|
||||
exit(1)
|
||||
try:
|
||||
comments = analyze_code(pull, diff)
|
||||
except Exception as e:
|
||||
print(f"Error analyze: {e}")
|
||||
exit(1)
|
||||
print("Comments:", comments)
|
||||
try:
|
||||
pull.create_review(body=review, comments=comments)
|
||||
except Exception as e:
|
||||
print(f"Error posting review: {e}")
|
||||
exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Add table
Add a link
Reference in a new issue