Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,17 @@
from typing import List

import streamlit as st
from langgraph.graph import StateGraph, END
from langgraph.graph import END, StateGraph

from llm_utils.graph_utils.base import (
QueryMakerState,
CONTEXT_ENRICHMENT,
GET_TABLE_INFO,
PROFILE_EXTRACTION,
CONTEXT_ENRICHMENT,
QUERY_MAKER,
QueryMakerState,
context_enrichment_node,
get_table_info_node,
profile_extraction_node,
context_enrichment_node,
query_maker_node,
)

Expand Down
26 changes: 26 additions & 0 deletions interface/app_pages/home.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
"""
홈 페이지 모듈.

Lang2SQL 데이터 분석 도구의 소개와 사용 방법을 안내합니다.
"""

import streamlit as st

st.title("🏠 홈")

st.markdown(
"""
### Lang2SQL 데이터 분석 도구에 오신 것을 환영합니다 🎉

이 도구는 자연어로 작성한 질문을 SQL 쿼리로 변환하고,
데이터베이스를 조회하여 결과를 **표와 차트**로 시각화합니다.

---
#### 사용 방법
1. 왼쪽 메뉴에서 원하는 기능 페이지를 선택하세요.
2. **🔍 Lang2SQL**: 자연어 → SQL 변환 및 결과 분석
3. **📊 그래프 빌더**: 데이터 시각화를 위한 차트 구성
"""
)

st.info("왼쪽 메뉴에서 기능 페이지를 선택해 시작하세요 🚀")
51 changes: 26 additions & 25 deletions interface/lang2sql.py → interface/app_pages/lang2sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,18 @@
ClickHouse 데이터베이스에 실행한 결과를 출력합니다.
"""

import re
from copy import deepcopy

import pandas as pd
import streamlit as st
from langchain_core.messages import AIMessage
from interface.dialects import PRESET_DIALECTS, DialectOption
from copy import deepcopy

from db_utils import get_db_connector
from db_utils.base_connector import BaseConnector
from viz.display_chart import DisplayChart
from engine.query_executor import execute_query as execute_query_common
from llm_utils.llm_response_parser import LLMResponseParser
from infra.observability.token_usage import TokenUtils
from llm_utils.graph_utils.enriched_graph import builder as enriched_builder
from llm_utils.graph_utils.basic_graph import builder

from interface.core.dialects import PRESET_DIALECTS, DialectOption
from llm_utils.llm_response_parser import LLMResponseParser
from viz.display_chart import DisplayChart

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


def _get_graph_builder(use_enriched: bool):
"""
순환 import를 피하기 위해 사용 시점에 그래프 빌더를 import한다.
"""
if use_enriched:
from llm_utils.graph_utils.enriched_graph import builder as _builder
else:
from llm_utils.graph_utils.basic_graph import builder as _builder
return _builder


def execute_query(
*,
query: str,
Expand Down Expand Up @@ -241,6 +248,7 @@ def _as_float(value):

if show_table_section or show_chart_section:
database = get_db_connector()
df = pd.DataFrame()
try:
sql_raw = (
res["generated_query"].content
Expand All @@ -255,9 +263,9 @@ def _as_float(value):
except Exception as e:
st.markdown("---")
st.error(f"쿼리 실행 중 오류 발생: {e}")
df = None
df = pd.DataFrame()

if df is not None and show_table_section:
if not df.empty and show_table_section:
st.markdown("---")
st.markdown("**쿼리 실행 결과:**")
try:
Expand Down Expand Up @@ -309,33 +317,26 @@ def _as_float(value):
"graph" not in st.session_state
or st.session_state.get("use_enriched") != use_enriched
):
# 그래프 선택 로직
if use_enriched:
graph_builder = enriched_builder
graph_type = "확장된"
else:
graph_builder = builder
graph_type = "기본"
graph_builder = _get_graph_builder(use_enriched)
graph_type = "확장된" if use_enriched else "기본"

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


# 새로고침 버튼 추가
if st.sidebar.button("Lang2SQL 새로고침"):
# 그래프 선택 로직
if st.session_state.get("use_enriched"):
graph_builder = enriched_builder
graph_type = "확장된"
else:
graph_builder = builder
graph_type = "기본"
use_enriched_curr = st.session_state.get("use_enriched", False)
graph_builder = _get_graph_builder(use_enriched_curr)
graph_type = "확장된" if use_enriched_curr else "기본"

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


user_query = st.text_area(
"쿼리를 입력하세요:",
value=DEFAULT_QUERY,
Expand Down
File renamed without changes.
86 changes: 19 additions & 67 deletions interface/streamlit_app.py
Original file line number Diff line number Diff line change
@@ -1,75 +1,27 @@
"""
Streamlit 멀티 페이지 애플리케이션 설정 파일.
Streamlit 애플리케이션 메인 실행 모듈.

- PAGES 딕셔너리로 페이지 정보(page 경로 및 제목)를 관리합니다.
- PAGES를 기반으로 Streamlit 네비게이션 메뉴를 생성하고 실행합니다.
이 모듈은 Lang2SQL 데이터 분석 도구의 내비게이션을 정의하고,
각 페이지를 연결하여 사용자가 원하는 기능을 선택할 수 있도록 합니다.

Example:
$ streamlit run interface/streamlit_app.py
"""

import streamlit as st

PAGES = {
"lang2sql": {
"page": "lang2sql.py",
"title": "Lang2SQL",
},
"graph_builder": {
"page": "graph_builder.py",
"title": "Graph Builder",
},
}


def validate_pages(
*,
pages_dict: dict,
) -> None:
"""
PAGES 딕셔너리의 구조와 값을 검증합니다.

검증 항목:
- 최상위 객체는 딕셔너리(dict)여야 합니다.
- 각 항목은 'page'와 'title' 키를 가진 딕셔너리여야 합니다.
- 'page'와 'title' 값은 비어 있지 않은 문자열이어야 합니다.

오류 발생:
- 조건을 만족하지 않으면 ValueError를 발생시킵니다.

Args:
pages_dict (dict): 페이지 정보가 담긴 딕셔너리

Raises:
ValueError: 유효하지 않은 구조나 값을 가진 경우
"""

if not isinstance(pages_dict, dict):
raise ValueError("PAGES must be a dictionary.")

for key, page_info in pages_dict.items():
if not isinstance(page_info, dict):
raise ValueError(
f"Each page info must be a dictionary. Error at key: {key}"
)

if "page" not in page_info or "title" not in page_info:
raise ValueError(
f"Each page must have 'page' and 'title' fields. Error at key: {key}"
)

if not isinstance(page_info["page"], str) or not page_info["page"]:
raise ValueError(f"'page' must be a non-empty string. Error at key: {key}")

if not isinstance(page_info["title"], str) or not page_info["title"]:
raise ValueError(f"'title' must be a non-empty string. Error at key: {key}")


validate_pages(pages_dict=PAGES)

pages = [
st.Page(
page=page_info["page"],
title=page_info["title"],
)
for page_info in PAGES.values()
st.set_page_config(
page_title="Lang2SQL 데이터 분석 도구",
page_icon="🔎",
layout="wide",
initial_sidebar_state="expanded",
)

PAGES = [
st.Page("app_pages/home.py", title="🏠 홈"),
st.Page("app_pages/lang2sql.py", title="🔍 Lang2SQL"),
st.Page("app_pages/graph_builder.py", title="📊 그래프 빌더"),
]

st.navigation(pages).run()
pg = st.navigation(PAGES)
pg.run()