xiaoli-cli 7.0.0: 重构底层架构 + 移除工具截断

- 重构 core/ 底层 (7文件): app/runner/engine/parser/history/safety/display
- FC原生调用为主,prompt-JSON保底
- 移除所有工具返回截断 (ai_search/git_tools/cmd_executor/browser_auto/network_tools/sub_agent/tool_search)
- 删除无用文件 (旧服务器/WebUI/彩蛋/启动器等21个文件)
- 清理 constants.py 死代码
- 版本号更新为 7.0.0
This commit is contained in:
shuiyu1123 committed 2026-07-05 13:17:27 +08:00
commit 78fea7ccdd
57 files changed
+18358

No files matched your search

+170
View File
@@ -0,0 +1,170 @@
"""引擎适配 — FC为主,prompt-JSON保底"""
import json, re, uuid
from .types import EngineResponse, ToolCall
class EngineAdapter:
def __init__(self, engine):
self._engine = engine
self._fc = hasattr(engine, 'generate_with_tools')
def supports_fc(self):
return self._fc
def name(self):
return getattr(self._engine, 'name', 'unknown')
def generate(self, messages, tools=None, system_prompt=None):
if self._fc and tools:
return self._gen_fc(messages, tools, system_prompt)
return self._gen_legacy(messages, system_prompt)
def _gen_fc(self, messages, tools, system_prompt):
try:
raw = self._engine.generate_with_tools(messages, tools, system_prompt=system_prompt)
text = ""
thinking = ""
tool_calls = None
if isinstance(raw, dict):
text = raw.get("content", "") or ""
thinking = raw.get("reasoning_content", "") or ""
tc_list = raw.get("tool_calls", []) or []
if tc_list:
tool_calls = []
for tc in tc_list:
func = tc.get("function", {})
cid = tc.get("id", str(uuid.uuid4())[:8])
name = func.get("name", "")
args_str = func.get("arguments", "{}")
arguments = None
try:
if isinstance(args_str, str) and args_str:
arguments = json.loads(args_str)
except json.JSONDecodeError:
pass
args = self._to_legacy(name, arguments) if arguments else (args_str or "")
tool_calls.append(ToolCall(id=cid, name=name, args=args,
arguments=arguments, raw=tc))
return EngineResponse(text=text, thinking=thinking, tool_calls=tool_calls, raw=raw)
except Exception as e:
return EngineResponse(text=f"引擎调用失败: {e}")
def _gen_legacy(self, messages, system_prompt):
user_input = ""
for msg in reversed(messages):
if msg.get("role") == "user":
user_input = msg.get("content", "")
break
try:
resp = self._engine.generate_response(user_input, system_prompt=system_prompt)
except Exception as e:
return EngineResponse(text=f"引擎调用失败: {e}")
if not resp:
return EngineResponse()
text, thinking = self._strip_thinking(resp)
return EngineResponse(text=text, thinking=thinking, raw=resp)
def _strip_thinking(self, text):
if not text:
return "", ""
thinking = ""
if "<thinking>" in text and "</thinking>" in text:
m = re.search(r'<thinking>(.*?)</thinking>(.*)', text, re.DOTALL)
if m:
thinking = m.group(1).strip()
text = m.group(2).strip()
start = getattr(self._engine, 'thinking_start_marker', None)
end = getattr(self._engine, 'thinking_end_marker', None)
if start and end and start in text and end in text:
parts = text.split(start)
if len(parts) >= 2:
rest = parts[1]
if end in rest:
tp, rp = rest.split(end, 1)
thinking = (thinking + "\n" + tp.strip()).strip() if thinking else tp.strip()
text = (parts[0] + rp).strip()
return text, thinking
def _to_legacy(self, tool_name, args):
"""FC dict参数 → 旧插件字符串参数"""
if not args:
return ""
if tool_name == "ai_search":
url = args.get("url", "")
q = args.get("query", args.get("search", ""))
if url: return f"url {url}"
if q: return f"search {q}"
if tool_name == "network_tools":
op = args.get("operation", args.get("op", "get"))
t = args.get("target", args.get("url", args.get("host", "")))
if op and t: return f"{op} {t}"
if tool_name == "code_editor":
op = args.get("operation", args.get("action", ""))
fp = args.get("file", args.get("path", args.get("filename", "")))
ct = args.get("content", args.get("text", args.get("code", "")))
old = args.get("old", args.get("old_code", args.get("find", "")))
new = args.get("new", args.get("new_code", args.get("replace", "")))
ln = args.get("line", args.get("line_number", ""))
sl = args.get("start", args.get("start_line", ""))
el = args.get("end", args.get("end_line", ""))
pat = args.get("pattern", args.get("file_pattern", ""))
d = args.get("directory", args.get("dir", "."))
if op == "create" and fp and ct: return f"create {fp}\n{ct}"
if op == "write" and fp and ct: return f"write {fp}\n{ct}"
if op == "append" and fp and ct: return f"append {fp}\n{ct}"
if op in ("read_range", "read"): return f"read_range {fp} {sl or 1} {el or 50}"
if op == "edit": return f"edit {fp}\n{old}\n<<<>>>\n{new}"
if op == "insert": return f"insert {fp} {ln}\n{ct}"
if op in ("find", "search"): return f"find {pat} {d}"
if op == "syntax_check": return f"syntax_check {fp}"
return json.dumps(args, ensure_ascii=False)
if tool_name == "code_search":
op = args.get("operation", "find")
pat = args.get("pattern", args.get("query", args.get("search", "")))
d = args.get("directory", args.get("dir", "."))
return f"{op} {pat} {d}"
if tool_name == "git_tools":
op = args.get("operation", "status")
a = args.get("args", "")
return f"{op} {a}" if a else op
if tool_name == "cmd_executor":
return args.get("command", args.get("cmd", args.get("shell", "")))
if tool_name == "file_manager":
op = args.get("operation", args.get("action", "list"))
p = args.get("path", args.get("file", "."))
ct = args.get("content", args.get("text", ""))
if op in ("read", "list", "info"): return f"{op} {p}"
if op == "write" and ct: return f'write {p} -c "{ct}"'
if op == "append" and ct: return f'append {p} -c "{ct}"'
if op and p: return f"{op} {p}"
if tool_name == "browser_auto":
op = args.get("operation", "")
url = args.get("url", "")
if op == "open" and url: return f"open {url}"
if tool_name == "auto_engineer":
op = args.get("operation", "")
t = args.get("target", args.get("path", ""))
if op and t: return f"{op} {t}"
return op
if tool_name == "sub_agent":
op = args.get("operation", "list")
if op == "create":
task = args.get("task", args.get("prompt", ""))
return f"create {task}"
return op
if tool_name in ("task_manager", "scheduler"):
return args.get("operation", "list")
# 通用
if "operation" in args:
op = args["operation"]
rest = " ".join(str(v) for k, v in args.items() if k != "operation")
return f"{op} {rest}".strip()
return " ".join(str(v) for v in args.values())
def handle_command(self, cmd):
if hasattr(self._engine, 'handle_command'):
return self._engine.handle_command(cmd)
return False
def get_raw(self):
return self._engine