CodeCommunity commited on
Commit
d7a64ed
·
verified ·
1 Parent(s): 984fc65

Update app/main.py

Browse files
Files changed (1) hide show
  1. app/main.py +36 -43
app/main.py CHANGED
@@ -16,8 +16,9 @@ import uvicorn
16
  load_dotenv()
17
 
18
  # Import internal services
19
- # Note: Ensure these modules exist in your /app directory
20
  from app.predictor import classifier, guide_generator, reviewer
 
21
 
22
  # 1. Setup Logging
23
  logging.basicConfig(level=logging.INFO)
@@ -25,7 +26,6 @@ logger = logging.getLogger(__name__)
25
 
26
  # 2. Initialize FastAPI
27
  app = FastAPI(title="GitGud AI Service")
28
- main = app # Alias for compatibility
29
 
30
  # Global embedding cache
31
  # Structure: { "repo_name": { "file_path": [embedding_vector] } }
@@ -46,15 +46,29 @@ class GuideRequest(BaseModel):
46
 
47
  class SearchRequest(BaseModel):
48
  query: str
49
- embeddings: Optional[Dict[str, List[float]]] = None # Path -> Embedding
50
  repoName: Optional[str] = None
51
 
52
  class ChatRequest(BaseModel):
53
  query: str
54
- context: List[Dict[str, str]] # List of { "fileName": str, "content": str }
55
  repoName: str
56
 
57
- # 4. Endpoints
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
 
59
  @app.get("/")
60
  def health_check():
@@ -68,8 +82,7 @@ def health_check():
68
 
69
  @app.get("/usage")
70
  def get_usage():
71
- """Returns AI Service usage statistics."""
72
- from app.core.model_loader import llm_engine
73
  return llm_engine.get_usage_stats()
74
 
75
  @app.post("/classify")
@@ -95,77 +108,65 @@ async def classify_file(request: FileRequest):
95
 
96
  @app.post("/review-batch-code")
97
  async def review_batch_code(request: BatchReviewRequest):
98
- """Batch review with detailed metrics and suggestions."""
99
  try:
100
  reviews = reviewer.service.review_batch_code(request.files)
101
- total_files = len(reviews)
102
- total_vulns = sum(len(r.get("vulnerabilities", [])) for r in reviews)
103
-
104
- m_scores = [r.get("metrics", {}).get("maintainability", 8) for r in reviews]
105
- avg_maint = sum(m_scores) / max(total_files, 1)
106
-
107
- return {
108
- "totalFiles": total_files,
109
- "totalVulnerabilities": total_vulns,
110
- "averageMaintainability": round(avg_maint, 1),
111
- "results": reviews,
112
- }
113
  except Exception as e:
114
  traceback.print_exc()
115
  raise HTTPException(status_code=500, detail=str(e))
116
 
117
  @app.post("/repo-dashboard-stats")
118
  async def get_dashboard_stats(request: BatchReviewRequest):
119
- """Aggregated stats using parallel processing for speed and review-first logic."""
120
  try:
121
- # 1. Run the heavy AI review process in a thread pool to prevent blocking
122
  loop = asyncio.get_event_loop()
123
  with ThreadPoolExecutor() as executor:
124
  raw_reviews = await loop.run_in_executor(
125
  executor, reviewer.service.review_batch_code, request.files
126
  )
127
 
128
- # 2. Extract Data from the Review Results
129
  total_vulns = 0
130
  maint_scores = []
131
  found_apis = set()
132
  api_regex = re.compile(r'(?:get|post|put|delete|patch)\([\'"]\/(.*?)[\'"]', re.IGNORECASE)
133
 
134
  for i, review in enumerate(raw_reviews):
135
- # Security Count from Review
136
  vulns = review.get("vulnerabilities", [])
137
  total_vulns += len(vulns)
138
 
139
- # Maintainability Score
140
- m_score = review.get("metrics", {}).get("maintainability", 8)
141
  maint_scores.append(m_score)
142
 
143
- # API Sniffing
144
  content = request.files[i].content
145
  if content:
146
  matches = api_regex.findall(content)
147
  for match in matches:
148
  found_apis.add(f"/{match}")
149
 
150
- # 3. Aggregation & Calculations
151
  num_files = len(maint_scores)
152
  avg_maint = (sum(maint_scores) / num_files) if num_files > 0 else 0
153
 
154
- # Health Calculation: Base 100, -12 per vulnerability
155
- health_score = max(10, 100 - (total_vulns * 12))
156
 
157
  return {
158
  "repo_health": health_score,
159
  "health_label": "Excellent" if health_score > 85 else "Good" if health_score > 60 else "Critical",
160
  "security_issues": total_vulns,
161
  "performance_ratio": f"{int(avg_maint * 10)}%",
162
- "exposed_apis": list(found_apis)[:12],
163
- "total_files_processed": num_files
 
164
  }
165
  except Exception as e:
166
  logger.error(f"Dashboard stats failed: {e}")
167
  traceback.print_exc()
168
- raise HTTPException(status_code=500, detail="Failed to aggregate repository stats")
169
 
170
  @app.post("/analyze-file")
171
  async def analyze_file(request: FileRequest):
@@ -175,11 +176,6 @@ async def analyze_file(request: FileRequest):
175
  summary = classifier.generate_file_summary(request.content, request.fileName)
176
  tags = classifier.extract_tags(request.content, request.fileName)
177
 
178
- if request.repoName:
179
- if request.repoName not in REPO_CACHE:
180
- REPO_CACHE[request.repoName] = {}
181
- REPO_CACHE[request.repoName][request.fileName] = result["embedding"]
182
-
183
  return {
184
  "fileName": request.fileName,
185
  "layer": result["label"],
@@ -213,17 +209,14 @@ async def chat(request: ChatRequest):
213
  """RAG-based chat using provided file context."""
214
  start_time = time.time()
215
  try:
216
- from app.core.model_loader import llm_engine
217
-
218
  context_str = ""
219
  for item in request.context:
220
  context_str += f"--- FILE: {item['fileName']} ---\n{item['content']}\n\n"
221
 
222
- has_context = len(request.context) > 0
223
  prompt = f"""
224
  You are "GitGud AI", an expert software architect.
225
  Repository: "{request.repoName}"
226
- CONTEXT: {context_str if has_context else "(NO CODE PROVIDED)"}
227
  USER QUESTION: {request.query}
228
  """
229
  response = llm_engine.generate_text(prompt)
@@ -244,7 +237,7 @@ async def generate_guide(request: GuideRequest):
244
  traceback.print_exc()
245
  raise HTTPException(status_code=500, detail=str(e))
246
 
247
- # 5. Application Entry Point
248
  if __name__ == "__main__":
249
  port = int(os.environ.get("PORT", 7860))
250
  uvicorn.run(app, host="0.0.0.0", port=port)
 
16
  load_dotenv()
17
 
18
  # Import internal services
19
+ # Ensure these modules exist in your /app directory
20
  from app.predictor import classifier, guide_generator, reviewer
21
+ from app.core.model_loader import llm_engine
22
 
23
  # 1. Setup Logging
24
  logging.basicConfig(level=logging.INFO)
 
26
 
27
  # 2. Initialize FastAPI
28
  app = FastAPI(title="GitGud AI Service")
 
29
 
30
  # Global embedding cache
31
  # Structure: { "repo_name": { "file_path": [embedding_vector] } }
 
46
 
47
  class SearchRequest(BaseModel):
48
  query: str
49
+ embeddings: Optional[Dict[str, List[float]]] = None
50
  repoName: Optional[str] = None
51
 
52
  class ChatRequest(BaseModel):
53
  query: str
54
+ context: List[Dict[str, str]]
55
  repoName: str
56
 
57
+ # 4. Core Logic Helpers
58
+ def calculate_repo_health(total_vulns: int, avg_maint: float) -> int:
59
+ """
60
+ Standardized health logic to keep Dashboard and Review metrics in sync.
61
+ Uses a weighted scale of maintainability vs security risk.
62
+ """
63
+ # Base starts from the maintainability average (0.0 to 10.0 scale mapped to 100)
64
+ base_score = avg_maint * 10
65
+ # Penalty: Subtract 8 points per vulnerability
66
+ penalty = total_vulns * 8
67
+
68
+ final_score = base_score - penalty
69
+ return int(max(10, min(100, final_score)))
70
+
71
+ # 5. Endpoints
72
 
73
  @app.get("/")
74
  def health_check():
 
82
 
83
  @app.get("/usage")
84
  def get_usage():
85
+ """Returns AI Service usage statistics from the LLM engine."""
 
86
  return llm_engine.get_usage_stats()
87
 
88
  @app.post("/classify")
 
108
 
109
  @app.post("/review-batch-code")
110
  async def review_batch_code(request: BatchReviewRequest):
111
+ """Detailed review results for the Review Section UI."""
112
  try:
113
  reviews = reviewer.service.review_batch_code(request.files)
114
+ return {"results": reviews}
 
 
 
 
 
 
 
 
 
 
 
115
  except Exception as e:
116
  traceback.print_exc()
117
  raise HTTPException(status_code=500, detail=str(e))
118
 
119
  @app.post("/repo-dashboard-stats")
120
  async def get_dashboard_stats(request: BatchReviewRequest):
121
+ """Aggregated stats using the exact same logic as batch review."""
122
  try:
123
+ # Run heavy AI review in thread pool to keep FastAPI responsive
124
  loop = asyncio.get_event_loop()
125
  with ThreadPoolExecutor() as executor:
126
  raw_reviews = await loop.run_in_executor(
127
  executor, reviewer.service.review_batch_code, request.files
128
  )
129
 
 
130
  total_vulns = 0
131
  maint_scores = []
132
  found_apis = set()
133
  api_regex = re.compile(r'(?:get|post|put|delete|patch)\([\'"]\/(.*?)[\'"]', re.IGNORECASE)
134
 
135
  for i, review in enumerate(raw_reviews):
136
+ # Sync Vulnerability count
137
  vulns = review.get("vulnerabilities", [])
138
  total_vulns += len(vulns)
139
 
140
+ # Sync Maintainability
141
+ m_score = review.get("metrics", {}).get("maintainability", 8.0)
142
  maint_scores.append(m_score)
143
 
144
+ # Extract APIs (No cap, show all discovered)
145
  content = request.files[i].content
146
  if content:
147
  matches = api_regex.findall(content)
148
  for match in matches:
149
  found_apis.add(f"/{match}")
150
 
 
151
  num_files = len(maint_scores)
152
  avg_maint = (sum(maint_scores) / num_files) if num_files > 0 else 0
153
 
154
+ # Calculate health using shared logic
155
+ health_score = calculate_repo_health(total_vulns, avg_maint)
156
 
157
  return {
158
  "repo_health": health_score,
159
  "health_label": "Excellent" if health_score > 85 else "Good" if health_score > 60 else "Critical",
160
  "security_issues": total_vulns,
161
  "performance_ratio": f"{int(avg_maint * 10)}%",
162
+ "exposed_apis": list(found_apis),
163
+ "total_files_processed": num_files,
164
+ "average_maintainability": round(avg_maint, 1)
165
  }
166
  except Exception as e:
167
  logger.error(f"Dashboard stats failed: {e}")
168
  traceback.print_exc()
169
+ raise HTTPException(status_code=500, detail="Failed to sync dashboard metrics")
170
 
171
  @app.post("/analyze-file")
172
  async def analyze_file(request: FileRequest):
 
176
  summary = classifier.generate_file_summary(request.content, request.fileName)
177
  tags = classifier.extract_tags(request.content, request.fileName)
178
 
 
 
 
 
 
179
  return {
180
  "fileName": request.fileName,
181
  "layer": result["label"],
 
209
  """RAG-based chat using provided file context."""
210
  start_time = time.time()
211
  try:
 
 
212
  context_str = ""
213
  for item in request.context:
214
  context_str += f"--- FILE: {item['fileName']} ---\n{item['content']}\n\n"
215
 
 
216
  prompt = f"""
217
  You are "GitGud AI", an expert software architect.
218
  Repository: "{request.repoName}"
219
+ CONTEXT: {context_str if request.context else "(NO CODE PROVIDED)"}
220
  USER QUESTION: {request.query}
221
  """
222
  response = llm_engine.generate_text(prompt)
 
237
  traceback.print_exc()
238
  raise HTTPException(status_code=500, detail=str(e))
239
 
240
+ # 6. Application Entry Point
241
  if __name__ == "__main__":
242
  port = int(os.environ.get("PORT", 7860))
243
  uvicorn.run(app, host="0.0.0.0", port=port)