Spaces:
Sleeping
Sleeping
fix: detect Git LFS pointer in FER model, auto-download real binary
Browse filesIf Docker COPY models/ copies a Git LFS pointer instead of the actual
ONNX binary, _init_fer() now:
1. Logs file size + first bytes for diagnosis
2. Detects LFS pointer (file <10KB starting with "version https://git-lfs")
3. Downloads the real binary from HF Space resolve URL
4. Loads the downloaded binary instead
Also adds onnxruntime version logging and full traceback on load failure.
- api/main.py +51 -1
api/main.py
CHANGED
|
@@ -115,6 +115,39 @@ _FER_CLASSES = ["Anger", "Contempt", "Disgust", "Fear", "Happy", "Neutral", "S
|
|
| 115 |
_VIDEO_EXTS = {".mp4", ".mkv", ".avi", ".mov", ".m4v"}
|
| 116 |
|
| 117 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 118 |
def _init_fer() -> None:
|
| 119 |
global _fer_session, _fer_input_name, _face_cascade
|
| 120 |
|
|
@@ -129,13 +162,30 @@ def _init_fer() -> None:
|
|
| 129 |
print("[voxtral] FER model not found — facial emotion disabled")
|
| 130 |
return
|
| 131 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
try:
|
| 133 |
import onnxruntime as rt
|
|
|
|
| 134 |
_fer_session = rt.InferenceSession(fer_path, providers=["CPUExecutionProvider"])
|
| 135 |
_fer_input_name = _fer_session.get_inputs()[0].name
|
| 136 |
-
print(f"[voxtral] FER model loaded: {fer_path} (input={_fer_input_name})")
|
| 137 |
except Exception as e:
|
|
|
|
| 138 |
print(f"[voxtral] FER model load failed: {e}")
|
|
|
|
| 139 |
return
|
| 140 |
|
| 141 |
try:
|
|
|
|
| 115 |
_VIDEO_EXTS = {".mp4", ".mkv", ".avi", ".mov", ".m4v"}
|
| 116 |
|
| 117 |
|
| 118 |
+
def _is_lfs_pointer(path: str) -> bool:
|
| 119 |
+
"""Return True if the file looks like a Git LFS pointer (small text file)."""
|
| 120 |
+
try:
|
| 121 |
+
size = os.path.getsize(path)
|
| 122 |
+
if size > 10_000:
|
| 123 |
+
return False
|
| 124 |
+
with open(path, "rb") as f:
|
| 125 |
+
header = f.read(64)
|
| 126 |
+
return header.startswith(b"version https://git-lfs")
|
| 127 |
+
except Exception:
|
| 128 |
+
return False
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
def _resolve_lfs_model(fer_path: str) -> str:
|
| 132 |
+
"""
|
| 133 |
+
If fer_path is a Git LFS pointer, download the real binary.
|
| 134 |
+
Returns the path to the actual model file.
|
| 135 |
+
"""
|
| 136 |
+
import urllib.request
|
| 137 |
+
real_path = fer_path + ".resolved"
|
| 138 |
+
# Use HF Space's own file resolution URL to download the actual binary
|
| 139 |
+
url = "https://huggingface.co/spaces/mistral-hackaton-2026/ethos/resolve/main/models/emotion_model_web.onnx"
|
| 140 |
+
print(f"[voxtral] FER: file is LFS pointer — downloading from {url}")
|
| 141 |
+
try:
|
| 142 |
+
urllib.request.urlretrieve(url, real_path)
|
| 143 |
+
size = os.path.getsize(real_path)
|
| 144 |
+
print(f"[voxtral] FER: downloaded {size} bytes to {real_path}")
|
| 145 |
+
return real_path
|
| 146 |
+
except Exception as e:
|
| 147 |
+
print(f"[voxtral] FER: download failed: {e}")
|
| 148 |
+
return fer_path
|
| 149 |
+
|
| 150 |
+
|
| 151 |
def _init_fer() -> None:
|
| 152 |
global _fer_session, _fer_input_name, _face_cascade
|
| 153 |
|
|
|
|
| 162 |
print("[voxtral] FER model not found — facial emotion disabled")
|
| 163 |
return
|
| 164 |
|
| 165 |
+
# Debug: log file size and first bytes to diagnose LFS pointer vs real binary
|
| 166 |
+
try:
|
| 167 |
+
file_size = os.path.getsize(fer_path)
|
| 168 |
+
with open(fer_path, "rb") as f:
|
| 169 |
+
first_bytes = f.read(32).hex()
|
| 170 |
+
print(f"[voxtral] FER file: {fer_path} size={file_size} first_bytes={first_bytes}")
|
| 171 |
+
except Exception as e:
|
| 172 |
+
print(f"[voxtral] FER file stat error: {e}")
|
| 173 |
+
|
| 174 |
+
# If it's a Git LFS pointer, download the actual binary
|
| 175 |
+
if _is_lfs_pointer(fer_path):
|
| 176 |
+
print("[voxtral] FER: detected Git LFS pointer — resolving...")
|
| 177 |
+
fer_path = _resolve_lfs_model(fer_path)
|
| 178 |
+
|
| 179 |
try:
|
| 180 |
import onnxruntime as rt
|
| 181 |
+
print(f"[voxtral] FER: onnxruntime version = {rt.__version__}")
|
| 182 |
_fer_session = rt.InferenceSession(fer_path, providers=["CPUExecutionProvider"])
|
| 183 |
_fer_input_name = _fer_session.get_inputs()[0].name
|
| 184 |
+
print(f"[voxtral] FER model loaded: {fer_path} (input={_fer_input_name}, shape={_fer_session.get_inputs()[0].shape})")
|
| 185 |
except Exception as e:
|
| 186 |
+
import traceback
|
| 187 |
print(f"[voxtral] FER model load failed: {e}")
|
| 188 |
+
print(f"[voxtral] FER traceback: {traceback.format_exc()}")
|
| 189 |
return
|
| 190 |
|
| 191 |
try:
|