planning-agent / database.py
Youmei295's picture
Upload 9 files
f23f939 unverified
Raw
History Blame Contribute Delete
6.23 kB
import os
import json
import logging
from pathlib import Path
import chromadb
from supabase import create_client, Client
logger = logging.getLogger(__name__)
CURRENT_DIR = Path(os.path.dirname(os.path.abspath(__file__)))
DB_PATH = CURRENT_DIR / "travel_db"
# ===========================================================================
# CẤU HÌNH KẾT NỐI SUPABASE
# ===========================================================================
# Lấy từ Environment Variables (Secrets trên Hugging Face)
SUPABASE_URL = os.getenv("SUPABASE_URL")
SUPABASE_KEY = os.getenv("SUPABASE_KEY")
if not SUPABASE_URL or not SUPABASE_KEY:
logger.warning("⚠️ SUPABASE_URL hoặc SUPABASE_KEY chưa được thiết lập. Hãy kiểm tra Secrets trên Hugging Face.")
try:
if SUPABASE_URL and SUPABASE_KEY:
supabase_client: Client = create_client(SUPABASE_URL, SUPABASE_KEY)
logger.info("🌐 Đã kết nối tới Supabase Cloud thành công!")
except Exception as e:
logger.error(f"❌ Không thể khởi tạo kết nối Supabase: {e}")
# ---------------------------------------------------------------------------
# 1. Khởi tạo ChromaDB Client (Singleton - Giữ nguyên bộ nhớ Vector)
# ---------------------------------------------------------------------------
chroma_client = None
saigon_collection = None
try:
chroma_client = chromadb.PersistentClient(path=str(DB_PATH))
saigon_collection = chroma_client.get_collection(name="saigon_places")
except Exception as e:
logger.error(f"Lỗi ChromaDB: {e}. Vui lòng đảm bảo đã tạo Vector DB thành công!")
# ---------------------------------------------------------------------------
# 2. Bộ nạp dữ liệu từ Supabase thay thế hoàn toàn cho JSONL cũ
# ---------------------------------------------------------------------------
_food_db, _act_db, _drink_db = None, None, None
def _load_supabase_table_to_dict(table_name: str) -> dict:
""" Tải toàn bộ dữ liệu từ một bảng trên Supabase và chuẩn hóa thuộc tính """
db = {}
logger.info(f"📥 Đang tải dữ liệu thời gian thực từ bảng Supabase: '{table_name}'...")
page_size = 1000
offset = 0
all_records = []
# Thực hiện phân trang (Pagination) để kéo trọn vẹn dữ liệu từ Cloud về
while True:
try:
response = supabase_client.table(table_name).select("*").range(offset, offset + page_size - 1).execute()
data = response.data
if not data:
break
all_records.extend(data)
if len(data) < page_size:
break
offset += page_size
except Exception as e:
logger.error(f"⚠️ Không thể đọc bảng '{table_name}' từ Supabase: {e}")
break
# Chuẩn hóa dữ liệu sang cấu trúc Dictionary để các Agent gọi .get() tìm kiếm nhanh
for record in all_records:
# Cơ chế Fallback thông minh: Chấp nhận cả viết hoa (Name) lẫn viết thường (name) từ giao diện
name = record.get("Name") or record.get("name")
if not name:
continue
address = record.get("Address") or record.get("address") or "N/A"
rating = record.get("Rating") or record.get("rating")
avg_price = record.get("avg_price")
district = record.get("district") or "N/A"
place_id = record.get("id") # <--- LẤY ID TỪ SUPABASE ĐỂ LÀM external_place_id
# Tạo cấu trúc đồng bộ tuyệt đối cho tất cả Agent
normalized = {
"id": place_id,
"Name": name,
"name": name,
"Address": address,
"address": address,
"Rating": float(rating) if rating is not None else 0.0,
"rating": float(rating) if rating is not None else 0.0,
"district": district,
"avg_price": int(avg_price) if avg_price is not None else 100000,
"category": record.get("category", ""),
"Latitude": record.get("Latitude") or record.get("latitude"),
"Longitude": record.get("Longitude") or record.get("longitude"),
}
# Chuẩn hóa trường URL ảnh (Xử lý cả dạng mảng lẫn chuỗi đơn)
url_val = record.get("URL") or record.get("url") or record.get("image_url")
if isinstance(url_val, list):
normalized["URL"] = url_val
elif isinstance(url_val, str):
normalized["URL"] = [url_val]
else:
normalized["URL"] = [None]
# Chuẩn hóa trường tags JsonB phức tạp
tags = record.get("tags", {})
if isinstance(tags, str):
try: tags = json.loads(tags)
except: tags = {}
normalized["tags"] = tags if isinstance(tags, dict) else {}
# Lưu vào dict bộ nhớ tạm với key là tên địa điểm
db[name] = normalized
logger.info(f" ↳ Kết nối thành công! Đã đồng bộ {len(db)} địa điểm từ '{table_name}'.")
return db
# ---------------------------------------------------------------------------
# Các cổng xuất dữ liệu công khai (API Interface cho các Agent sử dụng)
# ---------------------------------------------------------------------------
def get_food_db() -> dict:
global _food_db
if _food_db is None:
# Cập nhật tên bảng thức ăn chính xác
_food_db = _load_supabase_table_to_dict("places_food")
return _food_db
def get_act_db() -> dict:
global _act_db
if _act_db is None:
# Cập nhật tên bảng vui chơi chính xác
_act_db = _load_supabase_table_to_dict("places_activity")
return _act_db
def get_drink_db() -> dict:
global _drink_db
if _drink_db is None:
# Cập nhật tên bảng đồ uống chính xác
_drink_db = _load_supabase_table_to_dict("places_drink")
return _drink_db