Skip to content

Commit 1ca5d5d

Browse files
committed
설정 페이지 추가 및 데이터 소스 섹션 추가 & 기존 Cli 지원 제거
1 parent 61b93e7 commit 1ca5d5d

File tree

6 files changed

+332
-20
lines changed

6 files changed

+332
-20
lines changed

cli/__init__.py

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
"""Lang2SQL CLI 프로그램입니다.
2-
이 프로그램은 Datahub GMS 서버 URL을 설정하고, 필요 시 Streamlit 인터페이스를 실행합니다.
2+
이 프로그램은 환경 초기화와 Streamlit 실행을 제공합니다.
33
4-
명령어 예시: lang2sql --datahub_server http://localhost:8080 --run-streamlit
4+
주의: --datahub_server 옵션은 더 이상 사용되지 않습니다(deprecated).
5+
DataHub 설정은 UI의 설정 > 데이터 소스 탭에서 관리하세요.
56
"""
67

78
import click
@@ -11,8 +12,7 @@
1112
from cli.core.environment import initialize_environment
1213
from cli.core.streamlit_runner import run_streamlit_command
1314
from cli.utils.logger import configure_logging
14-
from infra.monitoring.check_server import CheckServer
15-
from llm_utils.tools import set_gms_server
15+
1616
from version import __version__
1717

