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') 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[\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[\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') and changed_lines: prompt = create_prompt(changed_lines, pull, current_file_path) 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(f"{offset_line}:{line}") offset_line += 1 return comments def create_prompt(changed_lines: list, pull: PullRequest, file_path: str): """ 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(changed_lines) example = '{"reviews": [{"line": , "body": ""}]}' 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()