Skip to content

Conversation

@banstack
Copy link
Owner

@banstack banstack commented Jun 6, 2025

Description

This contains the initial changes for implementing code review bot using GitHub actions and Groq API.

  • Here we make an API request to Groq API using llama-4-scout as our LLM of choice
  • We make a request to GitHub API for diff information

@banstack banstack force-pushed the test-change branch 2 times, most recently from 178fd58 to 3f3e415 Compare June 6, 2025 04:48
@github-actions
Copy link

github-actions bot commented Jun 6, 2025

Code Review

The provided code diff appears to be implementing a code review bot using GitHub Actions and the Groq API. Here are some improvements:

Security and Best Practices

  • In .github/workflows/code-review.yml, consider pinning the actions/checkout version to a specific commit hash for better security.
  • In reviewbot/main.py, avoid hardcoding API keys and secrets. Instead, use environment variables or a secrets manager.
  • In reviewbot/utils.py, handle exceptions more robustly. For example, what if the GITHUB_EVENT_PATH environment variable is set but the file is not a valid JSON?

Code Quality and Readability

  • In reviewbot/main.py, consider adding type hints for function parameters and return types.
  • In reviewbot/utils.py, use more descriptive variable names. For example, repo could be github_repository.
  • In reviewbot/utils.py, consider adding docstrings to explain the purpose of each function.

Functionality

  • In reviewbot/main.py, what if the Groq API returns an error? Consider adding error handling.
  • In reviewbot/utils.py, what if the GitHub API returns an error? Consider adding error handling.
  • In reviewbot/utils.py, the get_diff function assumes that the GitHub API returns a paginated response. Consider adding a check for this.

Performance

  • In reviewbot/utils.py, the get_diff function makes multiple API calls to fetch the diff. Consider batching these requests or using a more efficient approach.

Code Suggestions

  • Consider adding a timeout parameter to the requests.post call in reviewbot/main.py to avoid hanging indefinitely.
  • In reviewbot/utils.py, use a more efficient way to check if a file has a patch. For example, use if f.get("patch", "").strip():

Updated Code

Here is an updated version of the code incorporating some of these suggestions:

.github/workflows/code-review.yml:

jobs:
  review:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      pull-requests: write
    steps:
      - name: Checkout code
        uses: actions/checkout@v4@1234567890abcdef
      - name: Install dependencies
        run: |
          pip install -r requirements.txt
      - name: Run LLM code review
        env:
          GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          PYTHONPATH: ${{ github.workspace }}
          GITHUB_REPOSITORY: ${{ github.repository }}
          GITHUB_REF: ${{ github.ref }}
        run: |
          python3 -m reviewbot.main

reviewbot/main.py:

import os
import requests
from reviewbot.utils import get_diff, post_pr_comment, get_pr_number

def review_with_groq(diff: str) -> str:
    api_key = os.environ.get("GROQ_API_KEY")
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
    }
    payload = {
        "model": "meta-llama/llama-4-scout-17b-16e-instruct",
        "messages": [
            {"role": "system", "content": "You are a senior software engineer doing code reviews."},
            {"role": "user", "content": f"Please review this code diff, list improvements in bullet points:\n\n{diff}"}
        ],
        "temperature": 0.3,
    }
    try:
        response = requests.post("https://api.groq.com/openai/v1/chat/completions", headers=headers, json=payload, timeout=30)
        response.raise_for_status()
        return response.json()["choices"][0]["message"]["content"]
    except requests.RequestException as e:
        print(f"Error: {e}")
        return ""

def main():
    diff = get_diff()
    review = review_with_groq(diff)
    post_pr_comment(review)

if __name__ == "__main__":
    main()

reviewbot/utils.py:

import os
import requests
import json

def get_pr_number() -> int:
    event_path = os.getenv("GITHUB_EVENT_PATH")
    if not event_path:
        raise Exception("GITHUB_EVENT_PATH not set")
    with open(event_path, 'r') as f:
        event = json.load(f)
    return event["pull_request"]["number"]

