From 4a5043a658b961623c2bb4ed800ef1185a5dfcd7 Mon Sep 17 00:00:00 2001 From: Liang Jiaqing Date: Fri, 3 Apr 2026 12:14:08 +0800 Subject: [PATCH] Refactor: unify render_segments logic, add streaming cursor animation, and clean up unused container parameter --- frontends/stapp.py | 41 +++++++++++++++++++---------------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/frontends/stapp.py b/frontends/stapp.py index 533ea60..10525b4 100644 --- a/frontends/stapp.py +++ b/frontends/stapp.py @@ -74,7 +74,7 @@ def fold_turns(text): if parts[0].strip(): segments.append({'type': 'text', 'content': parts[0]}) turns = [] for i in range(1, len(parts), 2): - marker = parts[i].strip('*') + marker = parts[i] content = parts[i+1] if i+1 < len(parts) else '' turns.append((marker, content)) for idx, (marker, content) in enumerate(turns): @@ -84,17 +84,25 @@ def fold_turns(text): title = m.group(1).strip() title = title.split('\n')[0] if len(title) > 50: title = title[:50] + '...' - else: title = marker + else: title = marker.strip('*') segments.append({'type': 'fold', 'title': title, 'content': content}) else: segments.append({'type': 'text', 'content': marker + content}) return segments -def render_segments(segments, container=None): - """Render fold_turns output using st.expander (no unsafe_allow_html needed).""" - c = container or st - for seg in segments: +def render_segments(segments, placeholders=None, rendered_cache=None, suffix='', force_text=False): + def _render_seg(target, seg, suf=''): if seg['type'] == 'fold': - with c.expander(seg['title'], expanded=False): st.markdown(seg['content']) - else: c.markdown(seg['content']) + with target.expander(seg['title'], expanded=False): st.markdown(seg['content']) + else: target.markdown(seg['content'] + suf, unsafe_allow_html=not not suf) + if placeholders is not None: + while len(placeholders) < len(segments): + placeholders.append(st.empty()); rendered_cache.append(None) + for i, seg in enumerate(segments): + if rendered_cache[i] != (seg, suffix): + if not force_text or seg['type'] == 'text': + with placeholders[i].container(): _render_seg(st, seg, suffix) + rendered_cache[i] = (seg, suffix) + else: + for seg in segments: _render_seg(st, seg) def agent_backend_stream(prompt): display_queue = agent.put_task(prompt, source="user") @@ -127,21 +135,10 @@ if prompt := st.chat_input("请输入指令"): with st.chat_message("user"): st.markdown(prompt) with st.chat_message("assistant"): - turn_placeholders = [] - rendered_segments = [] - response = '' + turns = []; cache = []; response = '' for response in agent_backend_stream(prompt): - segments = fold_turns(response) - while len(turn_placeholders) < len(segments): - turn_placeholders.append(st.empty()) - rendered_segments.append(None) - for i, seg in enumerate(segments): - if rendered_segments[i] != seg: - with turn_placeholders[i].container(): - if seg['type'] == 'fold': - with st.expander(seg['title']): st.markdown(seg['content']) - else: st.markdown(seg['content']) - rendered_segments[i] = seg + render_segments(fold_turns(response), placeholders=turns, rendered_cache=cache, suffix='') + render_segments(fold_turns(response), placeholders=turns, rendered_cache=cache, force_text=True) st.session_state.messages.append({"role": "assistant", "content": response}) st.session_state.last_reply_time = int(time.time())