1313
1414import streamlit as st
1515import time , json , re , threading , queue
16+ from datetime import timedelta
1617from agentmain import GeneraticAgent
1718import chatapp_common # activate /continue command (monkey patches GeneraticAgent)
1819from continue_cmd import handle_frontend_command , reset_conversation , list_sessions , extract_ui_messages
3435I18N = {
3536 'zh' : {
3637 'force_stop' : '强行停止任务' ,
37- 'reinject_tools' : '重新注入工具' ,
3838 'desktop_pet' : '🐱 桌面宠物' ,
39+ 'suggest_btn' : '🎯 给我找点事做' ,
40+ 'suggest_prompt' : '按照自主行动的规划部分,充分分析我的情况,给我生成一批TODO,务必让我感兴趣' ,
41+ 'auto_start' : '开始空闲自主行动' ,
42+ 'auto_pause' : '⏸️ 禁止自主行动' ,
43+ 'auto_enable' : '▶️ 允许自主行动' ,
44+ 'auto_on_cap' : '🟢 自主行动运行中,会在你离开它30分钟后自动进行' ,
45+ 'auto_off_cap' : '🔴 自主行动已停止' ,
46+ 'auto_prompt' : '[AUTO]🤖 用户已经离开超过30分钟,作为自主智能体,请阅读自动化sop,执行自动任务。' ,
3947 },
4048 'en' : {
4149 'force_stop' : 'Force Stop' ,
42- 'reinject_tools' : 'Reinject Tools' ,
4350 'desktop_pet' : '🐱 Desktop Pet' ,
51+ 'suggest_btn' : '🎯 Suggest tasks' ,
52+ 'suggest_prompt' : 'Following the planning section of autonomous sop, analyze my situation thoroughly and generate a batch of TODOs that will interest me.' ,
53+ 'auto_start' : 'Start idle auto-action' ,
54+ 'auto_pause' : '⏸️ Pause auto-action' ,
55+ 'auto_enable' : '▶️ Enable auto-action' ,
56+ 'auto_on_cap' : '🟢 Auto-action enabled, triggers after 30min idle' ,
57+ 'auto_off_cap' : '🔴 Auto-action disabled' ,
58+ 'auto_prompt' : '[AUTO]🤖 User has been idle for over 30 minutes. As an autonomous agent, read the automation SOP and execute automatic tasks.' ,
4459 },
4560}
4661def T (key ): return I18N .get (LANG , I18N ['zh' ]).get (key , key )
@@ -72,14 +87,6 @@ def render_sidebar():
7287 agent .next_llm (selected_idx ); st .rerun (scope = "fragment" )
7388 if st .button (T ('force_stop' )):
7489 agent .abort (); st .toast ("Stop signal sended" ); st .rerun ()
75- if st .button (T ('reinject_tools' )):
76- agent .llmclient .last_tools = ''
77- try :
78- hist_path = os .path .join (script_dir , '..' , 'assets' , 'tool_usable_history.json' )
79- with open (hist_path , 'r' , encoding = 'utf-8' ) as f : tool_hist = json .load (f )
80- agent .llmclient .backend .history .extend (tool_hist )
81- st .toast (f"Tools injected" )
82- except Exception as e : st .toast (f"Injected tools failed: { e } " )
8390 if st .button (T ('desktop_pet' )):
8491 kwargs = {'creationflags' : 0x08 } if sys .platform == 'win32' else {}
8592 pet_script = os .path .join (script_dir , 'desktop_pet_v2.pyw' )
@@ -103,25 +110,37 @@ def _pet_hook(ctx):
103110 agent ._turn_end_hooks ['pet' ] = _pet_hook
104111 st .toast ("Desktop pet started" )
105112
106- if LANG == 'zh' :
107- if st .button ('🎯 给我找点事做' ):
108- st .session_state ['_inject_prompt' ] = '按照自主行动的规划部分,充分分析我的情况,给我生成一批TODO,务必让我感兴趣'
109- st .rerun (scope = "app" )
110- st .divider ()
111- if st .button ("开始空闲自主行动" ):
112- st .session_state .last_reply_time = int (time .time ()) - 1800
113+ if st .button (T ('suggest_btn' )):
114+ st .session_state ['_inject_prompt' ] = T ('suggest_prompt' )
115+ st .rerun (scope = "app" )
116+ st .divider ()
117+ if st .session_state .get ('loop_enabled' ):
118+ if st .button ("⏹️ Stop Loop" ):
119+ st .session_state .loop_enabled = False
120+ st .toast ("⏹️ Loop stopped" ); st .rerun (scope = "app" )
121+ st .caption ("🔁 Looping" )
122+ else :
123+ loop_prompt = st .text_input ("Loop prompt" , value = "继续" if LANG == 'zh' else 'next' , key = "loop_prompt_input" )
124+ if st .button ("🔁 Loop!" ):
125+ st .session_state .loop_enabled = True
126+ st .session_state .loop_prompt = loop_prompt
127+ st .session_state ['_inject_prompt' ] = loop_prompt
128+ st .toast ("🔁 Looping" ); st .rerun (scope = "app" )
129+ st .divider ()
130+ if st .button (T ('auto_start' )):
131+ st .session_state .last_reply_time = int (time .time ()) - 1800
132+ st .session_state .autonomous_enabled = True
133+ st .rerun (scope = "app" )
134+ if st .session_state .autonomous_enabled :
135+ if st .button (T ('auto_pause' )):
136+ st .session_state .autonomous_enabled = False
137+ st .toast (T ('auto_pause' )); st .rerun (scope = "app" )
138+ st .caption (T ('auto_on_cap' ))
139+ else :
140+ if st .button (T ('auto_enable' ), type = "primary" ):
113141 st .session_state .autonomous_enabled = True
114- st .toast ("已将上次回复时间设为1800秒前,自主行动已激活" ); st .rerun (scope = "app" )
115- if st .session_state .autonomous_enabled :
116- if st .button ("⏸️ 禁止自主行动" ):
117- st .session_state .autonomous_enabled = False
118- st .toast ("⏸️ 已禁止自主行动" ); st .rerun (scope = "app" )
119- st .caption ("🟢 自主行动运行中,会在你离开它30分钟后自动进行" )
120- else :
121- if st .button ("▶️ 允许自主行动" , type = "primary" ):
122- st .session_state .autonomous_enabled = True
123- st .toast ("✅ 已允许自主行动" ); st .rerun (scope = "app" )
124- st .caption ("🔴 自主行动已停止" )
142+ st .toast ("✅" ); st .rerun (scope = "app" )
143+ st .caption (T ('auto_off_cap' ))
125144with st .sidebar : render_sidebar ()
126145
127146def fold_turns (text ):
@@ -226,6 +245,10 @@ def render_main_stream(prompt=None):
226245 if response :
227246 st .session_state .messages .append ({"role" : "assistant" , "content" : response })
228247 st .session_state .last_reply_time = int (time .time ())
248+ # ── 循环回调:回答完成后自动注入下一条 ──
249+ if st .session_state .get ('loop_enabled' ):
250+ st .session_state ['_inject_prompt' ] = st .session_state .get ('loop_prompt' , '继续' )
251+ st .rerun ()
229252
230253if "messages" not in st .session_state : st .session_state .messages = []
231254for msg in st .session_state .messages :
@@ -334,5 +357,15 @@ def _reset_and_rerun():
334357 # No new prompt but a task is mid-flight (typically a /btw rerun) — resume drain.
335358 render_main_stream ()
336359
337- if st .session_state .autonomous_enabled :
338- st .markdown (f"""<div id="last-reply-time" style="display:none">{ st .session_state .get ('last_reply_time' , int (time .time ()))} </div>""" , unsafe_allow_html = True )
360+ # ── 空闲自主行动:fragment 定时检测,替代 launch.pyw 的 idle_monitor ──
361+ @st .fragment (run_every = timedelta (minutes = 5 ))
362+ def _idle_checker ():
363+ if not st .session_state .get ('autonomous_enabled' ): return
364+ if st .session_state .get ('display_queue' ) is not None : return # 正在运行中
365+ if st .session_state .get ('loop_enabled' ): return # 循环模式自己管
366+ last = st .session_state .get ('last_reply_time' , int (time .time ()))
367+ if time .time () - last > 1800 :
368+ st .session_state ['_inject_prompt' ] = T ('auto_prompt' )
369+ st .session_state ['last_reply_time' ] = int (time .time ()) # 防重入
370+ st .rerun (scope = "app" )
371+ _idle_checker ()
0 commit comments