File size: 3,798 Bytes
6c24b50
 
 
 
 
 
 
 
 
 
89157f5
6c24b50
2ebf97a
 
3379d24
6c24b50
 
 
 
 
3379d24
 
6c24b50
 
 
c25809b
6c24b50
 
 
 
 
 
 
 
 
 
c28ae12
6c24b50
 
 
 
4b54fab
3379d24
4b54fab
 
 
3379d24
2ebf97a
 
 
 
 
 
 
 
 
6c24b50
 
2ebf97a
 
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
97
98
99
100
101
102
103
104
105
106
107
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.core.redis_client import create_redis_client, close_redis
from app.core.scripts import load_scripts
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 384-dim model)...")
    loop = asyncio.get_running_loop()
    await loop.run_in_executor(None, _embedding_service.load_model, 384)
    # await loop.run_in_executor(None, _embedding_service.load_vision_model)  # DISABLED (OOM mitigation)
    _logger.info("Embedding service initialized with dims: %s", _embedding_service.loaded_dimensions)

    redis = create_redis_client(_settings.redis_url) if _settings.redis_url else None
    scripts = await load_scripts(redis) if redis else {}
    app.state.redis = redis
    app.state.scripts = scripts
    if redis:
        _logger.info("Redis and Lua scripts initialized")
    else:
        _logger.warning("Redis not configured, running in degraded mode")

    asyncio.create_task(_self_ping())
    yield
    _logger.info("Shutting down...")
    await close_redis(redis)
    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()