def get_diff() -> str:
    github_repository = os.environ.get("GITHUB_REPOSITORY")
    pr_number = get_pr_number()
    token = os.environ.get("GITHUB_TOKEN")
    if not token:
        raise Exception("Missing GITHUB_TOKEN environment variable.")
    headers = {
        "Authorization": f"Bearer {token}",
        "Accept": "application/vnd.github.v3+json",
    }
    url = f"https://api.github.com/repos/{github_repository}/pulls/{pr_number}/files"
    diffs = []
    page = 1
    while True:
        response = requests.get(url, headers=headers, params={"page": page, "per_page": 100})
        response.raise_for_status()
        files = response.json()
        if not files:
            break
        for f in files:
            patch = f.get("patch", "").strip()
            if patch:
                diffs.append(f"File: {f['filename']}\n{patch}")
        page += 1
    return "\n\n".join(diffs)

def post_pr_comment(body: str):
    github_repository = os.getenv("GITHUB_REPOSITORY")
    token = os.getenv("GITHUB_TOKEN")
    pr_number = get_pr_number()
    url = f"https://api.github.com/repos/{github_repository}/issues/{pr_number}/comments"
    headers = {
        "Authorization": f"Bearer {token}",
        "Accept": "application/vnd.github+json"
    }
    payload = {
        "body": body
    }
    try:
        response = requests.post(url, headers=headers, json=payload, timeout=30)
        response.raise_for_status()
    except requests.RequestException as e:
        print(f"Error: {e}")

@github-actions
Copy link

github-actions bot commented Jun 6, 2025

Here are the improvements in bullet points:

Code Quality and Best Practices

  • In .github/workflows/code-review.yml, consider adding a timeout to the job to prevent it from running indefinitely.
  • In reviewbot/main.py, the review_with_groq function does not handle exceptions well. Consider adding try-except blocks to handle potential errors.
  • In reviewbot/utils.py, the get_diff function does not handle pagination correctly. Although the code attempts to paginate, it does not account for the possibility of multiple pages of results. Consider using a more robust pagination approach.
  • In reviewbot/utils.py, the post_pr_comment function does not validate the body parameter. Consider adding a check to ensure that the comment body is not empty.

Security

  • In .github/workflows/code-review.yml, the GROQ_API_KEY and GITHUB_TOKEN are stored as secrets. However, it's still important to ensure that these secrets are not leaked in the workflow logs. Consider adding a step to mask these secrets.
  • In reviewbot/main.py, the api_key is retrieved from an environment variable. Consider using a more secure approach, such as using a secrets manager.

Performance

  • In reviewbot/utils.py, the get_diff function makes multiple API calls to retrieve the diff. Consider batching these calls or using a more efficient approach to retrieve the diff.

Readability and Maintainability

  • In reviewbot/main.py, the review_with_groq function has a magic string for the API endpoint. Consider defining a constant for this endpoint.
  • In reviewbot/utils.py, the get_diff function has a magic string for the GitHub API endpoint. Consider defining a constant for this endpoint.
  • In reviewbot/utils.py, the post_pr_comment function has a magic string for the GitHub API endpoint. Consider defining a constant for this endpoint.

Testing

  • There are no tests for the reviewbot code. Consider adding unit tests and integration tests to ensure that the code works correctly.

Code Style

  • In reviewbot/main.py, the code uses inconsistent indentation. Consider using a consistent number of spaces for indentation.
  • In reviewbot/utils.py, the code uses inconsistent naming conventions. Consider using a consistent naming convention throughout the codebase.

Here is an updated version of the code incorporating some of these suggestions:

.github/workflows/code-review.yml

name: Code Review

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  review:
    runs-on: ubuntu-latest

    permissions:
      contents: read
      pull-requests: write

    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: '3.9'

    - name: Install dependencies
      run: |
        pip install -r requirements.txt

    - name: Run LLM code review
      env:
        GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        PYTHONPATH: ${{ github.workspace }}
        GITHUB_REPOSITORY: ${{ github.repository }}
        GITHUB_REF: ${{ github.ref }}
      run: |
        python3 -m reviewbot.main

reviewbot/main.py

import os
import requests
from reviewbot.utils import get_diff, post_pr_comment, get_pr_number

GROQ_API_ENDPOINT = "https://api.groq.com/openai/v1/chat/completions"

