From e67e6638225240c16c35eb1306fa57604542af8f Mon Sep 17 00:00:00 2001 From: DibyoGit <64616330+DibyoGit@users.noreply.github.com> Date: Fri, 11 Jul 2025 11:49:46 +0530 Subject: [PATCH 01/15] Create tabby-ai-review.yaml --- .github/workflows/tabby-ai-review.yaml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 .github/workflows/tabby-ai-review.yaml diff --git a/.github/workflows/tabby-ai-review.yaml b/.github/workflows/tabby-ai-review.yaml new file mode 100644 index 00000000..793c0551 --- /dev/null +++ b/.github/workflows/tabby-ai-review.yaml @@ -0,0 +1,26 @@ +name: AI Code Review with Tabby + +on: + pull_request: + types: [opened, synchronize] + +jobs: + tabby-review: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: 3.11 + + - name: Install dependencies + run: pip install -r ai-review/requirements.txt + + - name: Run Tabby PR review + env: + TABBY_URL: ${{ secrets.TABBY_URL }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: python ai-review/review_bot.py From c1879a61fdcc04b7bbdd4312d2eac0f937809fc6 Mon Sep 17 00:00:00 2001 From: DibyoGit <64616330+DibyoGit@users.noreply.github.com> Date: Fri, 11 Jul 2025 11:52:15 +0530 Subject: [PATCH 02/15] Create review-bot.py --- ai-review/review-bot.py | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 ai-review/review-bot.py diff --git a/ai-review/review-bot.py b/ai-review/review-bot.py new file mode 100644 index 00000000..1ddcaae4 --- /dev/null +++ b/ai-review/review-bot.py @@ -0,0 +1,23 @@ +import os +import subprocess +from github_api import post_comment +from tabby_client import get_tabby_review + +def get_changed_files(): + output = subprocess.check_output(["git", "diff", "--name-only", "origin/main...HEAD"]) + return output.decode().splitlines() + +def main(): + tabby_url = os.getenv("TABBY_URL", "http://54.196.243.3:8080") + changed_files = get_changed_files() + + for file_path in changed_files: + with open(file_path, "r") as f: + code = f.read() + + prompt = f"Review this code for issues:\n\n{code}" + suggestion = get_tabby_review(prompt, tabby_url) + post_comment(file_path, suggestion) + +if __name__ == "__main__": + main() From 56a4bb8cf4c1a02d0dac568df2e9054df2ff9f6e Mon Sep 17 00:00:00 2001 From: DibyoGit <64616330+DibyoGit@users.noreply.github.com> Date: Fri, 11 Jul 2025 11:52:59 +0530 Subject: [PATCH 03/15] Create githin-api.py --- ai-review/githin-api.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 ai-review/githin-api.py diff --git a/ai-review/githin-api.py b/ai-review/githin-api.py new file mode 100644 index 00000000..6cc8a99f --- /dev/null +++ b/ai-review/githin-api.py @@ -0,0 +1,17 @@ +import os +import requests + +def post_comment(file_path, comment): + repo = os.getenv("GITHUB_REPOSITORY") + pr_number = os.getenv("GITHUB_REF").split("/")[-1] + token = os.getenv("GITHUB_TOKEN") + + url = f"https://api.github.com/repos/{repo}/issues/{pr_number}/comments" + headers = { + "Authorization": f"Bearer {token}", + "Accept": "application/vnd.github+json" + } + + body = f"💡 **AI Review Suggestion for `{file_path}`**\n\n{comment}" + response = requests.post(url, json={"body": body}, headers=headers) + response.raise_for_status() From 392e740c1ec7d52ad7b523e4dd1ca83b9e4743d0 Mon Sep 17 00:00:00 2001 From: DibyoGit <64616330+DibyoGit@users.noreply.github.com> Date: Fri, 11 Jul 2025 11:59:30 +0530 Subject: [PATCH 04/15] Create requirements.txt --- ai-review/requirements.txt | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 ai-review/requirements.txt diff --git a/ai-review/requirements.txt b/ai-review/requirements.txt new file mode 100644 index 00000000..f79c0da7 --- /dev/null +++ b/ai-review/requirements.txt @@ -0,0 +1,2 @@ +requests +rich From 975794684f9ed2a1453ac532616015dcf08d5242 Mon Sep 17 00:00:00 2001 From: DibyoGit <64616330+DibyoGit@users.noreply.github.com> Date: Fri, 11 Jul 2025 12:00:51 +0530 Subject: [PATCH 05/15] Rename review-bot.py to review_bot.py --- ai-review/{review-bot.py => review_bot.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ai-review/{review-bot.py => review_bot.py} (100%) diff --git a/ai-review/review-bot.py b/ai-review/review_bot.py similarity index 100% rename from ai-review/review-bot.py rename to ai-review/review_bot.py From 3380a94814ffa51184c0a9440a804ef5404d8c84 Mon Sep 17 00:00:00 2001 From: DibyoGit <64616330+DibyoGit@users.noreply.github.com> Date: Fri, 11 Jul 2025 12:01:07 +0530 Subject: [PATCH 06/15] Rename githin-api.py to githhub_api.py --- ai-review/{githin-api.py => githhub_api.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ai-review/{githin-api.py => githhub_api.py} (100%) diff --git a/ai-review/githin-api.py b/ai-review/githhub_api.py similarity index 100% rename from ai-review/githin-api.py rename to ai-review/githhub_api.py From ed20c624922e665958215ae646839ffa2bead13d Mon Sep 17 00:00:00 2001 From: DibyoGit <64616330+DibyoGit@users.noreply.github.com> Date: Fri, 11 Jul 2025 12:02:11 +0530 Subject: [PATCH 07/15] Rename githhub_api.py to github_api.py --- ai-review/{githhub_api.py => github_api.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename ai-review/{githhub_api.py => github_api.py} (100%) diff --git a/ai-review/githhub_api.py b/ai-review/github_api.py similarity index 100% rename from ai-review/githhub_api.py rename to ai-review/github_api.py From 817e17862601fe2da86324f0955dfea1025d6d64 Mon Sep 17 00:00:00 2001 From: DibyoGit <64616330+DibyoGit@users.noreply.github.com> Date: Fri, 11 Jul 2025 12:03:27 +0530 Subject: [PATCH 08/15] Create tabby_client.py --- ai-review/tabby_client.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 ai-review/tabby_client.py diff --git a/ai-review/tabby_client.py b/ai-review/tabby_client.py new file mode 100644 index 00000000..a254cd7a --- /dev/null +++ b/ai-review/tabby_client.py @@ -0,0 +1,31 @@ +import requests + +def get_tabby_review(prompt: str, tabby_url="http://54.196.243.3:8080") -> str: + """ + Sends a prompt to the TabbyML chat model and returns the response. + + Args: + prompt (str): The prompt text to analyze. + tabby_url (str): Base URL of the TabbyML server. + + Returns: + str: AI-generated response from TabbyML. + """ + try: + response = requests.post( + f"{tabby_url}/v1/chat/completions", + json={ + "messages": [ + {"role": "user", "content": prompt} + ], + "temperature": 0.4, + }, + timeout=30 + ) + response.raise_for_status() + result = response.json() + return result["choices"][0]["message"]["content"] + + except requests.exceptions.RequestException as e: + print(f"[ERROR] Failed to communicate with TabbyML: {e}") + return "⚠️ Error: Unable to connect to TabbyML server." From 2919dba1045f494de59e66255e351415794563b3 Mon Sep 17 00:00:00 2001 From: DibyoGit <64616330+DibyoGit@users.noreply.github.com> Date: Fri, 11 Jul 2025 12:05:21 +0530 Subject: [PATCH 09/15] Update review_bot.py --- ai-review/review_bot.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ai-review/review_bot.py b/ai-review/review_bot.py index 1ddcaae4..9c946c7d 100644 --- a/ai-review/review_bot.py +++ b/ai-review/review_bot.py @@ -4,7 +4,7 @@ from tabby_client import get_tabby_review def get_changed_files(): - output = subprocess.check_output(["git", "diff", "--name-only", "origin/main...HEAD"]) + output = subprocess.check_output(["git", "diff", "--name-only", "origin/master...HEAD"]) return output.decode().splitlines() def main(): From c319fa84f50756a3c08b8ab60473b0dceebcc07c Mon Sep 17 00:00:00 2001 From: DibyoGit <64616330+DibyoGit@users.noreply.github.com> Date: Fri, 11 Jul 2025 12:07:55 +0530 Subject: [PATCH 10/15] Update review_bot.py --- ai-review/review_bot.py | 76 ++++++++++++++++++++++++++++++++++------- 1 file changed, 64 insertions(+), 12 deletions(-) diff --git a/ai-review/review_bot.py b/ai-review/review_bot.py index 9c946c7d..8f46eada 100644 --- a/ai-review/review_bot.py +++ b/ai-review/review_bot.py @@ -1,23 +1,75 @@ import os -import subprocess -from github_api import post_comment +import requests from tabby_client import get_tabby_review -def get_changed_files(): - output = subprocess.check_output(["git", "diff", "--name-only", "origin/master...HEAD"]) - return output.decode().splitlines() +# --- Config --- +GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") +GITHUB_REPOSITORY = os.getenv("GITHUB_REPOSITORY") # e.g., "myuser/myrepo" +GITHUB_REF = os.getenv("GITHUB_REF", "") # e.g., "refs/pull/42/merge" +TABBY_URL = os.getenv("TABBY_URL", "http://54.196.243.3:8080") + + +def get_pull_request_number(): + """ + Extract the pull request number from GITHUB_REF (e.g. "refs/pull/42/merge"). + """ + try: + return GITHUB_REF.split("/")[2] + except IndexError: + raise RuntimeError(f"Cannot extract PR number from GITHUB_REF='{GITHUB_REF}'") + + +def get_changed_files(pr_number): + """ + Fetch the list of changed files in the PR using GitHub API. + """ + url = f"https://api.github.com/repos/{GITHUB_REPOSITORY}/pulls/{pr_number}/files" + headers = {"Authorization": f"Bearer {GITHUB_TOKEN}"} + response = requests.get(url, headers=headers) + response.raise_for_status() + files = response.json() + return [f["filename"] for f in files if f["filename"].endswith((".py", ".js", ".ts", ".java", ".go", ".rb"))] + + +def post_comment(pr_number, body): + """ + Post a comment on the pull request. + """ + url = f"https://api.github.com/repos/{GITHUB_REPOSITORY}/issues/{pr_number}/comments" + headers = { + "Authorization": f"Bearer {GITHUB_TOKEN}", + "Accept": "application/vnd.github+json" + } + response = requests.post(url, headers=headers, json={"body": body}) + response.raise_for_status() + def main(): - tabby_url = os.getenv("TABBY_URL", "http://54.196.243.3:8080") - changed_files = get_changed_files() + pr_number = get_pull_request_number() + print(f"🔍 Pull Request #{pr_number}") + + changed_files = get_changed_files(pr_number) + if not changed_files: + print("✅ No code files changed. Skipping review.") + return + + print(f"📂 Changed files: {changed_files}") for file_path in changed_files: - with open(file_path, "r") as f: - code = f.read() + try: + with open(file_path, "r", encoding="utf-8") as f: + code = f.read() + + prompt = f"Review this code and suggest improvements:\n\n{code}" + suggestion = get_tabby_review(prompt, tabby_url=TABBY_URL) + + comment_body = f"💡 **AI Review Suggestion for `{file_path}`**\n\n{suggestion}" + post_comment(pr_number, comment_body) + print(f"✅ Comment posted for {file_path}") + + except Exception as e: + print(f"⚠️ Skipping file `{file_path}` due to error: {e}") - prompt = f"Review this code for issues:\n\n{code}" - suggestion = get_tabby_review(prompt, tabby_url) - post_comment(file_path, suggestion) if __name__ == "__main__": main() From f537a98281b35e66103ec974d04e66dfb2c01337 Mon Sep 17 00:00:00 2001 From: DibyoGit <64616330+DibyoGit@users.noreply.github.com> Date: Mon, 14 Jul 2025 00:09:10 +0530 Subject: [PATCH 11/15] Update tabby-ai-review.yaml --- .github/workflows/tabby-ai-review.yaml | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.github/workflows/tabby-ai-review.yaml b/.github/workflows/tabby-ai-review.yaml index 793c0551..b8d72b57 100644 --- a/.github/workflows/tabby-ai-review.yaml +++ b/.github/workflows/tabby-ai-review.yaml @@ -4,12 +4,19 @@ on: pull_request: types: [opened, synchronize] +permissions: + contents: read + pull-requests: write # 🔑 Needed to post PR comments + jobs: tabby-review: runs-on: ubuntu-latest + steps: - name: Checkout code uses: actions/checkout@v3 + with: + fetch-depth: 0 # 🔁 Ensures full git history for diff - name: Set up Python uses: actions/setup-python@v4 @@ -22,5 +29,9 @@ jobs: - name: Run Tabby PR review env: TABBY_URL: ${{ secrets.TABBY_URL }} + TABBY_USERNAME: ${{ secrets.TABBY_USERNAME }} + TABBY_PASSWORD: ${{ secrets.TABBY_PASSWORD }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_REPOSITORY: ${{ github.repository }} + GITHUB_EVENT_PATH: ${{ github.event_path }} run: python ai-review/review_bot.py From 4fecd011164d1498d37b661db059e0b0489ffad9 Mon Sep 17 00:00:00 2001 From: DibyoGit <64616330+DibyoGit@users.noreply.github.com> Date: Mon, 14 Jul 2025 00:11:35 +0530 Subject: [PATCH 12/15] Update tabby_client.py --- ai-review/tabby_client.py | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/ai-review/tabby_client.py b/ai-review/tabby_client.py index a254cd7a..05a58d52 100644 --- a/ai-review/tabby_client.py +++ b/ai-review/tabby_client.py @@ -1,16 +1,12 @@ import requests +import os +from requests.auth import HTTPBasicAuth + +def get_tabby_review(prompt: str, tabby_url=None) -> str: + tabby_url = tabby_url or os.getenv("TABBY_URL", "http://localhost:8080") + username = os.getenv("TABBY_USERNAME") + password = os.getenv("TABBY_PASSWORD") -def get_tabby_review(prompt: str, tabby_url="http://54.196.243.3:8080") -> str: - """ - Sends a prompt to the TabbyML chat model and returns the response. - - Args: - prompt (str): The prompt text to analyze. - tabby_url (str): Base URL of the TabbyML server. - - Returns: - str: AI-generated response from TabbyML. - """ try: response = requests.post( f"{tabby_url}/v1/chat/completions", @@ -20,12 +16,13 @@ def get_tabby_review(prompt: str, tabby_url="http://54.196.243.3:8080") -> str: ], "temperature": 0.4, }, - timeout=30 + timeout=30, + auth=HTTPBasicAuth(username, password) if username and password else None ) response.raise_for_status() result = response.json() return result["choices"][0]["message"]["content"] - + except requests.exceptions.RequestException as e: - print(f"[ERROR] Failed to communicate with TabbyML: {e}") + print(f"Error: Failed to communicate with TabbyML: {e}") return "⚠️ Error: Unable to connect to TabbyML server." From 3c621ac2d030f08b9f82e17f883d6f3f5d28bcfc Mon Sep 17 00:00:00 2001 From: DibyoGit <64616330+DibyoGit@users.noreply.github.com> Date: Mon, 14 Jul 2025 00:16:43 +0530 Subject: [PATCH 13/15] Update tabby_client.py --- ai-review/tabby_client.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/ai-review/tabby_client.py b/ai-review/tabby_client.py index 05a58d52..84a2b5dc 100644 --- a/ai-review/tabby_client.py +++ b/ai-review/tabby_client.py @@ -3,7 +3,7 @@ from requests.auth import HTTPBasicAuth def get_tabby_review(prompt: str, tabby_url=None) -> str: - tabby_url = tabby_url or os.getenv("TABBY_URL", "http://localhost:8080") + tabby_url = tabby_url or os.getenv("TABBY_URL", "http://54.196.243.3:8080/") username = os.getenv("TABBY_USERNAME") password = os.getenv("TABBY_PASSWORD") @@ -11,17 +11,14 @@ def get_tabby_review(prompt: str, tabby_url=None) -> str: response = requests.post( f"{tabby_url}/v1/chat/completions", json={ - "messages": [ - {"role": "user", "content": prompt} - ], + "messages": [{"role": "user", "content": prompt}], "temperature": 0.4, }, timeout=30, - auth=HTTPBasicAuth(username, password) if username and password else None + auth=HTTPBasicAuth(username, password) ) response.raise_for_status() - result = response.json() - return result["choices"][0]["message"]["content"] + return response.json()["choices"][0]["message"]["content"] except requests.exceptions.RequestException as e: print(f"Error: Failed to communicate with TabbyML: {e}") From e8d732fc66a49f7b80950e99364cb710424cd9c3 Mon Sep 17 00:00:00 2001 From: DibyoGit <64616330+DibyoGit@users.noreply.github.com> Date: Mon, 14 Jul 2025 00:17:48 +0530 Subject: [PATCH 14/15] Update tabby_client.py --- ai-review/tabby_client.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ai-review/tabby_client.py b/ai-review/tabby_client.py index 84a2b5dc..1f9dab97 100644 --- a/ai-review/tabby_client.py +++ b/ai-review/tabby_client.py @@ -3,7 +3,7 @@ from requests.auth import HTTPBasicAuth def get_tabby_review(prompt: str, tabby_url=None) -> str: - tabby_url = tabby_url or os.getenv("TABBY_URL", "http://54.196.243.3:8080/") + tabby_url = tabby_url or os.getenv("TABBY_URL", "http://54.196.243.3:8080") username = os.getenv("TABBY_USERNAME") password = os.getenv("TABBY_PASSWORD") From 045a18d8fdd4ca858efe9bd54544ecc666778011 Mon Sep 17 00:00:00 2001 From: DibyoGit <64616330+DibyoGit@users.noreply.github.com> Date: Mon, 14 Jul 2025 00:20:57 +0530 Subject: [PATCH 15/15] Update tabby_client.py --- ai-review/tabby_client.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/ai-review/tabby_client.py b/ai-review/tabby_client.py index 1f9dab97..f88bcdfd 100644 --- a/ai-review/tabby_client.py +++ b/ai-review/tabby_client.py @@ -7,6 +7,10 @@ def get_tabby_review(prompt: str, tabby_url=None) -> str: username = os.getenv("TABBY_USERNAME") password = os.getenv("TABBY_PASSWORD") + if not username or not password: + print("⚠️ Missing TabbyML credentials. Set TABBY_USERNAME and TABBY_PASSWORD.") + return "⚠️ Error: Missing credentials for TabbyML." + try: response = requests.post( f"{tabby_url}/v1/chat/completions", @@ -21,5 +25,5 @@ def get_tabby_review(prompt: str, tabby_url=None) -> str: return response.json()["choices"][0]["message"]["content"] except requests.exceptions.RequestException as e: - print(f"Error: Failed to communicate with TabbyML: {e}") + print(f"Error: Failed to communicate with TabbyML: {e}") return "⚠️ Error: Unable to connect to TabbyML server."