Fix startup timeout with lazy model loading
Browse files- backend/model_handler.py +28 -5
- 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 23 |
-
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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)
|