"""Configuration management."""
from dataclasses import dataclass
from pathlib import Path
import json
import os
@dataclass
class Config:
"""Application configuration."""
zotero_data_dir: Path
chroma_db_path: Path
embedding_model: str
embedding_dimensions: int
chunk_size: int
chunk_overlap: int
gemini_api_key: str | None
@classmethod
def load(cls, path: Path | str | None = None) -> "Config":
"""Load config from file and/or environment."""
if path is not None:
config_path = Path(path).expanduser()
else:
config_path = Path("~/.config/zotero-chunk-rag/config.json").expanduser()
data = {}
if config_path.exists():
with open(config_path) as f:
data = json.load(f)
return cls(
zotero_data_dir=Path(data.get("zotero_data_dir", "~/Zotero")).expanduser(),
chroma_db_path=Path(data.get("chroma_db_path", "~/.local/share/zotero-chunk-rag/chroma")).expanduser(),
embedding_model=data.get("embedding_model", "gemini-embedding-001"),
embedding_dimensions=data.get("embedding_dimensions", 768),
chunk_size=data.get("chunk_size", 400),
chunk_overlap=data.get("chunk_overlap", 100),
gemini_api_key=data.get("gemini_api_key") or os.environ.get("GEMINI_API_KEY"),
)
def validate(self) -> list[str]:
"""Return list of validation errors, empty if valid."""
errors = []
if not self.zotero_data_dir.exists():
errors.append(f"Zotero data dir not found: {self.zotero_data_dir}")
if not (self.zotero_data_dir / "zotero.sqlite").exists():
errors.append(f"Zotero database not found: {self.zotero_data_dir / 'zotero.sqlite'}")
if not self.gemini_api_key:
errors.append("GEMINI_API_KEY not set")
return errors