"""런타임 설정 — `settings.json` 영속. UI 의 "설정" 페이지에서 SHAP 활성, ensemble α, 로그 레벨 등을 조정. 변경은 즉시 반영 (각 모듈이 settings.get() 으로 조회). """ from __future__ import annotations import json from pathlib import Path from threading import Lock SETTINGS_FILE = Path(__file__).resolve().parent / "settings.json" _lock = Lock() # 기본값 — 키는 여기 등록된 것만 허용 (UI 입력 검증용) DEFAULTS: dict = { "shap_enabled": False, # 분류 카드의 SHAP 버튼 노출 "shap_max_evals": 60, # PartitionExplainer 평가 횟수 (속도/정확도) "ensemble_alpha": 0.5, # rule vs neural blend (0=neural only, 1=rule only) "log_level": "INFO", # DEBUG/INFO/WARNING/ERROR "perf_collect": True, # 요청 timing 수집 여부 "dashboard_refresh_ms": 5000, # 대시보드 자동 새로고침 (0=수동만) "logs_refresh_ms": 3000, "neural_auto_load": False, # 부팅 시 신경망 모델 자동 로드 (느림) "neural_disable": False, # 분류 시 neural 호출 일괄 비활성화 # 한국어 NER 정확도 개선 (B안 + A안) "ko_ner_enabled": True, # KoELECTRA-NER 로드되면 사용 "ko_ner_auto_load": False, # 부팅 시 자동 로드 (느림) "drop_spacy_korean_ner": True, # ko_ner 가용 시 spaCy 한국어 PERSON/LOC/ORG 폐기 "heuristic_filter": True, # 길이/조사/어미 휴리스틱 후처리 } ALLOWED_TYPES = { "shap_enabled": bool, "shap_max_evals": int, "ensemble_alpha": float, "log_level": str, "perf_collect": bool, "dashboard_refresh_ms": int, "logs_refresh_ms": int, "neural_auto_load": bool, "neural_disable": bool, "ko_ner_enabled": bool, "ko_ner_auto_load": bool, "drop_spacy_korean_ner": bool, "heuristic_filter": bool, } def load() -> dict: if not SETTINGS_FILE.exists(): return dict(DEFAULTS) try: cur = json.loads(SETTINGS_FILE.read_text(encoding="utf-8")) return {**DEFAULTS, **(cur if isinstance(cur, dict) else {})} except Exception: return dict(DEFAULTS) def save(updates: dict) -> dict: """allowed key 만 적용 후 파일 저장. 반환 = 저장 직후 전체 상태.""" with _lock: cur = load() for k, v in (updates or {}).items(): if k not in DEFAULTS: continue try: cur[k] = ALLOWED_TYPES[k](v) except (TypeError, ValueError): continue SETTINGS_FILE.write_text( json.dumps(cur, ensure_ascii=False, indent=2), encoding="utf-8", ) return cur def get(key: str): return load().get(key, DEFAULTS.get(key))