"""Config loading for the Boardroom Map orchestrator. Defaults mirror startos/file-models/config.ts. The StartOS actions only persist the fields the user actually touched, and Python (unlike the zod schema) does not auto-fill defaults — so we apply them here. Keep in sync with the zod schema. """ from __future__ import annotations import os import spark_client as sc DATA_DIR = os.environ.get("BM_DATA_DIR", "/data") HF_TOKEN_PATH = os.path.join(DATA_DIR, "secrets", "hf_token") BDEF_PATH = os.path.join(os.path.dirname(os.path.abspath(__file__)), "bdef.md") # Scoring weights (composite 0-100 = quant 60 + qual 40 - penalties). # Every knob the deterministic scorer uses lives here so the user can retune # without a rebuild. Keep flat: StartOS action inputs are flat number fields. WEIGHTS_DEFAULTS = { "profitabilityKpi": 30, # profitability KPI attainment bucket "otherKpi": 20, # non-profitability measurable KPI bucket "forecastIntegrity": 10, # deck N actuals vs deck N-1 stated targets "qualCategoryMax": 5, # each BDEF category A-H maxes at this (8x5=40) "redFlagCap": 15, # max total penalty "kpiCreditFloor": 0.5, # actual/target ratio below which credit = 0 "droppedKpiPenalty": 2, # severity of a KPI that silently disappeared "droppedKpiMax": 3, # count at most this many dropped-KPI flags "evidenceFullCredit": 400, # quote chars for full qualitative weight "singleSourceFlagFactor": 0.5, # damping for flags raised by one source only } CONFIG_DEFAULTS = { # Spark connection "primarySparkHost": "", "primarySparkUser": "nvidia", "sshPort": 22, "secondarySparkHost": None, "useBothSparks": False, "headInternalHost": "127.0.0.1", "remoteWorkDir": "/home/nvidia/boardroom-map", # Images "servingImage": "boardroom-vllm:latest", "graderImage": "boardroom-grader:latest", # Serving "gpuMemoryUtilization": "0.85", "maxModelLen": 32768, "toolCallParser": "hermes", "proxyPort": 4000, "maxConcurrentModels": 1, "models": [ {"alias": "grader-a", "hfModel": "Qwen/Qwen3-32B-FP8", "spark": "primary", "port": 8001}, ], # Grading panel "graders": [ {"name": "munger-lens", "model": "grader-a", "persona": "", "temperature": None}, ], # Which catalog model runs the stage-1 structured extractor ("" = first model) "extractorModel": "", # Grading job settings "bdefOverride": "", # non-empty replaces the baked-in bdef.md rubric "weights": dict(WEIGHTS_DEFAULTS), "networkMode": "airgapped", "searxngUrl": "", "adjudicatorEnabled": True, "adjudicatorModel": "", "adjudicatorPersona": "", "wipeRemoteDocs": True, "autoRunOnDrop": False, "networkName": "boardroom-net", # Portfolio companies (authoritative source of pinned targets / aliases). # pinnedTargets: [{kpi, target, unit, direction: gte|lte, profitability}] # kpiAliases: newline-separated "canonical=alias1;alias2" lines. "companies": [], # Flags "hfTokenSet": False, } def load() -> dict: """Return the merged config (defaults <- saved), or just defaults if unset.""" merged = dict(CONFIG_DEFAULTS) try: saved = sc.load_config() except FileNotFoundError: return merged merged.update({k: v for k, v in saved.items() if v is not None}) # weights merge key-by-key so a partially-saved weights object keeps defaults w = dict(WEIGHTS_DEFAULTS) w.update({k: v for k, v in (merged.get("weights") or {}).items() if v is not None}) merged["weights"] = w return merged def bdef_text(cfg: dict) -> str: """The grading rubric: config override if set, else the baked-in spec.""" override = (cfg.get("bdefOverride") or "").strip() if override: return override with open(BDEF_PATH, encoding="utf-8") as f: return f.read() def hf_token() -> str | None: if os.path.exists(HF_TOKEN_PATH): t = open(HF_TOKEN_PATH).read().strip() return t or None return None