def review_with_groq(diff):
    try:
        api_key = os.environ.get("GROQ_API_KEY")
        headers = {
            "Authorization": f"Bearer {api_key}",
            "Content-Type": "application/json",
        }
        payload = {
            "model": "meta-llama/llama-4-scout-17b-16e-instruct",
            "messages": [
                {"role": "system", "content": "You are a senior software engineer doing code reviews."},
                {"role": "user", "content": f"Please review this code diff, list improvements in bullet points:\n\n{diff}"}
            ],
            "temperature": 0.3,
        }

        response = requests.post(GROQ_API_ENDPOINT, headers=headers, json=payload)
        response.raise_for_status()
        return response.json()["choices"][0]["message"]["content"]
    except requests.exceptions.RequestException as e:
        print(f"Error: {e}")
        return ""

def main():
    diff = get_diff()
    review = review_with_groq(diff)
    post_pr_comment(review)

if __name__ == "__main__":
    main()

reviewbot/utils.py

import os
import requests
import json

GITHUB_API_ENDPOINT = "https://api.github.com"

def get_pr_number():
    event_path = os.getenv("GITHUB_EVENT_PATH")
    if not event_path:
        raise Exception("GITHUB_EVENT_PATH not set")

    with open(event_path, 'r') as f:
        event = json.load(f)
    
    return event["pull_request"]["number"]

def get_diff():
    repo = os.environ.get("GITHUB_REPOSITORY") 
    pr_number = get_pr_number()

    token = os.environ.get("GITHUB_TOKEN")
    if not token:
        raise Exception("Missing GITHUB_TOKEN environment variable.")

    headers = {
        "Authorization": f"Bearer {token}",
        "Accept": "application/vnd.github.v3+json",
    }

    url = f"{GITHUB_API_ENDPOINT}/repos/{repo}/pulls/{pr_number}/files"

    diffs = []
    page = 1
    while True:
        response = requests.get(url, headers=headers, params={"page": page, "per_page": 100})
        response.raise_for_status()
        files = response.json()
        if not files:
            break

        for f in files:
            patch = f.get("patch")
            if patch:
                diffs.append(f"File: {f['filename']}\n{patch}")

        page += 1

    return "\n\n".join(diffs)

def post_pr_comment(body: str):
    repo = os.getenv("GITHUB_REPOSITORY")
    token = os.getenv("GITHUB_TOKEN")
    pr_number = get_pr_number()

    url = f"{GITHUB_API_ENDPOINT}/repos/{repo}/issues/{pr_number}/comments"
    headers = {
        "Authorization": f"Bearer {token}",
        "Accept": "application/vnd.github+json"
    }
    payload = {
        "body": body
    }

    response = requests.post(url, headers=headers, json=payload)
    print("GitHub API response:", response.status_code, response.text)
    response.raise_for_status()

@github-actions
Copy link

github-actions bot commented Jun 6, 2025

Code Review

The provided code diff appears to be implementing a code review bot using GitHub Actions and the Groq API. Here are some improvements:

Security and Best Practices

  • In .github/workflows/code-review.yml, consider pinning the actions/checkout version to a specific commit hash for better security.
  • In reviewbot/main.py, avoid hardcoding sensitive information like API keys. Instead, use environment variables or a secrets manager.
  • In reviewbot/utils.py, handle exceptions more robustly. For example, what if the GITHUB_EVENT_PATH environment variable is set but the file is not found?

Code Quality and Readability

  • In reviewbot/main.py, consider adding type hints for function parameters and return types.
  • In reviewbot/utils.py, use more descriptive variable names, such as github_token instead of token.
  • In reviewbot/utils.py, consider adding docstrings to functions to explain their purpose and behavior.

Functionality

  • In reviewbot/main.py, what if the Groq API returns an error or an empty response? Consider adding error handling and logging.
  • In reviewbot/utils.py, what if the GitHub API returns an error or an empty response? Consider adding error handling and logging.
  • In reviewbot/utils.py, the get_diff function assumes that the GitHub API returns a paginated response. Consider adding a check for this and handling pagination more robustly.

Performance

  • In reviewbot/utils.py, the get_diff function makes multiple API calls to fetch the diff. Consider batching these requests or using a more efficient approach.

Code Suggestions

Here are some specific code suggestions:

  • In .github/workflows/code-review.yml, add a permissions block to restrict the workflow's permissions:
permissions:
  contents: read
  pull-requests: write
  • In reviewbot/main.py, use a more descriptive variable name for the Groq API response:
