Skip to content

Commit 87feb63

Browse files
committed
ChatBot 세션 관리 및 사이드바 구성 개선
1 parent 0f73a5a commit 87feb63

File tree

3 files changed

+170
-102
lines changed

3 files changed

+170
-102
lines changed

interface/app_pages/chatbot.py

Lines changed: 105 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,68 @@
77
import streamlit as st
88

99
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
1018

1119

1220
def initialize_session_state():
1321
"""세션 상태 초기화 함수
1422
1523
Streamlit의 session_state를 사용하여 앱의 상태를 유지합니다.
24+
LLM 설정을 sidebar의 llm_selector에서 선택한 값으로부터 가져옵니다.
1625
"""
17-
# 채팅 세션 시작 여부 플래그
18-
if "chatbot_started" not in st.session_state:
19-
st.session_state.chatbot_started = False
20-
# 채팅 메시지 기록 저장
26+
# 채팅 메시지 기록 저장 (자동으로 시작)
2127
if "chatbot_messages" not in st.session_state:
2228
st.session_state.chatbot_messages = []
2329

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+
2441
# OpenAI API 키 확인
2542
openai_api_key = st.session_state.get("OPEN_AI_KEY") or os.getenv("OPEN_AI_KEY")
2643

2744
if not openai_api_key:
2845
st.error(
29-
"⚠️ OpenAI API 키가 설정되지 않았습니다. 설정 > LLM에서 OpenAI API 키를 입력해주세요."
46+
"⚠️ OpenAI API 키가 설정되지 않았습니다. 설정 > LLM에서 OpenAI API 키를 입력하거나, 사이드바에서 LLM 프로파일을 적용해주세요."
3047
)
3148
st.stop()
3249

33-
# ChatBot 인스턴스 생성 (OpenAI API 키 사용)
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+
# ChatBot 인스턴스 생성 또는 모델 업데이트
3458
if "chatbot_instance" not in st.session_state:
35-
st.session_state.chatbot_instance = ChatBot(openai_api_key)
59+
st.session_state.chatbot_instance = ChatBot(
60+
openai_api_key, model_name=model_name
61+
)
62+
else:
63+
# 기존 인스턴스가 있는 경우, 모델이나 API 키가 변경되었는지 확인
64+
existing_bot = st.session_state.chatbot_instance
65+
if (
66+
existing_bot.model_name != model_name
67+
or existing_bot.openai_api_key != openai_api_key
68+
):
69+
st.session_state.chatbot_instance = ChatBot(
70+
openai_api_key, model_name=model_name
71+
)
3672

3773

