Spaces:
Build error
Build error
File size: 5,824 Bytes
bfc9de1 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 |
import subprocess
import tempfile
import os
import openai
from typing import Dict, Any
from dotenv import load_dotenv
load_dotenv()
class ReviewerAgent:
"""
Agent responsible for reviewing code for quality, style, and potential issues.
Uses both static analysis (pylint) and LLM-based review.
"""
def __init__(self):
self.api_key = os.getenv("OPENAI_API_KEY")
openai.api_key = self.api_key
def static_analysis(self, code: str) -> Dict[str, Any]:
"""
Perform static code analysis using pylint.
Args:
code: Python code to analyze
Returns:
Dictionary with pylint results
"""
try:
# Create a temporary file with the code
with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
f.write(code)
temp_file_path = f.name
# Run pylint on the temporary file
result = subprocess.run(
['pylint', temp_file_path, '--output-format=json'],
capture_output=True,
text=True
)
# Clean up temporary file
os.unlink(temp_file_path)
# Parse pylint output
if result.returncode == 0:
# Parse JSON output
import json
try:
issues = json.loads(result.stdout)
return {
"status": "success",
"issues": issues,
"score": self._calculate_pylint_score(issues),
"summary": f"Found {len(issues)} issues"
}
except:
return {
"status": "success",
"issues": [],
"score": 10.0,
"summary": "No issues found"
}
else:
return {
"status": "error",
"error": result.stderr,
"issues": []
}
except Exception as e:
return {
"status": "error",
"error": str(e),
"issues": []
}
def _calculate_pylint_score(self, issues: list) -> float:
"""Calculate a normalized score from pylint issues."""
if not issues:
return 10.0
# Count issues by type
error_count = sum(1 for issue in issues if issue.get('type') == 'error')
warning_count = sum(1 for issue in issues if issue.get('type') == 'warning')
# Simple scoring: start from 10 and deduct points
score = 10.0
score -= error_count * 0.5
score -= warning_count * 0.1
return max(0, min(10, score))
def llm_review(self, code: str) -> Dict[str, Any]:
"""
Use LLM to review code for logical errors, improvements, and best practices.
Args:
code: Python code to review
Returns:
Dictionary with LLM review results
"""
try:
system_message = """You are an expert code reviewer. Analyze the code for:
1. Logical errors
2. Security issues
3. Performance improvements
4. Code style and best practices
5. Edge cases not handled
Provide specific, actionable feedback."""
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "system", "content": system_message},
{"role": "user", "content": f"Review this code:\n\n{code}"}
],
temperature=0.3,
max_tokens=300
)
review_text = response.choices[0].message.content
# Extract key points
import re
suggestions = re.findall(r'[-•]\s*(.*?)(?=\n\n|\Z)', review_text, re.DOTALL)
return {
"status": "success",
"review": review_text,
"suggestions": suggestions,
"tokens_used": response.usage.total_tokens
}
except Exception as e:
return {
"status": "error",
"error": str(e),
"review": ""
}
def comprehensive_review(self, code: str) -> Dict[str, Any]:
"""
Combine static analysis and LLM review for comprehensive feedback.
Args:
code: Python code to review
Returns:
Complete review results
"""
static_result = self.static_analysis(code)
llm_result = self.llm_review(code)
return {
"static_analysis": static_result,
"llm_review": llm_result,
"overall_score": self._calculate_overall_score(static_result, llm_result)
}
def _calculate_overall_score(self, static: Dict, llm: Dict) -> float:
"""Calculate an overall code quality score."""
if static.get("status") != "success":
return 0.0
static_score = static.get("score", 0.0)
# LLM review doesn't give numeric score, so we estimate based on suggestions
llm_suggestions = len(llm.get("suggestions", []))
llm_score = max(0, 10 - llm_suggestions * 0.5)
# Weighted average: 70% static analysis, 30% LLM review
return static_score * 0.7 + llm_score * 0.3 |