DEV Community

Mattias chaw
Mattias chaw

Posted on

Build an AI Code Review Bot in 60 Lines of Python Using Chinese LLMs

Code reviews are the part of development everyone agrees is important and nobody has time for. What if you could automate the first pass with an AI model that costs 95% less than GPT-4o but reviews code just as thoroughly?

In this guide, you'll build a GitHub PR review bot that uses Chinese LLMs (DeepSeek, GLM-5, Qwen) to automatically review pull requests — all through a single API endpoint.

Why Chinese LLMs for Code Review?

I've been running AI-powered code reviews in production for three months. Here's what I've learned:

  • DeepSeek V4 Pro excels at spotting logic errors and suggesting refactors
  • GLM-5 is surprisingly good at security vulnerability detection
  • Qwen 3 handles multi-file context beautifully for architectural feedback

The best part? You get all three through one API at aiwave.live — no need to juggle multiple API keys or provider accounts.

Prerequisites

You'll need:

  • Python 3.10+
  • A GitHub personal access token (for PR access)
  • An API key from AIWave (gives you access to 50+ Chinese AI models)

Step 1: Set Up the Core Review Engine

First, let's create the AI review client. We'll use the OpenAI-compatible endpoint so the code works as a drop-in replacement:

import httpx
import json

class AIReviewer:
    def __init__(self, api_key: str, model: str = "deepseek-chat"):
        self.client = httpx.Client(
            base_url="https://aiwave.live/v1",
            headers={"Authorization": f"Bearer {api_key}"},
            timeout=60.0
        )
        self.model = model

    def review_code(self, diff: str, language: str = "python") -> dict:
        prompt = f"""You are a senior code reviewer. Analyze this {language} code diff
and provide actionable feedback.

Focus on:
1. Bugs and potential crashes
2. Security vulnerabilities
3. Performance issues
4. Code style and best practices

Code diff:
Enter fullscreen mode Exit fullscreen mode


{language}
{diff}


Respond in JSON format:
{{
  "severity": "critical|warning|info",
  "issues": [...],
  "suggestions": [...],
  "summary": "overall assessment"
}}
"""
        response = self.client.post(
            "/chat/completions",
            json={
                "model": self.model,
                "messages": [{"role": "user", "content": prompt}],
                "temperature": 0.3,
                "response_format": {"type": "json_object"}
            }
        )
        return json.loads(response.json()["choices"][0]["message"]["content"])
Enter fullscreen mode Exit fullscreen mode


python

Notice we're hitting https://aiwave.live/v1 — this single endpoint routes to DeepSeek, GLM, Qwen, and 47 other models. You just change the model parameter.

Step 2: Fetch the Pull Request Diff

def get_pr_diff(github_token: str, repo: str, pr_number: int) -> list[dict]:
    """Fetch changed files from a pull request."""
    headers = {
        "Authorization": f"token {github_token}",
        "Accept": "application/vnd.github.v3.diff"
    }

    files = []
    with httpx.Client(headers=headers) as client:
        # Get list of changed files
        resp = client.get(
            f"https://api.github.com/repos/{repo}/pulls/{pr_number}/files"
        )

        for file in resp.json():
            if file["status"] == "removed":
                continue
            files.append({
                "filename": file["filename"],
                "patch": file.get("patch", ""),
                "language": detect_language(file["filename"])
            })

    return files


def detect_language(filename: str) -> str:
    ext_map = {
        ".py": "python", ".js": "javascript", ".ts": "typescript",
        ".go": "go", ".rs": "rust", ".java": "java", ".rb": "ruby"
    }
    for ext, lang in ext_map.items():
        if filename.endswith(ext):
            return lang
    return "text"
Enter fullscreen mode Exit fullscreen mode

Step 3: Run Multi-Model Review

Here's where it gets interesting. Different models catch different issues, so let's run a multi-pass review:

