import streamlit as st import streamlit.components.v1 as components import re import uuid import pandas as pd # ────────────────── 말풍선 생성 함수 # 색상 정의 PRIMARY_USER = "#e2f6e8" PRIMARY_BOT = "#f6f6f6" def render_message( message: str, sender: str = "bot", chips: list[str] | None = None, key: str | None = None, ) -> str | None: """ - `message` : 표시할 텍스트 (HTML 허용) - `sender` : "user" | "bot" - `chips` : 버튼 형태로 보여 줄 문자열 리스트 - return : 사용자가 클릭한 칩(문자열) 또는 None """ # 1) 말풍선 기본 속성 color = PRIMARY_USER if sender == "user" else PRIMARY_BOT align = "right" if sender == "user" else "left" # 개행 꼬리표 제거 message = str(message).rstrip() # 2) 풍선 출력 st.markdown( f'''

{message}
''', unsafe_allow_html=True, ) # 3) 칩 버튼이 있을 경우 if chips: prefix = f"{key or 'chips'}_{abs(hash(message))}" clicked = render_chip_buttons(chips, key_prefix=prefix) return clicked return None # ────────────────── 칩버튼 생성 함수 def render_chip_buttons(options, key_prefix="chip", selected_value=None): def slugify(text): return re.sub(r"[^a-zA-Z0-9]+", "-", str(text)).strip("-").lower() or "empty" session_key = f"{key_prefix}_selected" selected_value = st.session_state.get(session_key) # 스타일 적용 st.markdown(f""" """, unsafe_allow_html=True) clicked_val = None #cols = st.columns(len(options)) for idx, opt in enumerate(options): if opt is None or (isinstance(opt, float) and pd.isna(opt)) or str(opt).strip()=="": continue is_selected = (opt == selected_value) is_refresh_btn = "다른 여행지 보기" in str(opt) disabled = (opt == selected_value) and not is_refresh_btn label = f"{opt}" if is_selected else opt # stable key safe_opt = slugify(opt) stable_key = f"{key_prefix}_{idx}_{safe_opt}" if st.button(label, key=stable_key, disabled=disabled): clicked_val = opt return clicked_val # ────────────────── 메시지 리플레이 함수 def replay_log(chat_container=None): with chat_container: for sender, msg in st.session_state.chat_log: render_message(msg, sender=sender) # ────────────────── 메시지 로깅&생성 함수 def log_and_render(msg, sender, chat_container=None, key=None, chips=None): # 중복 방지 로직 sent_once = st.session_state.setdefault("sent_once", {}) if key and sent_once.get(key): return if key: sent_once[key] = True if st.session_state.chat_log and st.session_state.chat_log[-1] == (sender, msg): return # 로그에 저장 st.session_state.chat_log.append((sender, msg)) # 메시지 출력 with chat_container: rendered = render_message(msg, sender=sender, chips=chips, key=key) return rendered