File size: 3,072 Bytes
31fa536
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d3ee9ee
 
31fa536
d3ee9ee
 
 
 
 
 
 
31fa536
 
 
 
 
 
 
 
 
 
 
 
 
2559985
31fa536
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
"""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)