Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -45,6 +45,7 @@ os.environ.setdefault("HF_HUB_ENABLE_HF_TRANSFER", "1")
|
|
| 45 |
from huggingface_hub import hf_hub_download
|
| 46 |
import pandas as pd
|
| 47 |
import streamlit as st
|
|
|
|
| 48 |
from streamlit.components.v1 import html
|
| 49 |
from css import render_message, render_chip_buttons, log_and_render, replay_log, _get_colors
|
| 50 |
|
|
@@ -149,7 +150,174 @@ from chat_a import (
|
|
| 149 |
format_summary_tags_custom,
|
| 150 |
make_companion_age_message
|
| 151 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 152 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 153 |
# μ§μ° μ΄κΈ°ν: import μμ μλ λ°μ΄ν° μ κ·Ό κΈμ§, μ¬κΈ°μ ν λ²λ§ μ£Όμ
|
| 154 |
init_datasets(
|
| 155 |
travel_df=travel_df,
|
|
@@ -693,6 +861,11 @@ def region_ui(travel_df, external_score_df, festival_df, weather_df, package_df,
|
|
| 693 |
log_and_render("νμνμ€ λ μΈμ λ μ§ λ μ°Ύμμ£ΌμΈμ! βοΈ",
|
| 694 |
sender="bot", chat_container=chat_container,
|
| 695 |
key="goodbye")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 696 |
|
| 697 |
# βββββββββββββββββββββββββββββββββββββ intent λͺ¨λ
|
| 698 |
def intent_ui(travel_df, external_score_df, festival_df, weather_df, package_df,
|
|
@@ -1045,6 +1218,11 @@ def intent_ui(travel_df, external_score_df, festival_df, weather_df, package_df,
|
|
| 1045 |
log_and_render("νμνμ€ λ μΈμ λ μ§ λ μ°Ύμμ£ΌμΈμ! βοΈ",
|
| 1046 |
sender="bot", chat_container=chat_container,
|
| 1047 |
key="goodbye")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1048 |
|
| 1049 |
# βββββββββββββββββββββββββββββββββββββ emotion λͺ¨λ
|
| 1050 |
def emotion_ui(travel_df, external_score_df, festival_df, weather_df, package_df,
|
|
@@ -1469,6 +1647,11 @@ def emotion_ui(travel_df, external_score_df, festival_df, weather_df, package_df
|
|
| 1469 |
log_and_render("νμνμ€ λ μΈμ λ μ§ λ μ°Ύμμ£ΌμΈμ! βοΈ",
|
| 1470 |
sender="bot", chat_container=chat_container,
|
| 1471 |
key="goodbye")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1472 |
|
| 1473 |
# βββββββββββββββββββββββββββββββββββββ unknown λͺ¨λ
|
| 1474 |
def unknown_ui(country, city, chat_container, log_and_render):
|
|
@@ -1497,6 +1680,10 @@ def main():
|
|
| 1497 |
init_session()
|
| 1498 |
chat_container = st.container()
|
| 1499 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1500 |
# ποΈ λ§νμ /νμ μ΅μ
(β’, β£)
|
| 1501 |
st.sidebar.subheader("βοΈ λν νμ")
|
| 1502 |
st.sidebar.selectbox("ν
λ§", ["νΌμ€νμΉμ€", "μ€μΉ΄μ΄λΈλ£¨", "ν¬λ¦¬λ―Έμ€νΈ"], key="bubble_theme")
|
|
|
|
| 45 |
from huggingface_hub import hf_hub_download
|
| 46 |
import pandas as pd
|
| 47 |
import streamlit as st
|
| 48 |
+
import requests
|
| 49 |
from streamlit.components.v1 import html
|
| 50 |
from css import render_message, render_chip_buttons, log_and_render, replay_log, _get_colors
|
| 51 |
|
|
|
|
| 150 |
format_summary_tags_custom,
|
| 151 |
make_companion_age_message
|
| 152 |
)
|
| 153 |
+
OLLAMA_HOST = os.getenv("OLLAMA_HOST", "http://localhost:11434")
|
| 154 |
+
OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "gemma2:9b")
|
| 155 |
+
OLLAMA_TIMEOUT = int(os.getenv("OLLAMA_TIMEOUT", "60"))
|
| 156 |
+
|
| 157 |
+
KOREAN_SYSTEM_PROMPT = """λΉμ μ νκ΅μ΄ μ΄μμ€ν΄νΈμ
λλ€. νμ νκ΅μ΄λ‘ λ΅νμΈμ."""
|
| 158 |
+
|
| 159 |
+
STRUCTURED_EXTRACTION_SYSTEM = """\
|
| 160 |
+
You are a travel assistant that extracts structured fields from Korean user queries.
|
| 161 |
+
Return ONLY a valid JSON object:
|
| 162 |
+
{
|
| 163 |
+
"emotion": "happy|sad|stressed|excited|tired|none",
|
| 164 |
+
"intent": "beach|hiking|shopping|food|museum|relaxing|none",
|
| 165 |
+
"country_hint": "",
|
| 166 |
+
"city_hint": "",
|
| 167 |
+
"themes_hint": ["<0..3 words>"],
|
| 168 |
+
"notes": "<very short reasoning in Korean>"
|
| 169 |
+
}
|
| 170 |
+
If unknown, use "none" or "" and NEVER add extra text outside JSON.
|
| 171 |
+
"""
|
| 172 |
+
|
| 173 |
+
def _build_structured_user_prompt(user_text: str) -> str:
|
| 174 |
+
# λΆνμν λν μμ΄, λͺ¨λΈμ΄ JSONλ§ λ΄λλ‘ κΉλν μ λ¬
|
| 175 |
+
return user_text.strip()
|
| 176 |
+
|
| 177 |
+
def _call_ollama_chat(
|
| 178 |
+
messages,
|
| 179 |
+
model=OLLAMA_MODEL,
|
| 180 |
+
temperature=0.8, top_p=0.9, top_k=40, repeat_penalty=1.1,
|
| 181 |
+
system_prompt=None
|
| 182 |
+
):
|
| 183 |
+
url = f"{OLLAMA_HOST}/api/chat"
|
| 184 |
+
_msgs = []
|
| 185 |
+
if system_prompt:
|
| 186 |
+
_msgs.append({"role": "system", "content": system_prompt})
|
| 187 |
+
_msgs.extend(messages)
|
| 188 |
+
|
| 189 |
+
payload = {
|
| 190 |
+
"model": model,
|
| 191 |
+
"messages": _msgs,
|
| 192 |
+
"options": {
|
| 193 |
+
"temperature": temperature,
|
| 194 |
+
"top_p": top_p,
|
| 195 |
+
"top_k": top_k,
|
| 196 |
+
"repeat_penalty": repeat_penalty,
|
| 197 |
+
},
|
| 198 |
+
"stream": False,
|
| 199 |
+
}
|
| 200 |
+
try:
|
| 201 |
+
r = requests.post(url, json=payload, timeout=OLLAMA_TIMEOUT)
|
| 202 |
+
r.raise_for_status()
|
| 203 |
+
return (r.json().get("message") or {}).get("content", "") or ""
|
| 204 |
+
except requests.RequestException:
|
| 205 |
+
return ""
|
| 206 |
+
|
| 207 |
+
def _llm_structured_extract(user_text: str):
|
| 208 |
+
out = _call_ollama_chat(
|
| 209 |
+
[
|
| 210 |
+
{"role": "system", "content": STRUCTURED_EXTRACTION_SYSTEM},
|
| 211 |
+
{"role": "user", "content": _build_structured_user_prompt(user_text)}
|
| 212 |
+
],
|
| 213 |
+
system_prompt=None # μμμ systemμΌλ‘ μ΄λ―Έ λ£μμ
|
| 214 |
+
)
|
| 215 |
+
try:
|
| 216 |
+
data = json.loads(out)
|
| 217 |
+
except Exception:
|
| 218 |
+
data = {}
|
| 219 |
+
data.setdefault("emotion", "none")
|
| 220 |
+
data.setdefault("intent", "none")
|
| 221 |
+
data.setdefault("country_hint", "")
|
| 222 |
+
data.setdefault("city_hint", "")
|
| 223 |
+
data.setdefault("themes_hint", [])
|
| 224 |
+
data.setdefault("notes", "")
|
| 225 |
+
return data
|
| 226 |
+
|
| 227 |
+
# ββββββββββββββββββββββββββββββββ Streamlitμ© LLM λͺ¨λ UI ββββββββββββββββββββββββββββββββ
|
| 228 |
+
def _ensure_llm_state():
|
| 229 |
+
st.session_state.setdefault("llm_mode", False)
|
| 230 |
+
st.session_state.setdefault("llm_history", []) # [{'role':'user'|'assistant', 'content': str}, ...]
|
| 231 |
+
st.session_state.setdefault("llm_intro_needed", False)
|
| 232 |
+
st.session_state.setdefault("llm_input", "")
|
| 233 |
+
|
| 234 |
+
def render_llm_followup(chat_container):
|
| 235 |
+
"""
|
| 236 |
+
μΆμ² νλ‘μ° μ’
λ£ ν μλ μ§μ
νλ μμ μ§λ¬Έ(LLM) λͺ¨λ UI.
|
| 237 |
+
"""
|
| 238 |
+
_ensure_llm_state()
|
| 239 |
+
MAX_TURNS = 6
|
| 240 |
+
|
| 241 |
+
if st.session_state.get("llm_intro_needed"):
|
| 242 |
+
log_and_render(
|
| 243 |
+
"π§ μ΄μ λΆν°λ μμ μ§λ¬Έ λͺ¨λμμ. μ¬ν μΈμλ λλ νκ΅μ΄λ‘ λ¬Όμ΄λ³΄μΈμ!",
|
| 244 |
+
sender="bot",
|
| 245 |
+
chat_container=chat_container,
|
| 246 |
+
key=f"llm_intro_{random.randint(1,999999)}"
|
| 247 |
+
)
|
| 248 |
+
st.session_state["llm_intro_needed"] = False
|
| 249 |
+
|
| 250 |
+
# μ
λ ₯μ°½
|
| 251 |
+
q = st.text_input(
|
| 252 |
+
"LLM μ§λ¬Έ",
|
| 253 |
+
placeholder="무μμ΄λ λ¬Όμ΄λ³΄μΈμ (μ’
λ£νλ €λ©΄ 'μ’
λ£' μ
λ ₯)",
|
| 254 |
+
key="llm_input"
|
| 255 |
+
)
|
| 256 |
+
|
| 257 |
+
# λμ
|
| 258 |
+
if q:
|
| 259 |
+
# μ’
λ£ ν€μλ
|
| 260 |
+
if q.strip() in ("μ’
λ£", "quit", "exit"):
|
| 261 |
+
st.session_state["llm_mode"] = False
|
| 262 |
+
st.session_state["llm_input"] = ""
|
| 263 |
+
log_and_render(
|
| 264 |
+
"LLM λͺ¨λλ₯Ό μ’
λ£ν κ²μ. νμνμ€ λ μΈμ λ λ€μ μ§λ¬Έν΄ μ£ΌμΈμ! β¨",
|
| 265 |
+
sender="bot",
|
| 266 |
+
chat_container=chat_container,
|
| 267 |
+
key=f"llm_end_{random.randint(1,999999)}"
|
| 268 |
+
)
|
| 269 |
+
st.rerun()
|
| 270 |
+
return
|
| 271 |
+
|
| 272 |
+
# μ¬μ©μ λ²λΈ
|
| 273 |
+
log_and_render(
|
| 274 |
+
q,
|
| 275 |
+
sender="user",
|
| 276 |
+
chat_container=chat_container,
|
| 277 |
+
key=f"llm_user_{random.randint(1,999999)}"
|
| 278 |
+
)
|
| 279 |
+
st.session_state.llm_history.append({"role": "user", "content": q})
|
| 280 |
+
|
| 281 |
+
# μ΅κ·Ό νμ€ν λ¦¬λ‘ LLM νΈμΆ
|
| 282 |
+
msgs = st.session_state.llm_history[-(MAX_TURNS-1):]
|
| 283 |
+
a = _call_ollama_chat(
|
| 284 |
+
messages=msgs,
|
| 285 |
+
system_prompt=KOREAN_SYSTEM_PROMPT,
|
| 286 |
+
temperature=0.8, top_p=0.9, top_k=40, repeat_penalty=1.1
|
| 287 |
+
)
|
| 288 |
|
| 289 |
+
if not a:
|
| 290 |
+
log_and_render(
|
| 291 |
+
"β οΈ LLM μλ΅μ λ°μ§ λͺ»νμ΅λλ€. Ollama μλ² μνλ₯Ό νμΈν΄ μ£ΌμΈμ.",
|
| 292 |
+
sender="bot",
|
| 293 |
+
chat_container=chat_container,
|
| 294 |
+
key=f"llm_err_{random.randint(1,999999)}"
|
| 295 |
+
)
|
| 296 |
+
else:
|
| 297 |
+
log_and_render(
|
| 298 |
+
a,
|
| 299 |
+
sender="bot",
|
| 300 |
+
chat_container=chat_container,
|
| 301 |
+
key=f"llm_bot_{random.randint(1,999999)}"
|
| 302 |
+
)
|
| 303 |
+
st.session_state.llm_history.append({"role": "assistant", "content": a})
|
| 304 |
+
|
| 305 |
+
# μ
λ ₯ μ΄κΈ°ν
|
| 306 |
+
st.session_state["llm_input"] = ""
|
| 307 |
+
|
| 308 |
+
# νλ¨ μ’
λ£ λ²νΌ
|
| 309 |
+
cols = st.columns(3)
|
| 310 |
+
with cols[0]:
|
| 311 |
+
if st.button("π LLM λͺ¨λ μ’
λ£"):
|
| 312 |
+
st.session_state["llm_mode"] = False
|
| 313 |
+
log_and_render(
|
| 314 |
+
"LLM λͺ¨λλ₯Ό μ’
λ£ν κ²μ. λ€μμ λ λ§λμ! π",
|
| 315 |
+
sender="bot",
|
| 316 |
+
chat_container=chat_container,
|
| 317 |
+
key=f"llm_end_btn_{random.randint(1,999999)}"
|
| 318 |
+
)
|
| 319 |
+
st.rerun()
|
| 320 |
+
|
| 321 |
# μ§μ° μ΄κΈ°ν: import μμ μλ λ°μ΄ν° μ κ·Ό κΈμ§, μ¬κΈ°μ ν λ²λ§ μ£Όμ
|
| 322 |
init_datasets(
|
| 323 |
travel_df=travel_df,
|
|
|
|
| 861 |
log_and_render("νμνμ€ λ μΈμ λ μ§ λ μ°Ύμμ£ΌμΈμ! βοΈ",
|
| 862 |
sender="bot", chat_container=chat_container,
|
| 863 |
key="goodbye")
|
| 864 |
+
|
| 865 |
+
# β
μ¬κΈ°μ LLM λͺ¨λλ‘ μλ μ ν
|
| 866 |
+
st.session_state["llm_mode"] = True
|
| 867 |
+
st.session_state["llm_intro_needed"] = True
|
| 868 |
+
st.rerun()
|
| 869 |
|
| 870 |
# βββββββββββββββββββββββββββββββββββββ intent λͺ¨λ
|
| 871 |
def intent_ui(travel_df, external_score_df, festival_df, weather_df, package_df,
|
|
|
|
| 1218 |
log_and_render("νμνμ€ λ μΈμ λ μ§ λ μ°Ύμμ£ΌμΈμ! βοΈ",
|
| 1219 |
sender="bot", chat_container=chat_container,
|
| 1220 |
key="goodbye")
|
| 1221 |
+
|
| 1222 |
+
# β
μ¬κΈ°μ LLM λͺ¨λλ‘ μλ μ ν
|
| 1223 |
+
st.session_state["llm_mode"] = True
|
| 1224 |
+
st.session_state["llm_intro_needed"] = True
|
| 1225 |
+
st.rerun()
|
| 1226 |
|
| 1227 |
# βββββββββββββββββββββββββββββββββββββ emotion λͺ¨λ
|
| 1228 |
def emotion_ui(travel_df, external_score_df, festival_df, weather_df, package_df,
|
|
|
|
| 1647 |
log_and_render("νμνμ€ λ μΈμ λ μ§ λ μ°Ύμμ£ΌμΈμ! βοΈ",
|
| 1648 |
sender="bot", chat_container=chat_container,
|
| 1649 |
key="goodbye")
|
| 1650 |
+
|
| 1651 |
+
# β
μ¬κΈ°μ LLM λͺ¨λλ‘ μλ μ ν
|
| 1652 |
+
st.session_state["llm_mode"] = True
|
| 1653 |
+
st.session_state["llm_intro_needed"] = True
|
| 1654 |
+
st.rerun()
|
| 1655 |
|
| 1656 |
# βββββββββββββββββββββββββββββββββββββ unknown λͺ¨λ
|
| 1657 |
def unknown_ui(country, city, chat_container, log_and_render):
|
|
|
|
| 1680 |
init_session()
|
| 1681 |
chat_container = st.container()
|
| 1682 |
|
| 1683 |
+
if st.session_state.get("llm_mode"):
|
| 1684 |
+
render_llm_followup(chat_container)
|
| 1685 |
+
return
|
| 1686 |
+
|
| 1687 |
# ποΈ λ§νμ /νμ μ΅μ
(β’, β£)
|
| 1688 |
st.sidebar.subheader("βοΈ λν νμ")
|
| 1689 |
st.sidebar.selectbox("ν
λ§", ["νΌμ€νμΉμ€", "μ€μΉ΄μ΄λΈλ£¨", "ν¬λ¦¬λ―Έμ€νΈ"], key="bubble_theme")
|