Skip to content

Commit 3636b89

Browse files
Merge pull request #177 from CausalInferenceLab/169-multipage-setup
refactor: 멀티 페이지 구조를 Navigation API 기반으로 변경
2 parents 3da980e + 084acd3 commit 3636b89

File tree

5 files changed

+75
-96
lines changed

5 files changed

+75
-96
lines changed

interface/graph_builder.py renamed to interface/app_pages/graph_builder.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,17 +11,17 @@
1111
from typing import List
1212

1313
import streamlit as st
14-
from langgraph.graph import StateGraph, END
14+
from langgraph.graph import END, StateGraph
1515

1616
from llm_utils.graph_utils.base import (
17-
QueryMakerState,
17+
CONTEXT_ENRICHMENT,
1818
GET_TABLE_INFO,
1919
PROFILE_EXTRACTION,
20-
CONTEXT_ENRICHMENT,
2120
QUERY_MAKER,
21+
QueryMakerState,
22+
context_enrichment_node,
2223
get_table_info_node,
2324
profile_extraction_node,
24-
context_enrichment_node,
2525
query_maker_node,
2626
)
2727

interface/app_pages/home.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""
2+
홈 페이지 모듈.
3+
4+
Lang2SQL 데이터 분석 도구의 소개와 사용 방법을 안내합니다.
5+
"""
6+
7+
import streamlit as st
8+
9+
st.title("🏠 홈")
10+
11+
st.markdown(
12+
"""
13+
### Lang2SQL 데이터 분석 도구에 오신 것을 환영합니다 🎉
14+
15+
이 도구는 자연어로 작성한 질문을 SQL 쿼리로 변환하고,
16+
데이터베이스를 조회하여 결과를 **표와 차트**로 시각화합니다.
17+
18+
---
19+
#### 사용 방법
20+
1. 왼쪽 메뉴에서 원하는 기능 페이지를 선택하세요.
21+
2. **🔍 Lang2SQL**: 자연어 → SQL 변환 및 결과 분석
22+
3. **📊 그래프 빌더**: 데이터 시각화를 위한 차트 구성
23+
"""
24+
)
25+
26+
st.info("왼쪽 메뉴에서 기능 페이지를 선택해 시작하세요 🚀")

interface/lang2sql.py renamed to interface/app_pages/lang2sql.py

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,22 +5,18 @@
55
ClickHouse 데이터베이스에 실행한 결과를 출력합니다.
66
"""
77

8-
import re
8+
from copy import deepcopy
99

10+
import pandas as pd
1011
import streamlit as st
1112
from langchain_core.messages import AIMessage
12-
from interface.dialects import PRESET_DIALECTS, DialectOption
13-
from copy import deepcopy
1413

1514
from db_utils import get_db_connector
16-
from db_utils.base_connector import BaseConnector
17-
from viz.display_chart import DisplayChart
1815
from engine.query_executor import execute_query as execute_query_common
19-
from llm_utils.llm_response_parser import LLMResponseParser
2016
from infra.observability.token_usage import TokenUtils
21-
from llm_utils.graph_utils.enriched_graph import builder as enriched_builder
22-
from llm_utils.graph_utils.basic_graph import builder
23-
17+
from interface.core.dialects import PRESET_DIALECTS, DialectOption
18+
from llm_utils.llm_response_parser import LLMResponseParser
19+
from viz.display_chart import DisplayChart
2420

2521
TITLE = "Lang2SQL"
2622
DEFAULT_QUERY = "고객 데이터를 기반으로 유니크한 유저 수를 카운트하는 쿼리"
@@ -37,6 +33,17 @@
3733
}
3834

3935

