PRism / app /code_reviewer.py
pranav8tripathi@gmail.com
added summary
664e9b5
import os
import json
import httpx
import logging
logger = logging.getLogger(__name__)
GEMINI_API_KEY = os.getenv("GEMINI_API_KEY")
GEMINI_API_URL = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={GEMINI_API_KEY}"
async def generate_change_summary(files: list) -> str:
"""
Generate a concise summary of what changed in the PR using AI.
"""
logger.info(f"πŸ“ Generating change summary for {len(files)} files...")
# Build a summary of changes
changes_text = ""
for f in files:
status = f.get("status", "modified")
filename = f.get("filename", "unknown")
additions = f.get("additions", 0)
deletions = f.get("deletions", 0)
changes_text += f"- {status.upper()}: {filename} (+{additions}/-{deletions})\n"
prompt = f"""You are a code reviewer. Summarize what changed in this pull request in 1-2 short sentences.
Focus on WHAT was changed, not HOW. Be concise and clear.
Files changed:
{changes_text}
Respond with ONLY the summary text (no markdown, no extra formatting):"""
headers = {"Content-Type": "application/json"}
payload = {
"contents": [{"parts": [{"text": prompt}]}],
"generationConfig": {
"temperature": 0.5,
"maxOutputTokens": 150
}
}
try:
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.post(GEMINI_API_URL, headers=headers, json=payload)
response.raise_for_status()
data = response.json()
summary = data["candidates"][0]["content"]["parts"][0]["text"].strip()
logger.info(f"βœ… Generated summary: {summary[:100]}...")
return summary
except Exception as e:
logger.warning(f"⚠️ Failed to generate summary: {str(e)}")
return f"Modified {len(files)} file(s)"
async def analyze_code(file_name: str, patch: str) -> list:
"""
Analyze a single file diff using Google Gemini AI model.
Returns a list of structured comments.
"""
logger.info(f"πŸ€– Sending to Gemini AI: {file_name} ({len(patch)} chars)")
prompt = f"""You are a senior code reviewer focused on finding REAL issues.
Review the following diff from `{file_name}` and provide feedback ONLY for:
- Security vulnerabilities
- Bugs or logic errors
- Performance issues
- Code that will break in production
- Missing error handling for critical operations
- Resource leaks (memory, connections, files)
DO NOT comment on:
- Code style or formatting
- Comments or documentation
- Variable naming (unless critically confusing)
- Minor suggestions or preferences
- Things that are already working fine
Respond ONLY with a JSON array (no markdown, no explanation):
[
{{
"line": 42,
"severity": "high",
"comment": "Potential SQL injection vulnerability - use parameterized queries"
}}
]
Severity levels: "high" (critical bugs/security), "medium" (bugs/performance), "low" (minor issues)
If no REAL issues found, return an empty array: []
Code Diff:
{patch}
"""
headers = {
"Content-Type": "application/json"
}
payload = {
"contents": [
{
"parts": [
{"text": prompt}
]
}
],
"generationConfig": {
"temperature": 0.3,
"maxOutputTokens": 2048
}
}
try:
async with httpx.AsyncClient(timeout=60.0) as client:
logger.info("⏳ Waiting for Gemini response...")
response = await client.post(GEMINI_API_URL, headers=headers, json=payload)
response.raise_for_status()
data = response.json()
logger.info("βœ… Gemini response received")
# Extract text from Gemini response structure
text_output = data["candidates"][0]["content"]["parts"][0]["text"].strip()
logger.info(f"πŸ“„ Response length: {len(text_output)} chars")
# Defensive: handle non-JSON outputs
try:
# Remove markdown code blocks if present
if text_output.startswith("```json"):
text_output = text_output.replace("```json", "").replace("```", "").strip()
elif text_output.startswith("```"):
text_output = text_output.replace("```", "").strip()
parsed = json.loads(text_output)
logger.info(f"βœ… Parsed {len(parsed)} review comments")
return parsed
except Exception as e:
logger.warning(f"⚠️ Failed to parse JSON, returning raw text: {str(e)}")
logger.warning(f"Raw response: {text_output[:200]}")
return [{"line": 1, "severity": "info", "comment": text_output}]
except Exception as e:
logger.error(f"❌ Gemini API error: {str(e)}")
raise