fix: greedy match for file_content extraction; tgapp: add running indicator, HTML formatting, single instance lock; misc improvements
This commit is contained in:
@@ -7,7 +7,6 @@ class StepOutcome:
|
|||||||
next_prompt: Optional[str] = None
|
next_prompt: Optional[str] = None
|
||||||
should_exit: bool = False
|
should_exit: bool = False
|
||||||
|
|
||||||
|
|
||||||
def try_call_generator(func, *args, **kwargs):
|
def try_call_generator(func, *args, **kwargs):
|
||||||
ret = func(*args, **kwargs)
|
ret = func(*args, **kwargs)
|
||||||
if hasattr(ret, '__iter__') and not isinstance(ret, (str, bytes, dict, list)):
|
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
|
if tool_name == 'no_tool': pass
|
||||||
else:
|
else:
|
||||||
yield f"🛠️ **正在调用工具:** `{tool_name}`"
|
showarg = get_pretty_json(args)
|
||||||
if verbose: yield f"📥**参数:**\n````text\n{get_pretty_json(args)}\n````\n"
|
if not verbose and len(showarg) > 200: showarg = showarg[:200] + ' ...'
|
||||||
else: yield '\n\n\n'
|
yield f"🛠️ **正在调用工具:** `{tool_name}` 📥**参数:**\n````text\n{showarg}\n````\n"
|
||||||
gen = handler.dispatch(tool_name, args, response)
|
gen = handler.dispatch(tool_name, args, response)
|
||||||
if verbose:
|
if verbose:
|
||||||
yield '`````\n'
|
yield '`````\n'
|
||||||
|
|||||||
8
ga.py
8
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)
|
return json.dumps(truncate(data, 0), indent=2, ensure_ascii=False, default=str)
|
||||||
|
|
||||||
class GenericAgentHandler(BaseHandler):
|
class GenericAgentHandler(BaseHandler):
|
||||||
'''
|
'''Generic Agent 工具库,包含多种工具的实现。工具函数自动加上了 do_ 前缀。实际工具名没有前缀。
|
||||||
Generic Agent 工具库,包含多种工具的实现。工具函数自动加上了 do_ 前缀。实际工具名没有前缀。
|
|
||||||
'''
|
'''
|
||||||
def __init__(self, parent, last_history=None, cwd='./'):
|
def __init__(self, parent, last_history=None, cwd='./'):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
@@ -331,7 +330,7 @@ class GenericAgentHandler(BaseHandler):
|
|||||||
yield f"[Action] {action_str} file: {os.path.basename(path)}\n"
|
yield f"[Action] {action_str} file: {os.path.basename(path)}\n"
|
||||||
|
|
||||||
def extract_robust_content(text):
|
def extract_robust_content(text):
|
||||||
tag = re.search(r"<file_content>(.*?)</file_content>", text, re.DOTALL)
|
tag = re.search(r"<file_content>(.*)</file_content>", text, re.DOTALL)
|
||||||
if tag: return tag.group(1).strip()
|
if tag: return tag.group(1).strip()
|
||||||
s, e = text.find("```"), text.rfind("```")
|
s, e = text.find("```"), text.rfind("```")
|
||||||
if -1 < s < e: return text[text.find("\n", s)+1 : e].strip()
|
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<history>\n{h_str}\n</history>"
|
prompt = f"\n### [WORKING MEMORY]\n<history>\n{h_str}\n</history>"
|
||||||
if self.key_info: prompt += f"\n<key_info>{self.key_info}</key_info>"
|
if self.key_info: prompt += f"\n<key_info>{self.key_info}</key_info>"
|
||||||
if self.related_sop: prompt += f"\n有不清晰的地方请再次读取{self.related_sop}"
|
if self.related_sop: prompt += f"\n有不清晰的地方请再次读取{self.related_sop}"
|
||||||
print(prompt)
|
try: print(prompt)
|
||||||
|
except: pass
|
||||||
return prompt
|
return prompt
|
||||||
|
|
||||||
def get_global_memory():
|
def get_global_memory():
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ def get_screen_width():
|
|||||||
except: return 1920
|
except: return 1920
|
||||||
|
|
||||||
def start_streamlit(port):
|
def start_streamlit(port):
|
||||||
global proc
|
global proc, tgproc
|
||||||
cmd = [
|
cmd = [
|
||||||
sys.executable, "-m", "streamlit", "run", "stapp.py",
|
sys.executable, "-m", "streamlit", "run", "stapp.py",
|
||||||
"--server.port", str(port),
|
"--server.port", str(port),
|
||||||
@@ -26,6 +26,8 @@ def start_streamlit(port):
|
|||||||
]
|
]
|
||||||
proc = subprocess.Popen(cmd)
|
proc = subprocess.Popen(cmd)
|
||||||
atexit.register(proc.kill)
|
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):
|
def inject(text):
|
||||||
window.evaluate_js(f"""
|
window.evaluate_js(f"""
|
||||||
|
|||||||
11
sidercall.py
11
sidercall.py
@@ -53,10 +53,10 @@ class ClaudeSession:
|
|||||||
return result[::-1] or messages[-2:]
|
return result[::-1] or messages[-2:]
|
||||||
def raw_ask(self, messages, model=None, temperature=0.5, max_tokens=4096):
|
def raw_ask(self, messages, model=None, temperature=0.5, max_tokens=4096):
|
||||||
model = model or self.default_model
|
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}
|
payload = {"model": model, "messages": messages, "temperature": temperature, "max_tokens": max_tokens, "stream": True}
|
||||||
try:
|
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()
|
r.raise_for_status()
|
||||||
for line in r.iter_lines():
|
for line in r.iter_lines():
|
||||||
if not line: continue
|
if not line: continue
|
||||||
@@ -208,9 +208,8 @@ class GeminiSession:
|
|||||||
return iter([full_text]) if stream else full_text
|
return iter([full_text]) if stream else full_text
|
||||||
|
|
||||||
class MockFunction:
|
class MockFunction:
|
||||||
def __init__(self, name, arguments):
|
def __init__(self, name, arguments): self.name, self.arguments = name, arguments
|
||||||
self.name = name
|
|
||||||
self.arguments = arguments
|
|
||||||
|
|
||||||
class MockToolCall:
|
class MockToolCall:
|
||||||
def __init__(self, name, args):
|
def __init__(self, name, args):
|
||||||
@@ -265,7 +264,7 @@ class ToolClient:
|
|||||||
请按照以下步骤思考并行动,标签之间需要回车换行:
|
请按照以下步骤思考并行动,标签之间需要回车换行:
|
||||||
1. **思考**: 在 `<thinking>` 标签中先进行思考,分析现状和策略。
|
1. **思考**: 在 `<thinking>` 标签中先进行思考,分析现状和策略。
|
||||||
2. **总结**: 在 `<summary>` 中输出*极为简短*的高度概括的单行(<30字)物理快照,包括上次工具调用结果获取的新信息+本次工具调用意图和预期。此内容将进入长期工作记忆,记录关键信息,严禁输出无实际信息增量的描述。
|
2. **总结**: 在 `<summary>` 中输出*极为简短*的高度概括的单行(<30字)物理快照,包括上次工具调用结果获取的新信息+本次工具调用意图和预期。此内容将进入长期工作记忆,记录关键信息,严禁输出无实际信息增量的描述。
|
||||||
3. **行动**: 如果需要调用工具,请在回复正文之后输出一个 **<tool_use>块**,然后结束,我会稍后给你返回<tool_result>块。
|
3. **行动**: 如需调用工具,请在回复正文之后输出一个 **<tool_use>块**,然后结束,我会稍后给你返回<tool_result>块。
|
||||||
格式: ```<tool_use>\n{{"name": "工具名", "arguments": {{参数}}}}\n</tool_use>\n```
|
格式: ```<tool_use>\n{{"name": "工具名", "arguments": {{参数}}}}\n</tool_use>\n```
|
||||||
|
|
||||||
### 可用工具库(已挂载,持续有效)
|
### 可用工具库(已挂载,持续有效)
|
||||||
|
|||||||
32
tgapp.py
32
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__)))
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||||
from agentmain import GeneraticAgent
|
from agentmain import GeneraticAgent
|
||||||
from telegram import Update
|
from telegram import Update
|
||||||
@@ -17,6 +17,21 @@ def _clean(t):
|
|||||||
t = re.sub(p, '', t, flags=re.DOTALL)
|
t = re.sub(p, '', t, flags=re.DOTALL)
|
||||||
return re.sub(r'\n{3,}', '\n\n', t).strip() or '...'
|
return re.sub(r'\n{3,}', '\n\n', t).strip() or '...'
|
||||||
|
|
||||||
|
import html as _html
|
||||||
|
def _inline_md(s):
|
||||||
|
s = re.sub(r'\*\*(.+?)\*\*', r'<b>\1</b>', s)
|
||||||
|
s = re.sub(r'(?<!\*)\*(?!\*)(.+?)(?<!\*)\*(?!\*)', r'<i>\1</i>', s)
|
||||||
|
s = re.sub(r'`([^`]+)`', r'<code>\1</code>', 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('<pre><code>' + _html.escape(m.group(2)) + '</code></pre>')
|
||||||
|
pos = m.end()
|
||||||
|
parts.append(_inline_md(_html.escape(t[pos:])))
|
||||||
|
return ''.join(parts)
|
||||||
|
|
||||||
async def _stream(dq, msg):
|
async def _stream(dq, msg):
|
||||||
last_text = ""
|
last_text = ""
|
||||||
while True:
|
while True:
|
||||||
@@ -35,10 +50,13 @@ async def _stream(dq, msg):
|
|||||||
except Exception: pass
|
except Exception: pass
|
||||||
last_text = ""
|
last_text = ""
|
||||||
show = show[-3900:]
|
show = show[-3900:]
|
||||||
if show != last_text:
|
display = show if done else show + " ⏳"
|
||||||
try: await msg.edit_text(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
|
except Exception: pass
|
||||||
last_text = show
|
last_text = display
|
||||||
if done: break
|
if done: break
|
||||||
|
|
||||||
async def handle_msg(update, ctx):
|
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))
|
await update.message.reply_text("LLMs:\n" + "\n".join(lines))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
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:
|
if not ALLOWED:
|
||||||
sys.exit('ERROR: tg_allowed_users in mykey.py is empty or missing. Set it to avoid unauthorized access.')
|
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()
|
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 = ApplicationBuilder().token(mykey.tg_bot_token).proxy(proxy).get_updates_proxy(proxy).build()
|
||||||
app.add_handler(CommandHandler("stop", cmd_abort))
|
app.add_handler(CommandHandler("stop", cmd_abort))
|
||||||
app.add_handler(CommandHandler("llm", cmd_llm))
|
app.add_handler(CommandHandler("llm", cmd_llm))
|
||||||
|
|||||||
Reference in New Issue
Block a user