devranx commited on
Commit
ad6d315
·
1 Parent(s): 5ac57f3

Fix startup timeout with lazy model loading

Browse files
Files changed (2) hide show
  1. backend/model_handler.py +28 -5
  2. verify_startup.py +46 -0
backend/model_handler.py CHANGED
@@ -13,14 +13,24 @@ class ModelHandler:
13
  self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
14
  print(f"Using device: {self.device}", flush=True)
15
  self.transform = build_transform()
16
- self.load_models()
 
 
 
 
 
 
 
17
  except Exception as e:
18
  print(f"CRITICAL ERROR in ModelHandler.__init__: {e}", flush=True)
19
  import traceback
20
  traceback.print_exc()
21
 
22
- def load_models(self):
23
- # MODEL 1: InternVL
 
 
 
24
  try:
25
  # Check if local path exists, otherwise use HF Hub ID
26
  local_path = os.path.join("Models", "InternVL2_5-1B-MPO")
@@ -51,7 +61,11 @@ class ModelHandler:
51
  self.model_int = None
52
  self.tokenizer_int = None
53
 
54
- # MODEL 2: EasyOCR
 
 
 
 
55
  try:
56
  # EasyOCR automatically handles downloading if not present
57
  self.reader = easyocr.Reader(['en', 'hi'], gpu=False)
@@ -60,7 +74,11 @@ class ModelHandler:
60
  print(f"\nError initializing EasyOCR reader: {e}")
61
  self.reader = None
62
 
63
- # MODEL 3: CLIP
 
 
 
 
64
  try:
65
  local_path = os.path.join("Models", "clip-vit-base-patch32")
66
  if os.path.exists(local_path):
@@ -79,6 +97,7 @@ class ModelHandler:
79
  self.processor_clip = None
80
 
81
  def easyocr_ocr(self, image):
 
82
  if not self.reader:
83
  return ""
84
  image_np = np.array(image)
@@ -95,6 +114,7 @@ class ModelHandler:
95
  return ordered_text
96
 
97
  def intern(self, image, prompt, max_tokens):
 
98
  if not self.model_int or not self.tokenizer_int:
99
  return ""
100
 
@@ -123,6 +143,7 @@ class ModelHandler:
123
  return response
124
 
125
  def clip(self, image, labels):
 
126
  if not self.model_clip or not self.processor_clip:
127
  return None
128
 
@@ -138,6 +159,8 @@ class ModelHandler:
138
  return processed
139
 
140
  def get_clip_probs(self, image, labels):
 
 
141
  inputs = self.clip(image, labels)
142
  if inputs is None:
143
  return None
 
13
  self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
14
  print(f"Using device: {self.device}", flush=True)
15
  self.transform = build_transform()
16
+
17
+ # Initialize model placeholders
18
+ self.model_int = None
19
+ self.tokenizer_int = None
20
+ self.reader = None
21
+ self.model_clip = None
22
+ self.processor_clip = None
23
+
24
  except Exception as e:
25
  print(f"CRITICAL ERROR in ModelHandler.__init__: {e}", flush=True)
26
  import traceback
27
  traceback.print_exc()
28
 
29
+ def load_internvl(self):
30
+ if self.model_int is not None and self.tokenizer_int is not None:
31
+ return
32
+
33
+ print("Loading InternVL model...", flush=True)
34
  try:
35
  # Check if local path exists, otherwise use HF Hub ID
36
  local_path = os.path.join("Models", "InternVL2_5-1B-MPO")
 
61
  self.model_int = None
62
  self.tokenizer_int = None
63
 
64
+ def load_easyocr(self):
65
+ if self.reader is not None:
66
+ return
67
+
68
+ print("Loading EasyOCR model...", flush=True)
69
  try:
70
  # EasyOCR automatically handles downloading if not present
71
  self.reader = easyocr.Reader(['en', 'hi'], gpu=False)
 
74
  print(f"\nError initializing EasyOCR reader: {e}")
75
  self.reader = None
76
 
77
+ def load_clip(self):
78
+ if self.model_clip is not None and self.processor_clip is not None:
79
+ return
80
+
81
+ print("Loading CLIP model...", flush=True)
82
  try:
83
  local_path = os.path.join("Models", "clip-vit-base-patch32")
84
  if os.path.exists(local_path):
 
97
  self.processor_clip = None
98
 
99
  def easyocr_ocr(self, image):
100
+ self.load_easyocr()
101
  if not self.reader:
102
  return ""
103
  image_np = np.array(image)
 
114
  return ordered_text
115
 
116
  def intern(self, image, prompt, max_tokens):
117
+ self.load_internvl()
118
  if not self.model_int or not self.tokenizer_int:
119
  return ""
120
 
 
143
  return response
144
 
145
  def clip(self, image, labels):
146
+ self.load_clip()
147
  if not self.model_clip or not self.processor_clip:
148
  return None
149
 
 
159
  return processed
160
 
161
  def get_clip_probs(self, image, labels):
162
+ # clip() calls load_clip(), so we don't strictly need it here, but good for safety if clip() implementation changes
163
+ self.load_clip()
164
  inputs = self.clip(image, labels)
165
  if inputs is None:
166
  return None
verify_startup.py ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import sys
2
+ import os
3
+
4
+ # Add the current directory to sys.path so we can import backend
5
+ sys.path.append(os.getcwd())
6
+
7
+ print("Importing backend.model_handler...", flush=True)
8
+ from backend.model_handler import model_handler
9
+
10
+ print("\nChecking model states...", flush=True)
11
+
12
+ errors = []
13
+
14
+ if model_handler.model_int is not None:
15
+ errors.append("InternVL model should be None on startup")
16
+ else:
17
+ print("PASS: InternVL model is None")
18
+
19
+ if model_handler.tokenizer_int is not None:
20
+ errors.append("InternVL tokenizer should be None on startup")
21
+ else:
22
+ print("PASS: InternVL tokenizer is None")
23
+
24
+ if model_handler.reader is not None:
25
+ errors.append("EasyOCR reader should be None on startup")
26
+ else:
27
+ print("PASS: EasyOCR reader is None")
28
+
29
+ if model_handler.model_clip is not None:
30
+ errors.append("CLIP model should be None on startup")
31
+ else:
32
+ print("PASS: CLIP model is None")
33
+
34
+ if model_handler.processor_clip is not None:
35
+ errors.append("CLIP processor should be None on startup")
36
+ else:
37
+ print("PASS: CLIP processor is None")
38
+
39
+ if errors:
40
+ print("\nFAILED:", flush=True)
41
+ for error in errors:
42
+ print(f"- {error}", flush=True)
43
+ sys.exit(1)
44
+ else:
45
+ print("\nSUCCESS: All models are lazily loaded.", flush=True)
46
+ sys.exit(0)