edit phase 7 version 1

This commit is contained in:
2026-05-08 07:48:31 +00:00
parent 26d1298cf6
commit 9d04e7484c

View File

@@ -1,93 +1,138 @@
import logging
from fastapi import FastAPI, HTTPException, BackgroundTasks
from pydantic import BaseModel
from typing import List, Optional, Dict, Any
import uvicorn
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
# Thêm thư mục gốc vào PYTHONPATH để tìm thấy các module chat, search, ingestion...
# Đả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
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s")
logger = logging.getLogger("API")
# --- 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="PoC SharePoint RAG API",
description="Hệ thống hỏi đáp nội bộ dựa trên SharePoint và Distributed VLM",
version="1.0.0"
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"
)
# Khởi tạo RAG Engine (Singleton)
# Khi API khởi động, nó sẽ nạp sẵn Embedding Model vào RAM để phản hồi cực nhanh
try:
if settings.opensearch_host == "opensearch":
settings.opensearch_host = "localhost" # Fallback cho Local Dev
rag_engine = RAGEngine()
except Exception as e:
logger.error(f"Không thể khởi động RAG Engine: {e}")
rag_engine = None
# --- 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")
# --- MODELS ---
class ChatRequest(BaseModel):
query: str
history: Optional[List[Dict[str, str]]] = []
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
sources: List[Dict[str, Any]] = []
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")
def health_check():
"""Kiểm tra sức khỏe hệ thống"""
@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": "healthy",
"llm_provider": settings.llm_provider,
"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)
@app.post("/chat", response_model=ChatResponse, tags=["RAG"], status_code=status.HTTP_200_OK)
async def chat_endpoint(request: ChatRequest):
"""
Điểm cuối để thực hiện hỏi đáp (RAG).
Đ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=503, detail="RAG Engine chưa được khởi tạo thành công.")
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:
logger.info(f"Nhận câu hỏi: {request.query}")
result = rag_engine.chat(request.query, history=request.history)
# 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"]
sources=result["sources"],
context_used=result.get("context_used")
)
except Exception as e:
logger.error(f"Lỗi xử lý chat: {e}")
raise HTTPException(status_code=500, detail=str(e))
@app.post("/ingest")
async def start_ingestion(background_tasks: BackgroundTasks):
"""
Kích hoạt quá trình quét SharePoint và nạp dữ liệu vào OpenSearch.
Chạy dưới dạng Background Task để không làm treo API.
"""
# TODO: Kết nối với script sync.py và quy trình extraction
# Ở đây chúng ta sẽ gọi một function xử lý bất đồng bộ
background_tasks.add_task(dummy_ingest_task)
return {"message": "Quá trình đồng bộ dữ liệu đã bắt đầu chạy ngầm."}
async def dummy_ingest_task():
logger.info("Bắt đầu Ingestion task...")
# Sẽ tích hợp logic từ test_rag_pipeline.py vào đây
pass
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__":
# Chạy server tại cổng 8000
uvicorn.run(app, host="0.0.0.0", port=8000)
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)