From 72d28500a322c7253c425f40c9dc54de1e56236c Mon Sep 17 00:00:00 2001 From: Liang Jiaqing Date: Sat, 16 May 2026 18:03:51 +0800 Subject: [PATCH] =?UTF-8?q?fix:=20cancel=E2=86=92abort,=20cancel=5Frequest?= =?UTF-8?q?ed=20guard,=20wechatapp=20use=20turn/outputs=20instead=20of=20?= =?UTF-8?q?=5Fturn=5Fparts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- agentmain.py | 6 +++--- frontends/desktop_bridge.py | 12 ++++++++---- frontends/wechatapp.py | 34 ++++++++++++++-------------------- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/agentmain.py b/agentmain.py index c488efa7..2aa1d76a 100644 --- a/agentmain.py +++ b/agentmain.py @@ -155,17 +155,17 @@ class GenericAgent: full_resp += chunk; turn_resps[-1] += chunk if len(full_resp) - last_pos > 30 or 'LLM Running' in chunk: display_queue.put({'next': full_resp[last_pos:] if self.inc_out else full_resp, - 'source': source, 'turn': curr_turn, 'outputs': turn_resps[-1:]}) + 'source': source, 'turn': curr_turn, 'outputs': turn_resps[-2:]}) 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, - 'turn': curr_turn, 'outputs': turn_resps[-1:]}) + 'turn': curr_turn, 'outputs': turn_resps[-2:]}) #if '' in full_resp: full_resp = full_resp.replace('', '\n\n') #if '' in full_resp: full_resp = re.sub(r'\s*(.*?)\s*', r'\n````\n\n\1\n\n````', full_resp, flags=re.DOTALL) display_queue.put({'done': full_resp, 'source': source, 'turn': curr_turn, 'outputs': turn_resps.copy()}) self.history = handler.history_info except Exception as e: print(f"Backend Error: {format_error(e)}") - display_queue.put({'done': full_resp + f'\n```\n{format_error(e)}\n```', 'source': source}) + display_queue.put({'done': full_resp + f'\n```\n{format_error(e)}\n```', 'source': source, 'turn': curr_turn, 'outputs': turn_resps.copy()}) finally: if self.stop_sig: print('User aborted the task.') self.is_running = self.stop_sig = False diff --git a/frontends/desktop_bridge.py b/frontends/desktop_bridge.py index e7590d65..56a560c4 100644 --- a/frontends/desktop_bridge.py +++ b/frontends/desktop_bridge.py @@ -173,9 +173,9 @@ class AgentManager: raise web.HTTPNotFound(text=json.dumps({"error": f"session not found: {sid}"}, ensure_ascii=False), content_type="application/json") if self.active_session_id == sid: self.active_session_id = next(iter(self.sessions), None) - if sess.agent and hasattr(sess.agent, "cancel"): + if sess.agent and hasattr(sess.agent, "abort"): with contextlib.suppress(Exception): - sess.agent.cancel() + sess.agent.abort() emit_session_state(sess, "closed") return {"ok": True, "sessionId": sid} @@ -237,6 +237,10 @@ class AgentManager: full = "GenericAgent object has no put_task/run method" if not full: full = "(completed)" + if sess.cancel_requested: + with self.lock: + sess.partial = None + return with self.lock: sess.partial = None # Strip trailing [Info] Final response to user. marker @@ -280,9 +284,9 @@ class AgentManager: if not sess: raise web.HTTPNotFound(text=json.dumps({"error": f"session not found: {sid}"}, ensure_ascii=False), content_type="application/json") sess.cancel_requested = True - if sess.agent and hasattr(sess.agent, "cancel"): + if sess.agent and hasattr(sess.agent, "abort"): with contextlib.suppress(Exception): - sess.agent.cancel() + sess.agent.abort() sess.status = "cancelled" sess.partial = None sess.updated_at = time.time() diff --git a/frontends/wechatapp.py b/frontends/wechatapp.py index ab4aa76b..3c711014 100644 --- a/frontends/wechatapp.py +++ b/frontends/wechatapp.py @@ -298,15 +298,6 @@ def _clean(t): t = re.sub(r'', '', t) return re.sub(r'\n{3,}', '\n\n', _strip_md(t)).strip() -def _turn_parts(t): - _ph = [] - safe = re.sub(r'`{4,}.*?`{4,}', lambda m: (_ph.append(m.group(0)), f'\x00PH{len(_ph)-1}\x00')[1], t, flags=re.DOTALL) - parts = re.split(r'(\**LLM Running \(Turn \d+\) \.\.\.\**)', safe) - parts = [re.sub(r'\x00PH(\d+)\x00', lambda m: _ph[int(m.group(1))], p) for p in parts] - if len(parts) < 4: return [], t - turns = [parts[i] + (parts[i+1] if i+1 < len(parts) else '') for i in range(1, len(parts), 2)] - return (([parts[0]] if parts[0].strip() else []) + turns[:-1], turns[-1]) - def on_message(bot, msg): text = bot.extract_text(msg).strip() uid = msg.get('from_user_id', '') @@ -320,7 +311,6 @@ def on_message(bot, msg): # Commands if text in ('/stop', '/abort'): agent.abort() - bot.send_text(uid, '已停止', context_token=ctx) return if text.startswith('/llm'): args = text.split() @@ -347,7 +337,7 @@ def on_message(bot, msg): except: pass _typing_stop.wait(2.0) threading.Thread(target=_keep_typing, daemon=True).start() - result = ''; sent = 0; mi = 0; last_send = 0 + result = ''; sent = 0; mi = 0; last_send = 0; item = {} def _wx_send(text): s = text.strip(); t0 = time.time() try: @@ -362,24 +352,28 @@ def on_message(bot, msg): now = time.time() if mi >= 9 or not show.strip(): return False if mi and now - last_send < 6 * mi: return None - if _wx_send(show[:2000]): mi += 1; last_send = time.time(); return True + if _wx_send(show[:3000]): mi += 1; last_send = time.time(); return True return False try: + done = []; turn = 1 while True: item = dq.get(timeout=300) - if 'done' in item: result = item['done']; break - raw = item.get('next', '') - done, partial = _turn_parts(raw) + if 'done' in item: break + if item.get('turn', turn) > turn: + outputs = item.get('outputs', []) + lastdone = outputs[-2] if len(outputs) >= 2 else '' + turn = item['turn']; done.append(lastdone) if len(done) > sent: merged = _clean('\n\n'.join(done[sent:])) print(f'[WX] turns={len(done)}/{len(done)+1} sent={sent} sending={len(done)-sent}', file=sys.__stdout__) - if _send(merged): - sent = len(done) + if _send(merged): sent = len(done) except queue.Empty: result = '[超时]' _typing_stop.set() - done, partial = _turn_parts(result) - rest = '\n\n'.join(done[sent:] + [partial] + ['\n\n[任务已完成]']) - if rest.strip(): _wx_send((_clean(rest))[-2000:]) + + if 'done' in item: result, done = item['done'], item.get('outputs', []) + rest = _clean('\n\n'.join(done[sent:] + ['\n\n[任务已完成]']).strip()) + if rest: _wx_send(rest[-3000:]) + files = re.findall(r'\[FILE:([^\]]+)\]', result) bad = {'filepath', '', 'path', '', 'file_path', '', '...'} files = [f for f in files if f.strip().lower() not in bad and (f if os.path.isabs(f) else os.path.join(_TEMP_DIR, f)) not in media_paths]