Dannyar608 commited on
Commit
5b7059f
·
verified ·
1 Parent(s): e7a939b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +48 -79
app.py CHANGED
@@ -37,13 +37,8 @@ logging.basicConfig(
37
  format='%(asctime)s - %(levelname)s - %(message)s'
38
  )
39
 
40
- # Model configuration
41
- MODEL_CHOICES = {
42
- "TinyLlama (Fastest)": "TinyLlama/TinyLlama-1.1B-Chat-v1.0",
43
- "Phi-2 (Balanced)": "microsoft/phi-2",
44
- "DeepSeek-V3 (Most Powerful)": "deepseek-ai/DeepSeek-V3"
45
- }
46
- DEFAULT_MODEL = "TinyLlama (Fastest)"
47
 
48
  # Initialize Hugging Face API
49
  if HF_TOKEN:
@@ -61,11 +56,11 @@ class ModelLoader:
61
  self.loaded = False
62
  self.loading = False
63
  self.error = None
64
- self.current_model = None
65
 
66
- def load_model(self, model_name: str, progress: gr.Progress = None) -> Tuple[Optional[AutoModelForCausalLM], Optional[AutoTokenizer]]:
67
  """Lazy load the model with progress feedback"""
68
- if self.loaded and self.current_model == model_name:
69
  return self.model, self.tokenizer
70
 
71
  self.loading = True
@@ -85,48 +80,48 @@ class ModelLoader:
85
  # Load with optimized settings
86
  model_kwargs = {
87
  "trust_remote_code": True,
88
- "torch_dtype": torch.float16,
89
- "device_map": "auto",
90
  "low_cpu_mem_usage": True
91
  }
92
 
93
- if "TinyLlama" in model_name:
94
- model_kwargs["attn_implementation"] = "flash_attention_2"
 
95
 
96
  if progress:
97
  progress(0.3, desc="Loading tokenizer...")
98
  self.tokenizer = AutoTokenizer.from_pretrained(
99
- MODEL_CHOICES[model_name],
100
  trust_remote_code=True
101
  )
102
 
103
  if progress:
104
  progress(0.6, desc="Loading model...")
105
  self.model = AutoModelForCausalLM.from_pretrained(
106
- MODEL_CHOICES[model_name],
107
  **model_kwargs
108
- )
109
 
110
  # Verify model responsiveness
111
  if progress:
112
  progress(0.8, desc="Verifying model...")
113
- test_input = self.tokenizer("Test", return_tensors="pt").to(self.model.device)
114
  _ = self.model.generate(**test_input, max_new_tokens=1)
115
 
116
  self.model.eval() # Disable dropout
117
  if progress:
118
  progress(0.9, desc="Finalizing...")
119
  self.loaded = True
120
- self.current_model = model_name
121
  return self.model, self.tokenizer
122
 
123
  except torch.cuda.OutOfMemoryError:
124
- self.error = "Out of GPU memory. Try a smaller model."
125
  logging.error(self.error)
126
  return None, None
127
  except Exception as e:
128
- self.error = str(e)
129
- logging.error(f"Model loading error: {str(e)}")
130
  return None, None
131
  finally:
132
  self.loading = False
@@ -298,30 +293,36 @@ class TranscriptParser:
298
  }
299
 
300
  def _extract_student_info(self, text: str):
301
- """Extract student personal information"""
302
- header_match = re.search(
303
- r"(\d{7}) - ([\w\s,]+)\s*\|\s*Cohort \w+\s*\|\s*Un-weighted GPA ([\d.]+)\s*\|\s*Comm Serv Hours (\d+)",
304
- text
 
 
305
  )
 
 
306
  if header_match:
307
  self.student_data = {
308
- "id": header_match.group(1),
309
- "name": header_match.group(2).strip(),
310
- "unweighted_gpa": float(header_match.group(3)),
311
- "community_service_hours": int(header_match.group(4))
312
  }
313
 
