From 9491f97e5c4420c5ea2075406ebd05da75073df5 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Tue, 31 Dec 2024 00:47:55 +0900 Subject: [PATCH 01/52] add github test --- github_code.py | 188 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 github_code.py diff --git a/github_code.py b/github_code.py new file mode 100644 index 0000000..64988ba --- /dev/null +++ b/github_code.py @@ -0,0 +1,188 @@ +import os +import json +from typing import List, Dict, Any, Optional +from github import Github +from openai import OpenAI +import requests +import re + + +# Fetch input tokens from environment variables +OCTOKIT_TOKEN = os.getenv("OCTOKIT_TOKEN") +OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") +OPENAI_API_MODEL = os.getenv("OPENAI_API_MODEL") +GITHUB_EVENT_PATH = os.getenv("GITHUB_EVENT_PATH", "") + +# Initialize GitHub and OpenAI clients +github = Github(OCTOKIT_TOKEN) +openai = OpenAI(api_key=OPENAI_API_KEY) + + +class PRDetails: + def __init__(self, owner: str, repo: str, pull_number: int, title: str, description: str): + self.owner = owner + self.repo = repo + self.pull_number = pull_number + self.title = title + self.description = description + + +def get_pr_details() -> PRDetails: + with open(GITHUB_EVENT_PATH, "r") as f: + event_data = json.load(f) + + repository = event_data["repository"] + number = event_data["number"] + + repo = github.get_repo(f"{repository['owner']['login']}/{repository['name']}") + pr = repo.get_pull(number) + + return PRDetails( + owner=repository["owner"]["login"], + repo=repository["name"], + pull_number=number, + title=pr.title or "", + description=pr.body or "" + ) + + +def get_diff(owner: str, repo: str, pull_number: int) -> Optional[str]: + url = f"https://api.github.com/repos/{owner}/{repo}/pulls/{pull_number}" + headers = { + "Authorization": f"Bearer {OCTOKIT_TOKEN}", + "Accept": "application/vnd.github.v3.diff" + } + response = requests.get(url, headers=headers) + return response.text if response.status_code == 200 else None + + +def create_prompt(file: Dict[str, Any], chunk: Dict[str, Any], pr_details: PRDetails) -> str: + chunk_content = chunk["content"] + changes = "\n".join([f"{c.get('ln', c.get('ln2'))} {c['content']}" for c in chunk["changes"]]) + + return f"""Your task is to review pull requests. Instructions: +- Provide the response in the following JSON format: [{{"lineNumber": , "reviewComment": ""}}] +- Do not give positive comments or compliments. +- Provide comments and suggestions ONLY if there is something to improve, otherwise return 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['to']}" and take the pull request title and description into account when writing the response. + +Pull request title: {pr_details.title} +Pull request description: + +--- +{pr_details.description} +--- + +Git diff to review: + +```diff +{chunk_content} +{changes} +```""" + + +def get_ai_response(prompt: str) -> Optional[List[Dict[str, Any]]]: + try: + response = openai.chat.completions.create( + model=OPENAI_API_MODEL, + messages=[ + {"role": "system", "content": prompt} + ], + temperature=0.2, + max_tokens=700, + top_p=1, + frequency_penalty=0, + presence_penalty=0, + ) + content = response.choices[0].message.content.strip() or "[]" + return json.loads(content) + except Exception as e: + print(f"Error during AI response: {e}") + return None + + +def create_comment(file: Dict[str, Any], chunk: Dict[str, Any], ai_responses: List[Dict[str, Any]]) -> List[Dict[str, Any]]: + comments = [] + for ai_response in ai_responses: + if not file.get("to"): + continue + comments.append({ + "body": f"[GPT-REVIEW] {ai_response['reviewComment']}", + "path": file["to"], + "line": int(ai_response["lineNumber"]) + }) + return comments + + +def create_review_comment(owner: str, repo: str, pull_number: int, comments: List[Dict[str, Any]]) -> None: + url = f"https://api.github.com/repos/{owner}/{repo}/pulls/{pull_number}/reviews" + headers = { + "Authorization": f"Bearer {OCTOKIT_TOKEN}", + "Accept": "application/vnd.github.v3+json" + } + data = { + "comments": comments, + "event": "COMMENT" + } + response = requests.post(url, headers=headers, json=data) + if response.status_code != 200: + print(f"Failed to create review comment: {response.text}") + + +def analyze_code(parsed_diff: List[Dict[str, Any]], pr_details: PRDetails) -> List[Dict[str, Any]]: + comments = [] + for file in parsed_diff: + if file.get("to") == "/dev/null": + continue # Skip deleted files + for chunk in file.get("chunks", []): + prompt = create_prompt(file, chunk, pr_details) + ai_response = get_ai_response(prompt) + if ai_response: + new_comments = create_comment(file, chunk, ai_response) + comments.extend(new_comments) + return comments + + +def main(): + pr_details = get_pr_details() + with open(GITHUB_EVENT_PATH, "r") as f: + event_data = json.load(f) + + if event_data["action"] == "opened": + diff = get_diff(pr_details.owner, pr_details.repo, pr_details.pull_number) + elif event_data["action"] == "synchronize": + base_sha = event_data["before"] + head_sha = event_data["after"] + url = f"https://api.github.com/repos/{pr_details.owner}/{pr_details.repo}/compare/{base_sha}...{head_sha}" + headers = {"Authorization": f"Bearer {OCTOKIT_TOKEN}", "Accept": "application/vnd.github.v3.diff"} + response = requests.get(url, headers=headers) + diff = response.text if response.status_code == 200 else None + else: + print("Unsupported event.") + return + + if not diff: + print("No diff found.") + return + + parsed_diff = [{"to": match.group(1), "chunks": [{"content": match.group(2), "changes": []}]} + for match in re.finditer(r"diff --git a/(.+?) b/\1\n(.*?)\n", diff, re.S)] + + exclude_patterns = os.getenv("EXCLUDE", "").split(",") + filtered_diff = [file for file in parsed_diff if not any(re.match(pattern.strip(), file["to"]) for pattern in exclude_patterns)] + + comments = analyze_code(filtered_diff, pr_details) + if comments: + create_review_comment(pr_details.owner, pr_details.repo, pr_details.pull_number, comments) + + +if __name__ == "__main__": + try: + main() + except Exception as e: + print(f"Error: {e}") + exit(1) \ No newline at end of file -- 2.49.1 From 2158f8248147aa9ac568d0d6cc2074af505e40b1 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Tue, 31 Dec 2024 00:49:50 +0900 Subject: [PATCH 02/52] remake wrong test file --- .github/scripts/test.py | 9 +++++++++ test.py | 8 -------- 2 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 .github/scripts/test.py delete mode 100644 test.py diff --git a/.github/scripts/test.py b/.github/scripts/test.py new file mode 100644 index 0000000..dc9974b --- /dev/null +++ b/.github/scripts/test.py @@ -0,0 +1,9 @@ +import os +import json + +print(os.getenv("GITHUB_EVENT_PATH")) + +with open(os.getenv("GITHUB_EVENT_PATH"), "r") as f: + event_data = json.load(f) + +print(event_data) diff --git a/test.py b/test.py deleted file mode 100644 index ea80048..0000000 --- a/test.py +++ /dev/null @@ -1,8 +0,0 @@ -from gitea import Gitea - -g = Gitea( - "https://git.teahaven.kr", - "735a1106653ce9a63ca80667f32e93221427fecc", -) - - -- 2.49.1 From 50940ffa71ae0ddf7675bfc29deca22e5ac7f66f Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Tue, 31 Dec 2024 01:01:27 +0900 Subject: [PATCH 03/52] change test values --- .github/scripts/test.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/scripts/test.py b/.github/scripts/test.py index dc9974b..88132f6 100644 --- a/.github/scripts/test.py +++ b/.github/scripts/test.py @@ -1,9 +1,11 @@ -import os import json +import os print(os.getenv("GITHUB_EVENT_PATH")) with open(os.getenv("GITHUB_EVENT_PATH"), "r") as f: event_data = json.load(f) -print(event_data) +print(event_data['diff_url']) +print(event_data['base']['index']) +print(event_data['head']['index']) -- 2.49.1 From 2c3908f79050eb41e77c63920bbcc0f180e5c450 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Tue, 31 Dec 2024 01:11:10 +0900 Subject: [PATCH 04/52] fix json directory --- .github/scripts/test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/scripts/test.py b/.github/scripts/test.py index 88132f6..a5d1bc8 100644 --- a/.github/scripts/test.py +++ b/.github/scripts/test.py @@ -6,6 +6,6 @@ print(os.getenv("GITHUB_EVENT_PATH")) with open(os.getenv("GITHUB_EVENT_PATH"), "r") as f: event_data = json.load(f) -print(event_data['diff_url']) -print(event_data['base']['index']) -print(event_data['head']['index']) +print(event_data["pull_request"]["diff_url"]) +print(event_data["pull_request"]["base"]["index"]) +print(event_data["pull_request"]["head"]["index"]) -- 2.49.1 From fc34534f5bb1587585c6e569efcf1ca1ee0308f8 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Tue, 31 Dec 2024 01:11:57 +0900 Subject: [PATCH 05/52] fix typo 'index' to 'label' --- .github/scripts/test.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/test.py b/.github/scripts/test.py index a5d1bc8..f750fc6 100644 --- a/.github/scripts/test.py +++ b/.github/scripts/test.py @@ -7,5 +7,5 @@ with open(os.getenv("GITHUB_EVENT_PATH"), "r") as f: event_data = json.load(f) print(event_data["pull_request"]["diff_url"]) -print(event_data["pull_request"]["base"]["index"]) -print(event_data["pull_request"]["head"]["index"]) +print(event_data["pull_request"]["base"]["label"]) +print(event_data["pull_request"]["head"]["label"]) -- 2.49.1 From f0dcf788fe963dcec5fa01ad59386c054f4d5da3 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Wed, 8 Jan 2025 09:51:04 +0900 Subject: [PATCH 06/52] update test --- .github/scripts/test.py | 8 ++++++++ .github/workflows/code-review.yml | 9 +++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/.github/scripts/test.py b/.github/scripts/test.py index f750fc6..7af53e5 100644 --- a/.github/scripts/test.py +++ b/.github/scripts/test.py @@ -6,6 +6,14 @@ print(os.getenv("GITHUB_EVENT_PATH")) with open(os.getenv("GITHUB_EVENT_PATH"), "r") as f: event_data = json.load(f) +print(os.getenv("GITEA_TOKEN")) +print(os.getenv("CLAUDE_API_KEY")) +print(os.getenv("OPENAI_API_KEY")) +print(os.getenv("DEEPSEEK_API_KEY")) + +print(event_data) print(event_data["pull_request"]["diff_url"]) print(event_data["pull_request"]["base"]["label"]) print(event_data["pull_request"]["head"]["label"]) +print(event_data["before"]) +print(event_data["after"]) diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index 8047d78..b414a6c 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -25,8 +25,9 @@ jobs: - name: Run Code Review env: - GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} - CLAUDE_API_KEY: ${{ secrets.CLAUDE_API_KEY }} - OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + GITEA_TOKEN: ${{ secrets.ACCESS_TOKEN }} + CLAUDE_API_KEY: ${{ secrets.CLAUDE_TOKEN }} + OPENAI_API_KEY: ${{ secrets.OPENAI_TOKEN }} + DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number }} - run: python .github/scripts/code_review.py \ No newline at end of file + run: python .github/scripts/test.py \ No newline at end of file -- 2.49.1 From 0c4f83219efaf5347dbbc2b8b4aa4c1b199e11ee Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Wed, 8 Jan 2025 10:48:02 +0900 Subject: [PATCH 07/52] parse diff into chunk --- .github/scripts/code_review.py | 67 ++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 .github/scripts/code_review.py diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py new file mode 100644 index 0000000..c392d55 --- /dev/null +++ b/.github/scripts/code_review.py @@ -0,0 +1,67 @@ +import base64 +import os +import re +import json +from collections import defaultdict +from concurrent.futures import ThreadPoolExecutor, as_completed +from typing import Any + +import requests + +GITEA_TOKEN = os.getenv("GITEA_TOKEN", "") +HEADERS = {"Authorization": f"Bearer {GITEA_TOKEN}"} + +GITHUB_EVENT_PATH = os.getenv("GITHUB_EVENT_PATH", "") +with open(GITHUB_EVENT_PATH, "r") as f: + EVENT_DATA = json.load(f) + + +def get_diff() -> str | None: + """Get code difference between base and head from Gitea""" + url = EVENT_DATA["pull_request"]["diff_url"] + response = requests.get(url, headers=HEADERS) + response.raise_for_status() + + if response.status_code != 200: + print(f"Failed to get diff with code : {response.status_code}") + return None + return response.text + + +def parse_diff(diff: str) -> list[dict[str, Any]]: + """Parse diff into list of dicts + + Args: + diff: str, code difference between base and head + + Returns: + list[dict[str, Any]]: list of dicts, each dict represents a code chunks + """ + pattern = r"diff --git a/(.+?) b/\1\n(.*?)\n" + match_list = re.finditer(pattern, diff, re.S) + return [ + {"to": match.group(1), "chunks": [{"content": match.group(2), "changes": []}]} + for match in match_list + ] + + +def main() -> None: + """Code Reviewer for Gitea""" + + if EVENT_DATA["action"] != "opened": + print("Unsupproted event.") + return + + diff = get_diff() + if diff is None: + return + elif not diff: + print("No diff found.") + return + + parsed_diff = parse_diff(diff) + print(parsed_diff) + + +if __name__ == "__main__": + main() -- 2.49.1 From 832f6b8a5a19f999e0343dd9d0498302b93c2a37 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Wed, 8 Jan 2025 10:49:46 +0900 Subject: [PATCH 08/52] update code review yml --- .github/workflows/code-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index b414a6c..684439f 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -30,4 +30,4 @@ jobs: OPENAI_API_KEY: ${{ secrets.OPENAI_TOKEN }} DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number }} - run: python .github/scripts/test.py \ No newline at end of file + run: python .github/scripts/code_review.py \ No newline at end of file -- 2.49.1 From 37e1983c6d03c766efb4e28365e2c1888eff4ee6 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Wed, 8 Jan 2025 10:50:40 +0900 Subject: [PATCH 09/52] comment action tag until finish development --- .github/scripts/code_review.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index c392d55..0a734c6 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -48,9 +48,9 @@ def parse_diff(diff: str) -> list[dict[str, Any]]: def main() -> None: """Code Reviewer for Gitea""" - if EVENT_DATA["action"] != "opened": - print("Unsupproted event.") - return + # if EVENT_DATA["action"] != "opened": + # print("Unsupproted event.") + # return diff = get_diff() if diff is None: -- 2.49.1 From 5414c0c9ea5b59c01c777432434d43123fedee32 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Wed, 8 Jan 2025 10:56:13 +0900 Subject: [PATCH 10/52] update pattern --- .github/scripts/code_review.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 0a734c6..493889a 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -37,10 +37,13 @@ def parse_diff(diff: str) -> list[dict[str, Any]]: Returns: list[dict[str, Any]]: list of dicts, each dict represents a code chunks """ - pattern = r"diff --git a/(.+?) b/\1\n(.*?)\n" + pattern = r"diff --git a/(.+?) b/\1\n(.*?)(?=\ndiff --git|$)" match_list = re.finditer(pattern, diff, re.S) return [ - {"to": match.group(1), "chunks": [{"content": match.group(2), "changes": []}]} + { + "to": match.group(1), + "chunks": [{"content": match.group(2), "changes": [match.group(3)]}], + } for match in match_list ] -- 2.49.1 From b615707b0ba9e14cd6c696600fd9c3dc1f1fe843 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Wed, 8 Jan 2025 11:36:50 +0900 Subject: [PATCH 11/52] update pattern --- .github/scripts/code_review.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 493889a..f001b6f 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -37,12 +37,12 @@ def parse_diff(diff: str) -> list[dict[str, Any]]: Returns: list[dict[str, Any]]: list of dicts, each dict represents a code chunks """ - pattern = r"diff --git a/(.+?) b/\1\n(.*?)(?=\ndiff --git|$)" + pattern = r"(?s)diff --git a/(.+?) b/\1\r?\n(.*?)(?=diff --git a/|$)" match_list = re.finditer(pattern, diff, re.S) return [ { "to": match.group(1), - "chunks": [{"content": match.group(2), "changes": [match.group(3)]}], + "chunks": [{"content": match.group(2), "changes": []}], } for match in match_list ] -- 2.49.1 From 3f763d11b9c0368d2780c9905637a83beb8a9d09 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Wed, 8 Jan 2025 11:39:54 +0900 Subject: [PATCH 12/52] add exclude option --- .github/scripts/code_review.py | 11 ++++++++++- .github/workflows/code-review.yml | 3 ++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index f001b6f..61ee33a 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -15,6 +15,8 @@ GITHUB_EVENT_PATH = os.getenv("GITHUB_EVENT_PATH", "") with open(GITHUB_EVENT_PATH, "r") as f: EVENT_DATA = json.load(f) +EXCLUDE_PATTERNS = os.getenv("EXCLUDE", "").split(",") + def get_diff() -> str | None: """Get code difference between base and head from Gitea""" @@ -39,13 +41,20 @@ def parse_diff(diff: str) -> list[dict[str, Any]]: """ pattern = r"(?s)diff --git a/(.+?) b/\1\r?\n(.*?)(?=diff --git a/|$)" match_list = re.finditer(pattern, diff, re.S) - return [ + list_diff = [ { "to": match.group(1), "chunks": [{"content": match.group(2), "changes": []}], } for match in match_list ] + return [ + diff + for diff in list_diff + if not any( + re.match(pattern.strip(), diff["to"]) for pattern in EXCLUDE_PATTERNS + ) + ] def main() -> None: diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index 684439f..a01c8a5 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -30,4 +30,5 @@ jobs: OPENAI_API_KEY: ${{ secrets.OPENAI_TOKEN }} DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number }} - run: python .github/scripts/code_review.py \ No newline at end of file + EXCLUDE: "*.yml,*.yaml" + run: python .github/scripts/code_review.py -- 2.49.1 From 3f185277cef9edd529efad5cdcb4fccad507ea66 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Wed, 8 Jan 2025 11:43:05 +0900 Subject: [PATCH 13/52] update exclude pattern format --- .github/workflows/code-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index a01c8a5..35fcb95 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -30,5 +30,5 @@ jobs: OPENAI_API_KEY: ${{ secrets.OPENAI_TOKEN }} DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number }} - EXCLUDE: "*.yml,*.yaml" + EXCLUDE: "yml,yaml" run: python .github/scripts/code_review.py -- 2.49.1 From 356adc64143c82c15a54efe11724655c165c4e0e Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Wed, 8 Jan 2025 11:53:51 +0900 Subject: [PATCH 14/52] change to use fnmatch --- .github/scripts/code_review.py | 7 ++++++- .github/workflows/code-review.yml | 4 ++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 61ee33a..2b79d39 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -1,6 +1,7 @@ import base64 import os import re +import fnmatch import json from collections import defaultdict from concurrent.futures import ThreadPoolExecutor, as_completed @@ -52,7 +53,11 @@ def parse_diff(diff: str) -> list[dict[str, Any]]: diff for diff in list_diff if not any( - re.match(pattern.strip(), diff["to"]) for pattern in EXCLUDE_PATTERNS + fnmatch.fnmatch( + diff["to"], + pattern.strip(), + ) + for pattern in EXCLUDE_PATTERNS ) ] diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index 35fcb95..447b4e2 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -21,7 +21,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install requests py-gitea + pip install requests py-gitea fnmatch - name: Run Code Review env: @@ -30,5 +30,5 @@ jobs: OPENAI_API_KEY: ${{ secrets.OPENAI_TOKEN }} DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number }} - EXCLUDE: "yml,yaml" + EXCLUDE: "*.yml,*.yaml" run: python .github/scripts/code_review.py -- 2.49.1 From 1e371165bc221c89831e7e08719b8e89e29c74ea Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Wed, 8 Jan 2025 11:58:41 +0900 Subject: [PATCH 15/52] update pip dependencies --- .github/workflows/code-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index 447b4e2..a01c8a5 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -21,7 +21,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install requests py-gitea fnmatch + pip install requests py-gitea - name: Run Code Review env: -- 2.49.1 From cc67b07101c55a98a2b6a2ffd35c9182983755fd Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Wed, 8 Jan 2025 18:26:44 +0900 Subject: [PATCH 16/52] add single chunk comments --- .github/scripts/code_review.py | 272 +++++++++++++++++++++++++++--- .github/workflows/code-review.yml | 4 +- 2 files changed, 247 insertions(+), 29 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 2b79d39..d22c651 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -3,21 +3,121 @@ import os import re import fnmatch import json +from openai import OpenAI +from anthropic import Anthropic from collections import defaultdict from concurrent.futures import ThreadPoolExecutor, as_completed -from typing import Any +from typing import Any, Optional, Callable import requests GITEA_TOKEN = os.getenv("GITEA_TOKEN", "") HEADERS = {"Authorization": f"Bearer {GITEA_TOKEN}"} -GITHUB_EVENT_PATH = os.getenv("GITHUB_EVENT_PATH", "") +SINGLE_CHUNK_SYSTEM_PROMPT = """Your task is to review pull requests. Instructions: +- Provide the response in the following JSON format: [{{"lineNumber": , "reviewComment": ""}}] +- Do not give positive comments or compliments. +- Provide comments and suggestions ONLY if there is something to improve, otherwise return 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. +""" + +FULL_CONTEXT_SYSTEM_PROMPT = """You are an experienced software engineer specializing in reviewing pull requests. Your task is to provide an overall code review summary for a PR. Focus on assessing the following aspects: + +1. **Code Structure & Architecture:** Evaluate whether the code is well-organized, modular, and adheres to clean code principles. Suggest improvements if needed. + +2. **Refactoring Opportunities:** Identify areas where the code can be optimized or simplified without changing its behavior. + +3. **Potential Future Problems:** Highlight possible scalability, maintainability, or dependency issues that might arise in the future based on the current implementation. + +Be constructive and clear in your feedback. Avoid commenting on trivial issues or syntax errors—focus on high-level feedback. +""" + +GITHUB_EVENT_PATH = os.getenv("GITHUB_EVENT_PATH") with open(GITHUB_EVENT_PATH, "r") as f: EVENT_DATA = json.load(f) + +class PRDetails: + def __init__( + self, owner: str, repo: str, pull_number: int, title: str, description: str + ): + self.owner = owner + self.repo = repo + self.pull_number = pull_number + self.title = title + self.description = description + + +PR_DETAILS = PRDetails( + owner=EVENT_DATA["repository"]["owner"]["login"], + repo=EVENT_DATA["repository"]["name"], + pull_number=EVENT_DATA["number"], + title=EVENT_DATA["pull_request"]["title"], + description=EVENT_DATA["pull_request"]["body"], +) + EXCLUDE_PATTERNS = os.getenv("EXCLUDE", "").split(",") +FULL_CONTEXT_MODEL = os.getenv("FULL_CONTEXT_MODEL", "o1") +SINGLE_CHUNK_MODEL = os.getenv("SINGLE_CHUNK_MODEL", "claude-3-5-sonnet-20241022") + +OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "") +CLAUDE_API_KEY = os.getenv("CLAUDE_API_KEY", "") +DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY", "") + + +def parse_provider(model: str, is_full_context: bool = False) -> Callable: + max_tokens = 30000 if is_full_context else 700 + system_prompt = ( + FULL_CONTEXT_SYSTEM_PROMPT if is_full_context else SINGLE_CHUNK_SYSTEM_PROMPT + ) + if any(key in model for key in ["o1", "gpt"]): + openai = OpenAI(api_key=OPENAI_API_KEY) + return lambda prompt: openai.chat.completions.create( + model=model, + messages=[{"role": "user", "content": prompt}], + system=system_prompt, + temperature=0.2, + max_tokens=max_tokens, + top_p=1, + frequency_penalty=0, + presence_penalty=0, + ) + elif any(key in model for key in ["claude", "haiku"]): + claude = Anthropic(api_key=CLAUDE_API_KEY) + return lambda prompt: claude.messages.create( + model=model, + messages=[{"role": "user", "content": prompt}], + system=system_prompt, + temperature=0.2, + max_tokens=max_tokens, + top_p=1, + frequency_penalty=0, + presence_penalty=0, + ) + elif any(key in model for key in ["deepseek"]): + deepseek = OpenAI( + api_key=DEEPSEEK_API_KEY, base_url="https://api.deepseek.com/v1" + ) + return lambda prompt: deepseek.chat.completions.create( + model=model, + messages=[{"role": "user", "content": prompt}], + system=system_prompt, + temperature=0.2, + max_tokens=max_tokens, + top_p=1, + frequency_penalty=0, + presence_penalty=0, + ) + else: + raise ValueError(f"Invalid model: {model}") + + +FULL_CONTEXT_MESSAGE = parse_provider(FULL_CONTEXT_MODEL, is_full_context=True) +SINGLE_CHUNK_MESSAGE = parse_provider(SINGLE_CHUNK_MODEL, is_full_context=False) + def get_diff() -> str | None: """Get code difference between base and head from Gitea""" @@ -40,26 +140,96 @@ def parse_diff(diff: str) -> list[dict[str, Any]]: Returns: list[dict[str, Any]]: list of dicts, each dict represents a code chunks """ - pattern = r"(?s)diff --git a/(.+?) b/\1\r?\n(.*?)(?=diff --git a/|$)" - match_list = re.finditer(pattern, diff, re.S) - list_diff = [ - { - "to": match.group(1), - "chunks": [{"content": match.group(2), "changes": []}], - } - for match in match_list - ] - return [ - diff - for diff in list_diff - if not any( - fnmatch.fnmatch( - diff["to"], - pattern.strip(), - ) - for pattern in EXCLUDE_PATTERNS + file_pattern = re.compile( + r"(?s)diff --git a/(.+?) b/(.*?)\r?\n(.*?)(?=diff --git a/|$)", re.S + ) + old_new_pattern = re.compile(r"(?m)^(---|\+\+\+)\s+(.*)$") + list_diff = [] + for match in file_pattern.finditer(diff): + diff_text = match.group(3) + + old_new_match = list(old_new_pattern.finditer(diff_text)) + if len(old_new_match) != 2: + continue + + old_file = old_new_match[0].group(2) + old_file = old_file.lstrip("a/") if old_file.startswith("a/") else old_file + + new_file = old_new_match[1].group(2) + if new_file == "/dev/null": + print("Neglict deleted file") + continue + new_file = new_file.lstrip("b/") + + if any(fnmatch.fnmatch(new_file, pattern) for pattern in EXCLUDE_PATTERNS): + print(f"Exclude file {new_file}") + continue + + list_diff.append( + { + "file": new_file, + "chunk": diff_text, + } ) - ] + return list_diff + + +def create_single_chunk_prompt(file: str, chunk: str, pr_details: PRDetails) -> str: + return f""" +Review the following code diff in the file "{file}" and take the pull request title and description into account when writing the response. + +Pull request title: {pr_details.title} +Pull request description: + +--- +{pr_details.description} +--- + +Git diff to review: + +```diff +{chunk} +```""" + + +def get_ai_response_single_chunk(prompt: str) -> Optional[list[dict[str, Any]]]: + try: + response = SINGLE_CHUNK_MESSAGE(prompt) + content = response.choices[0].message.content.strip() or "[]" + return json.loads(content) + except Exception as e: + print(f"Error during AI response: {e}") + return None + + +def create_comment( + file: str, ai_response: list[dict[str, Any]] +) -> list[dict[str, Any]]: + comments = [] + for ai_response in ai_response: + comments.append( + { + "body": f"[REVIEW] {ai_response['reviewComment']}", + "path": file, + "line": int(ai_response["lineNumber"]), + } + ) + return comments + + +def analyze_single_chunks( + parsed_diff: list[dict[str, Any]], pr_details: PRDetails +) -> list[dict[str, Any]]: + comments = [] + for diff in parsed_diff: + file = diff["file"] + chunk = diff["chunk"] + prompt = create_single_chunk_prompt(file, chunk, pr_details) + ai_response = get_ai_response_single_chunk(prompt) + if ai_response: + new_comments = create_comment(file, ai_response) + comments.extend(new_comments) + return comments def main() -> None: @@ -69,15 +239,61 @@ def main() -> None: # print("Unsupproted event.") # return - diff = get_diff() - if diff is None: - return - elif not diff: - print("No diff found.") - return + # diff = get_diff() + # if diff is None: + # return + # elif not diff: + # print("No diff found.") + # return + diff = """diff --git a/.github/scripts/test.py b/.github/scripts/test.py +new file mode 100644 +index 0000000..7af53e5 +--- /dev/null ++++ b/.github/scripts/test.py +@@ -0,0 +1,19 @@ ++import json ++import os ++ ++print(os.getenv("GITHUB_EVENT_PATH")) ++ ++with open(os.getenv("GITHUB_EVENT_PATH"), "r") as f: ++ event_data = json.load(f) ++ ++print(os.getenv("GITEA_TOKEN")) ++print(os.getenv("CLAUDE_API_KEY")) ++print(os.getenv("OPENAI_API_KEY")) ++print(os.getenv("DEEPSEEK_API_KEY")) ++ ++print(event_data) ++print(event_data["pull_request"]["diff_url"]) ++print(event_data["pull_request"]["base"]["label"]) ++print(event_data["pull_request"]["head"]["label"]) ++print(event_data["before"]) ++print(event_data["after"]) +diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml +index 8047d78..b414a6c 100644 +--- a/.github/workflows/code-review.yml ++++ b/.github/workflows/code-review.yml +@@ -25,8 +25,9 @@ jobs: + + - name: Run Code Review + env: +- GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} +- CLAUDE_API_KEY: ${{ secrets.CLAUDE_API_KEY }} +- OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} ++ GITEA_TOKEN: ${{ secrets.ACCESS_TOKEN }} ++ CLAUDE_API_KEY: ${{ secrets.CLAUDE_TOKEN }} ++ OPENAI_API_KEY: ${{ secrets.OPENAI_TOKEN }} ++ DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} +- run: python .github/scripts/code_review.py +\ No newline at end of file ++ run: python .github/scripts/test.py +\ No newline at end of file""" parsed_diff = parse_diff(diff) - print(parsed_diff) + comments = analyze_single_chunks(parsed_diff, PR_DETAILS) + print(comments) if __name__ == "__main__": diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index a01c8a5..0a550c7 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -21,7 +21,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install requests py-gitea + pip install requests py-gitea openai anthropic - name: Run Code Review env: @@ -30,5 +30,7 @@ jobs: OPENAI_API_KEY: ${{ secrets.OPENAI_TOKEN }} DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number }} + FULL_CONTEXT_MODEL: o1 + SINGLE_CHUNK_MODEL: claude-3-5-sonnet-20241022 EXCLUDE: "*.yml,*.yaml" run: python .github/scripts/code_review.py -- 2.49.1 From 454e48656e2fe9636ba0ca746d8de1851da0f0dc Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Wed, 8 Jan 2025 18:28:21 +0900 Subject: [PATCH 17/52] test real data --- .github/scripts/code_review.py | 57 ++++------------------------------ 1 file changed, 6 insertions(+), 51 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index d22c651..f6e9a85 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -239,57 +239,12 @@ def main() -> None: # print("Unsupproted event.") # return - # diff = get_diff() - # if diff is None: - # return - # elif not diff: - # print("No diff found.") - # return - diff = """diff --git a/.github/scripts/test.py b/.github/scripts/test.py -new file mode 100644 -index 0000000..7af53e5 ---- /dev/null -+++ b/.github/scripts/test.py -@@ -0,0 +1,19 @@ -+import json -+import os -+ -+print(os.getenv("GITHUB_EVENT_PATH")) -+ -+with open(os.getenv("GITHUB_EVENT_PATH"), "r") as f: -+ event_data = json.load(f) -+ -+print(os.getenv("GITEA_TOKEN")) -+print(os.getenv("CLAUDE_API_KEY")) -+print(os.getenv("OPENAI_API_KEY")) -+print(os.getenv("DEEPSEEK_API_KEY")) -+ -+print(event_data) -+print(event_data["pull_request"]["diff_url"]) -+print(event_data["pull_request"]["base"]["label"]) -+print(event_data["pull_request"]["head"]["label"]) -+print(event_data["before"]) -+print(event_data["after"]) -diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml -index 8047d78..b414a6c 100644 ---- a/.github/workflows/code-review.yml -+++ b/.github/workflows/code-review.yml -@@ -25,8 +25,9 @@ jobs: - - - name: Run Code Review - env: -- GITEA_TOKEN: ${{ secrets.GITEA_TOKEN }} -- CLAUDE_API_KEY: ${{ secrets.CLAUDE_API_KEY }} -- OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} -+ GITEA_TOKEN: ${{ secrets.ACCESS_TOKEN }} -+ CLAUDE_API_KEY: ${{ secrets.CLAUDE_TOKEN }} -+ OPENAI_API_KEY: ${{ secrets.OPENAI_TOKEN }} -+ DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_TOKEN }} - PR_NUMBER: ${{ github.event.pull_request.number }} -- run: python .github/scripts/code_review.py -\ No newline at end of file -+ run: python .github/scripts/test.py -\ No newline at end of file""" + diff = get_diff() + if diff is None: + return + elif not diff: + print("No diff found.") + return parsed_diff = parse_diff(diff) comments = analyze_single_chunks(parsed_diff, PR_DETAILS) -- 2.49.1 From 5888dacda4c0824c18f657bd11da619a1ce73cd4 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Wed, 8 Jan 2025 18:34:26 +0900 Subject: [PATCH 18/52] fix api method format --- .github/scripts/code_review.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index f6e9a85..7ff27a9 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -90,12 +90,13 @@ def parse_provider(model: str, is_full_context: bool = False) -> Callable: return lambda prompt: claude.messages.create( model=model, messages=[{"role": "user", "content": prompt}], - system=system_prompt, + system={ + "type": "text", + "text": system_prompt, + "cache_control": {"type": "ephemeral"}, + }, temperature=0.2, max_tokens=max_tokens, - top_p=1, - frequency_penalty=0, - presence_penalty=0, ) elif any(key in model for key in ["deepseek"]): deepseek = OpenAI( -- 2.49.1 From eaf6f2551dc619c30f6eaa8d8b8b15da4e2385fa Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Wed, 8 Jan 2025 18:35:06 +0900 Subject: [PATCH 19/52] fix system argument --- .github/scripts/code_review.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 7ff27a9..e61a077 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -90,11 +90,13 @@ def parse_provider(model: str, is_full_context: bool = False) -> Callable: return lambda prompt: claude.messages.create( model=model, messages=[{"role": "user", "content": prompt}], - system={ - "type": "text", - "text": system_prompt, - "cache_control": {"type": "ephemeral"}, - }, + system=[ + { + "type": "text", + "text": system_prompt, + "cache_control": {"type": "ephemeral"}, + } + ], temperature=0.2, max_tokens=max_tokens, ) -- 2.49.1 From 1e6247ae666f53f8db9e0223ff05c26b495e0241 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Wed, 8 Jan 2025 18:37:35 +0900 Subject: [PATCH 20/52] test claude message --- .github/scripts/code_review.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index e61a077..74af981 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -230,6 +230,8 @@ def analyze_single_chunks( prompt = create_single_chunk_prompt(file, chunk, pr_details) ai_response = get_ai_response_single_chunk(prompt) if ai_response: + print(ai_response) + print(dir(ai_response)) new_comments = create_comment(file, ai_response) comments.extend(new_comments) return comments -- 2.49.1 From 0909b2c915a2649cf5e9e44994f61b7753b732b9 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Wed, 8 Jan 2025 18:39:26 +0900 Subject: [PATCH 21/52] test claude response --- .github/scripts/code_review.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 74af981..5f59099 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -202,6 +202,8 @@ def get_ai_response_single_chunk(prompt: str) -> Optional[list[dict[str, Any]]]: return json.loads(content) except Exception as e: print(f"Error during AI response: {e}") + print(response) + print(dir(response)) return None @@ -230,8 +232,6 @@ def analyze_single_chunks( prompt = create_single_chunk_prompt(file, chunk, pr_details) ai_response = get_ai_response_single_chunk(prompt) if ai_response: - print(ai_response) - print(dir(ai_response)) new_comments = create_comment(file, ai_response) comments.extend(new_comments) return comments -- 2.49.1 From 2b5a95b30b905e860189f691c2ea6972b1452b9c Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Wed, 8 Jan 2025 18:43:17 +0900 Subject: [PATCH 22/52] update parser --- .github/scripts/code_review.py | 83 ++++++++++++++++++++-------------- 1 file changed, 49 insertions(+), 34 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 5f59099..b5826f0 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -68,58 +68,73 @@ CLAUDE_API_KEY = os.getenv("CLAUDE_API_KEY", "") DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY", "") -def parse_provider(model: str, is_full_context: bool = False) -> Callable: +def parse_provider( + model: str, is_full_context: bool = False +) -> tuple[Callable, Callable]: max_tokens = 30000 if is_full_context else 700 system_prompt = ( FULL_CONTEXT_SYSTEM_PROMPT if is_full_context else SINGLE_CHUNK_SYSTEM_PROMPT ) if any(key in model for key in ["o1", "gpt"]): openai = OpenAI(api_key=OPENAI_API_KEY) - return lambda prompt: openai.chat.completions.create( - model=model, - messages=[{"role": "user", "content": prompt}], - system=system_prompt, - temperature=0.2, - max_tokens=max_tokens, - top_p=1, - frequency_penalty=0, - presence_penalty=0, + return ( + lambda prompt: openai.chat.completions.create( + model=model, + messages=[{"role": "user", "content": prompt}], + system=system_prompt, + temperature=0.2, + max_tokens=max_tokens, + top_p=1, + frequency_penalty=0, + presence_penalty=0, + ), + lambda response: response.choices[0].message.content.strip() or "[]", ) elif any(key in model for key in ["claude", "haiku"]): claude = Anthropic(api_key=CLAUDE_API_KEY) - return lambda prompt: claude.messages.create( - model=model, - messages=[{"role": "user", "content": prompt}], - system=[ - { - "type": "text", - "text": system_prompt, - "cache_control": {"type": "ephemeral"}, - } - ], - temperature=0.2, - max_tokens=max_tokens, + return ( + lambda prompt: claude.messages.create( + model=model, + messages=[{"role": "user", "content": prompt}], + system=[ + { + "type": "text", + "text": system_prompt, + "cache_control": {"type": "ephemeral"}, + } + ], + temperature=0.2, + max_tokens=max_tokens, + ), + lambda response: response.content[0].text.strip() or "[]", ) elif any(key in model for key in ["deepseek"]): deepseek = OpenAI( api_key=DEEPSEEK_API_KEY, base_url="https://api.deepseek.com/v1" ) - return lambda prompt: deepseek.chat.completions.create( - model=model, - messages=[{"role": "user", "content": prompt}], - system=system_prompt, - temperature=0.2, - max_tokens=max_tokens, - top_p=1, - frequency_penalty=0, - presence_penalty=0, + return ( + lambda prompt: deepseek.chat.completions.create( + model=model, + messages=[{"role": "user", "content": prompt}], + system=system_prompt, + temperature=0.2, + max_tokens=max_tokens, + top_p=1, + frequency_penalty=0, + presence_penalty=0, + ), + lambda response: response.choices[0].message.content.strip() or "[]", ) else: raise ValueError(f"Invalid model: {model}") -FULL_CONTEXT_MESSAGE = parse_provider(FULL_CONTEXT_MODEL, is_full_context=True) -SINGLE_CHUNK_MESSAGE = parse_provider(SINGLE_CHUNK_MODEL, is_full_context=False) +FULL_CONTEXT_MESSAGE, FULL_CONTEXT_RESPONSE_PARSER = parse_provider( + FULL_CONTEXT_MODEL, is_full_context=True +) +SINGLE_CHUNK_MESSAGE, SINGLE_CHUNK_RESPONSE_PARSER = parse_provider( + SINGLE_CHUNK_MODEL, is_full_context=False +) def get_diff() -> str | None: @@ -198,7 +213,7 @@ Git diff to review: def get_ai_response_single_chunk(prompt: str) -> Optional[list[dict[str, Any]]]: try: response = SINGLE_CHUNK_MESSAGE(prompt) - content = response.choices[0].message.content.strip() or "[]" + content = SINGLE_CHUNK_RESPONSE_PARSER(response) return json.loads(content) except Exception as e: print(f"Error during AI response: {e}") -- 2.49.1 From 6a4abe66c2f13045821390296111b1373059a793 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 08:41:02 +0900 Subject: [PATCH 23/52] deepseek api test --- .github/workflows/code-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index 0a550c7..4bcc4e9 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -31,6 +31,6 @@ jobs: DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number }} FULL_CONTEXT_MODEL: o1 - SINGLE_CHUNK_MODEL: claude-3-5-sonnet-20241022 + SINGLE_CHUNK_MODEL: deepseek-chat EXCLUDE: "*.yml,*.yaml" run: python .github/scripts/code_review.py -- 2.49.1 From 78882e915683d77c9e0b583f6c5d1eff932ef5b7 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 08:43:11 +0900 Subject: [PATCH 24/52] resolve system prompt parameter --- .github/scripts/code_review.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index b5826f0..931f6ef 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -80,8 +80,10 @@ def parse_provider( return ( lambda prompt: openai.chat.completions.create( model=model, - messages=[{"role": "user", "content": prompt}], - system=system_prompt, + messages=[ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": prompt}, + ], temperature=0.2, max_tokens=max_tokens, top_p=1, @@ -115,8 +117,10 @@ def parse_provider( return ( lambda prompt: deepseek.chat.completions.create( model=model, - messages=[{"role": "user", "content": prompt}], - system=system_prompt, + messages=[ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": prompt}, + ], temperature=0.2, max_tokens=max_tokens, top_p=1, -- 2.49.1 From 436d2f74128b732b81e560bfb280099ffbc35ccf Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 09:07:36 +0900 Subject: [PATCH 25/52] update deepseek base url --- .github/scripts/code_review.py | 4 +--- .github/workflows/code-review.yml | 1 + 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 931f6ef..3b0a982 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -111,9 +111,7 @@ def parse_provider( lambda response: response.content[0].text.strip() or "[]", ) elif any(key in model for key in ["deepseek"]): - deepseek = OpenAI( - api_key=DEEPSEEK_API_KEY, base_url="https://api.deepseek.com/v1" - ) + deepseek = OpenAI(api_key=DEEPSEEK_API_KEY, base_url="https://api.deepseek.com") return ( lambda prompt: deepseek.chat.completions.create( model=model, diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index 4bcc4e9..7fe11da 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -29,6 +29,7 @@ jobs: CLAUDE_API_KEY: ${{ secrets.CLAUDE_TOKEN }} OPENAI_API_KEY: ${{ secrets.OPENAI_TOKEN }} DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_TOKEN }} + GOOGLE_API_KEY: ${{ secrets.GOOGLE_TOKEN }} PR_NUMBER: ${{ github.event.pull_request.number }} FULL_CONTEXT_MODEL: o1 SINGLE_CHUNK_MODEL: deepseek-chat -- 2.49.1 From 1c0986341416df021bc567fe2ad7cd83b83a0d8e Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 09:30:43 +0900 Subject: [PATCH 26/52] update json parsing --- .github/scripts/code_review.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 3b0a982..c5daa26 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -125,7 +125,11 @@ def parse_provider( frequency_penalty=0, presence_penalty=0, ), - lambda response: response.choices[0].message.content.strip() or "[]", + lambda response: response.choices[0] + .message.content.strip("`") + .lstrip("json") + .strip() + or "[]", ) else: raise ValueError(f"Invalid model: {model}") -- 2.49.1 From 57c8dc3a68138d3db4089c81de75ca1fed5e4ca0 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 09:35:09 +0900 Subject: [PATCH 27/52] change secrets --- .github/workflows/code-review.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index 7fe11da..7543d13 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -26,10 +26,10 @@ jobs: - name: Run Code Review env: GITEA_TOKEN: ${{ secrets.ACCESS_TOKEN }} - CLAUDE_API_KEY: ${{ secrets.CLAUDE_TOKEN }} - OPENAI_API_KEY: ${{ secrets.OPENAI_TOKEN }} - DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_TOKEN }} - GOOGLE_API_KEY: ${{ secrets.GOOGLE_TOKEN }} + CLAUDE_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} + DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} + GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} PR_NUMBER: ${{ github.event.pull_request.number }} FULL_CONTEXT_MODEL: o1 SINGLE_CHUNK_MODEL: deepseek-chat -- 2.49.1 From 28898073eb889792b87c1708579adfe58355ed18 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 10:15:41 +0900 Subject: [PATCH 28/52] remove access_token --- .github/scripts/code_review.py | 3 +-- .github/workflows/code-review.yml | 1 - 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index c5daa26..8c7a5ed 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -11,8 +11,7 @@ from typing import Any, Optional, Callable import requests -GITEA_TOKEN = os.getenv("GITEA_TOKEN", "") -HEADERS = {"Authorization": f"Bearer {GITEA_TOKEN}"} +HEADERS = {} SINGLE_CHUNK_SYSTEM_PROMPT = """Your task is to review pull requests. Instructions: - Provide the response in the following JSON format: [{{"lineNumber": , "reviewComment": ""}}] diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index 7543d13..a4589d5 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -25,7 +25,6 @@ jobs: - name: Run Code Review env: - GITEA_TOKEN: ${{ secrets.ACCESS_TOKEN }} CLAUDE_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} -- 2.49.1 From 55412d5d9c58aade43ac0adf8111e6e50a10f09c Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 10:18:46 +0900 Subject: [PATCH 29/52] test google gemini --- .github/scripts/code_review.py | 9 +++++++++ .github/workflows/code-review.yml | 4 ++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 8c7a5ed..8ce0369 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -5,6 +5,7 @@ import fnmatch import json from openai import OpenAI from anthropic import Anthropic +import google.generativeai as genai from collections import defaultdict from concurrent.futures import ThreadPoolExecutor, as_completed from typing import Any, Optional, Callable @@ -65,6 +66,7 @@ SINGLE_CHUNK_MODEL = os.getenv("SINGLE_CHUNK_MODEL", "claude-3-5-sonnet-20241022 OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "") CLAUDE_API_KEY = os.getenv("CLAUDE_API_KEY", "") DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY", "") +GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY", "") def parse_provider( @@ -130,6 +132,13 @@ def parse_provider( .strip() or "[]", ) + elif any(key in model for key in ["gemini"]): + genai.configure(api_key=GOOGLE_API_KEY) + gemini = genai.GenerativeModel(model) + return ( + lambda prompt: gemini.generate_content(prompt), + lambda response: response.text.strip() or "[]", + ) else: raise ValueError(f"Invalid model: {model}") diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index a4589d5..f52a900 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -21,7 +21,7 @@ jobs: - name: Install dependencies run: | python -m pip install --upgrade pip - pip install requests py-gitea openai anthropic + pip install requests py-gitea openai anthropic google-generativeai - name: Run Code Review env: @@ -31,6 +31,6 @@ jobs: GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} PR_NUMBER: ${{ github.event.pull_request.number }} FULL_CONTEXT_MODEL: o1 - SINGLE_CHUNK_MODEL: deepseek-chat + SINGLE_CHUNK_MODEL: gemini-1.5-flash EXCLUDE: "*.yml,*.yaml" run: python .github/scripts/code_review.py -- 2.49.1 From 4474dc9ab1999856c14170477762dcb0568e98dc Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 10:23:00 +0900 Subject: [PATCH 30/52] update google system prompt --- .github/scripts/code_review.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 8ce0369..1824ec8 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -134,7 +134,7 @@ def parse_provider( ) elif any(key in model for key in ["gemini"]): genai.configure(api_key=GOOGLE_API_KEY) - gemini = genai.GenerativeModel(model) + gemini = genai.GenerativeModel(model, system_instruction=system_prompt) return ( lambda prompt: gemini.generate_content(prompt), lambda response: response.text.strip() or "[]", -- 2.49.1 From bd557996803ba98efdb5576e9754799ad9e3a13f Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 10:28:43 +0900 Subject: [PATCH 31/52] update cache and parsing algorithm --- .github/scripts/code_review.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 1824ec8..1a5a67c 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -3,9 +3,11 @@ import os import re import fnmatch import json +import datetime from openai import OpenAI from anthropic import Anthropic import google.generativeai as genai +from google.generativeai import caching from collections import defaultdict from concurrent.futures import ThreadPoolExecutor, as_completed from typing import Any, Optional, Callable @@ -134,10 +136,17 @@ def parse_provider( ) elif any(key in model for key in ["gemini"]): genai.configure(api_key=GOOGLE_API_KEY) - gemini = genai.GenerativeModel(model, system_instruction=system_prompt) + cache = caching.CachedContent.create( + model=f"models/{model}", + display_name="Gitea Code Reviewer", # used to identify the cache + system_instruction=system_prompt, + contents=[], + ttl=datetime.timedelta(minutes=5), + ) + gemini = genai.GenerativeModel.from_cached_content(cached_content=cache) return ( lambda prompt: gemini.generate_content(prompt), - lambda response: response.text.strip() or "[]", + lambda response: response.text.strip("`").lstrip("json").strip() or "[]", ) else: raise ValueError(f"Invalid model: {model}") -- 2.49.1 From 9410c18139ce735724e4d81cf1ba531fbc249ee3 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 10:32:20 +0900 Subject: [PATCH 32/52] remove cache --- .github/scripts/code_review.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 1a5a67c..5259041 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -7,7 +7,6 @@ import datetime from openai import OpenAI from anthropic import Anthropic import google.generativeai as genai -from google.generativeai import caching from collections import defaultdict from concurrent.futures import ThreadPoolExecutor, as_completed from typing import Any, Optional, Callable @@ -136,14 +135,7 @@ def parse_provider( ) elif any(key in model for key in ["gemini"]): genai.configure(api_key=GOOGLE_API_KEY) - cache = caching.CachedContent.create( - model=f"models/{model}", - display_name="Gitea Code Reviewer", # used to identify the cache - system_instruction=system_prompt, - contents=[], - ttl=datetime.timedelta(minutes=5), - ) - gemini = genai.GenerativeModel.from_cached_content(cached_content=cache) + gemini = genai.GenerativeModel(model, system_instruction=system_prompt) return ( lambda prompt: gemini.generate_content(prompt), lambda response: response.text.strip("`").lstrip("json").strip() or "[]", -- 2.49.1 From ee840d3001b21b532f6f9ad7ab165f93e6ed336c Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 10:33:20 +0900 Subject: [PATCH 33/52] update strip --- .github/scripts/code_review.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 5259041..948cf13 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -138,7 +138,8 @@ def parse_provider( gemini = genai.GenerativeModel(model, system_instruction=system_prompt) return ( lambda prompt: gemini.generate_content(prompt), - lambda response: response.text.strip("`").lstrip("json").strip() or "[]", + lambda response: response.text.strip().strip("`").lstrip("json").strip() + or "[]", ) else: raise ValueError(f"Invalid model: {model}") -- 2.49.1 From 6ca92025ab769c170a747310301a15414364172d Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 10:36:11 +0900 Subject: [PATCH 34/52] test for gpt --- .github/scripts/code_review.py | 10 ++++++++-- .github/workflows/code-review.yml | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 948cf13..4039d01 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -92,7 +92,12 @@ def parse_provider( frequency_penalty=0, presence_penalty=0, ), - lambda response: response.choices[0].message.content.strip() or "[]", + lambda response: response.choices[0] + .message.content.strip() + .strip("`") + .lstrip("json") + .strip() + or "[]", ) elif any(key in model for key in ["claude", "haiku"]): claude = Anthropic(api_key=CLAUDE_API_KEY) @@ -128,7 +133,8 @@ def parse_provider( presence_penalty=0, ), lambda response: response.choices[0] - .message.content.strip("`") + .message.content.strip() + .strip("`") .lstrip("json") .strip() or "[]", diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index f52a900..7627dcd 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -31,6 +31,6 @@ jobs: GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} PR_NUMBER: ${{ github.event.pull_request.number }} FULL_CONTEXT_MODEL: o1 - SINGLE_CHUNK_MODEL: gemini-1.5-flash + SINGLE_CHUNK_MODEL: gpt-4o EXCLUDE: "*.yml,*.yaml" run: python .github/scripts/code_review.py -- 2.49.1 From 0ce18a3ba1fa5177631a938e66b90e0b929c3410 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 12:49:53 +0900 Subject: [PATCH 35/52] test full context response --- .github/scripts/code_review.py | 62 +++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 4039d01..a976afa 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -214,15 +214,15 @@ def parse_diff(diff: str) -> list[dict[str, Any]]: return list_diff -def create_single_chunk_prompt(file: str, chunk: str, pr_details: PRDetails) -> str: +def create_single_chunk_prompt(file: str, chunk: str) -> str: return f""" Review the following code diff in the file "{file}" and take the pull request title and description into account when writing the response. -Pull request title: {pr_details.title} +Pull request title: {PR_DETAILS.title} Pull request description: --- -{pr_details.description} +{PR_DETAILS.description} --- Git diff to review: @@ -259,14 +259,12 @@ def create_comment( return comments -def analyze_single_chunks( - parsed_diff: list[dict[str, Any]], pr_details: PRDetails -) -> list[dict[str, Any]]: +def analyze_single_chunks(parsed_diff: list[dict[str, Any]]) -> list[dict[str, Any]]: comments = [] for diff in parsed_diff: file = diff["file"] chunk = diff["chunk"] - prompt = create_single_chunk_prompt(file, chunk, pr_details) + prompt = create_single_chunk_prompt(file, chunk) ai_response = get_ai_response_single_chunk(prompt) if ai_response: new_comments = create_comment(file, ai_response) @@ -274,6 +272,50 @@ def analyze_single_chunks( return comments +def get_file_content(file: str) -> str | None: + repo_url = EVENT_DATA["pull_request"]["head"]["url"] + branch = EVENT_DATA["pull_request"]["head"]["ref"] + url = f"{repo_url}/raw/{branch}/{file}" + response = requests.get(url, headers=HEADERS) + response.raise_for_status() + + if response.status_code != 200: + print(f"Failed to get file content with code : {response.status_code}") + return None + return response.text + + +def get_ai_response_full_context(prompt: str) -> Optional[str]: + try: + response = FULL_CONTEXT_MESSAGE(prompt) + content = FULL_CONTEXT_RESPONSE_PARSER(response) + return content + except Exception as e: + print(f"Error during AI response: {e}") + print(response) + print(dir(response)) + return None + + +def analyze_full_context(parsed_diff: list[dict[str, Any]]) -> str: + file_contents = [] + for diff in parsed_diff: + file = diff["file"] + chunk = diff["chunk"] + content = get_file_content(file) + if content is None: + continue + file_contents.append(content) + file_contents.append(chunk) + + whole_content = "\n".join(file_contents) + ai_response = get_ai_response_full_context(whole_content) + if ai_response is None: + return None + + return ai_response + + def main() -> None: """Code Reviewer for Gitea""" @@ -289,8 +331,10 @@ def main() -> None: return parsed_diff = parse_diff(diff) - comments = analyze_single_chunks(parsed_diff, PR_DETAILS) - print(comments) + # comments = analyze_single_chunks(parsed_diff) + + full_context_response = analyze_full_context(parsed_diff) + print(full_context_response) if __name__ == "__main__": -- 2.49.1 From f358a6da10f0d96ab66183d8bb8584bcba3bb485 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 12:51:13 +0900 Subject: [PATCH 36/52] fix url parsing structure --- .github/scripts/code_review.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index a976afa..948daea 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -273,7 +273,7 @@ def analyze_single_chunks(parsed_diff: list[dict[str, Any]]) -> list[dict[str, A def get_file_content(file: str) -> str | None: - repo_url = EVENT_DATA["pull_request"]["head"]["url"] + repo_url = EVENT_DATA["pull_request"]["url"] branch = EVENT_DATA["pull_request"]["head"]["ref"] url = f"{repo_url}/raw/{branch}/{file}" response = requests.get(url, headers=HEADERS) -- 2.49.1 From 8208b87e81899c8abf467c0799fdd6899bc9ee1b Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 12:53:10 +0900 Subject: [PATCH 37/52] fix / to %2F --- .github/scripts/code_review.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 948daea..46a3b61 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -275,7 +275,10 @@ def analyze_single_chunks(parsed_diff: list[dict[str, Any]]) -> list[dict[str, A def get_file_content(file: str) -> str | None: repo_url = EVENT_DATA["pull_request"]["url"] branch = EVENT_DATA["pull_request"]["head"]["ref"] - url = f"{repo_url}/raw/{branch}/{file}" + + replaced_file = file.replace("/", "%2F") + url = f"{repo_url}/raw/{branch}%2F{replaced_file}" + response = requests.get(url, headers=HEADERS) response.raise_for_status() -- 2.49.1 From 70ed84c99ba7295f494ec279586227335627e06d Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 12:55:52 +0900 Subject: [PATCH 38/52] fix base url for api --- .github/scripts/code_review.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 46a3b61..6bc475d 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -273,7 +273,9 @@ def analyze_single_chunks(parsed_diff: list[dict[str, Any]]) -> list[dict[str, A def get_file_content(file: str) -> str | None: - repo_url = EVENT_DATA["pull_request"]["url"] + repo_url = EVENT_DATA["pull_request"]["url"].replace( + "git.teahaven.kr", "git.teahaven.kr/api/v1/repos" + ) branch = EVENT_DATA["pull_request"]["head"]["ref"] replaced_file = file.replace("/", "%2F") -- 2.49.1 From 85ee57ef8a60c429714ee089896300ff5c709695 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 13:07:06 +0900 Subject: [PATCH 39/52] fix url --- .github/scripts/code_review.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 6bc475d..d592b05 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -273,13 +273,11 @@ def analyze_single_chunks(parsed_diff: list[dict[str, Any]]) -> list[dict[str, A def get_file_content(file: str) -> str | None: - repo_url = EVENT_DATA["pull_request"]["url"].replace( - "git.teahaven.kr", "git.teahaven.kr/api/v1/repos" - ) + repo_url = EVENT_DATA["pull_request"]["head"]["repo"]["url"] branch = EVENT_DATA["pull_request"]["head"]["ref"] replaced_file = file.replace("/", "%2F") - url = f"{repo_url}/raw/{branch}%2F{replaced_file}" + url = f"{repo_url}/raw/{branch}%2F{replaced_file}?ref={branch}" response = requests.get(url, headers=HEADERS) response.raise_for_status() -- 2.49.1 From 95c3f7caa38cc88b4aec0b9b75eb03e6ed4a6e6b Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 13:13:34 +0900 Subject: [PATCH 40/52] remove o1 because I cannot use it --- .github/workflows/code-review.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index 7627dcd..bc5722f 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -30,7 +30,7 @@ jobs: DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} PR_NUMBER: ${{ github.event.pull_request.number }} - FULL_CONTEXT_MODEL: o1 + FULL_CONTEXT_MODEL: gpt-4o SINGLE_CHUNK_MODEL: gpt-4o EXCLUDE: "*.yml,*.yaml" run: python .github/scripts/code_review.py -- 2.49.1 From 9cf1d20d3b40f0764dd10b0f4125838995f00bd0 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 13:21:02 +0900 Subject: [PATCH 41/52] reduce max token --- .github/scripts/code_review.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index d592b05..3962fa4 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -73,7 +73,7 @@ GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY", "") def parse_provider( model: str, is_full_context: bool = False ) -> tuple[Callable, Callable]: - max_tokens = 30000 if is_full_context else 700 + max_tokens = 4196 if is_full_context else 700 system_prompt = ( FULL_CONTEXT_SYSTEM_PROMPT if is_full_context else SINGLE_CHUNK_SYSTEM_PROMPT ) -- 2.49.1 From 160a425c49496dbbb8c35089d2c00f5a4c5a9194 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 13:24:30 +0900 Subject: [PATCH 42/52] add instruction --- .github/scripts/code_review.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 3962fa4..a1de375 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -33,6 +33,11 @@ FULL_CONTEXT_SYSTEM_PROMPT = """You are an experienced software engineer special 3. **Potential Future Problems:** Highlight possible scalability, maintainability, or dependency issues that might arise in the future based on the current implementation. Be constructive and clear in your feedback. Avoid commenting on trivial issues or syntax errors—focus on high-level feedback. + +Precise instructions: +- Do not give positive comments or compliments. +- Provide comments and suggestions ONLY if there is something to improve, otherwise return an empty string. +- Write the comment in GitHub Markdown format. """ GITHUB_EVENT_PATH = os.getenv("GITHUB_EVENT_PATH") -- 2.49.1 From 304ce34759ff18d6221943dd03fe5b852ae49a4b Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 13:29:05 +0900 Subject: [PATCH 43/52] improve prompt --- .github/scripts/code_review.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index a1de375..44d32fb 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -313,10 +313,21 @@ def analyze_full_context(parsed_diff: list[dict[str, Any]]) -> str: content = get_file_content(file) if content is None: continue + file_contents.append(f"File: {file}") file_contents.append(content) - file_contents.append(chunk) + file_contents.append(f"Diff: {chunk}") - whole_content = "\n".join(file_contents) + whole_content = f"""Review the following code and take the pull request title and description into account when writing the response. + +Pull request title: {PR_DETAILS.title} +Pull request description: +--- +{PR_DETAILS.description} +--- + +Code to review: + +""" + "\n".join(file_contents) ai_response = get_ai_response_full_context(whole_content) if ai_response is None: return None -- 2.49.1 From 956e2495f6751a43d90bbe4812dcbd8c3412acf4 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 14:05:45 +0900 Subject: [PATCH 44/52] restrict start message --- .github/scripts/code_review.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 44d32fb..007d70e 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -38,6 +38,7 @@ Precise instructions: - Do not give positive comments or compliments. - Provide comments and suggestions ONLY if there is something to improve, otherwise return an empty string. - Write the comment in GitHub Markdown format. +- Do not start with "markdown" or "```markdown". """ GITHUB_EVENT_PATH = os.getenv("GITHUB_EVENT_PATH") @@ -297,7 +298,7 @@ def get_ai_response_full_context(prompt: str) -> Optional[str]: try: response = FULL_CONTEXT_MESSAGE(prompt) content = FULL_CONTEXT_RESPONSE_PARSER(response) - return content + return content.lstrip("markdown").strip() except Exception as e: print(f"Error during AI response: {e}") print(response) -- 2.49.1 From c8da8b2f41a53426941c92c623833cd5bb174038 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 14:06:45 +0900 Subject: [PATCH 45/52] add code block restriction --- .github/scripts/code_review.py | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 007d70e..fdcd8c8 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -39,6 +39,7 @@ Precise instructions: - Provide comments and suggestions ONLY if there is something to improve, otherwise return an empty string. - Write the comment in GitHub Markdown format. - Do not start with "markdown" or "```markdown". +- Give example code block or pseudo code if needed. """ GITHUB_EVENT_PATH = os.getenv("GITHUB_EVENT_PATH") -- 2.49.1 From 7864effcb0b50faa767ee218d4f72f1ed353d6e8 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 14:09:34 +0900 Subject: [PATCH 46/52] strict restriction for give code --- .github/scripts/code_review.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index fdcd8c8..a9df714 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -39,7 +39,7 @@ Precise instructions: - Provide comments and suggestions ONLY if there is something to improve, otherwise return an empty string. - Write the comment in GitHub Markdown format. - Do not start with "markdown" or "```markdown". -- Give example code block or pseudo code if needed. +- IMPORTANT: Give example code block or pseudo code if you can. """ GITHUB_EVENT_PATH = os.getenv("GITHUB_EVENT_PATH") -- 2.49.1 From 1d7409b835977146e4e08f2173616e79db328fe5 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 14:27:26 +0900 Subject: [PATCH 47/52] test mock review --- .github/scripts/code_review.py | 35 ++++++++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 4 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index a9df714..3a8437e 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -16,7 +16,7 @@ import requests HEADERS = {} SINGLE_CHUNK_SYSTEM_PROMPT = """Your task is to review pull requests. Instructions: -- Provide the response in the following JSON format: [{{"lineNumber": , "reviewComment": ""}}] +- Provide the response in the following JSON format: [{{"new_position": , "old_position": , "reviewComment": ""}}] - Do not give positive comments or compliments. - Provide comments and suggestions ONLY if there is something to improve, otherwise return an empty array. - Write the comment in GitHub Markdown format. @@ -260,7 +260,8 @@ def create_comment( { "body": f"[REVIEW] {ai_response['reviewComment']}", "path": file, - "line": int(ai_response["lineNumber"]), + "new_position": int(ai_response["new_position"]), + "old_position": int(ai_response["old_position"]), } ) return comments @@ -337,6 +338,21 @@ Code to review: return ai_response +def post_review( + full_context_review: str, single_chunk_comments: list[dict[str, Any]] +) -> None: + repo_url = EVENT_DATA["pull_request"]["head"]["repo"]["url"] + pull_number = EVENT_DATA["number"] + url = f"{repo_url}/pulls/{pull_number}/reviews" + data = { + "body": full_context_review, + "event": "COMMENT", + "comments": single_chunk_comments, + } + response = requests.post(url, headers=HEADERS, json=data) + response.raise_for_status() + + def main() -> None: """Code Reviewer for Gitea""" @@ -353,9 +369,20 @@ def main() -> None: parsed_diff = parse_diff(diff) # comments = analyze_single_chunks(parsed_diff) + mock_comments = [ + { + "body": "[REVIEW] This is a mock comment.", + "path": "./github/scripts/test.py", + "new_position": 1, + "old_position": 1, + } + ] - full_context_response = analyze_full_context(parsed_diff) - print(full_context_response) + # full_context_response = analyze_full_context(parsed_diff) + mock_full_context_response = "This is a mock full context response." + + # post_review(full_context_response, comments) + post_review(mock_full_context_response, mock_comments) if __name__ == "__main__": -- 2.49.1 From 52043637eb975b14ceb067d463673621a25c9d67 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 14:28:18 +0900 Subject: [PATCH 48/52] add access token for write permission --- .github/scripts/code_review.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 3a8437e..68d1ecc 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -13,7 +13,8 @@ from typing import Any, Optional, Callable import requests -HEADERS = {} +ACCESS_TOKEN = os.getenv("ACCESS_TOKEN", "") +HEADERS = {"Authorization": f"token {ACCESS_TOKEN}"} SINGLE_CHUNK_SYSTEM_PROMPT = """Your task is to review pull requests. Instructions: - Provide the response in the following JSON format: [{{"new_position": , "old_position": , "reviewComment": ""}}] -- 2.49.1 From 2ed06e5d4735c0ec1470883a174e0a5966df2a65 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 16:00:01 +0900 Subject: [PATCH 49/52] final e2e test --- .github/scripts/code_review.py | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 68d1ecc..9ac4057 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -17,7 +17,7 @@ ACCESS_TOKEN = os.getenv("ACCESS_TOKEN", "") HEADERS = {"Authorization": f"token {ACCESS_TOKEN}"} SINGLE_CHUNK_SYSTEM_PROMPT = """Your task is to review pull requests. Instructions: -- Provide the response in the following JSON format: [{{"new_position": , "old_position": , "reviewComment": ""}}] +- Provide the response in the following JSON format: [{{"lineNumber": , "reviewComment": ""}}] - Do not give positive comments or compliments. - Provide comments and suggestions ONLY if there is something to improve, otherwise return an empty array. - Write the comment in GitHub Markdown format. @@ -248,7 +248,6 @@ def get_ai_response_single_chunk(prompt: str) -> Optional[list[dict[str, Any]]]: except Exception as e: print(f"Error during AI response: {e}") print(response) - print(dir(response)) return None @@ -261,8 +260,7 @@ def create_comment( { "body": f"[REVIEW] {ai_response['reviewComment']}", "path": file, - "new_position": int(ai_response["new_position"]), - "old_position": int(ai_response["old_position"]), + "new_position": int(ai_response["lineNumber"]), } ) return comments @@ -301,11 +299,10 @@ def get_ai_response_full_context(prompt: str) -> Optional[str]: try: response = FULL_CONTEXT_MESSAGE(prompt) content = FULL_CONTEXT_RESPONSE_PARSER(response) - return content.lstrip("markdown").strip() + return content except Exception as e: print(f"Error during AI response: {e}") print(response) - print(dir(response)) return None @@ -344,11 +341,13 @@ def post_review( ) -> None: repo_url = EVENT_DATA["pull_request"]["head"]["repo"]["url"] pull_number = EVENT_DATA["number"] + commit_id = EVENT_DATA["pull_request"]["head"]["sha"] url = f"{repo_url}/pulls/{pull_number}/reviews" data = { "body": full_context_review, "event": "COMMENT", "comments": single_chunk_comments, + "commit_id": commit_id, } response = requests.post(url, headers=HEADERS, json=data) response.raise_for_status() @@ -369,21 +368,11 @@ def main() -> None: return parsed_diff = parse_diff(diff) - # comments = analyze_single_chunks(parsed_diff) - mock_comments = [ - { - "body": "[REVIEW] This is a mock comment.", - "path": "./github/scripts/test.py", - "new_position": 1, - "old_position": 1, - } - ] + comments = analyze_single_chunks(parsed_diff) - # full_context_response = analyze_full_context(parsed_diff) - mock_full_context_response = "This is a mock full context response." + full_context_response = analyze_full_context(parsed_diff) - # post_review(full_context_response, comments) - post_review(mock_full_context_response, mock_comments) + post_review(full_context_response, comments) if __name__ == "__main__": -- 2.49.1 From 4731f4c932db98f0f461583e3ca71d151d5b1a58 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 16:07:29 +0900 Subject: [PATCH 50/52] add access token --- .github/workflows/code-review.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml index bc5722f..1558727 100644 --- a/.github/workflows/code-review.yml +++ b/.github/workflows/code-review.yml @@ -25,6 +25,7 @@ jobs: - name: Run Code Review env: + ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }} CLAUDE_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }} -- 2.49.1 From 94f625a096944ac26472e90100b9564352597926 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 16:16:24 +0900 Subject: [PATCH 51/52] remove tests --- .github/scripts/test.py | 19 ---- .github/workflows/test.yml | 21 ----- github_code.py | 188 ------------------------------------- 3 files changed, 228 deletions(-) delete mode 100644 .github/scripts/test.py delete mode 100644 .github/workflows/test.yml delete mode 100644 github_code.py diff --git a/.github/scripts/test.py b/.github/scripts/test.py deleted file mode 100644 index 7af53e5..0000000 --- a/.github/scripts/test.py +++ /dev/null @@ -1,19 +0,0 @@ -import json -import os - -print(os.getenv("GITHUB_EVENT_PATH")) - -with open(os.getenv("GITHUB_EVENT_PATH"), "r") as f: - event_data = json.load(f) - -print(os.getenv("GITEA_TOKEN")) -print(os.getenv("CLAUDE_API_KEY")) -print(os.getenv("OPENAI_API_KEY")) -print(os.getenv("DEEPSEEK_API_KEY")) - -print(event_data) -print(event_data["pull_request"]["diff_url"]) -print(event_data["pull_request"]["base"]["label"]) -print(event_data["pull_request"]["head"]["label"]) -print(event_data["before"]) -print(event_data["after"]) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml deleted file mode 100644 index 1b1202f..0000000 --- a/.github/workflows/test.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Test action - -on: - pull_request: - types: [opened, synchronize] - -permissions: - contents: read - pull-requests: write - -jobs: - review: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - name: Run Code Review - run: python .github/scripts/test.py diff --git a/github_code.py b/github_code.py deleted file mode 100644 index 64988ba..0000000 --- a/github_code.py +++ /dev/null @@ -1,188 +0,0 @@ -import os -import json -from typing import List, Dict, Any, Optional -from github import Github -from openai import OpenAI -import requests -import re - - -# Fetch input tokens from environment variables -OCTOKIT_TOKEN = os.getenv("OCTOKIT_TOKEN") -OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") -OPENAI_API_MODEL = os.getenv("OPENAI_API_MODEL") -GITHUB_EVENT_PATH = os.getenv("GITHUB_EVENT_PATH", "") - -# Initialize GitHub and OpenAI clients -github = Github(OCTOKIT_TOKEN) -openai = OpenAI(api_key=OPENAI_API_KEY) - - -class PRDetails: - def __init__(self, owner: str, repo: str, pull_number: int, title: str, description: str): - self.owner = owner - self.repo = repo - self.pull_number = pull_number - self.title = title - self.description = description - - -def get_pr_details() -> PRDetails: - with open(GITHUB_EVENT_PATH, "r") as f: - event_data = json.load(f) - - repository = event_data["repository"] - number = event_data["number"] - - repo = github.get_repo(f"{repository['owner']['login']}/{repository['name']}") - pr = repo.get_pull(number) - - return PRDetails( - owner=repository["owner"]["login"], - repo=repository["name"], - pull_number=number, - title=pr.title or "", - description=pr.body or "" - ) - - -def get_diff(owner: str, repo: str, pull_number: int) -> Optional[str]: - url = f"https://api.github.com/repos/{owner}/{repo}/pulls/{pull_number}" - headers = { - "Authorization": f"Bearer {OCTOKIT_TOKEN}", - "Accept": "application/vnd.github.v3.diff" - } - response = requests.get(url, headers=headers) - return response.text if response.status_code == 200 else None - - -def create_prompt(file: Dict[str, Any], chunk: Dict[str, Any], pr_details: PRDetails) -> str: - chunk_content = chunk["content"] - changes = "\n".join([f"{c.get('ln', c.get('ln2'))} {c['content']}" for c in chunk["changes"]]) - - return f"""Your task is to review pull requests. Instructions: -- Provide the response in the following JSON format: [{{"lineNumber": , "reviewComment": ""}}] -- Do not give positive comments or compliments. -- Provide comments and suggestions ONLY if there is something to improve, otherwise return 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['to']}" and take the pull request title and description into account when writing the response. - -Pull request title: {pr_details.title} -Pull request description: - ---- -{pr_details.description} ---- - -Git diff to review: - -```diff -{chunk_content} -{changes} -```""" - - -def get_ai_response(prompt: str) -> Optional[List[Dict[str, Any]]]: - try: - response = openai.chat.completions.create( - model=OPENAI_API_MODEL, - messages=[ - {"role": "system", "content": prompt} - ], - temperature=0.2, - max_tokens=700, - top_p=1, - frequency_penalty=0, - presence_penalty=0, - ) - content = response.choices[0].message.content.strip() or "[]" - return json.loads(content) - except Exception as e: - print(f"Error during AI response: {e}") - return None - - -def create_comment(file: Dict[str, Any], chunk: Dict[str, Any], ai_responses: List[Dict[str, Any]]) -> List[Dict[str, Any]]: - comments = [] - for ai_response in ai_responses: - if not file.get("to"): - continue - comments.append({ - "body": f"[GPT-REVIEW] {ai_response['reviewComment']}", - "path": file["to"], - "line": int(ai_response["lineNumber"]) - }) - return comments - - -def create_review_comment(owner: str, repo: str, pull_number: int, comments: List[Dict[str, Any]]) -> None: - url = f"https://api.github.com/repos/{owner}/{repo}/pulls/{pull_number}/reviews" - headers = { - "Authorization": f"Bearer {OCTOKIT_TOKEN}", - "Accept": "application/vnd.github.v3+json" - } - data = { - "comments": comments, - "event": "COMMENT" - } - response = requests.post(url, headers=headers, json=data) - if response.status_code != 200: - print(f"Failed to create review comment: {response.text}") - - -def analyze_code(parsed_diff: List[Dict[str, Any]], pr_details: PRDetails) -> List[Dict[str, Any]]: - comments = [] - for file in parsed_diff: - if file.get("to") == "/dev/null": - continue # Skip deleted files - for chunk in file.get("chunks", []): - prompt = create_prompt(file, chunk, pr_details) - ai_response = get_ai_response(prompt) - if ai_response: - new_comments = create_comment(file, chunk, ai_response) - comments.extend(new_comments) - return comments - - -def main(): - pr_details = get_pr_details() - with open(GITHUB_EVENT_PATH, "r") as f: - event_data = json.load(f) - - if event_data["action"] == "opened": - diff = get_diff(pr_details.owner, pr_details.repo, pr_details.pull_number) - elif event_data["action"] == "synchronize": - base_sha = event_data["before"] - head_sha = event_data["after"] - url = f"https://api.github.com/repos/{pr_details.owner}/{pr_details.repo}/compare/{base_sha}...{head_sha}" - headers = {"Authorization": f"Bearer {OCTOKIT_TOKEN}", "Accept": "application/vnd.github.v3.diff"} - response = requests.get(url, headers=headers) - diff = response.text if response.status_code == 200 else None - else: - print("Unsupported event.") - return - - if not diff: - print("No diff found.") - return - - parsed_diff = [{"to": match.group(1), "chunks": [{"content": match.group(2), "changes": []}]} - for match in re.finditer(r"diff --git a/(.+?) b/\1\n(.*?)\n", diff, re.S)] - - exclude_patterns = os.getenv("EXCLUDE", "").split(",") - filtered_diff = [file for file in parsed_diff if not any(re.match(pattern.strip(), file["to"]) for pattern in exclude_patterns)] - - comments = analyze_code(filtered_diff, pr_details) - if comments: - create_review_comment(pr_details.owner, pr_details.repo, pr_details.pull_number, comments) - - -if __name__ == "__main__": - try: - main() - except Exception as e: - print(f"Error: {e}") - exit(1) \ No newline at end of file -- 2.49.1 From a5ad6cb8bbf8297be7cda1764ea8e72697161cb6 Mon Sep 17 00:00:00 2001 From: Myeongseon Choi Date: Thu, 9 Jan 2025 16:16:55 +0900 Subject: [PATCH 52/52] set action to run only for opened --- .github/scripts/code_review.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/scripts/code_review.py b/.github/scripts/code_review.py index 9ac4057..c796027 100644 --- a/.github/scripts/code_review.py +++ b/.github/scripts/code_review.py @@ -356,9 +356,9 @@ def post_review( def main() -> None: """Code Reviewer for Gitea""" - # if EVENT_DATA["action"] != "opened": - # print("Unsupproted event.") - # return + if EVENT_DATA["action"] != "opened": + print("Unsupproted event.") + return diff = get_diff() if diff is None: -- 2.49.1