Spaces:
Sleeping
Sleeping
Robert Yaw Agyekum Addo commited on
Commit ·
8f61658
1
Parent(s): f38258d
Changes made
Browse files- Dockerfile +9 -7
- app/main.py +85 -6
Dockerfile
CHANGED
|
@@ -24,7 +24,8 @@ RUN apt-get update && \
|
|
| 24 |
libtesseract-dev \
|
| 25 |
tesseract-ocr-eng \
|
| 26 |
tesseract-ocr-fra \
|
| 27 |
-
tesseract-ocr-spa
|
|
|
|
| 28 |
rm -rf /var/lib/apt/lists/*
|
| 29 |
|
| 30 |
# Copy Python dependencies from system locations
|
|
@@ -33,10 +34,6 @@ COPY --from=builder /usr/local/bin/gunicorn /usr/local/bin/
|
|
| 33 |
COPY --from=builder /usr/local/bin/uvicorn /usr/local/bin/
|
| 34 |
COPY . .
|
| 35 |
|
| 36 |
-
# Health check endpoint
|
| 37 |
-
HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \
|
| 38 |
-
CMD curl -f http://localhost:8000/health || exit 1
|
| 39 |
-
|
| 40 |
# Create non-root user and set permissions
|
| 41 |
RUN useradd -m appuser && \
|
| 42 |
chown -R appuser:appuser /app && \
|
|
@@ -46,6 +43,11 @@ RUN useradd -m appuser && \
|
|
| 46 |
# Switch to non-root user
|
| 47 |
USER appuser
|
| 48 |
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
-
|
|
|
|
|
|
| 24 |
libtesseract-dev \
|
| 25 |
tesseract-ocr-eng \
|
| 26 |
tesseract-ocr-fra \
|
| 27 |
+
tesseract-ocr-spa \
|
| 28 |
+
curl && \
|
| 29 |
rm -rf /var/lib/apt/lists/*
|
| 30 |
|
| 31 |
# Copy Python dependencies from system locations
|
|
|
|
| 34 |
COPY --from=builder /usr/local/bin/uvicorn /usr/local/bin/
|
| 35 |
COPY . .
|
| 36 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
# Create non-root user and set permissions
|
| 38 |
RUN useradd -m appuser && \
|
| 39 |
chown -R appuser:appuser /app && \
|
|
|
|
| 43 |
# Switch to non-root user
|
| 44 |
USER appuser
|
| 45 |
|
| 46 |
+
# Health check with longer intervals and start period for model loading
|
| 47 |
+
HEALTHCHECK --interval=60s --timeout=30s --start-period=300s --retries=5 \
|
| 48 |
+
CMD curl -f http://localhost:7860/health || exit 1
|
| 49 |
+
|
| 50 |
+
EXPOSE 7860
|
| 51 |
|
| 52 |
+
# Use port 7860 which is standard for Hugging Face Spaces
|
| 53 |
+
CMD ["gunicorn", "-k", "uvicorn.workers.UvicornWorker", "-b", "0.0.0.0:7860", "--timeout", "300", "--worker-connections", "10", "app.main:app"]
|
app/main.py
CHANGED
|
@@ -1,18 +1,97 @@
|
|
| 1 |
from fastapi import FastAPI
|
| 2 |
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
|
|
|
| 3 |
from fastapi import FastAPI
|
| 4 |
from .api.v1.router import router as api_router
|
| 5 |
from app.api.v1.endpoints import transcription, ocr, translation, tts
|
|
|
|
|
|
|
| 6 |
|
| 7 |
app = FastAPI(generate_unique_id_function=lambda route: f"{route.tags[0]}_{route.name}")
|
| 8 |
|
| 9 |
-
|
|
|
|
|
|
|
| 10 |
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 16 |
|
| 17 |
# CORS configuration
|
| 18 |
app.add_middleware(
|
|
|
|
| 1 |
from fastapi import FastAPI
|
| 2 |
from fastapi.middleware.cors import CORSMiddleware
|
| 3 |
+
from fastapi.responses import JSONResponse
|
| 4 |
+
import asyncio
|
| 5 |
from fastapi import FastAPI
|
| 6 |
from .api.v1.router import router as api_router
|
| 7 |
from app.api.v1.endpoints import transcription, ocr, translation, tts
|
| 8 |
+
import logging
|
| 9 |
+
logging.basicConfig(level=logging.INFO)
|
| 10 |
|
| 11 |
app = FastAPI(generate_unique_id_function=lambda route: f"{route.tags[0]}_{route.name}")
|
| 12 |
|
| 13 |
+
# Global flag to track if models are loaded
|
| 14 |
+
models_loaded = False
|
| 15 |
+
loading_progress = {"status": "initializing", "message": "Starting model loading..."}
|
| 16 |
|
| 17 |
+
async def load_models():
|
| 18 |
+
"""Load heavy models asynchronously during startup"""
|
| 19 |
+
global models_loaded, loading_progress
|
| 20 |
+
|
| 21 |
+
try:
|
| 22 |
+
loading_progress["status"] = "loading"
|
| 23 |
+
loading_progress["message"] = "Loading Whisper model..."
|
| 24 |
+
|
| 25 |
+
# Load your Whisper model here
|
| 26 |
+
# whisper_model = whisper.load_model("base")
|
| 27 |
+
|
| 28 |
+
loading_progress["message"] = "Loading TTS model..."
|
| 29 |
+
# Load your TTS model here
|
| 30 |
+
|
| 31 |
+
loading_progress["message"] = "Loading translator..."
|
| 32 |
+
# Initialize deep-translator
|
| 33 |
+
|
| 34 |
+
models_loaded = True
|
| 35 |
+
loading_progress["status"] = "ready"
|
| 36 |
+
loading_progress["message"] = "All models loaded successfully"
|
| 37 |
+
logging.info("All models loaded successfully")
|
| 38 |
+
|
| 39 |
+
except Exception as e:
|
| 40 |
+
loading_progress["status"] = "error"
|
| 41 |
+
loading_progress["message"] = f"Error loading models: {str(e)}"
|
| 42 |
+
logging.error(f"Error loading models: {str(e)}")
|
| 43 |
+
|
| 44 |
+
@app.on_event("startup")
|
| 45 |
+
async def startup_event():
|
| 46 |
+
"""Start model loading in background during app startup"""
|
| 47 |
+
asyncio.create_task(load_models())
|
| 48 |
+
|
| 49 |
+
@app.get("/health")
|
| 50 |
+
async def health_check():
|
| 51 |
+
"""
|
| 52 |
+
Health check endpoint that responds quickly
|
| 53 |
+
Returns different status based on model loading progress
|
| 54 |
+
"""
|
| 55 |
+
if models_loaded:
|
| 56 |
+
return JSONResponse(
|
| 57 |
+
status_code=200,
|
| 58 |
+
content={
|
| 59 |
+
"status": "healthy",
|
| 60 |
+
"models_loaded": True,
|
| 61 |
+
"message": "Service is ready"
|
| 62 |
+
}
|
| 63 |
+
)
|
| 64 |
+
else:
|
| 65 |
+
# Return 200 even during loading to pass health checks
|
| 66 |
+
# But indicate that models are still loading
|
| 67 |
+
return JSONResponse(
|
| 68 |
+
status_code=200,
|
| 69 |
+
content={
|
| 70 |
+
"status": "loading",
|
| 71 |
+
"models_loaded": False,
|
| 72 |
+
"progress": loading_progress
|
| 73 |
+
}
|
| 74 |
+
)
|
| 75 |
+
|
| 76 |
+
@app.get("/ready")
|
| 77 |
+
async def readiness_check():
|
| 78 |
+
"""
|
| 79 |
+
Separate readiness endpoint for when models are fully loaded
|
| 80 |
+
"""
|
| 81 |
+
if models_loaded:
|
| 82 |
+
return JSONResponse(
|
| 83 |
+
status_code=200,
|
| 84 |
+
content={"status": "ready", "models_loaded": True}
|
| 85 |
+
)
|
| 86 |
+
else:
|
| 87 |
+
return JSONResponse(
|
| 88 |
+
status_code=503,
|
| 89 |
+
content={
|
| 90 |
+
"status": "not_ready",
|
| 91 |
+
"models_loaded": False,
|
| 92 |
+
"progress": loading_progress
|
| 93 |
+
}
|
| 94 |
+
)
|
| 95 |
|
| 96 |
# CORS configuration
|
| 97 |
app.add_middleware(
|