Spaces:
Runtime error
Runtime error
Update app.py
Browse files
app.py
CHANGED
@@ -37,13 +37,8 @@ logging.basicConfig(
|
|
37 |
format='%(asctime)s - %(levelname)s - %(message)s'
|
38 |
)
|
39 |
|
40 |
-
# Model configuration
|
41 |
-
|
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.
|
65 |
|
66 |
-
def load_model(self,
|
67 |
"""Lazy load the model with progress feedback"""
|
68 |
-
if self.loaded
|
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 |
-
|
94 |
-
|
|
|
95 |
|
96 |
if progress:
|
97 |
progress(0.3, desc="Loading tokenizer...")
|
98 |
self.tokenizer = AutoTokenizer.from_pretrained(
|
99 |
-
|
100 |
trust_remote_code=True
|
101 |
)
|
102 |
|
103 |
if progress:
|
104 |
progress(0.6, desc="Loading model...")
|
105 |
self.model = AutoModelForCausalLM.from_pretrained(
|
106 |
-
|
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.
|
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
|
125 |
logging.error(self.error)
|
126 |
return None, None
|
127 |
except Exception as e:
|
128 |
-
self.error = str(e)
|
129 |
-
logging.error(
|
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 |
-
"""
|
302 |
-
|
303 |
-
|
304 |
-
|
|
|
|
|
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 |
-
#
|
315 |
-
|
316 |
-
r"
|
317 |
-
|
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(
|
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.
|
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
|
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 |
-
#
|
1769 |
-
|
1770 |
-
|
1771 |
-
|
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
|