import os from dataclasses import dataclass from pathlib import Path from dotenv import load_dotenv DEFAULT_LLM_MODEL_FILE = "Llama-3.2-1B-Instruct-Q4_K_M.gguf" def read_int(name, default_value): raw_value = os.getenv(name, str(default_value)).strip() try: return int(raw_value) except ValueError: return default_value def resolve_path(root_dir, raw_value): path = Path(raw_value) if path.is_absolute(): return path return root_dir / path def resolve_llm_model_path(models_dir, model_file): if model_file: requested_path = models_dir / model_file if requested_path.exists(): return requested_path default_path = models_dir / DEFAULT_LLM_MODEL_FILE if default_path.exists(): return default_path gguf_files = sorted(models_dir.glob("*.gguf")) if gguf_files: return gguf_files[0] return models_dir / (model_file or DEFAULT_LLM_MODEL_FILE) def make_local_model_dir(hf_cache_dir, repo_id): safe_name = repo_id.replace("/", "--") return hf_cache_dir / "downloads" / safe_name def resolve_hf_source(hf_cache_dir, repo_id): local_dir = make_local_model_dir(hf_cache_dir, repo_id) if local_dir.exists(): return local_dir return repo_id @dataclass(frozen=True) class Settings: project_root: Path models_dir: Path hf_cache_dir: Path data_dir: Path llm_model_file: str llm_model_path: Path context_size: int live_asr_model_id: str live_asr_source: str | Path batch_asr_model_id: str batch_asr_source: str | Path image_model_id: str image_model_source: str | Path tts_en_model_id: str tts_en_source: str | Path tts_hi_model_id: str tts_hi_source: str | Path asr_compute_type: str embedding_model_id: str tesseract_cmd: str def load_settings(): project_root = Path(__file__).resolve().parent.parent load_dotenv(project_root / ".env") models_dir = resolve_path(project_root, os.getenv("EDUAI_MODELS_DIR", "models")) hf_cache_dir = resolve_path(project_root, os.getenv("EDUAI_HF_CACHE_DIR", "models/hf_cache")) data_dir = resolve_path(project_root, os.getenv("EDUAI_DATA_DIR", "data")) models_dir.mkdir(parents=True, exist_ok=True) hf_cache_dir.mkdir(parents=True, exist_ok=True) data_dir.mkdir(parents=True, exist_ok=True) os.environ.setdefault("HF_HOME", str(hf_cache_dir)) os.environ.setdefault("HF_HUB_CACHE", str(hf_cache_dir / "hub")) os.environ.setdefault("TOKENIZERS_PARALLELISM", "false") llm_model_file = os.getenv("EDUAI_LLM_MODEL_FILE", DEFAULT_LLM_MODEL_FILE).strip() live_asr_model_id = os.getenv("EDUAI_LIVE_ASR_MODEL_ID", "Systran/faster-whisper-small").strip() batch_asr_model_id = os.getenv("EDUAI_BATCH_ASR_MODEL_ID", "Systran/faster-whisper-large-v3").strip() image_model_id = os.getenv("EDUAI_IMAGE_MODEL_ID", "HuggingFaceTB/SmolVLM-256M-Instruct").strip() tts_en_model_id = os.getenv("EDUAI_TTS_EN_MODEL_ID", "facebook/mms-tts-eng").strip() tts_hi_model_id = os.getenv("EDUAI_TTS_HI_MODEL_ID", "facebook/mms-tts-hin").strip() embedding_model_id = os.getenv("EDUAI_EMBEDDING_MODEL_ID", "sentence-transformers/all-MiniLM-L6-v2").strip() return Settings( project_root=project_root, models_dir=models_dir, hf_cache_dir=hf_cache_dir, data_dir=data_dir, llm_model_file=llm_model_file, llm_model_path=resolve_llm_model_path(models_dir, llm_model_file), context_size=read_int("EDUAI_CONTEXT_SIZE", 8192), live_asr_model_id=live_asr_model_id, live_asr_source=resolve_hf_source(hf_cache_dir, live_asr_model_id), batch_asr_model_id=batch_asr_model_id, batch_asr_source=resolve_hf_source(hf_cache_dir, batch_asr_model_id), image_model_id=image_model_id, image_model_source=resolve_hf_source(hf_cache_dir, image_model_id), tts_en_model_id=tts_en_model_id, tts_en_source=resolve_hf_source(hf_cache_dir, tts_en_model_id), tts_hi_model_id=tts_hi_model_id, tts_hi_source=resolve_hf_source(hf_cache_dir, tts_hi_model_id), asr_compute_type=os.getenv("EDUAI_ASR_COMPUTE_TYPE", "int8").strip() or "int8", embedding_model_id=embedding_model_id, tesseract_cmd=os.getenv("TESSERACT_CMD", "").strip(), )