refactor: parameterize compress/trim intervals, add stop/kill subagent actions, remove web/pet CLI commands

This commit is contained in:
Liang Jiaqing committed 2026-05-15 12:18:33 +08:00
1 parent ffb268554e
commit fc6b5ad309
3 files changed
+19 -29

No files matched your search

+5 -4
View File
@@ -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"
-12
View File
@@ -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",
+14 -13
View File
@@ -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 <thinking>/<tool_use>/<tool_result> 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]*?)(</{tag}>)') for tag in ('thinking', 'think', 'tool_use', 'tool_result')}
_hist_pat = re.compile(r'<(history|key_info|earlier_context)>[\s\S]*?</\1>')
@@ -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)