Files
poc_system/api/main.py
2026-05-08 07:48:31 +00:00

139 lines
4.9 KiB
Python

import logging
import sys
import os
from enum import Enum
from typing import List, Optional, Dict, Any
from fastapi import FastAPI, HTTPException, BackgroundTasks, status
from pydantic import BaseModel, Field, validator
import uvicorn
# Đảm bảo đường dẫn module
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from chat.rag_engine import RAGEngine
from core.config import settings
# --- Cấu hình Logging chuyên nghiệp ---
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(name)s: %(message)s",
datefmt="%Y-%m-%d %H:%M:%S"
)
logger = logging.getLogger("RAG_API")
app = FastAPI(
title="Enterprise SharePoint RAG API",
description="Hệ thống hỏi đáp AI nội bộ sử dụng kiến trúc Modular Providers và Distributed VLM.",
version="1.1.0",
docs_url="/docs",
redoc_url="/redoc"
)
# --- Singleton Engine Instance ---
rag_engine = None
@app.on_event("startup")
async def startup_event():
global rag_engine
try:
logger.info(f"Đang khởi tạo RAG Engine với Provider: {settings.llm_provider}")
# Đảm bảo host OpenSearch đúng trong môi trường dev
if settings.opensearch_host == "opensearch" and os.environ.get("ENV") != "docker":
settings.opensearch_host = "localhost"
rag_engine = RAGEngine()
# Thông báo sẵn sàng kèm địa chỉ truy cập
host = "0.0.0.0"
port = 8000
logger.info("="*60)
logger.info("🚀 RAG ENGINE ĐÃ SẴN SÀNG PHỤC VỤ!")
logger.info(f"🔗 API Endpoint: http://localhost:{port}")
logger.info(f"📖 Swagger UI: http://localhost:{port}/docs")
logger.info(f"📊 Health Check: http://localhost:{port}/health")
logger.info("="*60)
except Exception as e:
logger.critical(f"❌ THẤT BẠI khi khởi động RAG Engine: {e}")
# --- SCHEMAS ---
class ChatRole(str, Enum):
user = "user"
assistant = "assistant"
system = "system"
class ChatHistoryItem(BaseModel):
role: ChatRole = Field(..., description="Vai trò của người gửi (user/assistant)")
content: str = Field(..., min_length=1, description="Nội dung tin nhắn")
class ChatRequest(BaseModel):
query: str = Field(
...,
min_length=2,
max_length=1000,
example="Quy trình bảo trì thiết bị là gì?",
description="Câu hỏi của người dùng"
)
history: List[ChatHistoryItem] = Field(
default_factory=list,
description="Lịch sử cuộc trò chuyện để duy trì ngữ cảnh"
)
class SourceCitation(BaseModel):
file_name: str
page: int
url: Optional[str] = None
class ChatResponse(BaseModel):
answer: str = Field(..., description="Câu trả lời từ AI")
sources: List[SourceCitation] = Field(default_factory=list, description="Danh sách các nguồn trích dẫn từ tài liệu")
context_used: Optional[str] = Field(None, description="Ngữ cảnh thực tế đã được trích xuất từ VectorDB (Dùng cho Debug/UI)")
# --- ENDPOINTS ---
@app.get("/health", tags=["System"])
async def health_check():
"""Kiểm tra trạng thái kết nối tới OpenSearch và LLM."""
return {
"status": "online" if rag_engine else "offline",
"engine_ready": rag_engine is not None,
"config": {
"provider": settings.llm_provider,
"opensearch_host": settings.opensearch_host
}
}
@app.post("/chat", response_model=ChatResponse, tags=["RAG"], status_code=status.HTTP_200_OK)
async def chat_endpoint(request: ChatRequest):
"""
Điểm cuối xử lý hội thoại RAG.
Hệ thống sẽ tự động trích xuất ngữ cảnh từ OpenSearch và sử dụng Provider đã cấu hình để trả lời.
"""
if not rag_engine:
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Hệ thống RAG đang khởi động hoặc gặp sự cố kết nối Database."
)
try:
# Chuyển đổi ChatHistoryItem sang format dict cho RAGEngine
history_data = [item.dict() for item in request.history]
logger.info(f"Xử lý truy vấn: {request.query[:50]}...")
result = rag_engine.chat(request.query, history=history_data)
return ChatResponse(
answer=result["answer"],
sources=result["sources"],
context_used=result.get("context_used")
)
except Exception as e:
logger.error(f"Lỗi thực thi RAG: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Đã xảy ra lỗi nội bộ trong quá trình xử lý ngôn ngữ."
)
if __name__ == "__main__":
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)