chore: sync core logic updates
This commit is contained in:
@@ -26,7 +26,7 @@ class BaseHandler:
|
|||||||
return ret
|
return ret
|
||||||
else:
|
else:
|
||||||
yield f"❌ 未知工具: {tool_name}\n"
|
yield f"❌ 未知工具: {tool_name}\n"
|
||||||
return StepOutcome(None, "未知工具", "ERROR")
|
return StepOutcome(None, next_prompt=f"未知工具 {tool_name}", should_exit=False)
|
||||||
|
|
||||||
def json_default(o):
|
def json_default(o):
|
||||||
if isinstance(o, set): return list(o)
|
if isinstance(o, set): return list(o)
|
||||||
|
|||||||
@@ -47,14 +47,18 @@ class GeneraticAgent:
|
|||||||
self.llm_no = 0
|
self.llm_no = 0
|
||||||
self.stop_sig = False
|
self.stop_sig = False
|
||||||
self.current_source = 'none'
|
self.current_source = 'none'
|
||||||
|
self.handler = None
|
||||||
|
|
||||||
def next_llm(self):
|
def next_llm(self):
|
||||||
self.llm_no = (self.llm_no + 1) % len(self.llmclient.raw_apis)
|
self.llm_no = (self.llm_no + 1) % len(self.llmclient.raw_apis)
|
||||||
self.llmclient.last_tools = ''
|
self.llmclient.last_tools = ''
|
||||||
|
|
||||||
def abort(self):
|
def abort(self):
|
||||||
|
print('About to abort current task...')
|
||||||
if not self.is_running: return
|
if not self.is_running: return
|
||||||
self.stop_sig = True
|
self.stop_sig = True
|
||||||
|
if self.handler is not None:
|
||||||
|
self.handler.code_stop_signal.append(1)
|
||||||
|
|
||||||
def put_task(self, query, source="user"):
|
def put_task(self, query, source="user"):
|
||||||
self.display_queue.queue.clear()
|
self.display_queue.queue.clear()
|
||||||
@@ -73,7 +77,7 @@ class GeneraticAgent:
|
|||||||
|
|
||||||
sys_prompt = get_system_prompt()
|
sys_prompt = get_system_prompt()
|
||||||
handler = GenericAgentHandler(None, self.history, './temp')
|
handler = GenericAgentHandler(None, self.history, './temp')
|
||||||
|
self.handler = handler
|
||||||
self.llmclient.raw_api = self.llmclient.raw_apis[self.llm_no]
|
self.llmclient.raw_api = self.llmclient.raw_apis[self.llm_no]
|
||||||
gen = agent_runner_loop(self.llmclient, sys_prompt,
|
gen = agent_runner_loop(self.llmclient, sys_prompt,
|
||||||
raw_query, handler, TOOLS_SCHEMA, max_turns=25)
|
raw_query, handler, TOOLS_SCHEMA, max_turns=25)
|
||||||
@@ -81,7 +85,8 @@ class GeneraticAgent:
|
|||||||
try:
|
try:
|
||||||
full_response = ""; last_pos = 0
|
full_response = ""; last_pos = 0
|
||||||
for chunk in gen:
|
for chunk in gen:
|
||||||
if self.stop_sig: break
|
if self.stop_sig:
|
||||||
|
self.abort(); break
|
||||||
full_response += chunk
|
full_response += chunk
|
||||||
if len(full_response) - last_pos > 50:
|
if len(full_response) - last_pos > 50:
|
||||||
self.display_queue.put({'next': f'{full_response}', 'source': source})
|
self.display_queue.put({'next': f'{full_response}', 'source': source})
|
||||||
|
|||||||
16
ga.py
16
ga.py
@@ -7,7 +7,7 @@ sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
|||||||
|
|
||||||
from agent_loop import BaseHandler, StepOutcome, try_call_generator
|
from agent_loop import BaseHandler, StepOutcome, try_call_generator
|
||||||
|
|
||||||
def code_run(code, code_type="python", timeout=60, cwd=None, code_cwd=None):
|
def code_run(code, code_type="python", timeout=60, cwd=None, code_cwd=None, stop_signal=[]):
|
||||||
"""代码执行器
|
"""代码执行器
|
||||||
python: 运行复杂的 .py 脚本(文件模式)
|
python: 运行复杂的 .py 脚本(文件模式)
|
||||||
powershell/bash: 运行单行指令(命令模式)
|
powershell/bash: 运行单行指令(命令模式)
|
||||||
@@ -52,11 +52,14 @@ def code_run(code, code_type="python", timeout=60, cwd=None, code_cwd=None):
|
|||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
while t.is_alive():
|
while t.is_alive():
|
||||||
if time.time() - start_t > timeout:
|
istimeout = time.time() - start_t > timeout
|
||||||
|
if istimeout or len(stop_signal) > 0:
|
||||||
process.kill()
|
process.kill()
|
||||||
full_stdout.append("\n[Timeout Error] 超时强制终止")
|
print("[Debug] Process killed due to timeout or stop signal.")
|
||||||
|
if istimeout: full_stdout.append("\n[Timeout Error] 超时强制终止")
|
||||||
|
else: full_stdout.append("\n[Stopped] 用户强制终止")
|
||||||
break
|
break
|
||||||
time.sleep(0.2)
|
time.sleep(1)
|
||||||
|
|
||||||
t.join(timeout=1)
|
t.join(timeout=1)
|
||||||
exit_code = process.poll()
|
exit_code = process.poll()
|
||||||
@@ -242,6 +245,7 @@ class GenericAgentHandler(BaseHandler):
|
|||||||
self.focus = ""
|
self.focus = ""
|
||||||
self.cwd = cwd
|
self.cwd = cwd
|
||||||
self.history_info = last_history if last_history else []
|
self.history_info = last_history if last_history else []
|
||||||
|
self.code_stop_signal = []
|
||||||
|
|
||||||
def _get_abs_path(self, path):
|
def _get_abs_path(self, path):
|
||||||
if not path: return ""
|
if not path: return ""
|
||||||
@@ -274,7 +278,7 @@ class GenericAgentHandler(BaseHandler):
|
|||||||
raw_path = os.path.join(self.cwd, args.get("cwd", './'))
|
raw_path = os.path.join(self.cwd, args.get("cwd", './'))
|
||||||
cwd = os.path.normpath(os.path.abspath(raw_path))
|
cwd = os.path.normpath(os.path.abspath(raw_path))
|
||||||
code_cwd = os.path.normpath(self.cwd)
|
code_cwd = os.path.normpath(self.cwd)
|
||||||
result = yield from code_run(code, code_type, timeout, cwd, code_cwd=code_cwd)
|
result = yield from code_run(code, code_type, timeout, cwd, code_cwd=code_cwd, stop_signal=self.code_stop_signal)
|
||||||
next_prompt = self._get_anchor_prompt() + warning
|
next_prompt = self._get_anchor_prompt() + warning
|
||||||
return StepOutcome(result, next_prompt=next_prompt)
|
return StepOutcome(result, next_prompt=next_prompt)
|
||||||
|
|
||||||
@@ -315,7 +319,7 @@ class GenericAgentHandler(BaseHandler):
|
|||||||
print("Web Execute JS Result:", smart_format(result))
|
print("Web Execute JS Result:", smart_format(result))
|
||||||
yield f"JS 执行结果:\n{smart_format(result)}\n"
|
yield f"JS 执行结果:\n{smart_format(result)}\n"
|
||||||
next_prompt = self._get_anchor_prompt()
|
next_prompt = self._get_anchor_prompt()
|
||||||
return StepOutcome(result, next_prompt=next_prompt)
|
return StepOutcome(smart_format(result, max_str_len=5000), next_prompt=next_prompt)
|
||||||
|
|
||||||
def do_file_patch(self, args, response):
|
def do_file_patch(self, args, response):
|
||||||
path = self._get_abs_path(args.get("path", ""))
|
path = self._get_abs_path(args.get("path", ""))
|
||||||
|
|||||||
48
sidercall.py
48
sidercall.py
@@ -12,12 +12,15 @@ class SiderLLMSession:
|
|||||||
self.default_model = default_model
|
self.default_model = default_model
|
||||||
def ask(self, prompt, model=None, stream=False):
|
def ask(self, prompt, model=None, stream=False):
|
||||||
if model is None: model = self.default_model
|
if model is None: model = self.default_model
|
||||||
if len(prompt) > 29000:
|
if len(prompt) > 28000:
|
||||||
print(f"[Warn] Prompt too long ({len(prompt)} chars), truncating.")
|
print(f"[Warn] Prompt too long ({len(prompt)} chars), truncating.")
|
||||||
prompt = prompt[-29000:]
|
prompt = prompt[-28000:]
|
||||||
gen = self._core.chat(prompt, model)
|
gen = self._core.chat(prompt, model)
|
||||||
if stream: return gen
|
full_text = ''.join(list(gen))
|
||||||
return ''.join(list(gen))
|
if stream:
|
||||||
|
def wrap_as_stream(): yield full_text
|
||||||
|
return wrap_as_stream() # gen有奇怪的死循环行为,sider足够快
|
||||||
|
return full_text
|
||||||
|
|
||||||
class LLMSession:
|
class LLMSession:
|
||||||
def __init__(self, api_key=oai_apikey, api_base=oai_apibase, model=oai_model, context_win=12000):
|
def __init__(self, api_key=oai_apikey, api_base=oai_apibase, model=oai_model, context_win=12000):
|
||||||
@@ -74,7 +77,7 @@ class LLMSession:
|
|||||||
keep = 0; tok = 0
|
keep = 0; tok = 0
|
||||||
for m in reversed(self.raw_msgs):
|
for m in reversed(self.raw_msgs):
|
||||||
l = len(str(m))//4
|
l = len(str(m))//4
|
||||||
if tok + l > self.context_win//3: break
|
if tok + l > self.context_win*0.2: break
|
||||||
tok += l; keep += 1
|
tok += l; keep += 1
|
||||||
keep = max(2, keep)
|
keep = max(2, keep)
|
||||||
old, self.raw_msgs = self.raw_msgs[:-keep], self.raw_msgs[-keep:]
|
old, self.raw_msgs = self.raw_msgs[:-keep], self.raw_msgs[-keep:]
|
||||||
@@ -84,28 +87,29 @@ class LLMSession:
|
|||||||
messages += [{"role":"user", "content":p}]
|
messages += [{"role":"user", "content":p}]
|
||||||
msg_lens = [1000 if isinstance(m["content"], list) else len(str(m["content"]))//4 for m in messages]
|
msg_lens = [1000 if isinstance(m["content"], list) else len(str(m["content"]))//4 for m in messages]
|
||||||
summary = ''.join(list(self.raw_ask(messages, model, temperature=0.1)))
|
summary = ''.join(list(self.raw_ask(messages, model, temperature=0.1)))
|
||||||
print('[Debug] Summary length:', len(summary)//4, '; Context lengths:', str(msg_lens))
|
print('[Debug] Summary length:', len(summary)//4, '; Orig context lengths:', str(msg_lens))
|
||||||
if not summary.startswith("Error:"):
|
if not summary.startswith("Error:"):
|
||||||
self.raw_msgs.insert(0, {"role":"assistant", "prompt":"Prev summary:\n"+summary, "image":None})
|
self.raw_msgs.insert(0, {"role":"assistant", "prompt":"Prev summary:\n"+summary, "image":None})
|
||||||
else: self.raw_msgs = old + self.raw_msgs # 不做了,下次再做
|
else: self.raw_msgs = old + self.raw_msgs # 不做了,下次再做
|
||||||
|
|
||||||
def ask(self, prompt, model=None, image_base64=None, stream=False):
|
def ask(self, prompt, model=None, image_base64=None, stream=False):
|
||||||
if model is None: model = self.model
|
if model is None: model = self.model
|
||||||
self.raw_msgs.append({"role": "user", "prompt": prompt, "image": image_base64})
|
|
||||||
messages = self.make_messages(self.raw_msgs[:-1], omit_images=True)
|
|
||||||
messages += self.make_messages([self.raw_msgs[-1]], omit_images=False)
|
|
||||||
msg_lens = [1000 if isinstance(m["content"], list) else len(str(m["content"]))//4 for m in messages]
|
|
||||||
total_len = sum(msg_lens) # estimate token count
|
|
||||||
def _ask_gen():
|
def _ask_gen():
|
||||||
content = ''
|
content = ''
|
||||||
with self.lock:
|
with self.lock:
|
||||||
gen = self.raw_ask(messages, model)
|
self.raw_msgs.append({"role": "user", "prompt": prompt, "image": image_base64})
|
||||||
for chunk in gen:
|
messages = self.make_messages(self.raw_msgs[:-1], omit_images=True)
|
||||||
content += chunk; yield chunk
|
messages += self.make_messages([self.raw_msgs[-1]], omit_images=False)
|
||||||
|
msg_lens = [1000 if isinstance(m["content"], list) else len(str(m["content"]))//4 for m in messages]
|
||||||
|
total_len = sum(msg_lens) # estimate token count
|
||||||
|
gen = self.raw_ask(messages, model)
|
||||||
|
for chunk in gen:
|
||||||
|
content += chunk; yield chunk
|
||||||
if not content.startswith("Error:"):
|
if not content.startswith("Error:"):
|
||||||
self.raw_msgs.append({"role": "assistant", "prompt": content, "image": None})
|
self.raw_msgs.append({"role": "assistant", "prompt": content, "image": None})
|
||||||
if total_len > 5000: print(f"[Debug] Whole context length {total_len} {str(msg_lens)}.")
|
if total_len > 5000: print(f"[Debug] Whole context length {total_len} {str(msg_lens)}.")
|
||||||
if total_len > self.context_win:
|
if total_len > self.context_win:
|
||||||
|
yield '[NextWillSummary]'
|
||||||
threading.Thread(target=self.summary_history, daemon=True).start()
|
threading.Thread(target=self.summary_history, daemon=True).start()
|
||||||
if stream: return _ask_gen()
|
if stream: return _ask_gen()
|
||||||
return ''.join(list(_ask_gen()))
|
return ''.join(list(_ask_gen()))
|
||||||
@@ -142,12 +146,18 @@ class ToolClient:
|
|||||||
def chat(self, messages, tools=None):
|
def chat(self, messages, tools=None):
|
||||||
full_prompt = self._build_protocol_prompt(messages, tools)
|
full_prompt = self._build_protocol_prompt(messages, tools)
|
||||||
print("Full prompt length:", len(full_prompt), 'chars')
|
print("Full prompt length:", len(full_prompt), 'chars')
|
||||||
gen = self.raw_api(full_prompt, stream=True)
|
|
||||||
raw_text = ''
|
|
||||||
for chunk in gen:
|
|
||||||
raw_text += chunk; yield chunk
|
|
||||||
with open('model_responses.txt', 'a', encoding='utf-8', errors="replace") as f:
|
with open('model_responses.txt', 'a', encoding='utf-8', errors="replace") as f:
|
||||||
f.write(f"=== Prompt ===\n{full_prompt}\n=== Response ===\n{raw_text}\n\n")
|
f.write(f"=== Prompt ===\n{full_prompt}\n")
|
||||||
|
gen = self.raw_api(full_prompt, stream=True)
|
||||||
|
raw_text = ''; summarytag = '[NextWillSummary]'
|
||||||
|
for chunk in gen:
|
||||||
|
raw_text += chunk;
|
||||||
|
if chunk != summarytag: yield chunk
|
||||||
|
print('Complete response received.')
|
||||||
|
if raw_text.endswith(summarytag):
|
||||||
|
self.last_tools = ''; raw_text = raw_text[:-len(summarytag)]
|
||||||
|
with open('model_responses.txt', 'a', encoding='utf-8', errors="replace") as f:
|
||||||
|
f.write(f"=== Response ===\n{raw_text}\n\n")
|
||||||
return self._parse_mixed_response(raw_text)
|
return self._parse_mixed_response(raw_text)
|
||||||
|
|
||||||
def _build_protocol_prompt(self, messages, tools):
|
def _build_protocol_prompt(self, messages, tools):
|
||||||
|
|||||||
Reference in New Issue
Block a user