Skip to content

Commit 17fa0bc

Browse files
authored
feat: docs for pr review bot (#613)
# Motivation <!-- Why is this change necessary? --> # Content <!-- Please include a summary of the change --> # Testing <!-- How was the change tested? --> # Please check the following before marking your PR as ready for review - [x ] I have added tests for my changes - [ x] I have updated the documentation or added new documentation as needed --------- Co-authored-by: kopekC <[email protected]>
1 parent b2f5d1a commit 17fa0bc

File tree

9 files changed

+3160
-198
lines changed

9 files changed

+3160
-198
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
GITHUB_TOKEN="..."
2+
# ====[ MODEL PROVIDERS ]===
3+
4+
ANTHROPIC_API_KEY="..."
5+
OPENAI_API_KEY="..."
6+
GOOGLE_API_KEY="..."
7+
GEMINI_API_KEY="..."
8+
9+
# ====[ SLACK ]===
10+
SLACK_SIGNING_SECRET="..."
11+
SLACK_BOT_TOKEN="..."
Lines changed: 27 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,129 +1,43 @@
11
# AI-Powered Pull Request Review Bot
22

3-
This example demonstrates how to use Codegen to create an intelligent PR review bot that analyzes code changes and their dependencies to provide comprehensive code reviews. The bot uses GPT-4 to generate contextual feedback based on modified code and its relationships.
3+
This example project demonstrates how to deploy an agentic bot that automatically reviews GitHub Pull Requests. The bot analyzes code changes and their dependencies to provide comprehensive code reviews using AI, considering both direct modifications and their impact on the codebase.
44

5-
> [!NOTE]
6-
> This codemod helps development teams by providing automated, context-aware code reviews that consider both direct and indirect code dependencies.
5+
## Prerequisites
76

8-
## How the PR Review Bot Works
7+
Before running this application, you'll need the following API tokens and credentials:
98

10-
The script analyzes pull requests in several key steps:
9+
- GitHub API Token
10+
- Anthropic API Token
11+
- GitHub Repository Access
1112

12-
1. **Symbol Analysis**
13+
## Setup
1314

14-
```python
15-
modified_symbols = codebase.get_modified_symbols_in_pr(pr_number)
16-
for symbol in modified_symbols:
17-
deps = codebase.get_symbol_dependencies(symbol, max_depth=2)
18-
rev_deps = codebase.get_symbol_dependents(symbol, max_depth=2)
19-
```
20-
21-
- Identifies modified symbols in the PR
22-
- Analyzes dependencies up to 2 levels deep
23-
- Tracks reverse dependencies (symbols that depend on changes)
24-
25-
1. **Context Building**
26-
27-
```python
28-
context = {"pr_title": pr.title, "pr_body": pr.body, "modified_symbols": [...], "context_symbols": [...]}
29-
```
30-
31-
- Gathers PR metadata
32-
- Collects modified code content
33-
- Includes relevant dependency context
34-
35-
1. **AI Review Generation**
36-
37-
```python
38-
review = codebase.ai_client.llm_query_with_retry(messages=[...], model="gpt-4", max_tokens=2000)
39-
```
40-
41-
- Uses GPT-4 for analysis
42-
- Generates comprehensive review feedback
43-
- Considers full context of changes
44-
45-
## Why This Makes Code Review Better
46-
47-
1. **Context-Aware Analysis**
48-
49-
- Understands code dependencies
50-
- Considers impact of changes
51-
- Reviews code in proper context
52-
53-
1. **Comprehensive Review**
54-
55-
- Analyzes direct modifications
56-
- Evaluates dependency impact
57-
- Suggests improvements
58-
59-
1. **Consistent Feedback**
60-
61-
- Structured review format
62-
- Thorough analysis every time
63-
- Scalable review process
64-
65-
## Review Output Format
66-
67-
The bot provides structured feedback including:
15+
1. Clone the repository
16+
1. Set up your environment variables in a `.env` file:
6817

18+
```env
19+
GITHUB_TOKEN=your_github_token
20+
ANTHROPIC_API_KEY=your_anthropic_token
21+
GITHUB_REPO_OWNER=your_repo_owner
22+
GITHUB_REPO_NAME=your_repo_name
23+
GITHUB_PR_NUMBER=your_pr_number
6924
```
70-
1. Overall Assessment
71-
- High-level review of changes
72-
- Impact analysis
73-
74-
2. Specific Code Feedback
75-
- Detailed code comments
76-
- Style suggestions
77-
- Best practices
78-
79-
3. Potential Issues
80-
- Security concerns
81-
- Performance impacts
82-
- Edge cases
83-
84-
4. Dependency Analysis
85-
- Impact on dependent code
86-
- Breaking changes
87-
- Integration considerations
88-
89-
```
90-
91-
## Key Benefits to Note
92-
93-
1. **Better Code Quality**
94-
95-
- Thorough code analysis
96-
- Consistent review standards
97-
- Early issue detection
98-
99-
1. **Time Savings**
100-
101-
- Automated initial review
102-
- Quick feedback loop
103-
- Reduced review burden
104-
105-
1. **Knowledge Sharing**
106-
107-
- Educational feedback
108-
- Best practice suggestions
109-
- Team learning
110-
111-
## Configuration Options
11225

113-
You can customize the review by:
26+
## Features
11427

115-
- Adjusting dependency depth
116-
- Modifying the AI prompt
117-
- Changing the review focus areas
118-
- Tuning the GPT-4 parameters
28+
- Automated PR code review using AI
29+
- Deep dependency analysis of code changes
30+
- Context-aware feedback generation
31+
- Structured review format with actionable insights
32+
- Integration with GitHub PR system
11933

120-
## Learn More
34+
## Usage
12135

122-
- [Codegen Documentation](https://docs.codegen.com)
123-
- [OpenAI API Documentation](https://platform.openai.com/docs/api-reference)
124-
- [GitHub API Documentation](https://docs.github.com/en/rest)
125-
- [Codegen llm integration](https://docs.codegen.com/building-with-codegen/calling-out-to-llms)
36+
1. `uv sync`
37+
1. `uv run modal deploy app.py`
38+
- This will deploy a modal app that can be triggered to review PRs
39+
1. Create or update a PR to trigger the review bot
12640

12741
## Contributing
12842

129-
Feel free to submit issues and enhancement requests! Contributions to improve the review bot's capabilities are welcome.
43+
Contributions are welcome! Please feel free to submit a Pull Request.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import logging
2+
from logging import getLogger
3+
import modal
4+
from codegen.extensions.events.app import CodegenApp
5+
from fastapi import Request
6+
from codegen.extensions.github.types.events.pull_request import PullRequestLabeledEvent, PullRequestUnlabeledEvent
7+
from helpers import remove_bot_comments, pr_review_agent
8+
9+
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
10+
logger = getLogger(__name__)
11+
12+
REPO_URL = "https://github.com/codegen-sh/codegen-sdk.git"
13+
COMMIT_ID = "20ba52b263ba8bab552b5fb6f68ca3667c0309fb"
14+
15+
base_image = (
16+
modal.Image.debian_slim(python_version="3.12")
17+
.apt_install("git")
18+
.pip_install(
19+
# =====[ Codegen ]=====
20+
# "codegen>=0.18",
21+
f"git+{REPO_URL}@{COMMIT_ID}",
22+
# =====[ Rest ]=====
23+
"openai>=1.1.0",
24+
"fastapi[standard]",
25+
"slack_sdk",
26+
)
27+
)
28+
29+
app = CodegenApp(name="github", image=base_image, modal_api_key="")
30+
31+
32+
@app.github.event("pull_request:labeled")
33+
def handle_labeled(event: PullRequestLabeledEvent):
34+
logger.info("[PULL_REQUEST:LABELED] Received pull request labeled event")
35+
logger.info(f"PR #{event.number} labeled with: {event.label.name}")
36+
logger.info(f"PR title: {event.pull_request.title}")
37+
# app.slack.client.chat_postMessage(
38+
# channel="C08DPPSL1CG",
39+
# text=f"PR #{event.number} labeled with: {event.label.name}",
40+
# )
41+
if event.label.name == "Codegen":
42+
app.slack.client.chat_postMessage(
43+
channel="C08DPPSL1CG",
44+
text=f"PR #{event.number} labeled with: {event.label.name}, waking up CodegenBot and starting review",
45+
)
46+
47+
logger.info(f"PR ID: {event.pull_request.id}")
48+
logger.info(f"PR title: {event.pull_request.title}")
49+
logger.info(f"pr number: {event.number}, ")
50+
pr_review_agent(event)
51+
52+
53+
@app.github.event("pull_request:unlabeled")
54+
def handle_unlabeled(event: PullRequestUnlabeledEvent):
55+
logger.info("unlabeled")
56+
logger.info(event.action)
57+
logger.info(event.label.name)
58+
if event.label.name == "Codegen":
59+
remove_bot_comments(event)
60+
61+
62+
@app.function(secrets=[modal.Secret.from_dotenv()])
63+
@modal.web_endpoint(method="POST")
64+
def entrypoint(event: dict, request: Request):
65+
logger.info("[OUTER] Received GitHub webhook")
66+
return app.github.handle(event, request)
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
from github import Github
2+
from codegen.extensions.github.types.events.pull_request import PullRequestUnlabeledEvent
3+
from logging import getLogger
4+
5+
import os
6+
7+
from codegen import Codebase
8+
9+
from codegen.extensions.github.types.events.pull_request import PullRequestLabeledEvent
10+
from codegen.configs.models.secrets import SecretsConfig
11+
from codegen import CodeAgent
12+
13+
from codegen.extensions.langchain.tools import (
14+
# Github
15+
GithubViewPRTool,
16+
GithubCreatePRCommentTool,
17+
GithubCreatePRReviewCommentTool,
18+
)
19+
20+
from dotenv import load_dotenv
21+
import logging
22+
23+
load_dotenv()
24+
25+
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
26+
logger = getLogger(__name__)
27+
28+
29+
def remove_bot_comments(event: PullRequestUnlabeledEvent):
30+
g = Github(os.getenv("GITHUB_API_KEY"))
31+
logger.info(f"{event.organization.login}/{event.repository.name}")
32+
repo = g.get_repo(f"{event.organization.login}/{event.repository.name}")
33+
34+
pr = repo.get_pull(int(event.number))
35+
comments = pr.get_comments()
36+
if comments:
37+
for comment in comments:
38+
logger.info("removing comment")
39+
logger.info(comment.user.login)
40+
if comment.user.login == "codegen-team":
41+
comment.delete()
42+
reviews = pr.get_reviews()
43+
44+
if reviews:
45+
for review in reviews:
46+
logger.info("removing review")
47+
logger.info(review.user.login)
48+
if review.user.login == "codegen-team":
49+
review.delete()
50+
51+
issue_comments = pr.get_issue_comments()
52+
if issue_comments:
53+
for comment in issue_comments:
54+
logger.info("removing comment")
55+
logger.info(comment.user.login)
56+
if comment.user.login == "codegen-team":
57+
comment.delete()
58+
59+
60+
def pr_review_agent(event: PullRequestLabeledEvent) -> None:
61+
# Pull a subset of SWE bench
62+
repo_str = f"{event.organization.login}/{event.repository.name}"
63+
codebase = Codebase.from_repo(repo_str, language="python", secrets=SecretsConfig(github_token=os.environ["GITHUB_TOKEN"]))
64+
review_atention_message = "CodegenBot is starting to review the PR please wait..."
65+
comment = codebase._op.create_pr_comment(event.number, review_atention_message)
66+
# Define tools first
67+
pr_tools = [
68+
GithubViewPRTool(codebase),
69+
GithubCreatePRCommentTool(codebase),
70+
GithubCreatePRReviewCommentTool(codebase),
71+
]
72+
73+
# Create agent with the defined tools
74+
agent = CodeAgent(codebase=codebase, tools=pr_tools)
75+
76+
# Using a prompt from SWE Bench
77+
prompt = f"""
78+
Hey CodegenBot!
79+
80+
Here's a SWE task for you. Please Review this pull request!
81+
{event.pull_request.url}
82+
Do not terminate until have reviewed the pull request and are satisfied with your review.
83+
84+
Review this Pull request like the señor ingenier you are
85+
be explicit about the changes, produce a short summary, and point out possible improvements where pressent dont be self congratulatory stick to the facts
86+
use the tools at your disposal to create propper pr reviews include code snippets if needed, and suggest improvements if feel its necesary
87+
"""
88+
# Run the agent
89+
agent.run(prompt)
90+
comment.delete()
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
[project]
2+
name = "ticket-to-pr"
3+
version = "0.1.0"
4+
description = "A example implementation of a agentic linear bot that can create PRs from linear tickets"
5+
readme = "README.md"
6+
requires-python = ">=3.12, <3.14"
7+
dependencies = [
8+
"codegen==0.31.1",
9+
"fastapi>=0.115.8",
10+
"modal>=0.73.51",
11+
"pydantic>=2.10.6",
12+
]

0 commit comments

Comments
 (0)