Skip to content

Commit 567ac80

Browse files
authored
Merge pull request #198 from #196
196 기능 질문 구체화 챗봇
2 parents 34ce19f + d22bf3c commit 567ac80

File tree

7 files changed

+764
-0
lines changed

7 files changed

+764
-0
lines changed

interface/app_pages/chatbot.py

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
"""
2+
AI ChatBot 페이지
3+
LangGraph와 OpenAI를 활용한 대화형 인터페이스
4+
"""
5+
6+
import os
7+
import streamlit as st
8+
9+
from utils.llm.chatbot import ChatBot
10+
from interface.app_pages.sidebar_components import (
11+
render_sidebar_data_source_selector,
12+
render_sidebar_llm_selector,
13+
render_sidebar_embedding_selector,
14+
render_sidebar_db_selector,
15+
render_sidebar_chatbot_session_controller,
16+
)
17+
from interface.core.config import load_config
18+
19+
20+
def initialize_session_state():
21+
"""세션 상태 초기화 함수
22+
23+
Streamlit의 session_state를 사용하여 앱의 상태를 유지합니다.
24+
LLM 설정을 sidebar의 llm_selector에서 선택한 값으로부터 가져옵니다.
25+
"""
26+
# 채팅 메시지 기록 저장 (자동으로 시작)
27+
if "chatbot_messages" not in st.session_state:
28+
st.session_state.chatbot_messages = []
29+
30+
# LLM 공급자 확인 (현재 ChatBot은 OpenAI만 지원)
31+
llm_provider = (
32+
st.session_state.get("LLM_PROVIDER") or os.getenv("LLM_PROVIDER") or "openai"
33+
).lower()
34+
35+
if llm_provider != "openai":
36+
st.error(
37+
f"⚠️ ChatBot은 현재 OpenAI만 지원합니다. 설정 > LLM에서 OpenAI 프로파일을 선택하거나 LLM_PROVIDER를 'openai'로 설정해주세요."
38+
)
39+
st.stop()
40+
41+
# OpenAI API 키 확인
42+
openai_api_key = st.session_state.get("OPEN_AI_KEY") or os.getenv("OPEN_AI_KEY")
43+
44+
if not openai_api_key:
45+
st.error(
46+
"⚠️ OpenAI API 키가 설정되지 않았습니다. 설정 > LLM에서 OpenAI API 키를 입력하거나, 사이드바에서 LLM 프로파일을 적용해주세요."
47+
)
48+
st.stop()
49+
50+
# 사용할 모델명 가져오기 (llm_selector에서 설정한 값)
51+
model_name = (
52+
st.session_state.get("OPEN_AI_LLM_MODEL")
53+
or os.getenv("OPEN_AI_LLM_MODEL")
54+
or "gpt-4o-mini"
55+
)
56+
57+
# DataHub 서버 URL 가져오기 (config에서 로드)
58+
config = load_config()
59+
gms_server = config.datahub_server
60+
61+
# ChatBot 인스턴스 생성 또는 모델 업데이트
62+
if "chatbot_instance" not in st.session_state:
63+
st.session_state.chatbot_instance = ChatBot(
64+
openai_api_key, model_name=model_name, gms_server=gms_server
65+
)
66+
else:
67+
# 기존 인스턴스가 있는 경우, 모델이나 API 키, gms_server가 변경되었는지 확인
68+
existing_bot = st.session_state.chatbot_instance
69+
if (
70+
existing_bot.model_name != model_name
71+
or existing_bot.openai_api_key != openai_api_key
72+
or existing_bot.gms_server != gms_server
73+
):
74+
st.session_state.chatbot_instance = ChatBot(
75+
openai_api_key, model_name=model_name, gms_server=gms_server
76+
)
77+
78+
79+
# 세션 상태 초기화 실행
80+
initialize_session_state()
81+
82+
# 페이지 제목
83+
st.title("🤖 AI ChatBot")
84+
85+
st.markdown(
86+
"""
87+
LangGraph 기반 AI ChatBot과 대화를 나눌 수 있습니다.
88+
- 데이터베이스 테이블 정보 검색
89+
- 용어집 조회
90+
- 쿼리 예제 조회
91+
- 대화를 통해 질문 구체화
92+
"""
93+
)
94+
95+
# 설정 로드
96+
config = load_config()
97+
98+
# 사이드바 UI 구성 (lang2sql.py와 동일한 구조)
99+
render_sidebar_data_source_selector(config)
100+
st.sidebar.divider()
101+
render_sidebar_llm_selector()
102+
st.sidebar.divider()
103+
render_sidebar_embedding_selector()
104+
st.sidebar.divider()
105+
render_sidebar_db_selector()
106+
st.sidebar.divider()
107+
108+
# ChatBot 전용 설정
109+
with st.sidebar:
110+
st.markdown("### 🤖 ChatBot 설정")
111+
st.divider()
112+
thread_id = render_sidebar_chatbot_session_controller()
113+
114+
115+
# 첫 메시지가 없으면 환영 메시지 추가
116+
if not st.session_state.chatbot_messages:
117+
hello_message = "안녕하세요! 무엇을 도와드릴까요? 🤖"
118+
st.session_state.chatbot_messages = [
119+
{"role": "assistant", "content": hello_message}
120+
]
121+
122+
# 저장된 모든 메시지를 순서대로 표시
123+
for message in st.session_state.chatbot_messages:
124+
with st.chat_message(message["role"]):
125+
st.markdown(message["content"])
126+
127+
# 사용자 입력 처리
128+
if prompt := st.chat_input("메시지를 입력하세요"):
129+
# 사용자 메시지를 기록에 추가
130+
st.session_state.chatbot_messages.append({"role": "user", "content": prompt})
131+
with st.chat_message("user"):
132+
st.markdown(prompt)
133+
134+
# AI 응답 생성 및 표시
135+
with st.chat_message("assistant"):
136+
try:
137+
# ChatBot을 통해 응답 생성
138+
response = st.session_state.chatbot_instance.chat(prompt, thread_id)
139+
140+
# 응답 내용 추출
141+
response_content = response["messages"][-1].content
142+
143+
# 모델 정보 표시
144+
model_name = st.session_state.chatbot_instance.model_name
145+
st.caption(f"🤖 모델: {model_name}")
146+
147+
# 응답 표시
148+
st.markdown(response_content)
149+
150+
# AI 응답을 기록에 추가
151+
st.session_state.chatbot_messages.append(
152+
{"role": "assistant", "content": response_content}
153+
)
154+
except Exception as e:
155+
error_msg = f"오류가 발생했습니다: {str(e)}"
156+
st.error(error_msg)
157+
st.session_state.chatbot_messages.append(
158+
{"role": "assistant", "content": error_msg}
159+
)

