|
|
""" |
|
|
Image Similarity Search API with FastAPI and Qdrant - Fixed Access |
|
|
This application provides endpoints for uploading images and searching for similar images |
|
|
using vector embeddings from the CLIP model. Implemented using OOP principles. |
|
|
""" |
|
|
|
|
|
import uvicorn |
|
|
from fastapi import FastAPI |
|
|
from contextlib import asynccontextmanager |
|
|
import os |
|
|
import ssl |
|
|
from fastapi.middleware.cors import CORSMiddleware |
|
|
|
|
|
from config import Config |
|
|
from services.embedding_service import ImageEmbeddingModel |
|
|
from services.vector_db_service import VectorDatabaseClient |
|
|
from api.routes import register_routes |
|
|
|
|
|
|
|
|
@asynccontextmanager |
|
|
async def lifespan(app: FastAPI): |
|
|
"""Lifespan context manager for FastAPI application startup and shutdown events""" |
|
|
|
|
|
vector_db = app.state.vector_db |
|
|
vector_db.ensure_collection_exists() |
|
|
|
|
|
yield |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ImageSimilarityAPI: |
|
|
"""Main application class that orchestrates all components""" |
|
|
|
|
|
def __init__(self): |
|
|
|
|
|
self.config = Config() |
|
|
|
|
|
|
|
|
self.embedding_model = ImageEmbeddingModel(self.config.model_name) |
|
|
self.vector_db = VectorDatabaseClient( |
|
|
self.config.qdrant_url, |
|
|
self.config.qdrant_api_key, |
|
|
self.config.collection_name, |
|
|
self.config.embedding_size |
|
|
) |
|
|
|
|
|
|
|
|
self.app = FastAPI( |
|
|
title="Image Similarity Search API", |
|
|
description="API for uploading images and searching for similar images using CLIP embeddings", |
|
|
version="1.0.0", |
|
|
lifespan=lifespan |
|
|
) |
|
|
|
|
|
|
|
|
self.app.add_middleware( |
|
|
CORSMiddleware, |
|
|
allow_origins=["*"], |
|
|
allow_credentials=True, |
|
|
allow_methods=["*"], |
|
|
allow_headers=["*"], |
|
|
) |
|
|
|
|
|
self.app.state.vector_db = self.vector_db |
|
|
|
|
|
|
|
|
register_routes(self.app, self.embedding_model, self.vector_db) |
|
|
|
|
|
def run(self, use_https=False, cert_file="./certs/cert.pem", key_file="./certs/key.pem"): |
|
|
"""Run the FastAPI application with optional HTTPS support |
|
|
|
|
|
Args: |
|
|
use_https: Whether to use HTTPS or plain HTTP |
|
|
cert_file: Path to SSL certificate file |
|
|
key_file: Path to SSL private key file |
|
|
""" |
|
|
host = "0.0.0.0" |
|
|
port = 8000 if not use_https else 8443 |
|
|
|
|
|
ssl_context = None |
|
|
if use_https: |
|
|
|
|
|
if not os.path.exists(cert_file) or not os.path.exists(key_file): |
|
|
print(f"ERROR: SSL certificate files not found at {cert_file} and/or {key_file}") |
|
|
print("Falling back to HTTP. To use HTTPS, please provide valid certificate files.") |
|
|
use_https = False |
|
|
else: |
|
|
|
|
|
ssl_context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) |
|
|
ssl_context.load_cert_chain(cert_file, key_file) |
|
|
|
|
|
|
|
|
protocol = "https" if use_https else "http" |
|
|
print(f"\n{'='*50}") |
|
|
print(f"Access the API at: {protocol}://{host}:{port}") |
|
|
print(f"Swagger UI available at: {protocol}://{host}:{port}/docs") |
|
|
print(f"ReDoc UI available at: {protocol}://{host}:{port}/redoc") |
|
|
print(f"{'='*50}\n") |
|
|
|
|
|
uvicorn.run( |
|
|
self.app, |
|
|
host=host, |
|
|
port=port, |
|
|
reload=self.config.environment == "development", |
|
|
ssl_certfile=cert_file if use_https else None, |
|
|
ssl_keyfile=key_file if use_https else None |
|
|
) |
|
|
|
|
|
|
|
|
def create_app() -> FastAPI: |
|
|
"""Create and return the FastAPI application""" |
|
|
api = ImageSimilarityAPI() |
|
|
return api.app |
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
api = ImageSimilarityAPI() |
|
|
|
|
|
api.run( |
|
|
use_https=False, |
|
|
cert_file="./certs/cert.pem", |
|
|
key_file="./certs/key.pem" |
|
|
) |