"""Summary-gate configuration and the fail policy. Pure module: no FastAPI imports and no HTTPException. Non-DB config is module-level load_dotenv() + os.getenv (house style). Model / token / effort / cache knobs come from the bulk-ats Settings, same as inbox_classifier.plugins.get_triage_settings. """ from __future__ import annotations import os from dotenv import load_dotenv from summary_gate.prompt import PROMPT_VERSION from app.core.config import Settings, get_settings load_dotenv() def _flag(name, default) -> bool: raw=(os.getenv(name) or "").strip().lower() if not raw: return default return raw in ("1","true","yes","on") # false restores pre-gate behaviour: every pair with a CV is ATS-scored. GATE_ENABLED=_flag("SUMMARY_GATE_ENABLED",True) # true: a provider outage or a missing key lets the pair through to ATS. # Fail-closed would silently skip scoring when OPENAI_API_KEY is unset. GATE_FAIL_OPEN=_flag("SUMMARY_GATE_FAIL_OPEN",True) GATE_MAX_SUMMARY_CHARS=max(int(os.getenv("SUMMARY_GATE_MAX_SUMMARY_CHARS") or 500),1) # Same bound as the inbox intake body cap — enough for title + requirements. GATE_MAX_JD_CHARS=max(int(os.getenv("SUMMARY_GATE_MAX_JD_CHARS") or 4000),1) # 0 disables the uncertainty branch (0.0 < 0.0 is False). GATE_MIN_CONFIDENCE=float(os.getenv("SUMMARY_GATE_MIN_CONFIDENCE") or 0) PROMPT_CACHE_KEY=f"summary-gate-{PROMPT_VERSION}" UNCLASSIFIED_PREFIX="unclassified:" CLASSIFIED="classified" LOW_CONFIDENCE="low_confidence" ERROR="error" # Backend-local skip code. Not an app.core.errors.ErrorCode — the bulk-ats # engine never sees this pair; the CV is never opened. SUMMARY_NOT_SUITABLE="SUMMARY_NOT_SUITABLE" def get_gate_settings() -> Settings: return get_settings() def clip_text(value, limit) -> str: text=str(value or "") if len(text)<=limit: return text return text[:limit] def should_score(verdict, error_code="") -> tuple[bool,str,str]: """(proceed, status, reason) — the entire fail policy, in one place. No verdict means the model could not be consulted. SUMMARY_GATE_FAIL_OPEN decides. A present verdict uses is_suitable, unless confidence is below SUMMARY_GATE_MIN_CONFIDENCE (the gradient threshold). """ if verdict is None: reason=f"{UNCLASSIFIED_PREFIX}{error_code or 'unknown'}"[:60] return GATE_FAIL_OPEN,ERROR,reason if verdict.confidence