ai-code-review / app.py
sunandan8n's picture
Update app.py
734e96b verified
import os
import zipfile
import tempfile
import gradio as gr
from docx import Document
from huggingface_hub import InferenceClient
# -----------------------------
# 1️⃣ Hugging Face client
# -----------------------------
HF_TOKEN = os.getenv("HUGGINGFACE_API_KEY")
client = InferenceClient(api_key=HF_TOKEN)
# -----------------------------
# 2️⃣ Review prompt
# -----------------------------
promt = """
You are a senior software engineer with 10+ years of experience reviewing code in multiple languages.
Provide feedback like a human: friendly, clear, and actionable.
Give short, clear feedback in bullet points.
**Rules:**
Review the code and provide actionable points under these 4 areas:
1. **Code Standards** β€” Naming, formatting, magic numbers, comments
2. **Security** β€” SQL injection, input validation, hardcoded secrets, authentication issues
3. **Reusability** β€” Duplicate code, missing helper functions, not using libraries
4. **Refactoring** β€” Simplify complex code, performance improvements, better error handling
**Format:**
- Each suggestion must be a single concise line:
`Line X: Problem β€” Fix`
- Always **show both the incorrect and corrected examples** when suggesting naming or syntax improvements.
- Use **real corrected form** (e.g., `_AuthService` β†’ `_authService`).
- Keep tone friendly, direct, and professional.
- Do not repeat identical feedback for multiple lines; combine where possible.
"""
ignore_folders = ['.venv', 'wwwroot', 'node_modules', '__pycache__', 'bin', 'obj', 'properties']
ALLOWED_EXTS = [".py", ".js", ".java", ".cs", ".cpp", ".ts", ".cshtml", ".razor"]
# -----------------------------
# 3️⃣ Helper functions
# -----------------------------
def analyze_code_with_ai(code_text: str, filename: str) -> str:
try:
completion = client.chat.completions.create(
model="meta-llama/Llama-3.1-8B-Instruct",
messages=[
{"role": "system", "content": promt},
{"role": "user", "content": f"Review the following code from {filename}:\n\n{code_text}"}
]
)
return completion.choices[0].message["content"]
except Exception as e:
return f"⚠️ Error: {str(e)}"
def extract_zip_to_temp(zip_file_path):
temp_dir = tempfile.mkdtemp()
with zipfile.ZipFile(zip_file_path, 'r') as zip_ref:
zip_ref.extractall(temp_dir)
return temp_dir
def list_subfolders(folder_path):
folders = [os.path.basename(folder_path)]
for root, dirs, _ in os.walk(folder_path):
for d in dirs:
if any(ignored in root for ignored in ignore_folders):
continue
rel_path = os.path.relpath(os.path.join(root, d), folder_path)
folders.append(rel_path)
return folders
def process_selected_folders(base_path, selected_folders):
reviews = {}
for subfolder in selected_folders:
full_path = base_path if subfolder == os.path.basename(base_path) else os.path.join(base_path, subfolder)
for root, _, files in os.walk(full_path):
if any(ignored in root for ignored in ignore_folders):
continue
for file in files:
if any(file.endswith(ext) for ext in ALLOWED_EXTS):
filepath = os.path.join(root, file)
with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
code = f.read()
reviews[file] = analyze_code_with_ai(code, file)
return reviews
def process_file(file_path):
filename = os.path.basename(file_path)
with open(file_path, "r", encoding="utf-8", errors="ignore") as f:
code = f.read()
return {filename: analyze_code_with_ai(code, filename)}
def generate_report(reviews, output_path="review_report.docx"):
doc = Document()
doc.add_heading("Code Review Report", 0)
for fname, review in reviews.items():
doc.add_heading(fname, level=1)
doc.add_paragraph(review)
doc.save(output_path)
return output_path
# -----------------------------
# 4️⃣ Gradio functions
# -----------------------------
# List subfolders after ZIP upload
def load_subfolders_from_zip(zip_file):
if not zip_file:
return gr.update(choices=[], value=[]), "⚠️ No ZIP uploaded."
folder_path = extract_zip_to_temp(zip_file.name)
folders = list_subfolders(folder_path)
return gr.update(choices=folders, value=folders), folder_path, f"βœ… Found {len(folders)} folders."
# Review selected subfolders
def review_zip_selected(folder_path, selected_folders):
if not folder_path or not os.path.isdir(folder_path):
return "⚠️ Invalid folder path.", None
if not selected_folders:
return "⚠️ No folders selected.", None
reviews = process_selected_folders(folder_path, selected_folders)
if not reviews:
return "⚠️ No code files found.", None
report_path = generate_report(reviews)
output_text = "\n\n".join([f"πŸ“Œ {f}:\n{r}" for f, r in reviews.items()])
return output_text, report_path
# Review single file
def review_single_file(file_obj):
if not file_obj:
return "⚠️ No file uploaded.", None
reviews = process_file(file_obj.name)
if not reviews:
return "⚠️ Could not analyze file.", None
report_path = generate_report(reviews)
output_text = "\n\n".join([f"πŸ“Œ {f}:\n{r}" for f, r in reviews.items()])
return output_text, report_path
# -----------------------------
# 5️⃣ Gradio UI
# -----------------------------
with gr.Blocks() as demo:
gr.Markdown("# πŸ€– AI Code Reviewer\nUpload a ZIP or a single file and get a code review report.")
with gr.Tab("πŸ“¦ Review ZIP with Folder Selection"):
zip_input = gr.File(label="Upload ZIP File", file_types=[".zip"])
load_btn = gr.Button("πŸ“ Load Subfolders")
folder_status = gr.Markdown("")
folder_select = gr.CheckboxGroup(label="Select Folders to Review")
run_zip_btn = gr.Button("πŸš€ Run Review")
zip_output_text = gr.Textbox(label="AI Review Output", lines=15)
zip_report_file = gr.File(label="Download DOCX Report", type="filepath")
# Store the temp folder path in gr.State
temp_folder_state = gr.State()
# When loading subfolders, also store folder path in state
load_btn.click(
fn=load_subfolders_from_zip,
inputs=zip_input,
outputs=[folder_select, temp_folder_state, folder_status]
)
# Use stored folder path from gr.State for review
run_zip_btn.click(
fn=review_zip_selected,
inputs=[temp_folder_state, folder_select],
outputs=[zip_output_text, zip_report_file]
)
with gr.Tab("πŸ“„ Review Single File"):
file_input = gr.File(label="Upload Single File", file_types=ALLOWED_EXTS)
run_file_btn = gr.Button("πŸš€ Run Review")
file_output_text = gr.Textbox(label="AI Review Output", lines=15)
file_report_file = gr.File(label="Download DOCX Report", type="filepath")
run_file_btn.click(fn=review_single_file, inputs=file_input, outputs=[file_output_text, file_report_file])
demo.launch()