139 lines
4.9 KiB
Python
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)
|