From 0424cf4b31ff6ef55aae141ad85834b24465c041 Mon Sep 17 00:00:00 2001 From: sal Date: Mon, 1 Jun 2026 10:26:29 +0900 Subject: [PATCH] Add thinking process visualization to WebUI - Introduced "thinking box" UI to display intermediate thought processes. - Added CSS styling for the thinking box with scrollable and formatted design. - Updated response handling to show thinking progress and completion dynamically. - Enhanced Gradio outputs to include the new thinking box component. --- app.py | 58 ++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/app.py b/app.py index b9c4b85..6aa5225 100644 --- a/app.py +++ b/app.py @@ -105,31 +105,53 @@ async def respond(message, history, show_thinking, user_id, use_tts, run_ids): yield history, "", None, run_ids collected_run_id: str | None = None - tts_text = "" # 순수 답변만 누적 (메타 토큰 제외) + tts_text = "" # 순수 답변만 누적 (TTS용) + thinking_acc = "" # 사고 과정 누적 + thinking_active = False + + # 사고 과정 박스 초기화 + yield history, "", None, run_ids, gr.update(value="", visible=False) + try: async for token, run_id in api_client.chat(message, user_id, show_thinking): if run_id is not None: collected_run_id = run_id break + + if isinstance(token, dict) and "__thinking" in token: + thinking_active = True + thinking_acc += token["__thinking"] + thinking_md = f"🤔 **사고 중...**\n\n{thinking_acc}▌" + yield history, "", None, run_ids, gr.update(value=thinking_md, visible=True) + continue + + if thinking_active: + # 첫 답변 토큰 도착 — 사고 완료 표시 + thinking_active = False + yield history, "", None, run_ids, gr.update( + value=f"💭 **사고 완료**\n\n{thinking_acc}", visible=True + ) + if isinstance(token, dict) and "__meta" in token: display_token = token["__meta"] else: display_token = token tts_text += display_token history[-1]["content"] += display_token - yield history, "", None, run_ids + yield history, "", None, run_ids, gr.update() + except Exception as e: history[-1]["content"] += f"\n\n[오류: {e}]" - yield history, "", None, run_ids + yield history, "", None, run_ids, gr.update() return run_ids.append(collected_run_id) if use_tts: audio_path = await tts_speak(tts_text) - yield history, "", audio_path, run_ids + yield history, "", audio_path, run_ids, gr.update() else: - yield history, "", None, run_ids + yield history, "", None, run_ids, gr.update() def handle_feedback(like_data: gr.LikeData, history, run_ids, user_id): @@ -205,7 +227,22 @@ def delete_doc(source): # ── UI 구성 ────────────────────────────────────────────────────── -with gr.Blocks(title="율봇") as demo: +_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; +} +""" + +with gr.Blocks(title="율봇", css=_THINKING_CSS) as demo: gr.Markdown("# 율봇\n육아·금융 전문 AI 상담 도우미") user_state = gr.State(DEFAULT_USER) @@ -220,6 +257,11 @@ with gr.Blocks(title="율봇") as demo: scale=1, ) + thinking_box = gr.Markdown( + value="", + visible=False, + elem_classes=["thinking-box"], + ) chatbot = gr.Chatbot(label="율봇", height=500) with gr.Row(): msg_box = gr.Textbox( @@ -260,12 +302,12 @@ with gr.Blocks(title="율봇") as demo: send_btn.click( respond, inputs=[msg_box, chatbot, show_thinking, user_state, use_tts, run_ids_state], - outputs=[chatbot, msg_box, tts_output, run_ids_state], + outputs=[chatbot, msg_box, tts_output, run_ids_state, thinking_box], ) msg_box.submit( respond, inputs=[msg_box, chatbot, show_thinking, user_state, use_tts, run_ids_state], - outputs=[chatbot, msg_box, tts_output, run_ids_state], + outputs=[chatbot, msg_box, tts_output, run_ids_state, thinking_box], ) reset_btn.click(reset_chat, inputs=[user_state], outputs=[chatbot, run_ids_state])