diff --git a/agentmain.py b/agentmain.py index 4ff0d13..97c4132 100644 --- a/agentmain.py +++ b/agentmain.py @@ -127,7 +127,7 @@ class GeneraticAgent: for chunk in gen: if self.stop_sig: break full_resp += chunk - if len(full_resp) - last_pos > 50: + if len(full_resp) - last_pos > 50 or 'LLM Running' in chunk: display_queue.put({'next': full_resp[last_pos:] if self.inc_out else full_resp, 'source': source}) last_pos = len(full_resp) if self.inc_out and last_pos < len(full_resp): display_queue.put({'next': full_resp[last_pos:], 'source': source}) diff --git a/frontends/stapp.py b/frontends/stapp.py index 8409a7c..533ea60 100644 --- a/frontends/stapp.py +++ b/frontends/stapp.py @@ -67,9 +67,11 @@ def render_sidebar(): with st.sidebar: render_sidebar() def fold_turns(text): - parts = re.split(r'(\**LLM Running \(Turn \d+\) \.\.\.*\**)', text) - if len(parts) < 4: return text - result = parts[0] + """Return list of segments: [{'type':'text','content':...}, {'type':'fold','title':...,'content':...}]""" + parts = re.split(r'(\**LLM Running \(Turn \d+\) \.\.\.\*\**)', text) + if len(parts) < 4: return [{'type': 'text', 'content': text}] + segments = [] + if parts[0].strip(): segments.append({'type': 'text', 'content': parts[0]}) turns = [] for i in range(1, len(parts), 2): marker = parts[i].strip('*') @@ -77,12 +79,22 @@ def fold_turns(text): turns.append((marker, content)) for idx, (marker, content) in enumerate(turns): if idx < len(turns) - 1: - m = re.search(r'\s*(.*?)\|*', content, re.DOTALL) - title = m.group(1).strip() if m else marker - result += f'
{title}\n\n{content}\n
\n\n' - else: - result += marker + content - return result + m = re.search(r'\s*(.*?)\s*', content, re.DOTALL) + if m: + title = m.group(1).strip() + title = title.split('\n')[0] + if len(title) > 50: title = title[:50] + '...' + else: title = marker + 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: + if seg['type'] == 'fold': + with c.expander(seg['title'], expanded=False): st.markdown(seg['content']) + else: c.markdown(seg['content']) def agent_backend_stream(prompt): display_queue = agent.put_task(prompt, source="user") @@ -102,10 +114,8 @@ def agent_backend_stream(prompt): if "messages" not in st.session_state: st.session_state.messages = [] for msg in st.session_state.messages: with st.chat_message(msg["role"]): - if msg["role"] == "assistant": - st.markdown(fold_turns(msg["content"]), unsafe_allow_html=True) - else: - st.markdown(msg["content"], unsafe_allow_html=False) + if msg["role"] == "assistant": render_segments(fold_turns(msg["content"])) + else: st.markdown(msg["content"]) # IME composition fix (macOS only) - prevents Enter from submitting during CJK input if os.name != 'nt': @@ -114,17 +124,26 @@ if os.name != 'nt': if prompt := st.chat_input("请输入指令"): st.session_state.messages.append({"role": "user", "content": prompt}) - with st.chat_message("user"): st.markdown(prompt, unsafe_allow_html=False) # 小心 XSS + with st.chat_message("user"): st.markdown(prompt) with st.chat_message("assistant"): - message_placeholder = st.empty() + turn_placeholders = [] + rendered_segments = [] response = '' for response in agent_backend_stream(prompt): - message_placeholder.markdown(fold_turns(response) + "...", unsafe_allow_html=True) - message_placeholder.markdown(fold_turns(response), unsafe_allow_html=True) + 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 st.session_state.messages.append({"role": "assistant", "content": response}) st.session_state.last_reply_time = int(time.time()) if st.session_state.autonomous_enabled: st.markdown(f"""""", unsafe_allow_html=True) - diff --git a/launch.pyw b/launch.pyw index 808316e..a951233 100644 --- a/launch.pyw +++ b/launch.pyw @@ -1,6 +1,6 @@ import webview, threading, subprocess, sys, time, os, ctypes, atexit, socket, random -WINDOW_WIDTH, WINDOW_HEIGHT, RIGHT_PADDING, TOP_PADDING = 600, 900, 0, 100 +WINDOW_WIDTH, WINDOW_HEIGHT, RIGHT_PADDING, TOP_PADDING = 700, 900, 0, 100 script_dir = os.path.dirname(os.path.abspath(__file__)) frontends_dir = os.path.join(script_dir, "frontends")