File size: 3,355 Bytes
6c24b50
 
 
 
 
 
 
 
 
 
89157f5
6c24b50
3379d24
6c24b50
 
 
 
 
3379d24
 
6c24b50
 
 
c25809b
6c24b50
 
 
 
 
 
 
 
 
 
c28ae12
6c24b50
 
 
 
3379d24
 
 
99c519a
 
 
3379d24
6c24b50
 
89157f5
 
6c24b50
 
 
 
 
89157f5
6c24b50
 
 
 
 
 
 
3379d24
ff6b176
6c24b50
 
 
 
 
 
 
 
 
 
 
 
4789772
6c24b50
62ec94a
 
 
 
6c24b50
 
 
 
 
 
89157f5
6c24b50
 
 
 
 
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
from __future__ import annotations

import asyncio
from contextlib import asynccontextmanager

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware

from app.config import get_settings
from app.core.database import pool_manager
from app.core.logger import get_logger
from app.services.embeddings_service import EmbeddingService
from app.api.v1.router import api_v1_router

_logger = get_logger(__name__)
_settings = get_settings()

_embedding_service: EmbeddingService = EmbeddingService()


async def _self_ping():
    import httpx
    health_url = _settings.self_ping_url
    while True:
        try:
            async with httpx.AsyncClient(timeout=30.0) as client:
                response = await client.get(health_url)
                if response.status_code == 200:
                    _logger.info("Self-ping successful: %s", health_url)
                else:
                    _logger.warning("Self-ping returned: %s - %s", health_url, response.status_code)
        except Exception as exc:
            _logger.error("Self-ping error: %s", exc)
        await asyncio.sleep(900)


@asynccontextmanager
async def lifespan(app: FastAPI):
    _logger.info("Initializing embedding service (loading all models)...")
    loop = asyncio.get_running_loop()
    await loop.run_in_executor(None, _embedding_service.load_all_models)
    await loop.run_in_executor(None, _embedding_service.load_vision_model)
    _logger.info("Embedding service initialized with dims: %s, vision=%s",
                 _embedding_service.loaded_dimensions, _embedding_service._vision_loaded)

    asyncio.create_task(_self_ping())
    yield
    _logger.info("Shutting down database connection pools...")
    await pool_manager.close_all()


def create_application() -> FastAPI:
    app = FastAPI(
        title=_settings.app_name,
        description="All API Collection - Document extraction, conversion, and database query API.",
        version=_settings.app_version,
        docs_url="/docs",
        redoc_url="/redoc",
        openapi_tags=[
            {"name": "Convert", "description": "Single-file and single-URL conversion"},
            {"name": "Batch", "description": "Bulk conversion of files and URLs"},
            {"name": "System", "description": "Health, info, and supported formats"},
            {"name": "Embeddings", "description": "Text embedding generation using transformer models"},
            {"name": "Verify", "description": "Phone number and identity verification"},
        ],
        lifespan=lifespan,
    )

    app.add_middleware(GZipMiddleware, minimum_size=1000)
    app.add_middleware(
        CORSMiddleware,
        allow_origins=["*"],
        allow_methods=["*"],
        allow_headers=["*"],
    )

    app.include_router(api_v1_router, prefix="/api/v1")

    @app.get("/", include_in_schema=False)
    async def root():
        return {"message": f"{_settings.app_name} v{_settings.app_version} is running"}

    @app.get("/health", include_in_schema=False)
    async def root_health():
        return {"status": "ok", "version": _settings.app_version}

    @app.get("/ping", include_in_schema=False)
    async def ping():
        return {"name": f"{_settings.app_name}", "version": _settings.app_version}

    return app


app = create_application()