Spaces:
Sleeping
Sleeping
File size: 6,495 Bytes
eeeea78 d3839bb eeeea78 d3839bb eeeea78 d3839bb eeeea78 d3839bb eeeea78 d3839bb eeeea78 d3839bb eeeea78 d3839bb eeeea78 d3839bb eeeea78 d3839bb eeeea78 d3839bb eeeea78 d3839bb eeeea78 d3839bb eeeea78 d3839bb eeeea78 d3839bb eeeea78 d3839bb eeeea78 d3839bb eeeea78 d3839bb eeeea78 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 | import os
import sys
import logging
from typing import Optional
from functools import lru_cache
from pathlib import Path
# -------------------------------------------------
# Logging setup
# -------------------------------------------------
logger = logging.getLogger(__name__)
logger.setLevel(logging.INFO)
# -------------------------------------------------
# TensorFlow import (lazy + fault-tolerant)
# -------------------------------------------------
try:
import tensorflow as tf
TF_AVAILABLE = True
logger.info("TensorFlow successfully imported.")
except ImportError as e:
TF_AVAILABLE = False
tf = None
logger.warning("TensorFlow import failed: %s", e)
# -------------------------------------------------
# Helper: Resolve absolute model directory
# -------------------------------------------------
def _get_models_dir() -> str:
"""Return absolute path to the models directory."""
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
models_dir = os.path.join(base_dir, "models")
# Log diagnostic information
logger.info("=" * 70)
logger.info("MODEL DIRECTORY RESOLUTION")
logger.info(f"Current working directory: {os.getcwd()}")
logger.info(f"Script location: {__file__}")
logger.info(f"Base directory: {base_dir}")
logger.info(f"Models directory: {models_dir}")
logger.info(f"Models directory exists: {os.path.exists(models_dir)}")
if os.path.exists(models_dir):
logger.info(f"Models directory contents: {os.listdir(models_dir)}")
else:
logger.error(f"β Models directory NOT FOUND: {models_dir}")
logger.error(f"Current directory contents: {os.listdir('.')}")
# Try alternative paths
alt_paths = ["models", "./models", "/app/models"]
for alt_path in alt_paths:
if os.path.exists(alt_path):
logger.info(f"β
Found models at alternative path: {alt_path}")
logger.info(f"Contents: {os.listdir(alt_path)}")
models_dir = os.path.abspath(alt_path)
break
logger.info("=" * 70)
return models_dir
# -------------------------------------------------
# Helper: Construct model path safely
# -------------------------------------------------
def _resolve_model_path(model_env_key: str, default_filename: str) -> str:
"""Resolve model path from env var or fallback to /models directory."""
logger.info(f"Resolving model path for: {default_filename}")
# Check environment variable first
env_path = os.getenv(model_env_key)
if env_path and os.path.exists(env_path):
logger.info(f"β
Using model from env var {model_env_key}: {env_path}")
return env_path
# Fallback to models directory
fallback_path = os.path.join(_get_models_dir(), default_filename)
if os.path.exists(fallback_path):
file_size = os.path.getsize(fallback_path)
logger.info(f"β
Model file found: {fallback_path}")
logger.info(f"π File size: {file_size:,} bytes ({file_size/1024:.2f} KB)")
else:
logger.error(f"β Model file missing: {fallback_path}")
# Try to find the file in current directory as last resort
if os.path.exists(default_filename):
logger.info(f"β
Found model in current directory: {default_filename}")
return os.path.abspath(default_filename)
models_in_cwd = os.path.join("models", default_filename)
if os.path.exists(models_in_cwd):
logger.info(f"β
Found model in ./models/: {models_in_cwd}")
return os.path.abspath(models_in_cwd)
return fallback_path
# -------------------------------------------------
# Generic model loader with caching
# -------------------------------------------------
@lru_cache(maxsize=4)
def _load_model(model_path: str) -> Optional[object]:
"""Load a Keras model once and cache it."""
logger.info("=" * 70)
logger.info(f"π₯ LOADING MODEL: {model_path}")
if not TF_AVAILABLE:
logger.error("β TensorFlow not available. Cannot load model: %s", model_path)
logger.info("=" * 70)
return None
if not os.path.exists(model_path):
logger.error(f"β Model file not found: {model_path}")
logger.error(f"Current working directory: {os.getcwd()}")
logger.error(f"Absolute path: {os.path.abspath(model_path)}")
logger.info("=" * 70)
return None
try:
file_size = os.path.getsize(model_path)
logger.info(f"π Model file size: {file_size:,} bytes ({file_size/1024/1024:.2f} MB)")
model = tf.keras.models.load_model(model_path)
logger.info(f"β
Model loaded successfully: {model_path}")
logger.info(f"Model input shape: {model.input_shape}")
logger.info(f"Model output shape: {model.output_shape}")
logger.info("=" * 70)
return model
except Exception as e:
logger.error(f"β Failed to load model: {model_path}")
logger.exception("Exception details: %s", e)
logger.info("=" * 70)
return None
# -------------------------------------------------
# Public APIs (for routes)
# -------------------------------------------------
def load_xception_model() -> Optional[object]:
"""Load and return XceptionNet model."""
logger.info("π§ Loading XceptionNet model...")
path = _resolve_model_path("MODEL_PATH_XCEPTION", "xceptionnet.keras")
model = _load_model(path)
if model is None:
logger.error("β XceptionNet model failed to load!")
return model
def load_mesonet_model() -> Optional[object]:
"""Load and return MesoNet model."""
logger.info("π§ Loading MesoNet model...")
path = _resolve_model_path("MODEL_PATH_MESONET", "mesonet.keras")
model = _load_model(path)
if model is None:
logger.error("β MesoNet model failed to load!")
return model
def clear_model_cache():
"""Clear in-memory cache (useful for memory control or testing)."""
_load_model.cache_clear()
logger.info("Model cache cleared.")
# -------------------------------------------------
# Health check utility
# -------------------------------------------------
def verify_models_ready() -> bool:
"""Verify both models are loaded successfully."""
xception_ok = load_xception_model() is not None
mesonet_ok = load_mesonet_model() is not None
return xception_ok and mesonet_ok
|