1818
logger = configure_logging()
@@ -24,12 +24,8 @@
2424
@click.pass_context
2525
@click.option(
2626
"--datahub_server",
27-
default="http://localhost:8080",
28-
help=(
29-
"Datahub GMS 서버의 URL을 설정합니다. "
30-
"기본값은 'http://localhost:8080'이며, "
31-
"운영 환경 또는 테스트 환경에 맞게 변경할 수 있습니다."
32-
),
27+
default=None,
28+
help=("[Deprecated] DataHub GMS URL. 이제는 UI 설정 > 데이터 소스에서 관리하세요."),
3329
)
3430
@click.option(
3531
"--run-streamlit",
@@ -76,7 +72,7 @@
7672
)
7773
def cli(
7874
ctx: click.Context,
79-
datahub_server: str,
75+
datahub_server: str | None,
8076
run_streamlit: bool,
8177
port: int,
8278
env_file_path: str | None = None,
@@ -87,7 +83,6 @@ def cli(
8783
"""Lang2SQL CLI 엔트리포인트.
8884
8985
- 환경 변수 및 VectorDB 설정 초기화
90-
- GMS 서버 연결 및 헬스체크
9186
- 필요 시 Streamlit 애플리케이션 실행
9287
"""
9388

@@ -103,18 +98,17 @@ def cli(
10398
ctx.exit(1)
10499

105100
logger.info(
106-
"Initialization started: GMS server = %s, run_streamlit = %s, port = %d",
107-
datahub_server,
101+
"Initialization started: run_streamlit = %s, port = %d",
108102
run_streamlit,
109103
port,
110104
)
111105

112-
if CheckServer.is_gms_server_healthy(url=datahub_server):
113-
set_gms_server(datahub_server)
114-
logger.info("GMS server URL successfully set: %s", datahub_server)
115-
else:
116-
logger.error("GMS server health check failed. URL: %s", datahub_server)
117-
# ctx.exit(1)
106+
# Deprecated 안내: CLI에서 DataHub 설정은 더 이상 처리하지 않습니다
107+
if datahub_server:
108+
click.secho(
109+
"[Deprecated] --datahub_server 옵션은 더 이상 사용되지 않습니다. 설정 > 데이터 소스 탭에서 설정하세요.",
110+
fg="yellow",
111+
)
118112

119113
if run_streamlit:
120114
run_streamlit_command(port)

interface/app_pages/settings.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
"""
2+
Settings 페이지 – 섹션 기반 UI
3+
"""
4+
5+
import streamlit as st
6+
7+
from interface.core.config import load_config
8+
from interface.app_pages.settings_sections.data_source_section import (
9+
render_data_source_section,
10+
)
11+
12+
13+
st.title("⚙️ 설정")
14+
15+
config = load_config()
16+
17+
tabs = st.tabs(["데이터 소스", "LLM", "DB", "VectorDB", "Device"])
18+
19+
with tabs[0]:
20+
render_data_source_section(config)
21+
22+
with tabs[1]:
23+
st.info("LLM 설정은 곧 제공됩니다.")
24+
25+
with tabs[2]:
26+
st.info("DB 연결 설정은 곧 제공됩니다.")
27+
28+
with tabs[3]:
29+
st.info("VectorDB 설정은 곧 제공됩니다.")
30+
31+
with tabs[4]:
32+
st.info("디바이스 설정은 곧 제공됩니다.")
33+
34+
st.divider()
35+
st.caption("민감 정보는 로그에 기록되지 않으며, 이 설정은 현재 세션에 우선 반영됩니다.")
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Namespace package for settings page sections
2+
3+
__all__ = [
4+
"data_source_section",
5+
"llm_section",
6+
"db_section",
7+
"vectordb_section",
8+
"device_section",
9+
]
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import streamlit as st
2+
3+
from infra.monitoring.check_server import CheckServer
4+
from interface.core.config import (
5+
Config,
6+
load_config,
7+
update_datahub_server,
8+
update_vectordb_settings,
9+
update_data_source_mode,
10+
)
11+
12+
13+
def _render_status_banner(config: Config) -> None:
14+
mode = config.data_source_mode
15+
ready_msgs = []
16+
17+
if mode == "datahub":
18+
is_ok = CheckServer.is_gms_server_healthy(url=config.datahub_server)
19+
if is_ok:
20+
st.success(f"데이터 소스 준비됨: DataHub ({config.datahub_server})")
21+
else:
22+
st.warning(
23+
"DataHub 헬스 체크 실패. URL을 확인하거나 VectorDB로 전환하세요."
24+
)
25+
elif mode == "vectordb":
26+
if config.vectordb_type and (
27+
(config.vectordb_type == "faiss" and config.vectordb_location)
28+
or (config.vectordb_type == "pgvector" and config.vectordb_location)
29+
):
30+
st.success(
31+
f"데이터 소스 준비됨: VectorDB ({config.vectordb_type}, {config.vectordb_location or '기본값'})"
32+
)
33+
else:
34+
st.warning("VectorDB 설정이 불완전합니다. 타입/위치를 확인하세요.")
35+
else:
36+
st.info(
37+
"데이터 소스를 선택해주세요: DataHub 또는 VectorDB 중 하나는 필수입니다."
38+
)
39+
40+
41+
def render_data_source_section(config: Config | None = None) -> None:
42+
st.subheader("데이터 소스 (필수)")
43+
44+
if config is None:
45+
config = load_config()
46+
47+
_render_status_banner(config)
48+
49+
# 선택 스위치
50+
col = st.columns([1, 3])[0]
51+
with col:
52+
mode = st.radio(
53+
"데이터 소스 선택",
54+
options=["DataHub", "VectorDB"],
55+
horizontal=True,
56+
index=(
57+
0 if (config.data_source_mode or "datahub").lower() == "datahub" else 1
58+
),
59+
)
60+
selected = mode.lower()
61+
update_data_source_mode(config, selected)
62+
63+
st.divider()
64+
65+
if selected == "datahub":
66+
with st.container(border=True):
67+
url = st.text_input(
68+
"DataHub GMS 서버 URL",
69+
value=config.datahub_server,
70+
placeholder="http://localhost:8080",
71+
help="예: http://localhost:8080",
72+
)
73+
74+
cols = st.columns([1, 1, 2])
75+
with cols[0]:
76+
if st.button("헬스 체크", key="ds_health"):
77+
ok = CheckServer.is_gms_server_healthy(url=url)
78+
if ok:
79+
st.success("GMS 서버가 정상입니다.")
80+
else:
81+
st.error(
82+
"GMS 서버 헬스 체크 실패. URL과 네트워크를 확인하세요."
83+
)
84+
85+
with cols[1]:
86+
if st.button("저장", key="ds_save"):
87+
if not url:
88+
st.warning("URL을 입력하세요.")
89+
else:
90+
ok = CheckServer.is_gms_server_healthy(url=url)
91+
if not ok:
92+
st.error("저장 실패: 헬스 체크가 통과되지 않았습니다.")
93+
else:
94+
try:
95+
update_datahub_server(config, url)
96+
st.success(
97+
"저장되었습니다. 현재 세션에 즉시 반영됩니다."
98+
)
99+
except Exception:
100+
st.error(
101+
"설정 적용 중 오류가 발생했습니다. 로그를 확인하세요."
102+
)
103+
104+
else: # VectorDB
105+
with st.container(border=True):
106+
vtype = st.selectbox(
107+
"VectorDB 타입",
108+
options=["faiss", "pgvector"],
109+
index=0 if (config.vectordb_type or "faiss") == "faiss" else 1,
110+
)
111+
112+
placeholder_text = (
113+
"FAISS 디렉토리 경로 (예: ./dev/table_info_db)"
114+
if vtype == "faiss"
115+
else "pgvector 연결 문자열 (postgresql://user:pass@host:port/db)"
116+
)
117+
118+
vloc = st.text_input(
119+
"VectorDB 위치",
120+
value=config.vectordb_location,
121+
placeholder=placeholder_text,
122+
help=placeholder_text,
123+
)
124+
125+
cols = st.columns([1, 1, 2])
126+
with cols[0]:
127+
if st.button("검증", key="vdb_validate"):
128+
try:
129+
update_vectordb_settings(
130+
config, vectordb_type=vtype, vectordb_location=vloc
131+
)
132+
st.success("VectorDB 설정이 유효합니다.")
133+
except Exception as e:
134+
st.error(f"검증 실패: {e}")
135+
136+
with cols[1]:
137+
if st.button("저장", key="vdb_save"):
138+
try:
139+
update_vectordb_settings(
140+
config, vectordb_type=vtype, vectordb_location=vloc
141+
)
142+
st.success("저장되었습니다. 현재 세션에 즉시 반영됩니다.")
143+
except Exception as e:
144+
st.error(f"저장 실패: {e}")

interface/core/config.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
from dataclasses import dataclass
2+
import os
3+
from pathlib import Path
4+
5+
try:
6+
import streamlit as st # type: ignore
7+
except Exception: # pragma: no cover - streamlit may not be present in non-UI contexts
8+
st = None # type: ignore
9+
10+
from llm_utils.tools import set_gms_server
11+
12+
13+
DEFAULT_DATAHUB_SERVER = "http://localhost:8080"
14+
DEFAULT_VECTORDB_TYPE = os.getenv("VECTORDB_TYPE", "faiss").lower()
15+
DEFAULT_VECTORDB_LOCATION = os.getenv("VECTORDB_LOCATION", "")
16+
17+
18+
@dataclass
19+
class Config:
20+
datahub_server: str = DEFAULT_DATAHUB_SERVER
21+
vectordb_type: str = DEFAULT_VECTORDB_TYPE
22+
vectordb_location: str = DEFAULT_VECTORDB_LOCATION
23+
data_source_mode: str | None = None # "datahub" | "vectordb" | None
24+
25+
26+
def _get_session_value(key: str) -> str | None:
27+
if st is None:
28+
return None
29+
try:
30+
if key in st.session_state and st.session_state[key]:
31+
return str(st.session_state[key])
32+
except Exception:
33+
return None
34+
return None
35+
36+
37+
def load_config() -> Config:
38+
"""Load configuration with priority: session_state > environment > defaults."""
39+
datahub = _get_session_value("datahub_server") or os.getenv(
40+
"DATAHUB_SERVER", DEFAULT_DATAHUB_SERVER
41+
)
42+
mode = _get_session_value("data_source_mode")
43+
44+
vectordb_type = _get_session_value("vectordb_type") or os.getenv(
45+
"VECTORDB_TYPE", DEFAULT_VECTORDB_TYPE
46+
)
47+
vectordb_location = _get_session_value("vectordb_location") or os.getenv(
48+
"VECTORDB_LOCATION", DEFAULT_VECTORDB_LOCATION
49+
)
50+
51+
return Config(
52+
datahub_server=datahub,
53+
vectordb_type=vectordb_type.lower() if vectordb_type else DEFAULT_VECTORDB_TYPE,
54+
vectordb_location=vectordb_location,
55+
data_source_mode=mode,
56+
)
57+
58+
59+
def update_datahub_server(config: Config, new_url: str) -> None:
60+
"""Update DataHub server URL across runtime config, env-aware clients, and session."""
61+
if not new_url:
62+
return
63+
config.datahub_server = new_url
64+
65+
# Propagate to underlying tooling/clients
66+
try:
67+
set_gms_server(new_url)
68+
except Exception:
69+
# Fail-soft: UI should surface errors from callers if needed
70+
pass
71+
72+
# Reflect into session state for immediate UI reuse
73+
if st is not None:
74+
try:
75+
st.session_state["datahub_server"] = new_url
76+
except Exception:
77+
pass
78+
79+
80+
def update_data_source_mode(config: Config, mode: str | None) -> None:
81+
"""Persist user's data source selection (datahub | vectordb)."""
82+
config.data_source_mode = mode
83+
if st is not None:
84+
try:
85+
st.session_state["data_source_mode"] = mode
86+
except Exception:
87+
pass
88+
89+
90+
def update_vectordb_settings(
91+
config: Config, *, vectordb_type: str, vectordb_location: str | None
92+
) -> None:
93+
"""Validate and update VectorDB settings into env and session.
94+
95+
Basic validation rules follow CLI's behavior:
96+
- vectordb_type must be 'faiss' or 'pgvector'
97+
- if type == 'faiss' and location provided: must be an existing directory
98+
- if type == 'pgvector' and location provided: must start with 'postgresql://'
99+
"""
100+
vtype = (vectordb_type or "").lower()
101+
if vtype not in ("faiss", "pgvector"):
102+
raise ValueError(f"지원하지 않는 VectorDB 타입: {vectordb_type}")
103+
104+
vloc = vectordb_location or ""
105+
if vloc:
106+
if vtype == "faiss":
107+
path = Path(vloc)
108+
if not path.exists() or not path.is_dir():
109+
raise ValueError(f"유효하지 않은 FAISS 디렉토리 경로: {vloc}")
110+
elif vtype == "pgvector":
111+
if not vloc.startswith("postgresql://"):
112+
raise ValueError("pgvector URL은 'postgresql://'로 시작해야 합니다")
113+
114+
# Persist to runtime config
115+
config.vectordb_type = vtype
116+
config.vectordb_location = vloc
117+
118+
# Reflect to process env for downstream modules
119+
os.environ["VECTORDB_TYPE"] = vtype
120+
if vloc:
121+
os.environ["VECTORDB_LOCATION"] = vloc
122+
123+
# Reflect to session state for UI
124+
if st is not None:
125+
try:
126+
st.session_state["vectordb_type"] = vtype
127+
st.session_state["vectordb_location"] = vloc
128+
except Exception:
129+
pass

0 commit comments

Comments
 (0)