File size: 3,217 Bytes
7d07e42 0a0d999 7d07e42 0a0d999 7d07e42 0a0d999 7d07e42 02f4591 7d07e42 0a0d999 7d07e42 0a0d999 7d07e42 | 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 | """
CV Pipeline API — main app.
Startup strategy (HF free CPU):
- API startup: INSTANT
- /health langsung ready
- Prewarm sequential: OCR → YOLO → Captioner (semua instant/cepat)
- CLIP tetap pure lazy — hanya di-load saat ada classification request
- Frontend polling /ready → tombol aktif saat model siap
"""
from __future__ import annotations
import os
import sys
import time
import threading
os.environ.setdefault("ANONYMIZED_TELEMETRY", "False")
os.environ.setdefault("CHROMA_TELEMETRY_ENABLED", "False")
os.environ.setdefault("POSTHOG_DISABLED", "1")
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from loguru import logger
from .routes import router, get_pipeline
from .readiness import get_readiness
from ..config import get_cv_settings
settings = get_cv_settings()
logger.remove()
logger.add(
sys.stderr,
level="INFO",
colorize=True,
format="<green>{time:HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan> - {message}",
)
logger.add("./logs/cv_api.log", rotation="10 MB", retention="7 days")
app = FastAPI(
title="CV Pipeline API",
version="1.4.0",
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(router, prefix="/api/v1")
def _sequential_prewarm():
"""
Load komponen satu per satu.
OCR/Tesseract: instant
YOLO ONNX: ~0.1s (12MB, baked di image)
Captioner (Groq): instant (cuma init httpx client)
CLIP: pure lazy, tidak di-prewarm
"""
readiness = get_readiness()
pipeline = get_pipeline()
startup_delay = int(os.environ.get("CV_PREWARM_DELAY", "0"))
logger.info(f"Sequential prewarm dimulai dalam {startup_delay}s...")
time.sleep(startup_delay)
targets = [
("ocr", lambda: pipeline.ocr),
("yolo", lambda: pipeline.yolo),
("captioner", lambda: pipeline.captioner),
]
for name, getter in targets:
if readiness.get_status(name).state.value == "ready":
logger.info(f" {name}: sudah ready, skip")
continue
try:
readiness.mark_loading(name)
t0 = time.perf_counter()
logger.info(f" loading {name}...")
_ = getter()
elapsed = time.perf_counter() - t0
readiness.mark_ready(name)
logger.info(f" {name} ready ({elapsed:.1f}s)")
except Exception as e:
readiness.mark_error(name, str(e))
logger.error(f" {name} failed: {e}")
snap = readiness.snapshot()
logger.info(f"Prewarm selesai. State: {snap['overall']}")
@app.on_event("startup")
async def startup():
logger.info("CV Pipeline API starting up...")
logger.info(f"Docs: http://{settings.api_host}:{settings.api_port}/docs")
prewarm = os.environ.get("CV_PREWARM", "true").lower()
if prewarm == "false":
logger.info("CV_PREWARM=false — pure lazy-load mode.")
return
logger.info("Starting sequential background prewarm...")
thread = threading.Thread(
target=_sequential_prewarm,
daemon=True,
name="cv-prewarm-sequential",
)
thread.start()
|