import os import array from fastapi import FastAPI, Query from fastapi.middleware.cors import CORSMiddleware from fastapi.responses import FileResponse from fastapi.staticfiles import StaticFiles from dotenv import load_dotenv from db_oracle import get_connection from embedder import embed_text load_dotenv() PHOTOS_DIR = os.getenv("PHOTOS_DIR") FRONTEND_DIR = os.path.join(os.path.dirname(__file__), "../../oravector-demo/frontend") app = FastAPI() app.add_middleware(CORSMiddleware, allow_origins=["*"], allow_methods=["*"], allow_headers=["*"]) app.mount("/ui", StaticFiles(directory=os.path.abspath(FRONTEND_DIR), html=True), name="ui") @app.get("/search") def search(q: str = Query(...), limit: int = Query(12)): # oracledb rejects a plain Python list for a VECTOR column. # array.array("f") produces a typed 32-bit float buffer that matches VECTOR(512, FLOAT32). vec = array.array("f", embed_text(q)) conn = get_connection() cur = conn.cursor() cur.execute( """ SELECT filename, 1 - VECTOR_DISTANCE(embedding, :vec, COSINE) AS score FROM images ORDER BY VECTOR_DISTANCE(embedding, :vec, COSINE) FETCH FIRST :lim ROWS ONLY """, {"vec": vec, "lim": limit}, ) rows = cur.fetchall() cur.close() conn.close() return [{"filename": r[0], "score": round(r[1], 4)} for r in rows] @app.get("/stats") def stats(): conn = get_connection() cur = conn.cursor() cur.execute("SELECT COUNT(*) FROM images") count = cur.fetchone()[0] cur.close() conn.close() return {"count": count} @app.get("/photos/{filename}") def get_photo(filename: str): path = os.path.join(PHOTOS_DIR, filename) return FileResponse(path, media_type="image/jpeg")