diff --git a/app.py b/app.py index a4b24e9..082e8bb 100644 --- a/app.py +++ b/app.py @@ -8,6 +8,7 @@ YOULBOT_API_TOKEN= ← api.py에 API_TOKEN 설정 시 동일 값 """ import asyncio +import html as _html import os import platform import subprocess @@ -95,14 +96,14 @@ async def tts_speak(text: str) -> str | None: async def respond(message, history, show_thinking, user_id, use_tts, run_ids): if not message.strip(): - yield history, "", None, run_ids, gr.update() + yield history, "", None, run_ids, "" return history = list(history) run_ids = list(run_ids) history.append({"role": "user", "content": message}) history.append({"role": "assistant", "content": ""}) - yield history, "", None, run_ids, gr.update(value="", visible=False) + yield history, "", None, run_ids, "" # thinking_box 초기화 collected_run_id: str | None = None tts_text = "" # 순수 답변만 누적 (TTS용) @@ -118,29 +119,23 @@ async def respond(message, history, show_thinking, user_id, use_tts, run_ids): # 사고 과정(LLM thinking) — 박스에 추가 if isinstance(token, dict) and "__thinking" in token: thinking_acc += token["__thinking"] - yield history, "", None, run_ids, gr.update( - value=f"🤔 **분석 중...**\n\n{thinking_acc}▌", visible=True - ) + yield history, "", None, run_ids, _thinking_html(thinking_acc) continue # 진행 로그(LangGraph, 검색 등) — 박스에 추가 (챗봇에는 표시 안 함) if isinstance(token, dict) and "__meta" in token: thinking_acc += token["__meta"] - yield history, "", None, run_ids, gr.update( - value=f"🤔 **분석 중...**\n\n{thinking_acc}▌", visible=True - ) + yield history, "", None, run_ids, _thinking_html(thinking_acc) continue # 첫 답변 토큰 도착 — 박스를 완료 상태로 전환 if thinking_acc and not thinking_finalized: thinking_finalized = True - yield history, "", None, run_ids, gr.update( - value=f"💭 **분석 완료**\n\n{thinking_acc}", visible=True - ) + yield history, "", None, run_ids, _thinking_html(thinking_acc, done=True) tts_text += token history[-1]["content"] += token - yield history, "", None, run_ids, gr.update() + yield history, "", None, run_ids, gr.update() # thinking_box 유지 except Exception as e: history[-1]["content"] += f"\n\n[오류: {e}]" @@ -227,22 +222,25 @@ def delete_doc(source): # ── UI 구성 ────────────────────────────────────────────────────── -_THINKING_CSS = """ -.thinking-box { - background: #f9f9f9; - border-left: 3px solid #bbb; - border-radius: 6px; - padding: 10px 14px; - margin-bottom: 6px; - max-height: 220px; - overflow-y: auto; - font-size: 0.85em; - color: #555; - white-space: pre-wrap; -} -""" +_THINKING_STYLE = ( + "background:#f9f9f9;border-left:3px solid #bbb;border-radius:6px;" + "padding:10px 14px;max-height:220px;overflow-y:auto;" + "font-size:0.85em;color:#555;white-space:pre-wrap;margin-bottom:6px;" +) -with gr.Blocks(title="율봇", css=_THINKING_CSS) as demo: + +def _thinking_html(text: str, done: bool = False) -> str: + icon = "💭" if done else "🤔" + label = "분석 완료" if done else "분석 중..." + cursor = "" if done else " ▌" + return ( + f'
' + f"{icon} {label}

" + f"{_html.escape(text)}{cursor}
" + ) + + +with gr.Blocks(title="율봇") as demo: gr.Markdown("# 율봇\n육아·금융 전문 AI 상담 도우미") user_state = gr.State(DEFAULT_USER) @@ -257,11 +255,7 @@ with gr.Blocks(title="율봇", css=_THINKING_CSS) as demo: scale=1, ) - thinking_box = gr.Markdown( - value="", - visible=False, - elem_classes=["thinking-box"], - ) + thinking_box = gr.HTML(value="") chatbot = gr.Chatbot(label="율봇", height=500) with gr.Row(): msg_box = gr.Textbox(