36+
def _get_graph_builder(use_enriched: bool):
37+
"""
38+
순환 import를 피하기 위해 사용 시점에 그래프 빌더를 import한다.
39+
"""
40+
if use_enriched:
41+
from llm_utils.graph_utils.enriched_graph import builder as _builder
42+
else:
43+
from llm_utils.graph_utils.basic_graph import builder as _builder
44+
return _builder
45+
46+
4047
def execute_query(
4148
*,
4249
query: str,
@@ -241,6 +248,7 @@ def _as_float(value):
241248

242249
if show_table_section or show_chart_section:
243250
database = get_db_connector()
251+
df = pd.DataFrame()
244252
try:
245253
sql_raw = (
246254
res["generated_query"].content
@@ -255,9 +263,9 @@ def _as_float(value):
255263
except Exception as e:
256264
st.markdown("---")
257265
st.error(f"쿼리 실행 중 오류 발생: {e}")
258-
df = None
266+
df = pd.DataFrame()
259267

260-
if df is not None and show_table_section:
268+
if not df.empty and show_table_section:
261269
st.markdown("---")
262270
st.markdown("**쿼리 실행 결과:**")
263271
try:
@@ -309,33 +317,26 @@ def _as_float(value):
309317
"graph" not in st.session_state
310318
or st.session_state.get("use_enriched") != use_enriched
311319
):
312-
# 그래프 선택 로직
313-
if use_enriched:
314-
graph_builder = enriched_builder
315-
graph_type = "확장된"
316-
else:
317-
graph_builder = builder
318-
graph_type = "기본"
320+
graph_builder = _get_graph_builder(use_enriched)
321+
graph_type = "확장된" if use_enriched else "기본"
319322

320323
st.session_state["graph"] = graph_builder.compile()
321324
st.session_state["use_enriched"] = use_enriched
322325
st.info(f"Lang2SQL이 성공적으로 시작되었습니다. ({graph_type} 워크플로우)")
323326

327+
324328
# 새로고침 버튼 추가
325329
if st.sidebar.button("Lang2SQL 새로고침"):
326-
# 그래프 선택 로직
327-
if st.session_state.get("use_enriched"):
328-
graph_builder = enriched_builder
329-
graph_type = "확장된"
330-
else:
331-
graph_builder = builder
332-
graph_type = "기본"
330+
use_enriched_curr = st.session_state.get("use_enriched", False)
331+
graph_builder = _get_graph_builder(use_enriched_curr)
332+
graph_type = "확장된" if use_enriched_curr else "기본"
333333

334334
st.session_state["graph"] = graph_builder.compile()
335335
st.sidebar.success(
336336
f"Lang2SQL이 성공적으로 새로고침되었습니다. ({graph_type} 워크플로우)"
337337
)
338338

339+
339340
user_query = st.text_area(
340341
"쿼리를 입력하세요:",
341342
value=DEFAULT_QUERY,
File renamed without changes.

interface/streamlit_app.py

Lines changed: 19 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,27 @@
11
"""
2-
Streamlit 멀티 페이지 애플리케이션 설정 파일.
2+
Streamlit 애플리케이션 메인 실행 모듈.
33
4-
- PAGES 딕셔너리로 페이지 정보(page 경로 및 제목)를 관리합니다.
5-
- PAGES를 기반으로 Streamlit 네비게이션 메뉴를 생성하고 실행합니다.
4+
이 모듈은 Lang2SQL 데이터 분석 도구의 내비게이션을 정의하고,
5+
각 페이지를 연결하여 사용자가 원하는 기능을 선택할 수 있도록 합니다.
6+
7+
Example:
8+
$ streamlit run interface/streamlit_app.py
69
"""
710

811
import streamlit as st
912

10-
PAGES = {
11-
"lang2sql": {
12-
"page": "lang2sql.py",
13-
"title": "Lang2SQL",
14-
},
15-
"graph_builder": {
16-
"page": "graph_builder.py",
17-
"title": "Graph Builder",
18-
},
19-
}
20-
21-
22-
def validate_pages(
23-
*,
24-
pages_dict: dict,
25-
) -> None:
26-
"""
27-
PAGES 딕셔너리의 구조와 값을 검증합니다.
28-
29-
검증 항목:
30-
- 최상위 객체는 딕셔너리(dict)여야 합니다.
31-
- 각 항목은 'page'와 'title' 키를 가진 딕셔너리여야 합니다.
32-
- 'page'와 'title' 값은 비어 있지 않은 문자열이어야 합니다.
33-
34-
오류 발생:
35-
- 조건을 만족하지 않으면 ValueError를 발생시킵니다.
36-
37-
Args:
38-
pages_dict (dict): 페이지 정보가 담긴 딕셔너리
39-
40-
Raises:
41-
ValueError: 유효하지 않은 구조나 값을 가진 경우
42-
"""
43-
44-
if not isinstance(pages_dict, dict):
45-
raise ValueError("PAGES must be a dictionary.")
46-
47-
for key, page_info in pages_dict.items():
48-
if not isinstance(page_info, dict):
49-
raise ValueError(
50-
f"Each page info must be a dictionary. Error at key: {key}"
51-
)
52-
53-
if "page" not in page_info or "title" not in page_info:
54-
raise ValueError(
55-
f"Each page must have 'page' and 'title' fields. Error at key: {key}"
56-
)
57-
58-
if not isinstance(page_info["page"], str) or not page_info["page"]:
59-
raise ValueError(f"'page' must be a non-empty string. Error at key: {key}")
60-
61-
if not isinstance(page_info["title"], str) or not page_info["title"]:
62-
raise ValueError(f"'title' must be a non-empty string. Error at key: {key}")
63-
64-
65-
validate_pages(pages_dict=PAGES)
66-
67-
pages = [
68-
st.Page(
69-
page=page_info["page"],
70-
title=page_info["title"],
71-
)
72-
for page_info in PAGES.values()
13+
st.set_page_config(
14+
page_title="Lang2SQL 데이터 분석 도구",
15+
page_icon="🔎",
16+
layout="wide",
17+
initial_sidebar_state="expanded",
18+
)
19+
20+
PAGES = [
21+
st.Page("app_pages/home.py", title="🏠 홈"),
22+
st.Page("app_pages/lang2sql.py", title="🔍 Lang2SQL"),
23+
st.Page("app_pages/graph_builder.py", title="📊 그래프 빌더"),
7324
]
7425

75-
st.navigation(pages).run()
26+
pg = st.navigation(PAGES)
27+
pg.run()

0 commit comments

Comments
 (0)