3874
# 세션 상태 초기화 실행
@@ -50,101 +86,68 @@ def initialize_session_state():
5086
"""
5187
)
5288

53-
# 사이드바 UI 구성
54-
with st.sidebar:
55-
st.markdown("### 🤖 ChatBot 설정")
56-
st.divider()
57-
58-
# LLM 모델 선택 드롭다운
59-
selected_model = st.selectbox(
60-
"LLM 모델",
61-
options=list(ChatBot.AVAILABLE_MODELS.keys()),
62-
format_func=lambda x: ChatBot.AVAILABLE_MODELS[x],
63-
key="chatbot_model_select",
64-
)
89+
# 설정 로드
90+
config = load_config()
6591

66-
# 선택된 모델이 변경되면 ChatBot 업데이트
67-
if selected_model != st.session_state.chatbot_instance.model_name:
68-
st.session_state.chatbot_instance.update_model(selected_model)
69-
st.sidebar.success(
70-
f"모델이 '{ChatBot.AVAILABLE_MODELS[selected_model]}'로 변경되었습니다."
71-
)
92+
# 사이드바 UI 구성 (lang2sql.py와 동일한 구조)
93+
render_sidebar_data_source_selector(config)
94+
st.sidebar.divider()
95+
render_sidebar_llm_selector()
96+
st.sidebar.divider()
97+
render_sidebar_embedding_selector()
98+
st.sidebar.divider()
99+
render_sidebar_db_selector()
100+
st.sidebar.divider()
72101

102+
# ChatBot 전용 설정
103+
with st.sidebar:
104+
st.markdown("### 🤖 ChatBot 설정")
73105
st.divider()
74-
75-
# 채팅 세션 ID 입력 (대화 기록을 구분하는 용도)
76-
thread_id = st.text_input(
77-
"세션 ID",
78-
value="default",
79-
key="chatbot_thread_id",
80-
help="대화 기록을 구분하는 고유 ID입니다.",
81-
)
82-
83-
# 채팅 세션 시작/종료 버튼
84-
if not st.session_state.chatbot_started:
85-
# 세션이 시작되지 않았을 때: 시작 버튼 표시
86-
if st.button("대화 시작", use_container_width=True, type="primary"):
87-
st.session_state.chatbot_started = True
88-
st.session_state.chatbot_messages = []
89-
st.rerun()
90-
else:
91-
# 세션이 시작되었을 때: 종료 버튼 표시
92-
if st.button("대화 종료", use_container_width=True):
93-
st.session_state.chatbot_started = False
94-
st.rerun()
95-
96-
st.divider()
97-
98-
# 세션 히스토리를 JSON 형식으로 표시 (접힌 상태)
99-
with st.expander("대화 기록 (JSON)", expanded=False):
100-
st.json(st.session_state.chatbot_messages)
101-
102-
# 채팅 세션이 시작된 경우에만 채팅 인터페이스 표시
103-
if st.session_state.chatbot_started:
104-
# 첫 메시지가 없으면 환영 메시지 추가
105-
if not st.session_state.chatbot_messages:
106-
hello_message = "안녕하세요! 무엇을 도와드릴까요? 🤖"
107-
st.session_state.chatbot_messages = [
108-
{"role": "assistant", "content": hello_message}
109-
]
110-
111-
# 저장된 모든 메시지를 순서대로 표시
112-
for message in st.session_state.chatbot_messages:
113-
with st.chat_message(message["role"]):
114-
st.markdown(message["content"])
115-
116-
# 사용자 입력 처리
117-
if prompt := st.chat_input("메시지를 입력하세요"):
118-
# 사용자 메시지를 기록에 추가
119-
st.session_state.chatbot_messages.append({"role": "user", "content": prompt})
120-
with st.chat_message("user"):
121-
st.markdown(prompt)
122-
123-
# AI 응답 생성 및 표시
124-
with st.chat_message("assistant"):
125-
try:
126-
# ChatBot을 통해 응답 생성
127-
response = st.session_state.chatbot_instance.chat(prompt, thread_id)
128-
129-
# 응답 내용 추출
130-
response_content = response["messages"][-1].content
131-
132-
# 스트리밍 방식으로 응답 표시 (타이핑 효과)
133-
response_str = ""
134-
response_container = st.empty()
135-
for token in response_content:
136-
response_str += token
137-
response_container.markdown(response_str)
138-
139-
# AI 응답을 기록에 추가
140-
st.session_state.chatbot_messages.append(
141-
{"role": "assistant", "content": response_content}
142-
)
143-
except Exception as e:
144-
error_msg = f"오류가 발생했습니다: {str(e)}"
145-
st.error(error_msg)
146-
st.session_state.chatbot_messages.append(
147-
{"role": "assistant", "content": error_msg}
148-
)
149-
else:
150-
st.info("👈 왼쪽 사이드바에서 '대화 시작' 버튼을 눌러 ChatBot과 대화를 시작하세요!")
106+
thread_id = render_sidebar_chatbot_session_controller()
107+
108+
109+
# 첫 메시지가 없으면 환영 메시지 추가
110+
if not st.session_state.chatbot_messages:
111+
hello_message = "안녕하세요! 무엇을 도와드릴까요? 🤖"
112+
st.session_state.chatbot_messages = [
113+
{"role": "assistant", "content": hello_message}
114+
]
115+
116+
# 저장된 모든 메시지를 순서대로 표시
117+
for message in st.session_state.chatbot_messages:
118+
with st.chat_message(message["role"]):
119+
st.markdown(message["content"])
120+
121+
# 사용자 입력 처리
122+
if prompt := st.chat_input("메시지를 입력하세요"):
123+
# 사용자 메시지를 기록에 추가
124+
st.session_state.chatbot_messages.append({"role": "user", "content": prompt})
125+
with st.chat_message("user"):
126+
st.markdown(prompt)
127+
128+
# AI 응답 생성 및 표시
129+
with st.chat_message("assistant"):
130+
try:
131+
# ChatBot을 통해 응답 생성
132+
response = st.session_state.chatbot_instance.chat(prompt, thread_id)
133+
134+
# 응답 내용 추출
135+
response_content = response["messages"][-1].content
136+
137+
# 모델 정보 표시
138+
model_name = st.session_state.chatbot_instance.model_name
139+
st.caption(f"🤖 모델: {model_name}")
140+
141+
# 응답 표시
142+
st.markdown(response_content)
143+
144+
# AI 응답을 기록에 추가
145+
st.session_state.chatbot_messages.append(
146+
{"role": "assistant", "content": response_content}
147+
)
148+
except Exception as e:
149+
error_msg = f"오류가 발생했습니다: {str(e)}"
150+
st.error(error_msg)
151+
st.session_state.chatbot_messages.append(
152+
{"role": "assistant", "content": error_msg}
153+
)

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

0 commit comments

Comments
 (0)