dhammawatthumpra's picture
feat: implement Search Architecture V3 (Hybrid search with Qwen query transformation, batch embeddings, batch Qdrant searches, and optimized CPU reranking)
c6f014f
Raw
History Blame Contribute Delete
3.12 kB
from pydantic_settings import BaseSettings
from functools import lru_cache
import os
from pathlib import Path
# Calculate the project root (tipitaka-api folder)
PROJECT_ROOT = Path(__file__).resolve().parent.parent
# Detect Data Directory
# 1. Environment variable DATA_DIR
# 2. Docker standard path /app/data (Only if on Linux/Docker)
# 3. Local project 'data' folder
# Detect Data Directory
if os.getenv("DATA_DIR"):
DATA_DIR_DEFAULT = os.getenv("DATA_DIR")
elif os.name != 'nt' and os.path.exists("/app/data"):
DATA_DIR_DEFAULT = "/app/data"
else:
# Use 'data' folder in project root as default
DATA_DIR_DEFAULT = str(PROJECT_ROOT / "data")
DATA_DIR = Path(DATA_DIR_DEFAULT)
class Settings(BaseSettings):
"""Application settings β€” loaded from .env file or environment variables."""
# ── LLM Provider ──
LLM_API_KEY: str = ""
LLM_BASE_URL: str = "https://api.deepseek.com"
LLM_MODEL_FAST: str = "deepseek-chat"
LLM_MODEL_REASONER: str = "deepseek-reasoner"
# ── Paths ──
DATA_DIR: str = str(DATA_DIR)
DATABASE_PATH: str = ""
QDRANT_PATH: str = ""
SNAPSHOT_DIR: str = ""
QDRANT_URL: str | None = None
ST_EMBED_MODEL: str = "jinaai/jina-embeddings-v5-text-small-retrieval"
QDRANT_COLLECTION: str = "tipitaka_chunks"
def __init__(self, **values):
super().__init__(**values)
data_path = Path(self.DATA_DIR)
rag_root = PROJECT_ROOT.parent.parent # F:\_Ai\Tipitaka-AI-Expert\RAG
# Initialize paths if not explicitly provided
if not self.DATABASE_PATH:
# Priority: 1. data/tipitaka_mcu.db, 2. RAG_ROOT/tipitaka_mcu.db
local_db = data_path / "tipitaka_mcu.db"
root_db = rag_root / "tipitaka_mcu.db"
if local_db.exists():
self.DATABASE_PATH = str(local_db)
elif root_db.exists():
self.DATABASE_PATH = str(root_db)
else:
self.DATABASE_PATH = str(local_db) # Fallback
if not self.QDRANT_PATH:
self.QDRANT_PATH = str(data_path / "qdrant_storage")
if not self.SNAPSHOT_DIR:
# Priority: 1. DATA_DIR/snapshots, 2. RAG_ROOT/snapshots
local_snapshots = data_path / "snapshots"
root_snapshots = rag_root / "snapshots"
if local_snapshots.exists():
self.SNAPSHOT_DIR = str(local_snapshots)
elif root_snapshots.exists():
self.SNAPSHOT_DIR = str(root_snapshots)
else:
self.SNAPSHOT_DIR = str(local_snapshots) # Fallback
# ── CORS ──
CORS_ORIGINS: str = "*"
# ── Discord Webhook (health alerts) ──
DISCORD_WEBHOOK_URL: str = ""
DEBUG: bool = True
PORT: int = 8000
# ── Production static file serving ──
SERVE_STATIC: bool = False
STATIC_DIR: str = ""
class Config:
env_file = ".env"
env_file_encoding = "utf-8"
extra = "ignore"
@lru_cache()
def get_settings() -> Settings:
return Settings()