"""Configuration from environment variables — no file parsing required for basic use.""" from __future__ import annotations import os from pathlib import Path DATA_DIR = Path(os.environ.get("PAGEPIPER_DATA_DIR", "data")) DATA_DIR.mkdir(parents=True, exist_ok=True) DB_PATH = str(DATA_DIR / "pagepiper.db") VEC_DB_PATH = str(DATA_DIR / "pagepiper_vecs.db") WATCH_DIR = Path(os.environ.get("PAGEPIPER_WATCH_DIR", "books")) VEC_DIMENSIONS = int(os.environ.get("PAGEPIPER_EMBED_DIMS", "1024")) LOCAL_USER_ID = "__local__" CF_LICENSE_KEY = os.environ.get("CF_LICENSE_KEY") # Priority: GPU_SERVER_URL env var -> CF_ORCH_URL env var (backward compat) # -> https://orch.circuitforge.tech when CF_LICENSE_KEY is present (Paid+) # Resolved value is written back to os.environ["CF_ORCH_URL"] so every existing # caller that reads os.environ.get("CF_ORCH_URL") directly (get_llm_config below, # app/api/chat.py) sees the right URL without any further changes. GPU_SERVER_URL = ( os.environ.get("GPU_SERVER_URL") or os.environ.get("CF_ORCH_URL") or ("https://orch.circuitforge.tech" if CF_LICENSE_KEY else None) ) if GPU_SERVER_URL: os.environ["CF_ORCH_URL"] = GPU_SERVER_URL def user_data_dir(user_id: str) -> Path: """Return (and create) the per-user data directory under DATA_DIR/users/.""" d = DATA_DIR / "users" / user_id d.mkdir(parents=True, exist_ok=True) return d def get_llm_config() -> dict | None: """Build LLMRouter config from env vars. Returns None only when neither PAGEPIPER_OLLAMA_URL nor CF_ORCH_URL is set. CF_ORCH_URL alone is sufficient — the coordinator resolves the service URL at allocation time so PAGEPIPER_OLLAMA_URL becomes optional. """ url = os.environ.get("PAGEPIPER_OLLAMA_URL", "").strip() orch_url = os.environ.get("CF_ORCH_URL", "").strip() if not url and not orch_url: return None chat_model = os.environ.get("PAGEPIPER_CHAT_MODEL", "mistral:7b") _base_url = "" if url: _clean = url.rstrip("/") _base_url = _clean if _clean.endswith("/v1") else _clean + "/v1" backend: dict = { "type": "openai_compat", "base_url": _base_url, "model": chat_model, "embedding_model": os.environ.get("PAGEPIPER_EMBED_MODEL", "nomic-embed-text"), "supports_images": False, } if orch_url: backend["cf_orch"] = { "service": os.environ.get("PAGEPIPER_ORCH_SERVICE", "ollama"), "model_candidates": [chat_model], "ttl_s": 3600, } return { "fallback_order": ["ollama"], "backends": {"ollama": backend}, }