Sgridda commited on
Commit
9d8ec9c
·
1 Parent(s): 8a1669f

Convert to test server to isolate routing issue

Browse files
Files changed (1) hide show
  1. main.py +27 -110
main.py CHANGED
@@ -1,73 +1,34 @@
 
1
  from fastapi import FastAPI, HTTPException
2
  from pydantic import BaseModel
3
- # We now import BitsAndBytesConfig to specify our quantization settings
4
- from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
5
- import torch
6
  import re
7
  import json
8
 
9
  # ----------------------------
10
- # 1. Configuration
11
- # ----------------------------
12
-
13
- MODEL_NAME = "TinyLlama/TinyLlama-1.1B-Chat-v1.0"
14
- # The device will be automatically handled by device_map="auto"
15
- # but we can keep this for logging.
16
- DEVICE = "cuda" if torch.cuda.is_available() else "cpu"
17
-
18
- # ----------------------------
19
- # 2. FastAPI App Initialization
20
  # ----------------------------
21
 
22
  app = FastAPI(
23
- title="AI Code Review Service",
24
- description="An API to get AI-powered code reviews for pull request diffs.",
25
  version="1.0.0",
26
  )
27
 
28
  # ----------------------------
29
- # 3. AI Model Loading
30
  # ----------------------------
31
 
32
- model = None
33
- tokenizer = None
34
-
35
- def load_model():
36
- """Loads the model and tokenizer into memory."""
37
- global model, tokenizer
38
- if model is None:
39
- print(f"Loading model: {MODEL_NAME} on device: {DEVICE}...")
40
- tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
41
-
42
- # FIX: Define the quantization configuration for 4-bit loading.
43
- # We explicitly set bnb_4bit_quant_type to "nf4", which is required for CPU execution.
44
- quantization_config = BitsAndBytesConfig(
45
- load_in_4bit=True,
46
- bnb_4bit_quant_type="nf4",
47
- bnb_4bit_compute_dtype=torch.bfloat16,
48
- bnb_4bit_use_double_quant=False,
49
- )
50
-
51
- # Load the model with the specified quantization config.
52
- # We also use device_map="auto" to let transformers handle device placement.
53
- model = AutoModelForCausalLM.from_pretrained(
54
- MODEL_NAME,
55
- trust_remote_code=True,
56
- quantization_config=quantization_config,
57
- device_map="auto", # This is crucial for bitsandbytes to work correctly
58
- )
59
- print("Model loaded successfully.")
60
-
61
  @app.on_event("startup")
62
  async def startup_event():
63
  """
64
- On server startup, we trigger the model loading.
 
65
  """
66
- print("Server starting up...")
67
- load_model()
68
 
69
  # ----------------------------
70
- # 4. API Request/Response Models
71
  # ----------------------------
72
 
73
  class ReviewRequest(BaseModel):
@@ -82,79 +43,35 @@ class ReviewResponse(BaseModel):
82
  comments: list[ReviewComment]
83
 
84
  # ----------------------------
85
- # 5. The AI Review Logic
86
  # ----------------------------
87
 
88
- def run_ai_inference(diff: str) -> str:
 
89
  """
90
- Runs the AI model to get the review.
 
91
  """
92
- if not model or not tokenizer:
93
- raise RuntimeError("Model is not loaded.")
 
94
 
95
- messages = [
96
- {
97
- "role": "system",
98
- "content": """You are an expert code reviewer. Your task is to analyze a pull request diff and provide constructive feedback.\nAnalyze the provided diff and identify potential issues, suggest improvements, or point out good practices.\n\nIMPORTANT: Respond with a JSON array of comment objects. Each object must have three fields: 'file_path', 'line_number', and 'comment_text'.\nThe 'file_path' should be the full path of the file being changed.\nThe 'line_number' must be an integer corresponding to the line number in the *new* version of the file where the comment applies.\nThe 'comment_text' should be your concise and clear review comment.\n\nExample response format:\n[\n {\n "file_path": "src/utils/helpers.py",\n "line_number": 42,\n "comment_text": "This function could be simplified by using a list comprehension."\n }\n]\n\nDo not add any introductory text or explanations outside of the JSON array.\n"""
99
- },
100
  {
101
- "role": "user",
102
- "content": f"Here is the diff to review:\n\n```diff\n{diff}\n```"
 
103
  }
104
  ]
105
-
106
- # Note: We don't need to manually move inputs to a device when using device_map="auto"
107
- inputs = tokenizer.apply_chat_template(messages, add_generation_prompt=True, return_tensors="pt")
108
-
109
- outputs = model.generate(inputs, max_new_tokens=1024, do_sample=False, top_k=50, top_p=0.95, num_return_sequences=1, eos_token_id=tokenizer.eos_token_id)
110
 