groq_response = response.json()
review = groq_response["choices"][0]["message"]["content"]
  • In reviewbot/utils.py, use a more descriptive variable name for the GitHub API response:
github_response = response.json()
  • In reviewbot/utils.py, consider adding a try-except block to handle exceptions:
try:
    # API call
except requests.RequestException as e:
    print(f"Error: {e}")

Updated Code

Here is an updated version of the code incorporating some of these suggestions:

.github/workflows/code-review.yml:

name: Code Review

on:
  pull_request:
    types: [opened, synchronize]

jobs:
  review:
    runs-on: ubuntu-latest

    permissions:
      contents: read
      pull-requests: write

    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Install dependencies
      run: |
        pip install -r requirements.txt

    - name: Run LLM code review
      env:
        GROQ_API_KEY: ${{ secrets.GROQ_API_KEY }}
        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        PYTHONPATH: ${{ github.workspace }}
        GITHUB_REPOSITORY: ${{ github.repository }}
        GITHUB_REF: ${{ github.ref }}
      run: |
        python3 -m reviewbot.main

reviewbot/main.py:

import os
import requests
from reviewbot.utils import get_diff, post_pr_comment, get_pr_number

def review_with_groq(diff: str) -> str:
    api_key = os.environ.get("GROQ_API_KEY")
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json",
    }
    payload = {
        "model": "meta-llama/llama-4-scout-17b-16e-instruct",
        "messages": [
            {"role": "system", "content": "You are a senior software engineer doing code reviews."},
            {"role": "user", "content": f"Please review this code diff, list improvements in bullet points:\n\n{diff}"}
        ],
        "temperature": 0.3,
    }

    try:
        response = requests.post("https://api.groq.com/openai/v1/chat/completions", headers=headers, json=payload)
        response.raise_for_status()
        groq_response = response.json()
        review = groq_response["choices"][0]["message"]["content"]
        return review
    except requests.RequestException as e:
        print(f"Error: {e}")
        return ""

def main():
    diff = get_diff()
    review = review_with_groq(diff)
    post_pr_comment(review)

if __name__ == "__main__":
    main()

reviewbot/utils.py:

import os
import requests
import json

def get_pr_number() -> int:
    event_path = os.getenv("GITHUB_EVENT_PATH")
    if not event_path:
        raise Exception("GITHUB_EVENT_PATH not set")

    with open(event_path, 'r') as f:
        event = json.load(f)
    
    return event["pull_request"]["number"]

def get_diff() -> str:
    repo = os.environ.get("GITHUB_REPOSITORY") 
    pr_number = get_pr_number()

    github_token = os.environ.get("GITHUB_TOKEN")
    if not github_token:
        raise Exception("Missing GITHUB_TOKEN environment variable.")

    headers = {
        "Authorization": f"Bearer {github_token}",
        "Accept": "application/vnd.github.v3+json",
    }

    url = f"https://api.github.com/repos/{repo}/pulls/{pr_number}/files"

    diffs = []
    page = 1
    while True:
        try:
            response = requests.get(url, headers=headers, params={"page": page, "per_page": 100})
            response.raise_for_status()
            files = response.json()
            if not files:
                break

            for f in files:
                patch = f.get("patch")
                if patch:
                    diffs.append(f"File: {f['filename']}\n{patch}")

            page += 1
        except requests.RequestException as e:
            print(f"Error: {e}")
            break

    return "\n\n".join(diffs)

def post_pr_comment(body: str):
    repo = os.getenv("GITHUB_REPOSITORY")
    github_token = os.getenv("GITHUB_TOKEN")
    pr_number = get_pr_number()

    url = f"https://api.github.com/repos/{repo}/issues/{pr_number}/comments"
    headers = {
        "Authorization": f"Bearer {github_token}",
        "Accept": "application/vnd.github+json"
    }
    payload = {
        "body": body
    }

    try:
        response = requests.post(url, headers=headers, json=payload)
        response.raise_for_status()
    except requests.RequestException as e:
        print(f"Error: {e}")

@banstack banstack changed the title Add twosum example to hello.py Utilize Groq to comment MR suggestions Jun 6, 2025
@banstack banstack merged commit 67394ff into main Jun 6, 2025
1 check passed
banstack added a commit that referenced this pull request Jun 8, 2025
* Add two-sum example

* Allow GH action to post review as comment
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants