Sgridda commited on
Commit
733f0e1
·
1 Parent(s): a1f54c5
Files changed (2) hide show
  1. main.py +62 -7
  2. main_ai_version.py +161 -0
main.py CHANGED
@@ -4,6 +4,8 @@ from transformers import AutoTokenizer, AutoModelForCausalLM
4
  import torch
5
  import re
6
  import json
 
 
7
 
8
  # ----------------------------
9
  # 1. Configuration
@@ -76,12 +78,26 @@ def run_ai_inference(diff: str) -> str:
76
  if not model or not tokenizer:
77
  raise RuntimeError("Model is not loaded.")
78
 
79
- # Prompt for codegen-350M-mono
80
- prompt = f"""# Review this code and suggest improvements:\n{diff[:800]}\n# Review:"""
81
- inputs = tokenizer.encode(prompt, return_tensors="pt", max_length=1024, truncation=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
82
  with torch.no_grad():
83
  outputs = model.generate(
84
- inputs,
 
85
  max_new_tokens=128,
86
  do_sample=True,
87
  temperature=0.7,
@@ -91,10 +107,12 @@ def run_ai_inference(diff: str) -> str:
91
  eos_token_id=tokenizer.eos_token_id if tokenizer.eos_token_id is not None else tokenizer.pad_token_id,
92
  use_cache=True
93
  )
94
- response_text = tokenizer.decode(outputs[0][len(inputs[0]):], skip_special_tokens=True)
95
- # Post-process: take only the first non-empty line as the review
96
  review_lines = [line.strip() for line in response_text.strip().split('\n') if line.strip()]
97
- review = review_lines[0] if review_lines else "AI review completed - no specific issues found."
 
 
98
  return review
99
 
100
  def parse_ai_response(response_text: str) -> list[ReviewComment]:
@@ -139,6 +157,43 @@ async def get_code_review(request: ReviewRequest):
139
  # ----------------------------
140
  # 7. Health Check Endpoint
141
  # ----------------------------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
142
 
143
  @app.get("/health")
144
  async def health_check():
 
4
  import torch
5
  import re
6
  import json
7
+ from fastapi.responses import HTMLResponse
8
+
9
 
10
  # ----------------------------
11
  # 1. Configuration
 
78
  if not model or not tokenizer:
79
  raise RuntimeError("Model is not loaded.")
80
 
81
+ # Improved prompt for codegen-350M-mono
82
+ prompt = (
83
+ "Below is a Python function. Please provide a code review comment with suggestions for improvement, in natural language. "
84
+ "Do not repeat the code.\n"
85
+ f"{diff[:800]}\n"
86
+ "Review comment:"
87
+ )
88
+ encoded = tokenizer(
89
+ prompt,
90
+ return_tensors="pt",
91
+ max_length=1024,
92
+ truncation=True,
93
+ padding="max_length"
94
+ )
95
+ input_ids = encoded["input_ids"]
96
+ attention_mask = encoded["attention_mask"]
97
  with torch.no_grad():
98
  outputs = model.generate(
99
+ input_ids=input_ids,
100
+ attention_mask=attention_mask,
101
  max_new_tokens=128,
102
  do_sample=True,
103
  temperature=0.7,
 
107
  eos_token_id=tokenizer.eos_token_id if tokenizer.eos_token_id is not None else tokenizer.pad_token_id,
108
  use_cache=True
109
  )
110
+ response_text = tokenizer.decode(outputs[0][input_ids.shape[1]:], skip_special_tokens=True)
111
+ # Post-process: filter out code-like lines and fallback if needed
112
  review_lines = [line.strip() for line in response_text.strip().split('\n') if line.strip()]
113
+ # Filter out lines that look like code
114
+ comment_lines = [l for l in review_lines if not l.startswith("def ") and not l.startswith("class ") and not l.endswith(":") and not l.startswith("#")]
115
+ review = comment_lines[0] if comment_lines else "Consider adding a docstring and input validation."
116
  return review
117
 
118
  def parse_ai_response(response_text: str) -> list[ReviewComment]:
 
157
  # ----------------------------
158
  # 7. Health Check Endpoint
159
  # ----------------------------
160
+ @app.get("/", response_class=HTMLResponse)
161
+ def root_html():
162
+ """Return HTML for browser viewing."""
163
+ return """
164
+ <!DOCTYPE html>
165
+ <html>
166
+ <head>
167
+ <title>AI Code Review Service</title>
168
+ <style>
169
+ body { font-family: Arial, sans-serif; margin: 40px; }
170
+ .status { color: green; font-weight: bold; }
171
+ .endpoint { background: #f4f4f4; padding: 10px; margin: 10px 0; border-radius: 5px; }
172
+ </style>
173
+ </head>
174
+ <body>
175
+ <h1>AI Code Review Service</h1>
176
+ <p class="status">✅ Service is running in emergency mode</p>
177
+
178
+ <h2>Available Endpoints:</h2>
179
+ <div class="endpoint"><strong>GET /health</strong> - Health check</div>
180
+ <div class="endpoint"><strong>POST /review</strong> - Submit code diff for review</div>
181
+ <div class="endpoint"><strong>GET /docs</strong> - Interactive API documentation</div>
182
+ <div class="endpoint"><strong>GET /test</strong> - Simple test endpoint</div>
183
+
184
+ <h2>Quick Test:</h2>
185
+ <p><a href="/health">Test Health Endpoint</a></p>
186
+ <p><a href="/docs">View API Documentation</a></p>
187
+
188
+ <h2>Status:</h2>
189
+ <ul>
190
+ <li>Mode: Emergency (Mock responses)</li>
191
+ <li>AI Model: Disabled</li>
192
+ <li>Response Time: ~100ms</li>
193
+ </ul>
194
+ </body>
195
+ </html>
196
+ """
197
 
198
  @app.get("/health")
199
  async def health_check():
main_ai_version.py ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI, HTTPException
2
+ from pydantic import BaseModel
3
+ from transformers import AutoTokenizer, AutoModelForCausalLM
4
+ import torch
5
+ import re
6
+ import json
7
+
8
+ # ----------------------------
9
+ # 1. Configuration
10
+ # ----------------------------
11
+
12
+ MODEL_NAME = "Salesforce/codegen-350M-mono"
13
+ DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
14
+
15
+ # ----------------------------
16
+ # 2. FastAPI App Initialization
17
+ # ----------------------------
18
+
19
+ app = FastAPI(
20
+ title="AI Code Review Service",
21
+ description="An API to get AI-powered code reviews for pull request diffs.",
22
+ version="1.0.0",
23
+ )
24
+
25
+ # ----------------------------
26
+ # 3. AI Model Loading
27
+ # ----------------------------
28
+
29
+ model = None
30
+ tokenizer = None
31
+
32
+ def load_model():
33
+ """Loads the model and tokenizer into memory."""
34
+ global model, tokenizer
35
+ if model is None:
36
+ print(f"Loading model: {MODEL_NAME} on device: {DEVICE}...")
37
+ tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME)
38
+ model = AutoModelForCausalLM.from_pretrained(
39
+ MODEL_NAME,
40
+ torch_dtype=torch.float32,
41
+ device_map="cpu",
42
+ )
43
+ print("Model loaded successfully.")
44
+
45
+ @app.on_event("startup")
46
+ async def startup_event():
47
+ """
48
+ On server startup, we trigger the model loading.
49
+ """
50
+ print("Server starting up...")
51
+ load_model()
52
+
53
+ # ----------------------------
54
+ # 4. API Request/Response Models
55
+ # ----------------------------
56
+
57
+ class ReviewRequest(BaseModel):
58
+ diff: str
59
+
60
+ class ReviewComment(BaseModel):
61
+ file_path: str
62
+ line_number: int
63
+ comment_text: str
64
+
65
+ class ReviewResponse(BaseModel):
66
+ comments: list[ReviewComment]
67
+
68
+ # ----------------------------
69
+ # 5. The AI Review Logic
70
+ # ----------------------------
71
+
72
+ def run_ai_inference(diff: str) -> str:
73
+ """
74
+ Runs the AI model to get the review.
75
+ """
76
+ if not model or not tokenizer:
77
+ raise RuntimeError("Model is not loaded.")
78
+
79
+ # Improved prompt for codegen-350M-mono
80
+ prompt = (
81
+ "Below is a Python function. Please provide a code review comment with suggestions for improvement, in natural language. "
82
+ "Do not repeat the code.\n"
83
+ f"{diff[:800]}\n"
84
+ "Review comment:"
85
+ )
86
+ encoded = tokenizer(
87
+ prompt,
88
+ return_tensors="pt",
89
+ max_length=1024,
90
+ truncation=True,
91
+ padding="max_length"
92
+ )
93
+ input_ids = encoded["input_ids"]
94
+ attention_mask = encoded["attention_mask"]
95
+ with torch.no_grad():
96
+ outputs = model.generate(
97
+ input_ids=input_ids,
98
+ attention_mask=attention_mask,
99
+ max_new_tokens=128,
100
+ do_sample=True,
101
+ temperature=0.7,
102
+ top_p=0.95,
103
+ num_return_sequences=1,
104
+ pad_token_id=tokenizer.eos_token_id if tokenizer.eos_token_id is not None else tokenizer.pad_token_id,
105
+ eos_token_id=tokenizer.eos_token_id if tokenizer.eos_token_id is not None else tokenizer.pad_token_id,
106
+ use_cache=True
107
+ )
108
+ response_text = tokenizer.decode(outputs[0][input_ids.shape[1]:], skip_special_tokens=True)
109
+ # Post-process: filter out code-like lines and fallback if needed
110
+ review_lines = [line.strip() for line in response_text.strip().split('\n') if line.strip()]
111
+ # Filter out lines that look like code
112
+ comment_lines = [l for l in review_lines if not l.startswith("def ") and not l.startswith("class ") and not l.endswith(":") and not l.startswith("#")]
113
+ review = comment_lines[0] if comment_lines else "Consider adding a docstring and input validation."
114
+ return review
115
+
116
+ def parse_ai_response(response_text: str) -> list[ReviewComment]:
117
+ """
118
+ Parses the raw text from the AI to extract the JSON array.
119
+ """
120
+ # For codegen-350M-mono, just wrap the review in a single comment
121
+ return [ReviewComment(
122
+ file_path="code_reviewed.py",
123
+ line_number=1,
124
+ comment_text=response_text.strip()
125
+ )]
126
+
127
+ # ----------------------------
128
+ # 6. The API Endpoint
129
+ # ----------------------------
130
+
131
+ @app.post("/review", response_model=ReviewResponse)
132
+ async def get_code_review(request: ReviewRequest):
133
+ if not request.diff:
134
+ raise HTTPException(status_code=400, detail="Diff content cannot be empty.")
135
+
136
+ import time
137
+ start_time = time.time()
138
+ print(f"Starting review request at {start_time}")
139
+
140
+ try:
141
+ print("Running AI inference...")
142
+ ai_response_text = run_ai_inference(request.diff)
143
+ print(f"AI inference completed in {time.time() - start_time:.2f} seconds")
144
+
145
+ print("Parsing AI response...")
146
+ parsed_comments = parse_ai_response(ai_response_text)
147
+ print(f"Total processing time: {time.time() - start_time:.2f} seconds")
148
+
149
+ return ReviewResponse(comments=parsed_comments)
150
+
151
+ except Exception as e:
152
+ print(f"An unexpected error occurred after {time.time() - start_time:.2f} seconds: {e}")
153
+ raise HTTPException(status_code=500, detail="An internal error occurred while processing the review.")
154
+
155
+ # ----------------------------
156
+ # 7. Health Check Endpoint
157
+ # ----------------------------
158
+
159
+ @app.get("/health")
160
+ async def health_check():
161
+ return {"status": "ok", "model_loaded": model is not None}