From 98c349a1e8703467cea907aa0b70b9e58a8657a4 Mon Sep 17 00:00:00 2001 From: Liang Jiaqing Date: Fri, 13 Feb 2026 13:34:21 +0800 Subject: [PATCH] fix: greedy match for file_content extraction; tgapp: add running indicator, HTML formatting, single instance lock; misc improvements --- agent_loop.py | 7 +++---- ga.py | 8 ++++---- launch.pyw | 8 +++++--- sidercall.py | 11 +++++------ tgapp.py | 34 ++++++++++++++++++++++++++++------ 5 files changed, 45 insertions(+), 23 deletions(-) diff --git a/agent_loop.py b/agent_loop.py index 4b3cd8c..2f756c4 100644 --- a/agent_loop.py +++ b/agent_loop.py @@ -7,7 +7,6 @@ class StepOutcome: next_prompt: Optional[str] = None should_exit: bool = False - def try_call_generator(func, *args, **kwargs): ret = func(*args, **kwargs) if hasattr(ret, '__iter__') and not isinstance(ret, (str, bytes, dict, list)): @@ -66,9 +65,9 @@ def agent_runner_loop(client, system_prompt, user_input, handler, tools_schema, if tool_name == 'no_tool': pass else: - yield f"🛠️ **正在调用工具:** `{tool_name}`" - if verbose: yield f"📥**参数:**\n````text\n{get_pretty_json(args)}\n````\n" - else: yield '\n\n\n' + showarg = get_pretty_json(args) + if not verbose and len(showarg) > 200: showarg = showarg[:200] + ' ...' + yield f"🛠️ **正在调用工具:** `{tool_name}` 📥**参数:**\n````text\n{showarg}\n````\n" gen = handler.dispatch(tool_name, args, response) if verbose: yield '`````\n' diff --git a/ga.py b/ga.py index f62368b..74ca8b8 100644 --- a/ga.py +++ b/ga.py @@ -226,8 +226,7 @@ def smart_format(data, max_depth=2, max_str_len=100, omit_str=' ... '): return json.dumps(truncate(data, 0), indent=2, ensure_ascii=False, default=str) class GenericAgentHandler(BaseHandler): - ''' - Generic Agent 工具库,包含多种工具的实现。工具函数自动加上了 do_ 前缀。实际工具名没有前缀。 + '''Generic Agent 工具库,包含多种工具的实现。工具函数自动加上了 do_ 前缀。实际工具名没有前缀。 ''' def __init__(self, parent, last_history=None, cwd='./'): self.parent = parent @@ -331,7 +330,7 @@ class GenericAgentHandler(BaseHandler): yield f"[Action] {action_str} file: {os.path.basename(path)}\n" def extract_robust_content(text): - tag = re.search(r"(.*?)", text, re.DOTALL) + tag = re.search(r"(.*)", text, re.DOTALL) if tag: return tag.group(1).strip() s, e = text.find("```"), text.rfind("```") if -1 < s < e: return text[text.find("\n", s)+1 : e].strip() @@ -450,7 +449,8 @@ class GenericAgentHandler(BaseHandler): prompt = f"\n### [WORKING MEMORY]\n\n{h_str}\n" if self.key_info: prompt += f"\n{self.key_info}" if self.related_sop: prompt += f"\n有不清晰的地方请再次读取{self.related_sop}" - print(prompt) + try: print(prompt) + except: pass return prompt def get_global_memory(): diff --git a/launch.pyw b/launch.pyw index a659082..95ecf30 100644 --- a/launch.pyw +++ b/launch.pyw @@ -17,15 +17,17 @@ def get_screen_width(): except: return 1920 def start_streamlit(port): - global proc + global proc, tgproc cmd = [ - sys.executable, "-m", "streamlit", "run", "stapp.py", - "--server.port", str(port), + sys.executable, "-m", "streamlit", "run", "stapp.py", + "--server.port", str(port), "--server.headless", "true", "--theme.base", "dark" #以此默认开启暗黑模式,更有极客感 ] proc = subprocess.Popen(cmd) atexit.register(proc.kill) + tgproc = subprocess.Popen([sys.executable, "tgapp.py"], creationflags=subprocess.CREATE_NO_WINDOW if os.name=='nt' else 0) + atexit.register(tgproc.kill) def inject(text): window.evaluate_js(f""" diff --git a/sidercall.py b/sidercall.py index 804786d..eb58566 100644 --- a/sidercall.py +++ b/sidercall.py @@ -53,10 +53,10 @@ class ClaudeSession: return result[::-1] or messages[-2:] def raw_ask(self, messages, model=None, temperature=0.5, max_tokens=4096): model = model or self.default_model - headers = {"x-api-key": self.api_key, "Content-Type": "application/json"} + headers = {"x-api-key": self.api_key, "Content-Type": "application/json", "anthropic-version": "2023-06-01"} payload = {"model": model, "messages": messages, "temperature": temperature, "max_tokens": max_tokens, "stream": True} try: - with requests.post(f"{self.api_base}/v1/messages", headers=headers, json=payload, stream=True, timeout=(5,60), verify=False) as r: + with requests.post(f"{self.api_base}/v1/messages", headers=headers, json=payload, stream=True, timeout=(5,60)) as r: r.raise_for_status() for line in r.iter_lines(): if not line: continue @@ -208,9 +208,8 @@ class GeminiSession: return iter([full_text]) if stream else full_text class MockFunction: - def __init__(self, name, arguments): - self.name = name - self.arguments = arguments + def __init__(self, name, arguments): self.name, self.arguments = name, arguments + class MockToolCall: def __init__(self, name, args): @@ -265,7 +264,7 @@ class ToolClient: 请按照以下步骤思考并行动,标签之间需要回车换行: 1. **思考**: 在 `` 标签中先进行思考,分析现状和策略。 2. **总结**: 在 `` 中输出*极为简短*的高度概括的单行(<30字)物理快照,包括上次工具调用结果获取的新信息+本次工具调用意图和预期。此内容将进入长期工作记忆,记录关键信息,严禁输出无实际信息增量的描述。 -3. **行动**: 如果需要调用工具,请在回复正文之后输出一个 **块**,然后结束,我会稍后给你返回块。 +3. **行动**: 如需调用工具,请在回复正文之后输出一个 **块**,然后结束,我会稍后给你返回块。 格式: ```\n{{"name": "工具名", "arguments": {{参数}}}}\n\n``` ### 可用工具库(已挂载,持续有效) diff --git a/tgapp.py b/tgapp.py index 54fdf1c..c9d86f6 100644 --- a/tgapp.py +++ b/tgapp.py @@ -1,4 +1,4 @@ -import os, sys, re, threading, asyncio, queue as Q +import os, sys, re, threading, asyncio, queue as Q, socket sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from agentmain import GeneraticAgent from telegram import Update @@ -17,6 +17,21 @@ def _clean(t): t = re.sub(p, '', t, flags=re.DOTALL) return re.sub(r'\n{3,}', '\n\n', t).strip() or '...' +import html as _html +def _inline_md(s): + s = re.sub(r'\*\*(.+?)\*\*', r'\1', s) + s = re.sub(r'(?\1', s) + s = re.sub(r'`([^`]+)`', r'\1', s) + return s +def _to_html(t): + parts, pos = [], 0 + for m in re.finditer(r'(`{3,})(?:\w*\n)?([\s\S]*?)\1', t): + parts.append(_inline_md(_html.escape(t[pos:m.start()]))) + parts.append('
' + _html.escape(m.group(2)) + '
') + pos = m.end() + parts.append(_inline_md(_html.escape(t[pos:]))) + return ''.join(parts) + async def _stream(dq, msg): last_text = "" while True: @@ -35,10 +50,13 @@ async def _stream(dq, msg): except Exception: pass last_text = "" show = show[-3900:] - if show != last_text: - try: await msg.edit_text(show) - except Exception: pass - last_text = show + display = show if done else show + " ⏳" + if display != last_text: + try: await msg.edit_text(_to_html(display), parse_mode='HTML') + except Exception: + try: await msg.edit_text(display) + except Exception: pass + last_text = display if done: break async def handle_msg(update, ctx): @@ -67,10 +85,14 @@ async def cmd_llm(update, ctx): await update.message.reply_text("LLMs:\n" + "\n".join(lines)) if __name__ == '__main__': + # Single instance lock using socket + try: + _lock_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM); _lock_sock.bind(('127.0.0.1', 19527)) + except OSError: sys.exit('Another instance is already running.') if not ALLOWED: sys.exit('ERROR: tg_allowed_users in mykey.py is empty or missing. Set it to avoid unauthorized access.') threading.Thread(target=agent.run, daemon=True).start() - proxy = os.environ.get('HTTPS_PROXY') or 'http://127.0.0.1:2082' + proxy = vars(mykey).get('proxy', '127.0.0.1:2082') app = ApplicationBuilder().token(mykey.tg_bot_token).proxy(proxy).get_updates_proxy(proxy).build() app.add_handler(CommandHandler("stop", cmd_abort)) app.add_handler(CommandHandler("llm", cmd_llm))