bugfix
Browse files
main.py
CHANGED
|
@@ -13,20 +13,18 @@ from typing import Any, Dict
|
|
| 13 |
import os
|
| 14 |
import pkgutil
|
| 15 |
|
| 16 |
-
|
| 17 |
from huggingface_hub import hf_hub_download
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
from typing import Any, Dict
|
| 19 |
|
| 20 |
import tensorflow as tf
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
from tensorflow import keras
|
| 25 |
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
app = FastAPI(title="1.3 - AI Model Deployment")
|
| 31 |
''' browser: http://localhost:8000/docs'''
|
| 32 |
|
|
@@ -40,10 +38,6 @@ app.add_middleware(
|
|
| 40 |
)
|
| 41 |
|
| 42 |
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
ANIMALS = ['Cat', 'Dog', 'Panda'] # Animal names here, these represent the labels of the images that we trained our model on.
|
| 48 |
|
| 49 |
# 1) download your SavedModel from the Hub into a writable directory (Spaces often
|
|
@@ -57,24 +51,64 @@ try:
|
|
| 57 |
except PermissionError:
|
| 58 |
raise RuntimeError(f"Cannot create model directory '{local_model_dir}'. Ensure the process has write access or set HF_MODEL_DIR to a writable path.")
|
| 59 |
|
| 60 |
-
# download files into local_model_dir
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
hf_hub_download(repo_id, filename="model.weights.h5", repo_type="model", local_dir=local_model_dir)
|
| 64 |
-
|
| 65 |
-
# 2) load it
|
| 66 |
-
# If the hub returns an unpacked model folder, point load_model at that directory.
|
| 67 |
try:
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
|
|
|
|
|
|
|
|
|
|
| 73 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 74 |
|
| 75 |
|
| 76 |
@app.post('/upload/image')
|
| 77 |
async def uploadImage(img: UploadFile = File(...)):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
original_image = Image.open(img.file) # Read the bytes and process as an image
|
| 79 |
if original_image.mode == 'RGBA':
|
| 80 |
original_image = original_image.convert('RGB')
|
|
@@ -111,6 +145,8 @@ def versions() -> Dict[str, Any]:
|
|
| 111 |
def predict_stub() -> Dict[str, Any]:
|
| 112 |
|
| 113 |
# This is a stub, so we're not doing a real prediction
|
|
|
|
|
|
|
| 114 |
return {"prediction": "stub, we're not doing a real prediction"}
|
| 115 |
|
| 116 |
|
|
|
|
| 13 |
import os
|
| 14 |
import pkgutil
|
| 15 |
|
|
|
|
| 16 |
from huggingface_hub import hf_hub_download
|
| 17 |
+
from huggingface_hub import hf_hub_url
|
| 18 |
+
import requests
|
| 19 |
+
import tempfile
|
| 20 |
+
import shutil
|
| 21 |
from typing import Any, Dict
|
| 22 |
|
| 23 |
import tensorflow as tf
|
| 24 |
+
import traceback
|
| 25 |
+
import logging
|
|
|
|
| 26 |
from tensorflow import keras
|
| 27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
app = FastAPI(title="1.3 - AI Model Deployment")
|
| 29 |
''' browser: http://localhost:8000/docs'''
|
| 30 |
|
|
|
|
| 38 |
)
|
| 39 |
|
| 40 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 41 |
ANIMALS = ['Cat', 'Dog', 'Panda'] # Animal names here, these represent the labels of the images that we trained our model on.
|
| 42 |
|
| 43 |
# 1) download your SavedModel from the Hub into a writable directory (Spaces often
|
|
|
|
| 51 |
except PermissionError:
|
| 52 |
raise RuntimeError(f"Cannot create model directory '{local_model_dir}'. Ensure the process has write access or set HF_MODEL_DIR to a writable path.")
|
| 53 |
|
| 54 |
+
# download files into local_model_dir and load model with resilient error handling
|
| 55 |
+
model = None
|
| 56 |
+
model_load_error = None
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
try:
|
| 58 |
+
# First try using a cache dir so downloads happen in a shared cache and final move
|
| 59 |
+
# into local_model_dir is less likely to require risky renames inside the repo.
|
| 60 |
+
cache_dir = '/tmp/.cache/huggingface'
|
| 61 |
+
os.makedirs(cache_dir, exist_ok=True)
|
| 62 |
|
| 63 |
+
hf_hub_download(repo_id, filename="config.json", repo_type="model", local_dir=local_model_dir, cache_dir=cache_dir)
|
| 64 |
+
hf_hub_download(repo_id, filename="metadata.json", repo_type="model", local_dir=local_model_dir, cache_dir=cache_dir)
|
| 65 |
+
hf_hub_download(repo_id, filename="model.weights.h5", repo_type="model", local_dir=local_model_dir, cache_dir=cache_dir)
|
| 66 |
|
| 67 |
+
# 2) load it
|
| 68 |
+
model = tf.keras.models.load_model(local_model_dir)
|
| 69 |
+
logging.info(f"Model loaded successfully from {local_model_dir}")
|
| 70 |
+
except Exception as e:
|
| 71 |
+
# Primary download attempt failed -> try streamed fallback which writes directly
|
| 72 |
+
# into local_model_dir (avoids internal temp/move when those operations are blocked).
|
| 73 |
+
primary_err = e
|
| 74 |
+
logging.error("Primary hf_hub_download failed, attempting streamed fallback: %s", e)
|
| 75 |
+
try:
|
| 76 |
+
for filename in ("config.json", "metadata.json", "model.weights.h5"):
|
| 77 |
+
url = hf_hub_url(repo_id=repo_id, filename=filename, repo_type='model')
|
| 78 |
+
logging.info(f"Streaming {filename} from {url} into {local_model_dir}")
|
| 79 |
+
resp = requests.get(url, stream=True, timeout=60)
|
| 80 |
+
resp.raise_for_status()
|
| 81 |
+
# write to a temp file inside the target dir then move atomically
|
| 82 |
+
with tempfile.NamedTemporaryFile(dir=local_model_dir, delete=False) as tmpf:
|
| 83 |
+
for chunk in resp.iter_content(chunk_size=8192):
|
| 84 |
+
if chunk:
|
| 85 |
+
tmpf.write(chunk)
|
| 86 |
+
tmp_path = tmpf.name
|
| 87 |
+
final_path = os.path.join(local_model_dir, filename)
|
| 88 |
+
try:
|
| 89 |
+
shutil.move(tmp_path, final_path)
|
| 90 |
+
except Exception:
|
| 91 |
+
# if atomic move fails, try copy+remove
|
| 92 |
+
shutil.copy(tmp_path, final_path)
|
| 93 |
+
os.remove(tmp_path)
|
| 94 |
+
|
| 95 |
+
# After streamed download, try loading
|
| 96 |
+
model = tf.keras.models.load_model(local_model_dir)
|
| 97 |
+
logging.info(f"Model loaded successfully from {local_model_dir} after streamed fallback")
|
| 98 |
+
except Exception as e2:
|
| 99 |
+
model_load_error = f"primary: {primary_err}; fallback: {e2}"
|
| 100 |
+
tb = traceback.format_exc()
|
| 101 |
+
logging.error("Streamed fallback failed: %s", e2)
|
| 102 |
+
logging.error(tb)
|
| 103 |
+
model = None
|
| 104 |
|
| 105 |
|
| 106 |
@app.post('/upload/image')
|
| 107 |
async def uploadImage(img: UploadFile = File(...)):
|
| 108 |
+
if model is None:
|
| 109 |
+
# Model isn't available — return a helpful error to the caller instead of crashing.
|
| 110 |
+
return fastapi.Response(status_code=503, content=f"Model not loaded: {model_load_error}")
|
| 111 |
+
|
| 112 |
original_image = Image.open(img.file) # Read the bytes and process as an image
|
| 113 |
if original_image.mode == 'RGBA':
|
| 114 |
original_image = original_image.convert('RGB')
|
|
|
|
| 145 |
def predict_stub() -> Dict[str, Any]:
|
| 146 |
|
| 147 |
# This is a stub, so we're not doing a real prediction
|
| 148 |
+
if model is None:
|
| 149 |
+
return {"prediction": "model not loaded", "error": model_load_error}
|
| 150 |
return {"prediction": "stub, we're not doing a real prediction"}
|
| 151 |
|
| 152 |
|