From fc6b5ad309fc2b8f4158eba56ad5f9ab6fc33ca0 Mon Sep 17 00:00:00 2001 From: Liang Jiaqing Date: Fri, 15 May 2026 12:18:33 +0800 Subject: [PATCH] refactor: parameterize compress/trim intervals, add stop/kill subagent actions, remove web/pet CLI commands --- frontends/conductor.py | 9 +++++---- ga_cli/cli.py | 12 ------------ llmcore.py | 27 ++++++++++++++------------- 3 files changed, 19 insertions(+), 29 deletions(-) diff --git a/frontends/conductor.py b/frontends/conductor.py index 3471d4bf..eb37ec3f 100644 --- a/frontends/conductor.py +++ b/frontends/conductor.py @@ -228,7 +228,8 @@ def conductor_readme() -> str: "POST /subagent\tbody: {\"prompt\": \"...\"}\t启动新subagent,返回 {\"id\": \"xxx\"}", 'POST /subagent/{id}\tbody: {\"action\": \"keyinfo\", \"msg\": \"...\"}\t注入key_info(agent下轮可见)', 'POST /subagent/{id}\tbody: {\"action\": \"input\", \"msg\": \"...\"}\t开新一轮任务(agent停下后追加)', - 'POST /subagent/{id}\tbody: {\"action\": \"abort\"}\t停止该subagent', + 'POST /subagent/{id}\tbody: {\"action\": \"stop\"}\t中断执行但保留(可继续input/reply)', + 'POST /subagent/{id}\tbody: {\"action\": \"kill\"}\t彻底杀死(从卡片消失,不可复用)', "GET /chat?last=N\t返回最近N条对话(默认20)", "GET /subagent\t返回 {\"items\": [...]}\t查看所有subagent状态", "GET /readme\t本文档", @@ -363,12 +364,12 @@ def api_subagent_action(sid: str, body: SubagentActionIn): result = input_subagent(sid, body.msg) result["instruction"] = "Task received. I'll handle it from here. You MUST stop now and end your reply." return result - if action == "abort": + if action in ("abort", "stop"): s.agent.abort() - s.status = "aborted" + s.status = "stopped" s.updated_at = time.time() push_cards() - return {"id": sid, "status": "aborted"} + return {"id": sid, "status": "stopped"} if action == "kill": s.agent.abort() s.status = "aborted" diff --git a/ga_cli/cli.py b/ga_cli/cli.py index 8a73a805..2c7bf4c8 100644 --- a/ga_cli/cli.py +++ b/ga_cli/cli.py @@ -50,13 +50,6 @@ COMMANDS = { "desc": "启动基于 PyQt5 的完整桌面聊天界面(气泡代码高亮、文件拖拽、历史搜索)", "cmd": ["python", "{FRONTENDS}/qtapp.py"], }, - "web": { - "help": "启动Web增强版 (stapp2) 加 --native 使用 webview 桌面壳", - "desc": "启动 Streamlit Web 界面,浏览器访问,适合远程/跨设备", - "cmd": ["python", "-m", "streamlit", "run", "{FRONTENDS}/stapp2.py", - "--server.headless", "true"], - "flags": {"--native": {"help": "用 webview 原生窗口启动(stapp基础版)", "cmd": ["python", "{PROJECT_DIR}/launch.pyw"]}}, - }, "configure": { "help": "运行初始配置向导 (configure_mykey.py)", "desc": "首次安装后配置 API Key、模型参数等基础设置", @@ -67,11 +60,6 @@ COMMANDS = { "desc": "启动 hub 前端管理面板(系统托盘 + 浏览器界面)", "cmd": ["python", "{PROJECT_DIR}/hub.pyw"], }, - "pet": { - "help": "启动桌面宠物", - "desc": "启动桌面宠物皮肤系统,桌面上陪伴你的虚拟宠物", - "cmd": ["python", "{FRONTENDS}/desktop_pet_v2.pyw"], - }, "tui": { "help": "启动终端 TUI (tuiapp)", "desc": "启动终端图形界面(Textual),适合纯终端环境或 SSH", diff --git a/llmcore.py b/llmcore.py index 47994f3e..65130d17 100644 --- a/llmcore.py +++ b/llmcore.py @@ -30,11 +30,11 @@ def __getattr__(name): # once guard in PEP 562 if name == 'mykeys': return reload_mykeys()[0] raise AttributeError(f"module 'llmcore' has no attribute {name}") -def compress_history_tags(messages, keep_recent=10, max_len=800, force=False): +def compress_history_tags(messages, keep_recent=10, max_len=800, force=False, interval=5): """Compress // tags in older messages to save tokens.""" compress_history_tags._cd = getattr(compress_history_tags, '_cd', 0) + 1 if force: compress_history_tags._cd = 0 - if compress_history_tags._cd % 5 != 0: return messages + if compress_history_tags._cd % interval != 0: return messages _before = sum(len(json.dumps(m, ensure_ascii=False)) for m in messages) _pats = {tag: re.compile(rf'(<{tag}>)([\s\S]*?)()') for tag in ('thinking', 'think', 'tool_use', 'tool_result')} _hist_pat = re.compile(r'<(history|key_info|earlier_context)>[\s\S]*?') @@ -87,11 +87,11 @@ def safeprint(*argv): except OSError: pass print = safeprint -def trim_messages_history(history, context_win): - cap = context_win * 3 - target = int(cap * 0.6) +def trim_messages_history(history, sess): + cap = sess.context_win * 3 + target = int(cap * getattr(sess, 'trim_keep_rate', 0.6)) def cost(): return sum(len(json.dumps(m, ensure_ascii=False)) for m in history) - compress_history_tags(history) + compress_history_tags(history, interval=getattr(sess, 'cut_msg_interval', 5)) print(f'[Debug] Current context: {cost()} chars, {len(history)} messages.') if cost() <= cap: return compress_history_tags(history, keep_recent=4, force=True) @@ -513,12 +513,13 @@ class BaseSession: self.api_key = cfg['apikey'] self.api_base = cfg['apibase'].rstrip('/') self.model = cfg.get('model', '') - self.context_win = cfg.get('context_win', 30000) - self.history = [] - self.lock = threading.Lock() - self.system = "" + default_context_win = 30000 + if 'deepseek' in self.model.lower(): + default_context_win = 70000; self.cut_msg_interval = 25; self.trim_keep_rate = 0.3 + self.context_win = cfg.get('context_win', default_context_win) + self.history = []; self.lock = threading.Lock(); self.system = "" self.name = cfg.get('name', self.model) - proxy = cfg.get('proxy') + proxy = cfg.get('proxy'); self.proxies = {"http": proxy, "https": proxy} if proxy else None self.max_retries = max(0, int(cfg.get('max_retries', 4))) self.verify = cfg.get('verify', True) @@ -553,7 +554,7 @@ class BaseSession: def _ask_gen(): with self.lock: self.history.append({"role": "user", "content": [{"type": "text", "text": prompt}]}) - trim_messages_history(self.history, self.context_win) + trim_messages_history(self.history, self) messages = self.make_messages(self.history) content_blocks = None; content = '' gen = self.raw_ask(messages) @@ -676,7 +677,7 @@ class NativeClaudeSession(BaseSession): assert type(msg) is dict with self.lock: self.history.append(msg) - trim_messages_history(self.history, self.context_win) + trim_messages_history(self.history, self) messages = [{"role": m["role"], "content": list(m["content"])} for m in self.history] content_blocks = None gen = self.raw_ask(messages)