import os, sys import html if sys.stdout is None: sys.stdout = open(os.devnull, "w") if sys.stderr is None: sys.stderr = open(os.devnull, "w") try: sys.stdout.reconfigure(errors='replace') except: pass try: sys.stderr.reconfigure(errors='replace') except: pass sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) import streamlit as st import streamlit.components.v1 as components import time, json, re, threading, queue from datetime import datetime from agentmain import GeneraticAgent st.set_page_config(page_title="Cowork", layout="wide") # ─── Anthropic Light Theme CSS ─── ANTHROPIC_CSS = """ """ ANTHROPIC_SELECTBOX_SCRIPT = """
""" @st.cache_resource def init(): agent = GeneraticAgent() if agent.llmclient is None: st.error("⚠️ 未配置任何可用的 LLM 接口,请在 mykey.py 中添加 sider_cookie 或 oai_apikey+oai_apibase 等信息后重启。") st.stop() else: threading.Thread(target=agent.run, daemon=True).start() return agent def build_dynamic_font_css(scale_percent: float) -> str: root_percent = max(100.0, min(200.0, float(scale_percent))) rem_scale = root_percent / 100.0 return f""" """ def build_dynamic_font_update_script(scale_percent: float) -> str: css = json.dumps(build_dynamic_font_css(scale_percent)) return f""" """ def build_header_agent_badge_script() -> str: return """ """ agent = init() def init_session_state(): for key, value in { 'agent_name': 'GenericAgent', 'streaming': False, 'stopping': False, 'display_queue': None, 'partial_response': '', 'reply_ts': '', 'current_prompt': '', 'selected_llm_idx': agent.llm_no, 'autonomous_enabled': False, 'messages': [], }.items(): st.session_state.setdefault(key, value) init_session_state() # Inject Anthropic theme st.markdown(ANTHROPIC_CSS, unsafe_allow_html=True) st.markdown(build_dynamic_font_css(110.0), unsafe_allow_html=True) components.html(ANTHROPIC_SELECTBOX_SCRIPT, height=0, width=0) components.html(build_header_agent_badge_script(), height=0, width=0) st.session_state.agent_name = 'Generic Agent' with st.chat_message("assistant"): st.markdown(f'
{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
', unsafe_allow_html=True) st.write("欢迎使用GenericAgent~") @st.fragment def render_sidebar(): llm_options, current_idx = agent.list_llms(), agent.llm_no st.session_state.selected_llm_idx = current_idx llm_labels = {idx: f"{idx}: {(name or '').strip()}" for idx, name, _ in llm_options} st.caption(f"当前使用的LLM为:{current_idx}: {agent.get_llm_name()}", help="可在下方选择链路") st.markdown(f'
{html.escape(max(llm_labels.values(), key=len, default=""))}
', unsafe_allow_html=True) selected_idx = st.selectbox("选择链路:", [idx for idx, _, _ in llm_options], index=next((i for i, (idx, _, _) in enumerate(llm_options) if idx == current_idx), 0), format_func=llm_labels.get, key="sidebar_llm_select") if selected_idx != current_idx: agent.next_llm(selected_idx) st.session_state.selected_llm_idx = selected_idx st.toast(f"已切换到备用链路:{llm_labels[selected_idx]}") st.rerun() st.divider() if st.button("重新注入System Prompt"): agent.llmclient.last_tools = '' st.toast("下次将重新注入System Prompt") with st.sidebar: render_sidebar() def start_agent_task(prompt): st.session_state.display_queue = agent.put_task(prompt, source="user") st.session_state.streaming, st.session_state.stopping, st.session_state.partial_response = True, False, '' st.session_state.reply_ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S") st.session_state.current_prompt = prompt def poll_agent_output(max_items=20): q = st.session_state.display_queue if q is None: st.session_state.streaming = False return False done = False for _ in range(max_items): try: item = q.get_nowait() except queue.Empty: break if 'next' in item: st.session_state.partial_response = item['next'] if 'done' in item: st.session_state.partial_response = item['done'] done = True break if done: st.session_state.streaming = st.session_state.stopping = False; st.session_state.display_queue = None return done def _get_response_segments(text): return [p for p in re.split(r'(?=\*\*LLM Running \(Turn \d+\) \.\.\.\*\*)', text) if p.strip()] or [text] def render_message(role, content, ts='', unsafe_allow_html=True): with st.chat_message(role): if ts: st.markdown(f'
{ts}
', unsafe_allow_html=True) st.markdown(content, unsafe_allow_html=unsafe_allow_html) def finish_streaming_message(): reply_ts = st.session_state.reply_ts st.session_state.messages.extend({"role": "assistant", "content": seg, "time": reply_ts} for seg in _get_response_segments(st.session_state.partial_response)) st.session_state.last_reply_time = int(time.time()) st.session_state.partial_response = st.session_state.reply_ts = st.session_state.current_prompt = '' def render_streaming_area(): if not st.session_state.streaming: return with st.container(): st.markdown('', unsafe_allow_html=True) if st.button("⏹️ 停止生成", type="primary"): agent.abort(); st.session_state.stopping = True; st.toast("已发送停止信号"); st.rerun() reply_ts = st.session_state.reply_ts with st.empty().container(): segments = _get_response_segments(st.session_state.partial_response) for i, seg in enumerate(segments): render_message("assistant", seg + ("" if i < len(segments) - 1 else "▌"), ts=reply_ts, unsafe_allow_html=False) if poll_agent_output(): finish_streaming_message() else: time.sleep(0.2) st.rerun() for msg in st.session_state.messages: render_message(msg["role"], msg["content"], ts=msg.get("time", ""), unsafe_allow_html=True) if st.session_state.streaming: render_streaming_area() if prompt := st.chat_input("请输入指令", disabled=st.session_state.streaming): st.session_state.messages.append({"role": "user", "content": prompt, "time": datetime.now().strftime("%Y-%m-%d %H:%M:%S")}) start_agent_task(prompt) st.rerun()