You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
77 lines
2.8 KiB
Python
77 lines
2.8 KiB
Python
# backend/app/runtime_config.py
|
|
# 런타임 LLM 설정 오버레이(인메모리). app_setting 테이블 → 프로세스 메모리.
|
|
#
|
|
# 설계 의도:
|
|
# - .env(Settings)는 ops 가 관리하는 읽기전용 기본값.
|
|
# - 사용자가 설정 페이지에서 바꾸는 LLM 연결만 이 오버레이가 흡수해 즉시 반영한다.
|
|
# - 오버레이가 비어 있으면(기본) effective_* 는 Settings 값과 100% 동일 → 데모/테스트 무손상.
|
|
# - 테스트는 lifespan 을 띄우지 않으므로 load_from_db 가 호출되지 않아 항상 비어 있다.
|
|
# _test/reset 은 clear() 를 호출해 같은 프로세스 내 잔여 오버레이를 비운다.
|
|
from dataclasses import dataclass
|
|
|
|
from .config import get_settings
|
|
|
|
# 허용된 LLM 오버레이 키(이 외의 키는 무시).
|
|
LLM_KEYS = ("llm.provider", "llm.model", "llm.host", "llm.timeout")
|
|
_PROVIDERS = ("ollama",) # 실 LLM 전용(auto/heuristic 제거, phase-16+)
|
|
|
|
# 인메모리 오버레이. key → str value.
|
|
_overrides: dict[str, str] = {}
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class EffectiveLLM:
|
|
provider: str # auto | ollama | heuristic
|
|
model: str
|
|
host: str
|
|
timeout: float
|
|
|
|
|
|
def clear() -> None:
|
|
"""오버레이 비우기 → effective_* 가 .env 기본값으로 복귀."""
|
|
_overrides.clear()
|
|
|
|
|
|
def snapshot() -> dict[str, str]:
|
|
"""현재 오버레이의 사본(디버그/응답용)."""
|
|
return dict(_overrides)
|
|
|
|
|
|
def set_llm(values: dict[str, object]) -> None:
|
|
"""LLM 오버레이 갱신. None/빈 값은 '기본으로 되돌리기'로 간주해 키를 제거한다."""
|
|
for k, v in values.items():
|
|
if k not in LLM_KEYS:
|
|
continue
|
|
if v is None or (isinstance(v, str) and v.strip() == ""):
|
|
_overrides.pop(k, None)
|
|
else:
|
|
_overrides[k] = str(v)
|
|
|
|
|
|
def load_from_db(session) -> None:
|
|
"""앱 시작 시(lifespan) app_setting → 오버레이 적재."""
|
|
from sqlmodel import select
|
|
|
|
from .models import AppSetting
|
|
|
|
_overrides.clear()
|
|
for row in session.exec(select(AppSetting)).all():
|
|
if row.key in LLM_KEYS:
|
|
_overrides[row.key] = row.value
|
|
|
|
|
|
def effective_llm() -> EffectiveLLM:
|
|
"""오버레이 > .env 우선순위로 합성한 유효 LLM 설정."""
|
|
st = get_settings()
|
|
provider = _overrides.get("llm.provider") or st.llm_provider
|
|
if provider not in _PROVIDERS:
|
|
provider = "ollama" # 미지원/구 값(auto·heuristic) → 실 LLM 으로 강제
|
|
model = _overrides.get("llm.model") or st.ollama_model
|
|
host = _overrides.get("llm.host") or st.ollama_host
|
|
raw_timeout = _overrides.get("llm.timeout")
|
|
try:
|
|
timeout = float(raw_timeout) if raw_timeout else st.llm_timeout
|
|
except ValueError:
|
|
timeout = st.llm_timeout
|
|
return EffectiveLLM(provider=provider, model=model, host=host, timeout=timeout)
|