111
- response_text = tokenizer.decode(outputs[0][len(inputs[0]):], skip_special_tokens=True)
112
- return response_text.strip()
113
-
114
- def parse_ai_response(response_text: str) -> list[ReviewComment]:
115
- """
116
- Parses the raw text from the AI to extract the JSON array.
117
- """
118
- print(f"Raw AI Response:\n---\n{response_text}\n---")
119
-
120
- json_match = re.search(r'\[.*\]', response_text, re.DOTALL)
121
- if not json_match:
122
- print("Warning: Could not find a JSON array in the AI response.")
123
- return []
124
-
125
- json_string = json_match.group(0)
126
-
127
- try:
128
- comments_data = json.loads(json_string)
129
- validated_comments = [ReviewComment(**item) for item in comments_data]
130
- return validated_comments
131
- except (json.JSONDecodeError, TypeError, KeyError) as e:
132
- print(f"Error parsing JSON from AI response: {e}")
133
- print(f"Invalid JSON string: {json_string}")
134
- return []
135
-
136
- # ----------------------------
137
- # 6. The API Endpoint
138
- # ----------------------------
139
-
140
- @app.post("/review", response_model=ReviewResponse)
141
- async def get_code_review(request: ReviewRequest):
142
- if not request.diff:
143
- raise HTTPException(status_code=400, detail="Diff content cannot be empty.")
144
-
145
- try:
146
- ai_response_text = run_ai_inference(request.diff)
147
- parsed_comments = parse_ai_response(ai_response_text)
148
- return ReviewResponse(comments=parsed_comments)
149
-
150
- except Exception as e:
151
- print(f"An unexpected error occurred: {e}")
152
- raise HTTPException(status_code=500, detail="An internal error occurred while processing the review.")
153
 
154
  # ----------------------------
155
- # 7. Health Check Endpoint
156
  # ----------------------------
157
 
158
  @app.get("/health")
159
  async def health_check():
160
- return {"status": "ok", "model_loaded": model is not None}
 
 
1
+
2
  from fastapi import FastAPI, HTTPException
3
  from pydantic import BaseModel
 
 
 
4
  import re
5
  import json
6
 
7
  # ----------------------------
8
+ # 1. FastAPI App Initialization
 
 
 
 
 
 
 
 
 
9
  # ----------------------------
10
 
11
  app = FastAPI(
12
+ title="AI Code Review Service (Test Mode)",
13
+ description="A test version of the API without a live AI model.",
14
  version="1.0.0",
15
  )
16
 
17
  # ----------------------------
18
+ # 2. Mock AI Model Loading (Simulated)
19
  # ----------------------------
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  @app.on_event("startup")
22
  async def startup_event():
23
  """
24
+ In this test version, we just print a message.
25
+ We are not loading any real model.
26
  """
27
+ print("Server starting up in test mode.")
28
+ print("Model loading is disabled.")
29
 
30
  # ----------------------------
31
+ # 3. API Request/Response Models
32
  # ----------------------------
33
 
34
  class ReviewRequest(BaseModel):
 
43
  comments: list[ReviewComment]
44
 
45
  # ----------------------------
46
+ # 4. The API Endpoint (with Mocked Response)
47
  # ----------------------------
48
 
49
+ @app.post("/review", response_model=ReviewResponse)
50
+ async def get_code_review(request: ReviewRequest):
51
  """
52
+ This endpoint now returns a hardcoded, successful response.
53
+ It does not call an AI model.
54
  """
55
+ print("Received request for /review. Returning mocked response.")
56
+ if not request.diff:
57
+ raise HTTPException(status_code=400, detail="Diff content cannot be empty.")
58
 
59
+ # Create a fake response to prove the endpoint is working.
60
+ mock_comments = [
 
 
 
61
  {
62
+ "file_path": "src/mock/test.py",
63
+ "line_number": 10,
64
+ "comment_text": "This is a test comment from the mock server. If you see this, the API is working!"
65
  }
66
  ]
 
 
 
 
 
67
 
68
+ return ReviewResponse(comments=[ReviewComment(**c) for c in mock_comments])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
 
70
  # ----------------------------
71
+ # 5. Health Check Endpoint
72
  # ----------------------------
73
 
74
  @app.get("/health")
75
  async def health_check():
76
+ """A simple endpoint to confirm the server is running."""
77
+ return {"status": "ok", "model_loaded": False} # Model is not loaded in test mode