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

+274
View File
@@ -0,0 +1,274 @@
"""xiaoli-cli 7.0.0 主类"""
import os, sys, json, importlib
from datetime import datetime
from .types import ToolCall, ToolResult
from .history import History
from .safety import Safety
from .display import Display
from .engine import EngineAdapter
from .runner import Runner
class XiaoliCLI:
def __init__(self):
self.shared_conversation_history = []
self.max_history = 999999
self.current_engine = None
self.engines = {}
self.engine_name = "openai"
self.liugin_manager = None
self.tools = []
self.liugin_commands = {}
self.chat_dir = "chat_history"
self.memory_manager = None
self._load_config()
self._load_engines()
self._load_plugins()
self._init_memory()
self._init_core()
self._register_commands()
print("xiaoli-cli 7.0.0 初始化完成")
def _load_config(self):
try:
from xcli_core.config import get_system_config
self.engine_name = get_system_config("default_engine", "openai")
except Exception:
self.engine_name = "openai"
def _load_engines(self):
print("正在加载AI引擎...")
d = os.path.join(os.path.dirname(os.path.dirname(__file__)), "ai_engines")
if not os.path.isdir(d):
return
for fn in os.listdir(d):
if fn.endswith("_engine.py") and not fn.startswith("__"):
mn = fn[:-3]
try:
mod = importlib.import_module(f"ai_engines.{mn}")
for an in dir(mod):
obj = getattr(mod, an)
if isinstance(obj, type) and an.endswith("AI") and hasattr(obj, "generate_response"):
eng = obj()
eng.cli = self
eng.shared_conversation_history = self.shared_conversation_history
self.engines[eng.name] = eng
print(f" 已加载: {eng.name} ({an})")
except Exception as e:
print(f" [ERROR] {mn}: {e}")
if self.engine_name not in self.engines and self.engines:
self.engine_name = list(self.engines.keys())[0]
self.current_engine = self.engines.get(self.engine_name)
print(f" 共 {len(self.engines)} 个引擎,当前: {self.engine_name}")
def _load_plugins(self):
print("正在加载插件...")
try:
from unified_tool_manager import UnifiedToolManager
self.liugin_manager = UnifiedToolManager(self)
pd = os.path.join(os.path.dirname(os.path.dirname(__file__)), "plugins")
sd = os.path.join(os.path.dirname(os.path.dirname(__file__)), "skills")
self.liugin_manager.initialize(pd, sd)
self.tools = self.liugin_manager.tools if hasattr(self.liugin_manager, "tools") else []
print(f" 已加载 {len(self.tools)} 个插件")
except Exception as e:
print(f" [ERROR] 插件加载失败: {e}")
import traceback; traceback.print_exc()
def _init_memory(self):
try:
from xcli_core.memory import MemoryManager
self.memory_manager = MemoryManager(self)
except Exception:
pass
def _init_core(self):
self.history = History(self.max_history)
self.safety = Safety(self, lambda: self.current_engine)
self.display = Display()
self.engine_adapter = EngineAdapter(self.current_engine)
self.runner = Runner(self.engine_adapter, self.liugin_manager,
self.history, self.safety, self.display)
self.runner.set_prompt_builder(self._build_prompt)
def _build_prompt(self):
p = '你叫小狸,是一个强大的智能编程助手。\n\n'
p += '【工具调用】\n当你需要执行操作时,直接调用工具。\n工具执行后,根据结果继续回复或调用下一个工具。\n\n'
p += '【补充回复】\n如果你需要补充之前的回复,输出:\n'
p += '{"action": "continue", "content": "要补充的内容"}\n\n'
p += '【核心工具】\n'
p += '1. code_editor - 代码编辑\n2. code_search - 代码搜索\n3. git_tools - Git\n'
p += '4. cmd_executor - Shell命令\n5. file_manager - 文件管理\n6. browser_auto - 浏览器\n'
p += '7. sub_agent - 子Agent\n8. auto_engineer - 工程化\n9. network_tools - 网络\n'
p += '10. ai_search - 网页内容 (url/search/web)\n\n'
p += '说话时可适当加nyan表现可爱性格。'
return p
def register_liugin_command(self, name, handler):
self.liugin_commands[name] = handler
def process_conversation(self, user_input):
return self.runner.run(user_input)
def switch_engine(self, name):
if name in self.engines:
self.engine_name = name
self.current_engine = self.engines[name]
self.current_engine.shared_conversation_history = self.shared_conversation_history
self.engine_adapter = EngineAdapter(self.current_engine)
self.runner.set_engine(self.engine_adapter)
self.safety._engine = lambda: self.current_engine
try:
from xcli_core.config import set_system_config
set_system_config("default_engine", name)
except Exception:
pass
return f"已切换到: {name}"
return f"引擎 {name} 不存在"
def _register_commands(self):
self._commands = {
"/help": self._cmd_help,
"/engine": self._cmd_engine,
"/mode": self._cmd_mode,
"/clear": self._cmd_clear,
"/history": self._cmd_history,
"/tools": self._cmd_tools,
"/exit": self._cmd_exit,
"/quit": self._cmd_exit,
"/save": self._cmd_save,
"/config": self._cmd_config,
"/new": self._cmd_new,
}
def handle_command(self, user_input):
parts = user_input.strip().split(None, 1)
cmd = parts[0].lower().lstrip("/")
args = parts[1] if len(parts) > 1 else ""
h = self._commands.get("/" + cmd)
if h:
return h(args)
if cmd in self.liugin_commands:
try:
r = self.liugin_commands[cmd](args)
if r: print(r)
except Exception as e:
print(f"命令失败: {e}")
return ""
return None
def _cmd_help(self, a):
return "\n".join(["/help - 帮助", "/engine [list|<name>] - 引擎",
"/mode [0-3] - 安全模式", "/clear - 清空历史",
"/history - 历史", "/tools - 工具", "/save - 保存",
"/new - 新对话", "/config - 配置", "/exit - 退出"])
def _cmd_engine(self, a):
if not a or a == "list":
lines = [f"当前: {self.engine_name}", "可用:"]
for n in self.engines:
lines.append(f" {n}{' <==' if n == self.engine_name else ''}")
return "\n".join(lines)
return self.switch_engine(a)
def _cmd_mode(self, a):
if not a: return f"当前模式: {self.safety.get_mode()}"
try:
m = int(a)
self.safety.set_mode(m)
names = {0: "无限制", 1: "普通", 2: "人工", 3: "计划"}
return f"已切换: {names.get(m, '未知')}"
except ValueError:
return "用法: /mode [0-3]"
def _cmd_clear(self, a):
self.history.clear()
self.shared_conversation_history.clear()
return "已清空"
def _cmd_history(self, a):
msgs = self.history.snapshot()
if not msgs: return "无历史"
lines = [f"共 {len(msgs)} 条:"]
for m in msgs[-10:]:
c = m.content[:50] + "..." if len(m.content) > 50 else m.content
lines.append(f" [{m.role}] {c}")
return "\n".join(lines)
def _cmd_tools(self, a):
if not self.tools: return "无工具"
lines = [f"共 {len(self.tools)} 个:"]
for t in self.tools:
lines.append(f" {t.get('name','')}: {t.get('description','')[:60]}")
return "\n".join(lines)
def _cmd_save(self, a):
try:
os.makedirs(self.chat_dir, exist_ok=True)
ts = datetime.now().strftime("%Y%m%d_%H%M%S")
fp = os.path.join(self.chat_dir, f"{ts}_chat.json")
msgs = self.history.snapshot()
data = [{"role": m.role, "content": m.content} for m in msgs]
with open(fp, "w", encoding="utf-8") as f:
json.dump(data, f, ensure_ascii=False, indent=2)
return f"已保存: {fp}"
except Exception as e:
return f"失败: {e}"
def _cmd_new(self, a):
self._cmd_save("")
self.history.clear()
self.shared_conversation_history.clear()
return "新对话"
def _cmd_config(self, a):
try:
with open("config.json", "r", encoding="utf-8") as f:
return json.dumps(json.load(f), ensure_ascii=False, indent=2)[:1000]
except Exception as e:
return f"失败: {e}"
def _cmd_exit(self, a):
self._cmd_save("")
return "__EXIT__"
def run(self):
try:
from colorama import init, Fore, Style
init()
except ImportError:
class _D:
CYAN = GREEN = YELLOW = RESET_ALL = ''
Fore = Style = _D()
print()
print(f" {Fore.CYAN}+========================================+{Style.RESET_ALL}")
print(f" {Fore.CYAN}| xiaoli-cli 7.0.0 (重构版) |{Style.RESET_ALL}")
print(f" {Fore.CYAN}| 小狸 AI 编程助手 |{Style.RESET_ALL}")
print(f" {Fore.CYAN}+========================================+{Style.RESET_ALL}")
print()
print(f" 引擎: {Fore.GREEN}{self.engine_name}{Style.RESET_ALL if hasattr(Style,'RESET_ALL') else ''}")
print(f" 工具: {Fore.GREEN}{len(self.tools)}{Style.RESET_ALL if hasattr(Style,'RESET_ALL') else ''} 个插件")
print(f" FC: {'支持' if self.engine_adapter.supports_fc() else '不支持'}")
print(f" 输入 /help 查看命令")
print()
while True:
try:
ui = input(f"{Fore.CYAN}[用户ID: xiaoli]{Style.RESET_ALL if hasattr(Style,'RESET_ALL') else ''} ").strip()
if not ui: continue
if ui.startswith("/"):
r = self.handle_command(ui)
if r == "__EXIT__":
print(f" {Fore.YELLOW}再见~{Style.RESET_ALL if hasattr(Style,'RESET_ALL') else ''}")
break
if r: print(r)
continue
self.process_conversation(ui)
print()
except KeyboardInterrupt:
print(f"\n {Fore.YELLOW}(Ctrl+C, /exit退出){Style.RESET_ALL if hasattr(Style,'RESET_ALL') else ''}")
except EOFError:
break
except Exception as e:
print(f" {Fore.RED}错误: {e}{Style.RESET_ALL if hasattr(Style,'RESET_ALL') else ''}")