interface/app_pages/sidebar_components/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@
22
from .llm_selector import render_sidebar_llm_selector
33
from .embedding_selector import render_sidebar_embedding_selector
44
from .db_selector import render_sidebar_db_selector
5+
from .chatbot_session_controller import render_sidebar_chatbot_session_controller
56

67
__all__ = [
78
"render_sidebar_data_source_selector",
89
"render_sidebar_llm_selector",
910
"render_sidebar_embedding_selector",
1011
"render_sidebar_db_selector",
12+
"render_sidebar_chatbot_session_controller",
1113
]
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
"""ChatBot 세션 제어를 위한 사이드바 컴포넌트"""
2+
3+
import streamlit as st
4+
import uuid
5+
6+
7+
def render_sidebar_chatbot_session_controller() -> str:
8+
"""ChatBot 세션 관리 및 대화 기록 표시 (사이드바 전용)
9+
10+
Returns:
11+
str: 현재 thread_id
12+
"""
13+
# 세션 ID 자동 생성 (처음 방문 시에만)
14+
if "chatbot_thread_id" not in st.session_state:
15+
st.session_state.chatbot_thread_id = str(uuid.uuid4())[:8] # 8자리 짧은 ID
16+
17+
thread_id = st.session_state.chatbot_thread_id
18+
19+
# 세션 관리 섹션
20+
st.markdown("### 📋 세션 관리")
21+
22+
# 세션 정보 표시
23+
st.markdown(f"**현재 세션:** `{thread_id}`")
24+
st.caption("대화 기록을 구분하는 고유 ID입니다.")
25+
26+
# 새 세션 시작 버튼
27+
if st.button(
28+
"🔄 새 세션 시작",
29+
use_container_width=True,
30+
help="새로운 대화 세션을 시작합니다.",
31+
):
32+
st.session_state.chatbot_thread_id = str(uuid.uuid4())[:8]
33+
st.session_state.chatbot_messages = []
34+
st.rerun()
35+
36+
# 대화 기록 섹션
37+
if st.session_state.get("chatbot_messages"):
38+
st.divider()
39+
st.markdown("### 💬 대화 기록")
40+
41+
# 메시지 개수 표시
42+
message_count = len(st.session_state.chatbot_messages)
43+
st.caption(f"총 {message_count}개의 메시지")
44+
45+
# 대화 기록 표시 (접힌 상태)
46+
with st.expander("📄 전체 기록 보기 (JSON)", expanded=False):
47+
st.json(st.session_state.chatbot_messages)
48+
49+
# 최근 메시지 미리보기
50+
if message_count > 0:
51+
with st.expander("👀 최근 메시지 미리보기", expanded=False):
52+
recent_messages = st.session_state.chatbot_messages[-3:] # 최근 3개
53+
for msg in recent_messages:
54+
role_icon = "👤" if msg["role"] == "user" else "🤖"
55+
role_text = "사용자" if msg["role"] == "user" else "AI"
56+
content_preview = (
57+
msg["content"][:50] + "..."
58+
if len(msg["content"]) > 50
59+
else msg["content"]
60+
)
61+
st.caption(f"{role_icon} {role_text}: {content_preview}")
62+
63+
return thread_id

interface/pages_config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
- 홈 페이지
99
- Lang2SQL 페이지
1010
- 그래프 빌더 페이지
11+
- ChatBot 페이지
12+
- 설정 페이지
1113
"""
1214

1315
import streamlit as st
@@ -16,5 +18,6 @@
1618
st.Page("app_pages/home.py", title="🏠 홈"),
1719
st.Page("app_pages/lang2sql.py", title="🔍 Lang2SQL"),
1820
st.Page("app_pages/graph_builder.py", title="📊 그래프 빌더"),
21+
st.Page("app_pages/chatbot.py", title="🤖 ChatBot"),
1922
st.Page("app_pages/settings.py", title="⚙️ 설정"),
2023
]

0 commit comments

Comments
 (0)