Habiba A. Elbehairy commited on
Commit
728d3b4
·
1 Parent(s): 892252c
Files changed (1) hide show
  1. app.py +32 -141
app.py CHANGED
@@ -4,13 +4,10 @@ from typing import Dict, List, Optional
4
  import torch
5
  import torch.nn as nn
6
  import os
7
- import re
8
  import uvicorn
9
  from transformers import AutoTokenizer, AutoConfig, AutoModel
10
 
11
- # Author: habibaelbehairy
12
- # Last updated: 2025-04-21 21:51:33
13
-
14
  app = FastAPI(
15
  title="CodeBERT Multitask Similarity API",
16
  description="Compare test case similarity using a fine-tuned CodeBERT model.",
@@ -27,45 +24,11 @@ class MultitaskCodeSimilarityModel(nn.Module):
27
  self.encoder = AutoModel.from_pretrained(model_name, config=self.config)
28
  self.classifier = nn.Linear(self.config.hidden_size, num_labels)
29
 
30
- # For explanation generation
31
- self.decoder_embedding = nn.Linear(self.config.hidden_size, self.config.hidden_size)
32
- self.decoder = nn.GRU(
33
- input_size=self.config.hidden_size,
34
- hidden_size=self.config.hidden_size,
35
- batch_first=True
36
- )
37
- self.explanation_head = nn.Linear(self.config.hidden_size, len(tokenizer))
38
-
39
- def forward(self, input_ids, attention_mask, explanation_ids=None, explanation_mask=None):
40
  outputs = self.encoder(input_ids=input_ids, attention_mask=attention_mask)
41
  pooled = outputs.last_hidden_state[:, 0]
42
  logits = self.classifier(pooled)
43
-
44
- explanation_logits = None
45
- if explanation_ids is not None:
46
- batch_size = input_ids.size(0)
47
- seq_length = explanation_ids.size(1)
48
-
49
- # Initialize decoder with pooled representation
50
- decoder_input = self.decoder_embedding(pooled).unsqueeze(1).expand(-1, seq_length, -1)
51
-
52
- # Run decoder
53
- decoder_outputs, _ = self.decoder(decoder_input)
54
-
55
- # Generate logits for each position
56
- explanation_logits = self.explanation_head(decoder_outputs)
57
-
58
- return logits, explanation_logits
59
-
60
- # Helper function to extract test fixture from test code
61
- def extract_test_fixture(test_code):
62
- """Extract the test fixture name from the test code."""
63
- match = re.search(r'TEST\(\s*(\w+)\s*,', test_code)
64
- if not match:
65
- match = re.search(r'TEST_F\(\s*(\w+)\s*,', test_code)
66
- if match:
67
- return match.group(1)
68
- return None
69
 
70
  # Load model and tokenizer
71
  try:
@@ -79,35 +42,36 @@ try:
79
  # Initialize the custom model
80
  model = MultitaskCodeSimilarityModel(model_name, num_labels=num_labels, tokenizer=tokenizer)
81
 
82
- # Try to load the model from different possible locations
83
  try:
84
- # Try loading from standard path
85
  model.load_state_dict(torch.load(os.path.join(model_name, "pytorch_model.bin"), map_location="cpu"))
86
  except:
87
  try:
88
- # Try local path
89
  model_weights_path = os.path.join(os.path.dirname(__file__), "pytorch_model.bin")
90
  if os.path.exists(model_weights_path):
91
  model.load_state_dict(torch.load(model_weights_path, map_location="cpu"))
92
  except Exception as e:
93
- print(f"Failed to load custom model: {e}")
94
- # Fall back to standard model as last resort
95
  model = AutoModelForSequenceClassification.from_pretrained(model_name)
96
 
97
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
98
  model.to(device)
99
  model.eval()
 
 
 
 
 
100
 
101
  except Exception as e:
102
- print(f"Error loading model: {e}")
103
- # Fall back to standard model
104
- from transformers import AutoModelForSequenceClassification
105
- model = AutoModelForSequenceClassification.from_pretrained(model_name, from_tf=False)
106
- model.to(device)
107
- model.eval()
108
-
109
- # Default label mapping
110
- label_to_class = {0: "Duplicate", 1: "Redundant", 2: "Distinct"}
111
 
112
  # Input schema definitions
113
  class SourceCode(BaseModel):
@@ -131,106 +95,38 @@ class SimilarityInput(BaseModel):
131
  @app.get("/health")
132
  async def health_check():
133
  """Check if the API is up and running."""
134
- return {
135
- "status": "healthy",
136
- "model": "CodeBERT Multitask Similarity",
137
- "timestamp": "2025-04-21 21:51:33",
138
- "user": "habibaelbehairy"
139
- }
140
 
141
  @app.post("/predict")
142
  async def predict(data: SimilarityInput):
143
  """
144
  Predict similarity class between two test cases for a given source class.
145
  """
 
 
 
146
  try:
147
- # Extract test related information
148
- source_code = data.source_code.code
149
- test_code_1 = data.test_case_1.code
150
- test_code_2 = data.test_case_2.code
151
-
152
- # Extract class and method information
153
- class_1 = data.test_case_1.target_class
154
- class_2 = data.test_case_2.target_class
155
- method_1 = data.test_case_1.target_method
156
- method_2 = data.test_case_2.target_method
157
-
158
- # Apply heuristics like in your local testing
159
-
160
- # 1. If classes are different and not Unknown, tests are Distinct
161
- if class_1 and class_2 and class_1 != "Unknown" and class_2 != "Unknown" and class_1 != class_2:
162
- prediction = 2 # Distinct
163
- classification = label_to_class[prediction]
164
- return {
165
- "pair_id": data.pair_id,
166
- "test_case_1_name": data.test_case_1.name,
167
- "test_case_2_name": data.test_case_2.name,
168
- "similarity": {
169
- "score": prediction,
170
- "classification": classification,
171
- "explanation": ""
172
- },
173
- "probabilities": [0.1, 0.1, 0.8] # Confidence towards Distinct
174
- }
175
-
176
- # 2. If methods are completely different, tests are likely Distinct
177
- if (method_1 and method_2 and set(method_1).isdisjoint(set(method_2)) and
178
- not (len(method_1) == 1 and len(method_2) == 1 and
179
- ("test" in method_1[0].lower() or "test" in method_2[0].lower()))):
180
- prediction = 2 # Distinct
181
- classification = label_to_class[prediction]
182
- return {
183
- "pair_id": data.pair_id,
184
- "test_case_1_name": data.test_case_1.name,
185
- "test_case_2_name": data.test_case_2.name,
186
- "similarity": {
187
- "score": prediction,
188
- "classification": classification,
189
- "explanation": ""
190
- },
191
- "probabilities": [0.1, 0.1, 0.8] # Confidence towards Distinct
192
- }
193
-
194
- # 3. Extract test fixtures to compare if available
195
- fixture_1 = data.test_case_1.test_fixture
196
- fixture_2 = data.test_case_2.test_fixture
197
-
198
- # If fixtures don't match, tests might be Distinct as well
199
- if fixture_1 and fixture_2 and fixture_1 != fixture_2:
200
- prediction = 2 # Distinct
201
- classification = label_to_class[prediction]
202
- return {
203
- "pair_id": data.pair_id,
204
- "test_case_1_name": data.test_case_1.name,
205
- "test_case_2_name": data.test_case_2.name,
206
- "similarity": {
207
- "score": prediction,
208
- "classification": classification,
209
- "explanation": ""
210
- },
211
- "probabilities": [0.1, 0.1, 0.8] # Confidence towards Distinct
212
- }
213
-
214
- # For all other cases, use the model for prediction
215
- # Format input
216
  combined_input = (
217
- f"SOURCE CODE: {source_code}\n"
218
- f"TEST 1: {test_code_1}\n"
219
- f"TEST 2: {test_code_2}"
220
  )
221
 
222
  # Tokenize input
223
  inputs = tokenizer(combined_input, return_tensors="pt", padding=True, truncation=True, max_length=512).to(device)
224
 
225
- # Get prediction
226
  with torch.no_grad():
227
- # Handle both custom and standard models
228
- if isinstance(model, MultitaskCodeSimilarityModel):
229
- logits, _ = model(
 
230
  input_ids=inputs["input_ids"],
231
  attention_mask=inputs["attention_mask"]
232
  )
233
  else:
 
234
  outputs = model(**inputs)
235
  logits = outputs.logits
236
 
@@ -241,9 +137,6 @@ async def predict(data: SimilarityInput):
241
  # Map prediction to class name
242
  classification = label_to_class.get(prediction, "Unknown")
243
 
244
- # Empty explanation as requested
245
- explanation = ""
246
-
247
  return {
248
  "pair_id": data.pair_id,
249
  "test_case_1_name": data.test_case_1.name,
@@ -251,14 +144,12 @@ async def predict(data: SimilarityInput):
251
  "similarity": {
252
  "score": prediction,
253
  "classification": classification,
254
- "explanation": explanation
255
  },
256
  "probabilities": probs
257
  }
258
 
259
  except Exception as e:
260
  import traceback
261
- print(f"Error in prediction: {str(e)}")
262
  print(traceback.format_exc())
263
  raise HTTPException(status_code=500, detail=f"Prediction error: {str(e)}")
264
 
 
4
  import torch
5
  import torch.nn as nn
6
  import os
7
+ import json
8
  import uvicorn
9
  from transformers import AutoTokenizer, AutoConfig, AutoModel
10
 
 
 
 
11
  app = FastAPI(
12
  title="CodeBERT Multitask Similarity API",
13
  description="Compare test case similarity using a fine-tuned CodeBERT model.",
 
24
  self.encoder = AutoModel.from_pretrained(model_name, config=self.config)
25
  self.classifier = nn.Linear(self.config.hidden_size, num_labels)
26
 
27
+ def forward(self, input_ids, attention_mask):
 
 
 
 
 
 
 
 
 
28
  outputs = self.encoder(input_ids=input_ids, attention_mask=attention_mask)
29
  pooled = outputs.last_hidden_state[:, 0]
30
  logits = self.classifier(pooled)
31
+ return logits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
  # Load model and tokenizer
34
  try:
 
42
  # Initialize the custom model
43
  model = MultitaskCodeSimilarityModel(model_name, num_labels=num_labels, tokenizer=tokenizer)
44
 
45
+ # Load the weights - try different paths
46
  try:
47
+ # Try to load from hub path
48
  model.load_state_dict(torch.load(os.path.join(model_name, "pytorch_model.bin"), map_location="cpu"))
49
  except:
50
  try:
51
+ # Try local path relative to file
52
  model_weights_path = os.path.join(os.path.dirname(__file__), "pytorch_model.bin")
53
  if os.path.exists(model_weights_path):
54
  model.load_state_dict(torch.load(model_weights_path, map_location="cpu"))
55
  except Exception as e:
56
+ print(f"Error loading weights: {e}")
57
+ # Try to use hub's model directly as fallback
58
  model = AutoModelForSequenceClassification.from_pretrained(model_name)
59
 
60
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
61
  model.to(device)
62
  model.eval()
63
+ print(f"Model loaded successfully and running on {device}")
64
+
65
+ # Load label mapping or use default
66
+ # Default mapping - 1-indexed (adjust based on your trained model)
67
+ label_to_class = {1: "Duplicate", 2: "Redundant", 3: "Distinct"}
68
 
69
  except Exception as e:
70
+ print(f"Error during model initialization: {e}")
71
+ # Create a dummy model for API documentation/testing
72
+ tokenizer = None
73
+ model = None
74
+ label_to_class = {1: "Duplicate", 2: "Redundant", 3: "Distinct"}
 
 
 
 
75
 
76
  # Input schema definitions
77
  class SourceCode(BaseModel):
 
95
  @app.get("/health")
96
  async def health_check():
97
  """Check if the API is up and running."""
98
+ return {"status": "healthy", "model": "CodeBERT Multitask Similarity", "timestamp": "2025-04-21 20:08:44"}
 
 
 
 
 
99
 
100
  @app.post("/predict")
101
  async def predict(data: SimilarityInput):
102
  """
103
  Predict similarity class between two test cases for a given source class.
104
  """
105
+ if model is None:
106
+ raise HTTPException(status_code=500, detail="Model not loaded correctly")
107
+
108
  try:
109
+ # Format input to match training format
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  combined_input = (
111
+ f"SOURCE CODE: {data.source_code.code}\n"
112
+ f"TEST 1: {data.test_case_1.code}\n"
113
+ f"TEST 2: {data.test_case_2.code}"
114
  )
115
 
116
  # Tokenize input
117
  inputs = tokenizer(combined_input, return_tensors="pt", padding=True, truncation=True, max_length=512).to(device)
118
 
119
+ # Get prediction from the model
120
  with torch.no_grad():
121
+ # Check if using custom model or fallback
122
+ if hasattr(model, 'forward'):
123
+ # Our custom model
124
+ logits = model(
125
  input_ids=inputs["input_ids"],
126
  attention_mask=inputs["attention_mask"]
127
  )
128
  else:
129
+ # Fallback to standard model
130
  outputs = model(**inputs)
131
  logits = outputs.logits
132
 
 
137
  # Map prediction to class name
138
  classification = label_to_class.get(prediction, "Unknown")
139
 
 
 
 
140
  return {
141
  "pair_id": data.pair_id,
142
  "test_case_1_name": data.test_case_1.name,
 
144
  "similarity": {
145
  "score": prediction,
146
  "classification": classification,
 
147
  },
148
  "probabilities": probs
149
  }
150
 
151
  except Exception as e:
152
  import traceback
 
153
  print(traceback.format_exc())
154
  raise HTTPException(status_code=500, detail=f"Prediction error: {str(e)}")
155