314
- # Extract additional info
315
- grade_match = re.search(
316
- r"Current Grade: (\d+)\s*\|\s*YOG (\d{4})\s*\|\s*Weighted GPA ([\d.]+)\s*\|\s*Total Credits Earned ([\d.]+)",
317
- text
318
  )
 
 
319
  if grade_match:
320
  self.student_data.update({
321
- "current_grade": grade_match.group(1),
322
- "graduation_year": grade_match.group(2),
323
- "weighted_gpa": float(grade_match.group(3)),
324
- "total_credits": float(grade_match.group(4))
325
  })
326
 
327
  def _extract_requirements(self, text: str):
@@ -401,7 +402,7 @@ async def parse_transcript_async(file_obj, progress=gr.Progress()):
401
 
402
  def parse_transcript_with_ai(text: str, progress=gr.Progress()) -> Dict:
403
  """Use AI model to parse transcript text with progress feedback"""
404
- model, tokenizer = model_loader.load_model(model_loader.current_model or DEFAULT_MODEL, progress)
405
  if model is None or tokenizer is None:
406
  raise gr.Error(f"Model failed to load. {model_loader.error or 'Please try loading a model first.'}")
407
 
@@ -472,7 +473,7 @@ def parse_transcript_with_ai_fallback(text: str, progress=gr.Progress()) -> Dict
472
  progress(0.1, desc="Processing transcript with AI...")
473
 
474
  # Tokenize and generate response
475
- inputs = model_loader.tokenizer(prompt, return_tensors="pt").to(model_loader.model.device)
476
  if progress:
477
  progress(0.4)
478
 
@@ -502,7 +503,7 @@ def parse_transcript_with_ai_fallback(text: str, progress=gr.Progress()) -> Dict
502
  return validate_parsed_data(parsed_data)
503
 
504
  except torch.cuda.OutOfMemoryError:
505
- raise gr.Error("The model ran out of memory. Try with a smaller transcript or use a smaller model.")
506
  except Exception as e:
507
  logging.error(f"AI parsing error: {str(e)}")
508
  raise gr.Error(f"Error processing transcript: {str(e)}")
@@ -1306,12 +1307,6 @@ def create_interface():
1306
  background-color: #fff3e0;
1307
  color: #e65100;
1308
  }
1309
- .model-selection {
1310
- margin-bottom: 20px;
1311
- padding: 15px;
1312
- background: #f8f9fa;
1313
- border-radius: 8px;
1314
- }
1315
  """
1316
 
1317
  gr.Markdown("""
@@ -1320,20 +1315,6 @@ def create_interface():
1320
  Complete each step to get customized learning recommendations.