def multi_model_review(diff: str, language: str, api_key: str) -> dict:
    """Run review with multiple models and merge results."""

    models = [
        ("deepseek-chat", "logic & bugs"),
        ("glm-5", "security"),
        ("qwen-max", "architecture")
    ]

    results = {}
    for model, specialty in models:
        reviewer = AIReviewer(api_key, model=model)
        result = reviewer.review_code(diff, language)
        results[specialty] = result

    return {
        "critical": [r for v in results.values() for r in v.get("issues", [])
                      if r.get("severity") == "critical"],
        "warnings": [r for v in results.values() for r in v.get("issues", [])
                      if r.get("severity") == "warning"],
        "summary": "\n".join(v["summary"] for v in results.values()),
        "models_used": [m[0] for m in models]
    }
Enter fullscreen mode Exit fullscreen mode

Step 4: Post Review Comments Back to GitHub

def post_review_comment(
    github_token: str, repo: str, pr_number: int,
    body: str, filename: str = None, line: int = None
):
    headers = {
        "Authorization": f"token {github_token}",
        "Accept": "application/vnd.github.v3+json"
    }

    payload = {"body": body}
    if filename and line:
        payload["subject_type"] = "line"
        payload["path"] = filename
        payload["line"] = line

    with httpx.Client(headers=headers) as client:
        client.post(
            f"https://api.github.com/repos/{repo}/pulls/{pr_number}/comments",
            json=payload
        )
Enter fullscreen mode Exit fullscreen mode

Step 5: Put It All Together

import os

def review_pr(repo: str, pr_number: int):
    github_token = os.environ["GITHUB_TOKEN"]
    aiwave_key = os.environ["AIWAVE_API_KEY"]

    # Fetch changed files
    files = get_pr_diff(github_token, repo, pr_number)

    for file in files:
        if not file["patch"]:
            continue

        print(f"Reviewing {file['filename']}...")

        # Multi-model review
        review = multi_model_review(
            file["patch"], file["language"], aiwave_key
        )

        # Post summary comment
        comment = f"🤖 **AI Review** ({', '.join(review['models_used'])})\n\n"
        comment += review["summary"]

        if review["critical"]:
            comment += f"\n\n🔴 **{len(review['critical'])} critical issues found**"
        if review["warnings"]:
            comment += f"\n\n🟡 **{len(review['warnings'])} warnings**"

        post_review_comment(github_token, repo, pr_number, comment)

# Usage
if __name__ == "__main__":
    review_pr("yourorg/yourrepo", 42)
Enter fullscreen mode Exit fullscreen mode

Cost Breakdown: Multi-Model vs Single Model

Here's the real-world cost for reviewing a typical PR (~500 lines of diff):

Approach Models Cost per Review
GPT-4o only 1 ~$0.08
Claude Opus only 1 ~$0.12
Multi-model (DeepSeek + GLM + Qwen) 3 ~$0.006

That's 13× cheaper while catching more issues because each model brings different strengths. The AIWave unified endpoint means you're not managing three separate accounts.

Going Further: GitHub Actions Integration

To run this automatically on every PR, create .github/workflows/ai-review.yml:

name: AI Code Review
on:
  pull_request:
    types: [opened, synchronize]

jobs:
  review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with:
          python-version: "3.12"
      - run: pip install httpx
      - run: python review_bot.py
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          AIWAVE_API_KEY: ${{ secrets.AIWAVE_API_KEY }}
          PR_NUMBER: ${{ github.event.number }}
          REPO: ${{ github.repository }}
Enter fullscreen mode Exit fullscreen mode

Wrapping Up

You now have a production-ready AI code review bot that:

  • Uses three different models for comprehensive coverage
  • Costs less than a cent per PR
  • Catches bugs, security issues, and architectural problems
  • Integrates directly into your GitHub workflow

The multi-model approach works because Chinese LLMs have gotten genuinely good at code analysis. Each model has distinct strengths, and routing through a unified API like AIWave makes it trivial to leverage all of them together.

The full code is ~60 lines of Python. No frameworks, no complex setup, just straightforward HTTP calls and JSON parsing. That's the beauty of well-designed APIs.

Happy reviewing! 🚀

Top comments (0)