afterimage / backend /app /main.py
KryptosK's picture
Afterimage live backend (FastAPI + FastEmbed + embedded Qdrant)
850c319 verified
Raw
History Blame Contribute Delete
3.82 kB
from threading import Lock
from fastapi import FastAPI, HTTPException, Query
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from .config import get_settings
from .schemas import ObjectSearchRequest, TrailRequest
from .services import AfterimageService
settings = get_settings()
_service_lock = Lock()
_service_instance: AfterimageService | None = None
app = FastAPI(title="Afterimage API", version="0.1.0")
app.add_middleware(
CORSMiddleware,
allow_origins=settings.cors_origin_list,
allow_credentials=False,
allow_methods=["*"],
allow_headers=["*"],
)
app.mount("/assets", StaticFiles(directory=settings.resolved_asset_root), name="assets")
def service() -> AfterimageService:
global _service_instance
if _service_instance is None:
with _service_lock:
if _service_instance is None:
_service_instance = AfterimageService(settings)
return _service_instance
def bad_request(exc: Exception) -> HTTPException:
return HTTPException(status_code=400, detail=str(exc))
@app.get("/api/health")
def health():
try:
return service().health()
except Exception as exc:
raise HTTPException(status_code=503, detail=str(exc)) from exc
@app.get("/api/scenarios")
def scenarios():
try:
return service().scenarios()
except Exception as exc:
raise HTTPException(status_code=503, detail=str(exc)) from exc
@app.get("/api/memories")
def memories(
scenario: str = Query(default="vault", max_length=60),
zone: str | None = Query(default=None),
region_id: str | None = Query(default=None),
is_baseline: bool | None = Query(default=None),
):
try:
return service().memories(scenario=scenario, zone=zone, region_id=region_id, is_baseline=is_baseline)
except ValueError as exc:
raise bad_request(exc) from exc
@app.post("/api/anomaly/scan")
def anomaly_scan(scenario: str = Query(default="vault", max_length=60)):
try:
return service().scan_anomalies(scenario=scenario)
except (FileNotFoundError, ValueError) as exc:
raise bad_request(exc) from exc
@app.post("/api/object/search")
def object_search(request: ObjectSearchRequest):
try:
return service().object_search(
scenario=request.scenario,
asset_id=request.asset_id or request.crop_id,
image_ref=request.image_ref,
filters=request.filters,
limit=request.limit,
)
except (FileNotFoundError, ValueError) as exc:
raise bad_request(exc) from exc
@app.post("/api/trail")
def trail(request: TrailRequest):
try:
return service().trail(
scenario=request.scenario,
asset_id=request.asset_id or request.crop_id,
image_ref=request.image_ref,
score_cutoff=request.score_cutoff,
)
except (FileNotFoundError, ValueError) as exc:
raise bad_request(exc) from exc
@app.get("/api/outliers")
def outliers(scenario: str = Query(default="vault", max_length=60)):
try:
return service().outliers(scenario=scenario)
except ValueError as exc:
raise bad_request(exc) from exc
@app.get("/api/matrix")
def matrix(scenario: str = Query(default="vault", max_length=60), sample: int = Query(default=24, ge=1, le=100)):
try:
return service().matrix(scenario=scenario, sample=sample)
except ValueError as exc:
raise bad_request(exc) from exc
@app.get("/api/text_search")
def text_search(q: str = Query(..., max_length=120), scenario: str | None = Query(default=None, max_length=60)):
try:
return service().text_search(query=q, scenario=scenario)
except (FileNotFoundError, ValueError) as exc:
raise bad_request(exc) from exc