1321
  """)
1322
 
1323
- # Model selection section
1324
- with gr.Group(elem_classes="model-selection"):
1325
- model_selector = gr.Dropdown(
1326
- choices=list(MODEL_CHOICES.keys()),
1327
- value=DEFAULT_MODEL,
1328
- label="Select AI Model",
1329
- interactive=True
1330
- )
1331
- load_model_btn = gr.Button("Load Selected Model", variant="secondary")
1332
- model_status = gr.HTML(
1333
- value="<div class='model-loading'>Model not loaded yet. Please select and load a model.</div>",
1334
- visible=True
1335
- )
1336
-
1337
  # Progress tracker
1338
  with gr.Row():
1339
  with gr.Column(scale=1):
@@ -1353,8 +1334,8 @@ def create_interface():
1353
  # Navigation message
1354
  nav_message = gr.HTML(elem_classes="nav-message", visible=False)
1355
 
1356
- # Main tabs
1357
- with gr.Tabs() as tabs:
1358
  # ===== TAB 1: Transcript Upload =====
1359
  with gr.Tab("Transcript Upload", id=0) as tab1:
1360
  with gr.Row():
@@ -1765,22 +1746,10 @@ def create_interface():
1765
  outputs=[tabs, nav_message]
1766
  )
1767
 
1768
- # Model loading functions
1769
- def load_selected_model(model_name, progress=gr.Progress()):
1770
- try:
1771
- model_loader.load_model(model_name, progress)
1772
- if model_loader.loaded:
1773
- return gr.update(value=f"<div class='alert-box'>{model_name} loaded successfully!</div>", visible=True)
1774
- else:
1775
- return gr.update(value=f"<div class='nav-message'>Failed to load model: {model_loader.error}</div>", visible=True)
1776
- except Exception as e:
1777
- logging.error(f"Model loading error: {str(e)}")
1778
- return gr.update(value=f"<div class='nav-message'>Error: {str(e)}</div>", visible=True)
1779
-
1780
- load_model_btn.click(
1781
- fn=load_selected_model,
1782
- inputs=model_selector,
1783
- outputs=model_status
1784
  )
1785
 
1786
  return app
 
37
  format='%(asctime)s - %(levelname)s - %(message)s'
38
  )
39
 
40
+ # Model configuration - Only DeepSeek
41
+ MODEL_NAME = "deepseek-ai/DeepSeek-V3"
 
 
 
 
 
42
 
43
  # Initialize Hugging Face API
44
  if HF_TOKEN:
 
56
  self.loaded = False
57
  self.loading = False
58
  self.error = None
59
+ self.device = "cuda" if torch.cuda.is_available() else "cpu"
60
 
61
+ def load_model(self, progress: gr.Progress = None) -> Tuple[Optional[AutoModelForCausalLM], Optional[AutoTokenizer]]:
62
  """Lazy load the model with progress feedback"""
63
+ if self.loaded:
64
  return self.model, self.tokenizer
65
 
66
  self.loading = True
 
80
  # Load with optimized settings
81
  model_kwargs = {
82
  "trust_remote_code": True,
83
+ "torch_dtype": torch.float16 if self.device == "cuda" else torch.float32,
84
+ "device_map": "auto" if self.device == "cuda" else None,
85
  "low_cpu_mem_usage": True
86
  }
87
 
88
+ # Add quantization config for low-memory devices
89
+ if self.device == "cpu":
90
+ model_kwargs["load_in_8bit"] = True
91
 
92
  if progress:
93
  progress(0.3, desc="Loading tokenizer...")
94
  self.tokenizer = AutoTokenizer.from_pretrained(
95
+ MODEL_NAME,
96
  trust_remote_code=True
97
  )
98
 
99
  if progress:
100
  progress(0.6, desc="Loading model...")
101
  self.model = AutoModelForCausalLM.from_pretrained(
102
+ MODEL_NAME,
103
  **model_kwargs
104
+ ).to(self.device)
105
 
106
  # Verify model responsiveness
107
  if progress:
108
  progress(0.8, desc="Verifying model...")
109
+ test_input = self.tokenizer("Test", return_tensors="pt").to(self.device)
110
  _ = self.model.generate(**test_input, max_new_tokens=1)
111
 
112
  self.model.eval() # Disable dropout
113
  if progress:
114
  progress(0.9, desc="Finalizing...")
115
  self.loaded = True
 
116
  return self.model, self.tokenizer
117
 
118
  except torch.cuda.OutOfMemoryError:
119
+ self.error = "Out of GPU memory. Try using CPU instead."
120
  logging.error(self.error)
121
  return None, None
122
  except Exception as e:
123
+ self.error = f"Model loading error: {str(e)}"
124
+ logging.error(self.error)
125
  return None, None
126
  finally:
127
  self.loading = False
 
293
  }
294
 
295
  def _extract_student_info(self, text: str):
296
+ """Enhanced student info extraction with more robust regex"""
297
+ # Unified pattern that handles variations in transcript formats
298
+ header_pattern = (
299
+ r"(?:Student\s*[:]?\s*|Name\s*[:]?\s*)?"
300
+ r"(\d{7})\s*[-]?\s*([\w\s,]+?)\s*"
301
+ r"(?:\||Cohort\s*\w+\s*\||Un-weighted\s*GPA\s*([\d.]+)\s*\||Comm\s*Serv\s*Hours\s*(\d+))?"
302
  )
303
+
304
+ header_match = re.search(header_pattern, text, re.IGNORECASE)
305
  if header_match:
306
  self.student_data = {
307
+ "id": header_match.group(1) if header_match.group(1) else "Unknown",
308
+ "name": header_match.group(2).strip() if header_match.group(2) else "Unknown",
309
+ "unweighted_gpa": float(header_match.group(3)) if header_match.group(3) else 0.0,
310
+ "community_service_hours": int(header_match.group(4)) if header_match.group(4) else 0
311
  }
312
 
313
+ # More flexible grade info pattern
314
+ grade_pattern = (
315
+ r"(?:Grade|Level)\s*[:]?\s*(\d+)\s*"
316
+ r"(?:\||YOG\s*[:]?\s*(\d{4})\s*\||Weighted\s*GPA\s*([\d.]+)\s*\||Total\s*Credits\s*Earned\s*([\d.]+))?"
317
  )
318
+
319
+ grade_match = re.search(grade_pattern, text, re.IGNORECASE)
320
  if grade_match:
321
  self.student_data.update({
322
+ "current_grade": grade_match.group(1) if grade_match.group(1) else "Unknown",
323
+ "graduation_year": grade_match.group(2) if grade_match.group(2) else "Unknown",
324
+ "weighted_gpa": float(grade_match.group(3)) if grade_match.group(3) else 0.0,
325
+ "total_credits": float(grade_match.group(4)) if grade_match.group(4) else 0.0
326
  })
327
 
328
  def _extract_requirements(self, text: str):
 
402
 
403
  def parse_transcript_with_ai(text: str, progress=gr.Progress()) -> Dict:
404
  """Use AI model to parse transcript text with progress feedback"""
405
+ model, tokenizer = model_loader.load_model(progress)
406
  if model is None or tokenizer is None:
407
  raise gr.Error(f"Model failed to load. {model_loader.error or 'Please try loading a model first.'}")
408
 
 
473
  progress(0.1, desc="Processing transcript with AI...")
474
 
475
  # Tokenize and generate response
476
+ inputs = model_loader.tokenizer(prompt, return_tensors="pt").to(model_loader.device)
477
  if progress:
478
  progress(0.4)
479
 
 
503
  return validate_parsed_data(parsed_data)
504
 
505
  except torch.cuda.OutOfMemoryError:
506
+ raise gr.Error("The model ran out of memory. Try with a smaller transcript.")
507
  except Exception as e:
508
  logging.error(f"AI parsing error: {str(e)}")
509
  raise gr.Error(f"Error processing transcript: {str(e)}")
 
1307
  background-color: #fff3e0;
1308
  color: #e65100;
1309
  }
 
 
 
 
 
 
1310
  """
1311
 
1312
  gr.Markdown("""
 
1315
  Complete each step to get customized learning recommendations.
1316
  """)
1317
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1318
  # Progress tracker
1319
  with gr.Row():
1320
  with gr.Column(scale=1):
 
1334
  # Navigation message
1335
  nav_message = gr.HTML(elem_classes="nav-message", visible=False)
1336
 
1337
+ # Main tabs (hidden since we're using the button navigation)
1338
+ with gr.Tabs(visible=False) as tabs:
1339
  # ===== TAB 1: Transcript Upload =====
1340
  with gr.Tab("Transcript Upload", id=0) as tab1:
1341
  with gr.Row():
 
1746
  outputs=[tabs, nav_message]
1747
  )
1748
 
1749
+ # Load DeepSeek model automatically
1750
+ app.load(
1751
+ fn=lambda: model_loader.load_model(),
1752
+ outputs=[]
 
 
 
 
 
 
 
 
 
 
 
 
1753
  )
1754
 
1755
  return app