File size: 3,413 Bytes
4b380de
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 os

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

from src.api.routes import chat, chat_stream, health, metrics
from src.common.env_loader import load_project_env


API_VERSION = "0.1.0"

load_project_env()

import sys

if sys.stdout and sys.stdout.encoding.lower() != "utf-8":
    sys.stdout.reconfigure(encoding="utf-8")

from contextlib import asynccontextmanager


@asynccontextmanager
async def lifespan(app: FastAPI):
    """Quản lý vòng đời của ứng dụng FastAPI, bao gồm việc tải trước các mô hình.

    Hàm này được gọi khi ứng dụng FastAPI khởi động và tắt.
    Khi ứng dụng khởi động, nó sẽ tải trước các mô hình học máy nặng
    như dịch vụ trả lời và bộ truy xuất BM25
    để đảm bảo các yêu cầu đầu tiên không bị chậm do quá trình khởi tạo mô hình.
    Điều này giúp tránh hiện tượng "cold start" (khởi động lạnh).

    Args:
        app: Đối tượng ứng dụng FastAPI hiện tại.

    Yields:
        Không có giá trị cụ thể nào được trả về, nhưng nó cho phép ứng dụng chạy
        trong khi các tài nguyên đã được chuẩn bị.
    """
    print("🚀 [FastAPI] Preloading heavy models to prevent Cold Start...")
    from src.api.deps import get_answer_service
    from src.retrieval.core.bm25_retriever import get_bm25_retriever

    # Kích hoạt Singleton ngay từ lúc server khởi động
    get_answer_service()
    get_bm25_retriever()
    print("✅ [FastAPI] All models preloaded successfully!")
    yield


app = FastAPI(
    title="Student Handbook RAG API",
    version=API_VERSION,
    lifespan=lifespan,
)

cors_origins = [
    origin.strip()
    for origin in os.getenv("STUDENT_RAG_CORS_ORIGINS", "").split(",")
    if origin.strip()
]
if cors_origins:
    app.add_middleware(
        CORSMiddleware,
        allow_origins=cors_origins,
        allow_credentials=False,
        allow_methods=["*"],
        allow_headers=["*"],
    )

app.include_router(health.router)
app.include_router(metrics.router)
app.include_router(chat.router)
app.include_router(chat_stream.router)


@app.get("/")
def root() -> dict[str, str]:
    """Trả về thông tin cơ bản về API.

    Khi người dùng truy cập đường dẫn gốc của API ("/"), hàm này sẽ được gọi
    và trả về một dictionary chứa các thông tin hữu ích về dịch vụ,
    như tên, phiên bản, và các đường dẫn đến tài liệu API hoặc kiểm tra sức khỏe.

    Returns:
        Một dictionary chứa các cặp khóa-giá trị mô tả dịch vụ:
        - 'service': Tên định danh của dịch vụ.
        - 'name': Tên đầy đủ của API.
        - 'version': Phiên bản hiện tại của API.
        - 'health': Đường dẫn để kiểm tra trạng thái sức khỏe của API.
        - 'docs': Đường dẫn đến tài liệu API (Swagger UI/Redoc).
    """
    return {
        "service": "student_handbook_rag",
        "name": "Student Handbook RAG API",
        "version": API_VERSION,
        "health": "/health",
        "docs": "/docs",
    }


if __name__ == "__main__":
    import uvicorn

    uvicorn.run("src.api.main:app", host="127.0.0.1", port=8000, reload=True)