File size: 2,762 Bytes
d8423e6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
"""
FastAPI application factory with lifespan management.

The lifespan context manager handles:
    - Startup: initialize Qdrant, embedding, LLM, reranker services
    - Shutdown: close connections gracefully

Services are stored in app.state and injected into route handlers.
This avoids global state and makes the app testable.
"""

from __future__ import annotations

from contextlib import asynccontextmanager
from typing import AsyncGenerator

import structlog
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from ahcgr import __app_name__, __version__
from ahcgr.api.middleware import RequestLoggingMiddleware
from ahcgr.api.routes import router
from ahcgr.pipeline.p1b_entity import EntityIndex
from ahcgr.services.embedding import EmbeddingService
from ahcgr.services.llm import LLMService
from ahcgr.services.qdrant_client import QdrantService
from ahcgr.services.reranker import RerankerService
from ahcgr.utils.logging import setup_logging

logger = structlog.get_logger("ahcgr.api.server")


@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
    """
    Application lifespan: initialize shared services, then close them cleanly.
    """
    setup_logging()
    logger.info("startup_begin", app=__app_name__, version=__version__)

    qdrant = QdrantService()
    embedding = EmbeddingService()
    llm = LLMService()
    reranker = RerankerService()

    entity_index = EntityIndex()
    entity_index.build_from_qdrant(qdrant)

    app.state.qdrant = qdrant
    app.state.embedding = embedding
    app.state.llm = llm
    app.state.reranker = reranker
    app.state.entity_index = entity_index

    health = qdrant.health_check()
    logger.info("qdrant_health", **health)

    logger.info(
        "startup_complete",
        services=["qdrant", "embedding", "llm", "reranker", "entity_index"],
        entity_acts=entity_index.act_count,
    )

    yield

    logger.info("shutdown_begin")
    qdrant.close()
    logger.info("shutdown_complete")


def create_app() -> FastAPI:
    """
    Build the FastAPI application.
    """
    app = FastAPI(
        title=__app_name__,
        version=__version__,
        description=(
            "AHCGR - Adaptive Hierarchical Confidence-Guided Retrieval. "
            "A 7-phase Indian legal inference engine with hybrid search, "
            "RAPTOR tree traversal, and multi-gate quality evaluation."
        ),
        lifespan=lifespan,
    )

    app.add_middleware(
        CORSMiddleware,
        allow_origins=["*"],
        allow_credentials=True,
        allow_methods=["*"],
        allow_headers=["*"],
    )

    app.add_middleware(RequestLoggingMiddleware)
    app.include_router(router, prefix="/api/v1")

    return app