""" app.py — Property Classifier FastAPI inference server with embedded web UI. No gradio = no dependency conflicts on HuggingFace Spaces. """ import os import io import json import time import numpy as np from PIL import Image from fastapi import FastAPI, File, UploadFile, HTTPException from fastapi.responses import HTMLResponse, JSONResponse from fastapi.middleware.cors import CORSMiddleware os.environ["TF_CPP_MIN_LOG_LEVEL"] = "3" os.environ["TF_ENABLE_ONEDNN_OPTS"] = "0" import tensorflow as tf # ── Paths ───────────────────────────────────────────────────────────────── MODEL_PATH = "models/building_classifier.h5" TOKENIZER_PATH = "models/tokenizer.json" IMG_SIZE = (224, 224) # ── Class display names ─────────────────────────────────────────────────── CLASS_DISPLAY = { "exterior_facade": "Exterior Facade", "office_interior": "Office Interior", "warehouse": "Warehouse", "hvac_pipeline": "HVAC Pipeline", } CLASS_COLORS = { "exterior_facade": "#3b82f6", "office_interior": "#22c55e", "warehouse": "#f59e0b", "hvac_pipeline": "#a855f7", } # ── Load model once at startup ──────────────────────────────────────────── model: tf.keras.Model | None = None classes: list[str] = [] try: if os.path.exists(MODEL_PATH) and os.path.exists(TOKENIZER_PATH): print("Loading model...") model = tf.keras.models.load_model(MODEL_PATH) tok = json.loads(open(TOKENIZER_PATH, encoding="utf-8").read()) classes = tok["class_names"] print(f"Model loaded successfully. Classes: {classes}") else: print(f"WARNING: Model not found at {MODEL_PATH}") except Exception as e: print(f"ERROR loading model: {e}") # ── FastAPI app ─────────────────────────────────────────────────────────── app = FastAPI(title="Property Classifier", version="2.0.0") app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"], ) # ── Embedded HTML UI ────────────────────────────────────────────────────── HTML = """