Spaces:
Sleeping
Sleeping
| import uvicorn | |
| from fastapi import FastAPI, Body | |
| from sentence_transformers import SentenceTransformer | |
| from sklearn.metrics.pairwise import cosine_similarity | |
| import numpy as np | |
| # Tải model 1 lần duy nhất khi Server khởi động | |
| model = SentenceTransformer('all-MiniLM-L6-v2') | |
| print("✅ Model đã sẵn sàng!") | |
| product_db = { | |
| "ids": [], | |
| "vectors": None, | |
| "cached_result": {} | |
| } | |
| #App | |
| app = FastAPI() | |
| async def home(): | |
| return {"status": "AI Server is RUNNING", "model": "all-MiniLM-L6-v2"} | |
| async def status(): | |
| count = len(product_db["ids"]) | |
| return {"count": count} | |
| async def vectorize_products(payload: dict = Body(...)): | |
| products = payload.get("products", []) | |
| k_item = payload.get("k_item",5) | |
| if not products: | |
| return {"error": "Danh sách sản phẩm trống"} | |
| product_names = [x['name'] for x in products] | |
| product_ids = [x['id'] for x in products] | |
| # Tính ra vector | |
| vectors = model.encode(product_names) | |
| product_db["vectors"] = vectors # lưu lại | |
| product_db["ids"] = product_ids | |
| sim_matrix = cosine_similarity(vectors) | |
| res = {} | |
| # Duyệt qua từng dòng của ma trận | |
| for i in range(len(product_ids)): | |
| current_id = product_ids[i] | |
| scores = sim_matrix[i] | |
| # Sắp xếp index giảm dần | |
| sorted_indices = np.argsort(scores)[::-1] | |
| # Lấy top k (bỏ qua index 0 là chính nó) | |
| top_indices = sorted_indices[1 : k_item + 1] | |
| # Map từ Index -> ID thật | |
| similar_ids = [product_ids[idx] for idx in top_indices] | |
| # Lưu vào dict kết quả (Key phải là string) | |
| res[str(current_id)] = [int(x) for x in similar_ids] | |
| product_db['cached_result'] = res | |
| # Trả về đúng cấu trúc AiResponse bên Java | |
| return {"result": res} | |
| async def refresh_cache(): | |
| return {"result": product_db.get("cached_result", {})} | |
| async def add_product(payload: dict = Body(...)): | |
| # 1. Lấy dữ liệu đầu vào | |
| new_id = payload.get("id") | |
| new_name = payload.get("name") | |
| if new_id is None or not new_name: | |
| return {"error": "Dữ liệu không hợp lệ"} | |
| print(f"⚡ Đang thêm sản phẩm mới: ID {new_id} - {new_name}") | |
| # 2. Vector hóa CHỈ 1 SẢN PHẨM MỚI (Cực nhanh) | |
| # Kết quả là 1 vector (Shape: 1 x 768) | |
| new_vector = model.encode([new_name]) | |
| # 3. Lấy kho dữ liệu cũ từ RAM | |
| existing_vectors = product_db["vectors"] | |
| existing_ids = product_db["ids"] | |
| cached_result = product_db["cached_result"] | |
| # Trường hợp chưa Init dữ liệu thì coi như init mới | |
| if existing_vectors is None or len(existing_ids) == 0: | |
| return {"result": {}} | |
| # 4. TÍNH TOÁN TƯƠNG ĐỒNG (Điểm mấu chốt của tốc độ) | |
| # So sánh vector mới với toàn bộ vector cũ | |
| # cosine_similarity(new, old) -> Trả về mảng điểm số | |
| sim_scores = cosine_similarity(new_vector, existing_vectors)[0] | |
| # 5. Tìm Top K cho sản phẩm mới | |
| k_item = 5 | |
| # Sắp xếp giảm dần và lấy top k | |
| top_indices = np.argsort(sim_scores)[::-1][:k_item] | |
| # Map từ index sang ID thật | |
| similar_ids = [int(existing_ids[idx]) for idx in top_indices] | |
| # 6. CẬP NHẬT DATABASE TRONG RAM (Để lần sau thêm tiếp còn dùng) | |
| # Thêm ID mới vào danh sách | |
| product_db["ids"].append(new_id) | |
| # Nối vector mới vào ma trận cũ (Append vào dòng cuối) | |
| product_db["vectors"] = np.vstack([existing_vectors, new_vector]) | |
| # Cập nhật Cache kết quả | |
| result_map = {str(new_id): similar_ids} | |
| # (Optional) Update ngược: Nếu sản phẩm cũ thấy sản phẩm mới này giống nó | |
| # Đoạn này làm phức tạp hơn, tạm thời ta chỉ quan tâm chiều xuôi: New -> Old | |
| cached_result.update(result_map) | |
| print(f"✅ Đã thêm xong ID {new_id}. Tìm thấy {len(similar_ids)} sản phẩm tương tự.") | |
| # Trả về kết quả để Java update cache | |
| return {"result": result_map} | |
| if __name__ == "__main__": | |
| # Hugging Face yêu cầu bắt buộc chạy ở port 7860 | |
| uvicorn.run(app, host="0.0.0.0", port=7860) |