"""Central configuration: model ids, storage paths, and pipeline constants. Everything tunable lives here so models/limits can be swapped in one place. """ from __future__ import annotations import os from pathlib import Path # --- Models (all served via HF Inference Providers, billed to the user's token) --- # Fast, capable general model used for reasoning-style tasks (search terms, image prompts). MODEL_REASONING = os.environ.get("MODEL_REASONING", "openai/gpt-oss-120b") # Strong long-form writer for the actual blog post, with a fallback if unavailable. MODEL_WRITER = os.environ.get("MODEL_WRITER", "deepseek-ai/DeepSeek-V3-0324") MODEL_WRITER_FALLBACK = os.environ.get("MODEL_WRITER_FALLBACK", "Qwen/Qwen2.5-72B-Instruct") # Text-to-image model (as requested). Generated via remote Inference Providers — this # Space has no GPU, so images are always produced by serverless inference calls. MODEL_IMAGE = os.environ.get("MODEL_IMAGE", "black-forest-labs/FLUX.1-schnell") # Providers that currently serve FLUX.1-schnell, tried in order after auto-routing. # One user-token InferenceClient per provider; billing follows the token. IMAGE_PROVIDERS = [ p.strip() for p in os.environ.get( "IMAGE_PROVIDERS", "fal-ai,nscale,together,hf-inference,replicate,wavespeed" ).split(",") if p.strip() ] # Vision-language model for captioning generated images. MODEL_VISION = os.environ.get("MODEL_VISION", "Qwen/Qwen2.5-VL-72B-Instruct") # --- External services --- SEARXNG_URL = os.environ.get("SEARXNG_URL", "http://127.0.0.1:8080") OPR_API_KEY = os.environ.get("OPR_API_KEY", "") OPR_ENDPOINT = "https://openpagerank.com/api/v1.0/getPageRank" # --- Pipeline constants --- N_SEARCH_TERMS = int(os.environ.get("N_SEARCH_TERMS", "5")) # queries generated by the LLM TOP_N = int(os.environ.get("TOP_N", "25")) # results ranked by OpenPageRank TOP_K = int(os.environ.get("TOP_K", "5")) # top pages used as source material N_IMAGES = int(os.environ.get("N_IMAGES", "3")) # illustrations per post DEFAULT_WORD_COUNT = int(os.environ.get("DEFAULT_WORD_COUNT", "1200")) # target post length SOURCE_CHAR_CAP = int(os.environ.get("SOURCE_CHAR_CAP", "4000")) # chars kept per source page HTTP_TIMEOUT = int(os.environ.get("HTTP_TIMEOUT", "20")) # seconds for outbound HTTP def _resolve_data_dir() -> Path: """Prefer HF persistent storage (/data); fall back to a local dir if not writable.""" candidate = Path(os.environ.get("DATA_DIR", "/data")) try: candidate.mkdir(parents=True, exist_ok=True) probe = candidate / ".write_test" probe.write_text("ok", encoding="utf-8") probe.unlink() return candidate except Exception: fallback = Path(__file__).resolve().parent.parent / ".cache" fallback.mkdir(parents=True, exist_ok=True) return fallback DATA_DIR = _resolve_data_dir() CACHE_DIR = DATA_DIR / "cache" OUT_DIR = DATA_DIR / "out" CACHE_DIR.mkdir(parents=True, exist_ok=True) OUT_DIR.mkdir(parents=True, exist_ok=True)