import os # Embedding model (late-interaction / ColBERT-style page embeddings). # Revisions are pinned because both models load trust_remote_code; bump # deliberately after reviewing upstream changes. COLEMBED_MODEL_ID = os.environ.get( "COLEMBED_MODEL_ID", "nvidia/nemotron-colembed-vl-4b-v2" ) COLEMBED_REVISION = os.environ.get( "COLEMBED_REVISION", "0ed152d91f8ad4c5d48296b51c220f686641a398" ) # sdpa works for this model and needs no extra wheels on ZeroGPU; set # COLEMBED_ATTN=flash_attention_2 if flash-attn is installed. COLEMBED_ATTN = os.environ.get("COLEMBED_ATTN", "sdpa") # Page rendering (both for embedding at index time and for the answering model). RENDER_DPI = 150 # Indexing: pages embedded per ZeroGPU call, and model batch size within a call. # Chunking keeps each GPU call well under its duration limit; progress is # reported between chunks. Bigger chunks mean fewer ZeroGPU queue waits per # manual (~0.5s/page observed, so 64 pages ≈ 35-40s of a 240s budget — a # 1000-page manual is ~16 GPU calls). EMBED_PAGES_PER_CALL = 64 EMBED_BATCH_SIZE = 8 EMBED_GPU_DURATION = 240 # Retrieval: MaxSim is computed on GPU over fixed-size batches of pages # streamed from the on-disk store. SCORE_PAGES_PER_BATCH = 32 DEFAULT_TOP_K = 3 MAX_TOP_K = 5 # Answering model (runs locally on ZeroGPU). MINICPM_MODEL_ID = os.environ.get("MINICPM_MODEL_ID", "openbmb/MiniCPM-V-4_5") MINICPM_REVISION = os.environ.get( "MINICPM_REVISION", "fd3209b2e0580e346fc33d2c6f85b6e9332eecda" ) ANSWER_MAX_NEW_TOKENS = 2048 # One ZeroGPU call covers the whole question: query embedding + MaxSim + # page rendering + answer generation. ASK_GPU_DURATION = 120 # Manual stores. HF Spaces persistent storage mounts at /data; fall back to a # local directory for development. Pre-indexed manuals (large PDFs embedded # offline, see scripts/index_local.py) are synced from the library dataset at # startup; in-app uploads are indexed on ZeroGPU and page-capped to protect # the Space's quota. _DATA_ROOT = os.environ.get("DATA_ROOT") or ( "/data" if os.path.isdir("/data") else os.path.join(os.path.dirname(os.path.dirname(__file__)), "data") ) PREINDEXED_DIR = os.path.join(_DATA_ROOT, "preindexed") UPLOADS_DIR = os.path.join(_DATA_ROOT, "uploads") LIBRARY_DATASET_ID = os.environ.get( "LIBRARY_DATASET_ID", "build-small-hackathon/repair-guy-library" ) MAX_UPLOAD_PAGES = int(os.environ.get("MAX_UPLOAD_PAGES", "50"))