fr-cli 2.2.4__tar.gz → 2.2.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (84) hide show
  1. {fr_cli-2.2.4/fr_cli.egg-info → fr_cli-2.2.6}/PKG-INFO +6 -6
  2. {fr_cli-2.2.4 → fr_cli-2.2.6}/README.md +3 -3
  3. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/README.md +11 -4
  4. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/__init__.py +1 -1
  5. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/agent/master.py +1 -0
  6. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/core/chat.py +27 -16
  7. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/core/intent.py +1 -1
  8. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/core/llm.py +17 -18
  9. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/main.py +0 -1
  10. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/repl/commands.py +16 -21
  11. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/weapon/mcp.py +0 -2
  12. {fr_cli-2.2.4 → fr_cli-2.2.6/fr_cli.egg-info}/PKG-INFO +6 -6
  13. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli.egg-info/SOURCES.txt +1 -14
  14. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli.egg-info/requires.txt +2 -2
  15. {fr_cli-2.2.4 → fr_cli-2.2.6}/pyproject.toml +3 -3
  16. fr_cli-2.2.4/tests/test_agent_client.py +0 -199
  17. fr_cli-2.2.4/tests/test_agent_server.py +0 -199
  18. fr_cli-2.2.4/tests/test_ai_save_file_with_verify.py +0 -228
  19. fr_cli-2.2.4/tests/test_all.py +0 -917
  20. fr_cli-2.2.4/tests/test_auto_session.py +0 -116
  21. fr_cli-2.2.4/tests/test_builtins.py +0 -67
  22. fr_cli-2.2.4/tests/test_dataframe.py +0 -42
  23. fr_cli-2.2.4/tests/test_gatekeeper.py +0 -118
  24. fr_cli-2.2.4/tests/test_integration.py +0 -224
  25. fr_cli-2.2.4/tests/test_intent_classification.py +0 -268
  26. fr_cli-2.2.4/tests/test_launcher.py +0 -98
  27. fr_cli-2.2.4/tests/test_master_agent.py +0 -162
  28. fr_cli-2.2.4/tests/test_structured_tools.py +0 -245
  29. {fr_cli-2.2.4 → fr_cli-2.2.6}/LICENSE +0 -0
  30. {fr_cli-2.2.4 → fr_cli-2.2.6}/MANIFEST.in +0 -0
  31. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/WEAPON.MD +0 -0
  32. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/addon/plugin.py +0 -0
  33. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/agent/__init__.py +0 -0
  34. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/agent/builtins/__init__.py +0 -0
  35. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/agent/builtins/_utils.py +0 -0
  36. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/agent/builtins/db.py +0 -0
  37. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/agent/builtins/local.py +0 -0
  38. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/agent/builtins/rag.py +0 -0
  39. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/agent/builtins/rag_watcher_daemon.py +0 -0
  40. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/agent/builtins/remote.py +0 -0
  41. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/agent/builtins/spider.py +0 -0
  42. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/agent/client.py +0 -0
  43. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/agent/executor.py +0 -0
  44. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/agent/generator.py +0 -0
  45. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/agent/manager.py +0 -0
  46. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/agent/master_prompt.py +0 -0
  47. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/agent/remote.py +0 -0
  48. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/agent/server.py +0 -0
  49. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/agent/workflow.py +0 -0
  50. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/breakthrough/update.py +0 -0
  51. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/command/__init__.py +0 -0
  52. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/command/executor.py +0 -0
  53. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/command/registry.py +0 -0
  54. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/command/security.py +0 -0
  55. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/conf/config.py +0 -0
  56. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/conf/wizard.py +0 -0
  57. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/core/core.py +0 -0
  58. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/core/recommender.py +0 -0
  59. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/core/stream.py +0 -0
  60. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/core/sysmon.py +0 -0
  61. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/core/thinking.py +0 -0
  62. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/gatekeeper/__init__.py +0 -0
  63. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/gatekeeper/daemon.py +0 -0
  64. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/gatekeeper/manager.py +0 -0
  65. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/lang/i18n.py +0 -0
  66. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/memory/context.py +0 -0
  67. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/memory/history.py +0 -0
  68. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/memory/session.py +0 -0
  69. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/repl/__init__.py +0 -0
  70. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/security/security.py +0 -0
  71. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/ui/ui.py +0 -0
  72. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/weapon/cron.py +0 -0
  73. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/weapon/dataframe.py +0 -0
  74. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/weapon/disk.py +0 -0
  75. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/weapon/fs.py +0 -0
  76. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/weapon/launcher.py +0 -0
  77. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/weapon/loader.py +0 -0
  78. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/weapon/mail.py +0 -0
  79. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/weapon/vision.py +0 -0
  80. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli/weapon/web.py +0 -0
  81. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli.egg-info/dependency_links.txt +0 -0
  82. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli.egg-info/entry_points.txt +0 -0
  83. {fr_cli-2.2.4 → fr_cli-2.2.6}/fr_cli.egg-info/top_level.txt +0 -0
  84. {fr_cli-2.2.4 → fr_cli-2.2.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fr-cli
3
- Version: 2.2.4
3
+ Version: 2.2.6
4
4
  Summary: 凡人打字机 - 支持多模型(Zhipu/DeepSeek/Kimi/Qwen/StepFun/MiniMax/Spark)的终极全能终端工具
5
5
  Author: FANREN CLI Author
6
6
  License-Expression: MIT
@@ -37,7 +37,7 @@ Requires-Dist: pyodbc>=4.0.0; extra == "db"
37
37
  Requires-Dist: oracledb>=1.3.0; extra == "db"
38
38
  Provides-Extra: rag
39
39
  Requires-Dist: chromadb>=0.4.0; extra == "rag"
40
- Requires-Dist: sentence-transformers>=2.2.4; extra == "rag"
40
+ Requires-Dist: sentence-transformers>=2.2.0; extra == "rag"
41
41
  Provides-Extra: remote
42
42
  Requires-Dist: paramiko>=3.0.0; extra == "remote"
43
43
  Provides-Extra: spider
@@ -56,7 +56,7 @@ Requires-Dist: psycopg2-binary>=2.9.0; extra == "all"
56
56
  Requires-Dist: pyodbc>=4.0.0; extra == "all"
57
57
  Requires-Dist: oracledb>=1.3.0; extra == "all"
58
58
  Requires-Dist: chromadb>=0.4.0; extra == "all"
59
- Requires-Dist: sentence-transformers>=2.2.4; extra == "all"
59
+ Requires-Dist: sentence-transformers>=2.2.0; extra == "all"
60
60
  Requires-Dist: paramiko>=3.0.0; extra == "all"
61
61
  Requires-Dist: selenium>=4.10.0; extra == "all"
62
62
  Requires-Dist: bypy; extra == "all"
@@ -71,11 +71,11 @@ Dynamic: license-file
71
71
 
72
72
  # 凡人打字机 (fr-cli)
73
73
 
74
- 基于智谱 AI (ZhipuAI/GLM) 的终极全能终端工具。
74
+ 支持多模型(智谱/DeepSeek/Kimi/Qwen/StepFun/MiniMax/讯飞星火)的终极全能终端工具。
75
75
 
76
76
  ## ✨ 功能特性
77
77
 
78
- - 🤖 **AI 对话**:基于 GLM-4 系列模型的智能对话
78
+ - 🤖 **AI 对话**:支持多模型(智谱 GLM / DeepSeek / Kimi / 通义千问 / StepFun / MiniMax / 讯飞星火)
79
79
  - 🧠 **MasterAgent 主控**:自我进化的 ReAct 主控 Agent,自动规划、调用工具、反思进化
80
80
  - 🧩 **思维模式**:direct / CoT / ToT / ReAct 四种推理模式切换
81
81
  - 📁 **文件沙盒**:安全的虚拟文件系统,支持读写/目录操作
@@ -105,7 +105,7 @@ pip install fr-cli
105
105
  fr-cli
106
106
  ```
107
107
 
108
- 首次运行会引导输入智谱 API Key。
108
+ 首次运行会引导输入当前道统的 API Key。
109
109
 
110
110
  ## 📝 使用示例
111
111
 
@@ -1,10 +1,10 @@
1
1
  # 凡人打字机 (fr-cli)
2
2
 
3
- 基于智谱 AI (ZhipuAI/GLM) 的终极全能终端工具。
3
+ 支持多模型(智谱/DeepSeek/Kimi/Qwen/StepFun/MiniMax/讯飞星火)的终极全能终端工具。
4
4
 
5
5
  ## ✨ 功能特性
6
6
 
7
- - 🤖 **AI 对话**:基于 GLM-4 系列模型的智能对话
7
+ - 🤖 **AI 对话**:支持多模型(智谱 GLM / DeepSeek / Kimi / 通义千问 / StepFun / MiniMax / 讯飞星火)
8
8
  - 🧠 **MasterAgent 主控**:自我进化的 ReAct 主控 Agent,自动规划、调用工具、反思进化
9
9
  - 🧩 **思维模式**:direct / CoT / ToT / ReAct 四种推理模式切换
10
10
  - 📁 **文件沙盒**:安全的虚拟文件系统,支持读写/目录操作
@@ -34,7 +34,7 @@ pip install fr-cli
34
34
  fr-cli
35
35
  ```
36
36
 
37
- 首次运行会引导输入智谱 API Key。
37
+ 首次运行会引导输入当前道统的 API Key。
38
38
 
39
39
  ## 📝 使用示例
40
40
 
@@ -1,14 +1,14 @@
1
1
  # 凡人打字机 (fr-cli)
2
2
 
3
- 基于智谱 AI (ZhipuAI / GLM) 的终极全能终端工具。
3
+ 支持多模型(智谱/DeepSeek/Kimi/Qwen/StepFun/MiniMax/讯飞星火)的终极全能终端工具。
4
4
 
5
5
  **🇨🇳 中文简介**
6
6
 
7
- 支持:AI 智能对话、MasterAgent 自我进化主控、思维模式切换(direct/CoT/ToT/ReAct)、文件沙盒操作、联网搜索(SSRF 防护)、图片生成与识别、邮件收发、定时任务(shlex 安全解析)、云盘集成、会话记忆、按日期自动存档、插件进化(子进程隔离)、四阶安全拦截、Shell 管道直通 AI。
7
+ 支持:多模型 AI 对话(智谱/DeepSeek/Kimi/Qwen/StepFun/MiniMax/讯飞星火)、MasterAgent 自我进化主控、思维模式切换(direct/CoT/ToT/ReAct)、文件沙盒操作、联网搜索(SSRF 防护)、图片生成与识别、邮件收发、定时任务(shlex 安全解析)、云盘集成、会话记忆、按日期自动存档、插件进化(子进程隔离)、四阶安全拦截、Shell 管道直通 AI。
8
8
 
9
9
  **🇺🇸 English Intro**
10
10
 
11
- The ultimate all-knowing terminal tool based on Zhipu AI. Supports AI chat, MasterAgent self-evolving controller, thinking modes (direct/CoT/ToT/ReAct), virtual filesystem, web search (SSRF-protected), image generation & vision, email, cron jobs (shlex-safe), cloud drive, session memory, auto date-based archiving, self-evolving plugins (subprocess-isolated), and powerful Shell piping.
11
+ The ultimate all-knowing terminal tool supporting multiple LLM providers (Zhipu/DeepSeek/Kimi/Qwen/StepFun/MiniMax/Spark). Supports AI chat, MasterAgent self-evolving controller, thinking modes (direct/CoT/ToT/ReAct), virtual filesystem, web search (SSRF-protected), image generation & vision, email, cron jobs (shlex-safe), cloud drive, session memory, auto date-based archiving, self-evolving plugins (subprocess-isolated), and powerful Shell piping.
12
12
 
13
13
  ---
14
14
 
@@ -19,7 +19,7 @@ pip install fr-cli
19
19
  fr-cli
20
20
  ```
21
21
 
22
- 首次运行会引导输入智谱 API Key。
22
+ 首次运行会引导输入当前道统的 API Key。
23
23
 
24
24
  ## 🎮 使用方式
25
25
 
@@ -41,6 +41,13 @@ fr-cli
41
41
  /session_load <idx> 加载存档会话
42
42
  /mode direct|cot|tot|react 切换思维模式
43
43
  /master on|off|status MasterAgent 主控
44
+ /model <模型名> 切换当前道统模型
45
+ /model <道统>:<模型名> 同时切换道统和模型
46
+ /key <key> 修改当前道统 API Key
47
+ /key <道统> <key> 为指定道统设置 Key
48
+ /providers 查看所有道统配置
49
+ /providers add <p> <k> [m] 添加/更新道统配置
50
+ /providers use <p> 切换到指定道统
44
51
  /mcp_list 列出 MCP 服务器及工具
45
52
  /mcp_add <名> <命令> [参数] 添加 MCP 服务器
46
53
  /mcp_del <名> 删除 MCP 服务器
@@ -1,4 +1,4 @@
1
1
  """
2
2
  凡人打字机 - 基于智谱AI的终极全能终端工具
3
3
  """
4
- __version__ = "2.2.4"
4
+ __version__ = "2.2.6"
@@ -20,6 +20,7 @@ from fr_cli.memory.context import extract_recent_turns, build_context_summary, s
20
20
  from fr_cli.memory.session import create_session, update_session
21
21
  from fr_cli.addon.plugin import extract_code, PLUGIN_DIR
22
22
  from fr_cli.ui.ui import RED, YELLOW, GREEN, DIM, RESET
23
+ from fr_cli.lang.i18n import T
23
24
 
24
25
  MASTER_DIR = Path.home() / ".fr_cli_master"
25
26
  PERSONA_FILE = MASTER_DIR / "persona.md"
@@ -20,6 +20,28 @@ from fr_cli.memory.session import create_session, update_session
20
20
  from fr_cli.core.intent import should_force_tool, classify_intent, has_info_fetch_intent, has_save_intent
21
21
 
22
22
 
23
+ def _fetch_mcp_tools(mcp_manager):
24
+ """安全获取 MCP 工具列表"""
25
+ if mcp_manager and hasattr(mcp_manager, "list_all_tools"):
26
+ try:
27
+ tools = mcp_manager.list_all_tools()
28
+ return tools if isinstance(tools, list) else []
29
+ except Exception:
30
+ pass
31
+ return []
32
+
33
+
34
+ def _fetch_mcp_desc(mcp_manager):
35
+ """安全获取 MCP 工具描述文本"""
36
+ if mcp_manager and hasattr(mcp_manager, "get_server_tools_desc"):
37
+ try:
38
+ desc = mcp_manager.get_server_tools_desc()
39
+ return desc if isinstance(desc, str) and desc else ""
40
+ except Exception:
41
+ pass
42
+ return ""
43
+
44
+
23
45
  def handle_ai_chat(state, u):
24
46
  """处理 AI 正常对话流程"""
25
47
  lang = state.lang
@@ -31,14 +53,7 @@ def handle_ai_chat(state, u):
31
53
  tools = get_available_tools(state.weapon_tools, state.plugins)
32
54
  # 将 MCP 外部神通纳入意图判定视野
33
55
  mcp_manager = getattr(state, "mcp", None)
34
- mcp_tools_summary = []
35
- if mcp_manager and hasattr(mcp_manager, "list_all_tools"):
36
- try:
37
- _mcp_tools = mcp_manager.list_all_tools()
38
- if isinstance(_mcp_tools, list):
39
- mcp_tools_summary = _mcp_tools
40
- except Exception:
41
- pass
56
+ mcp_tools_summary = _fetch_mcp_tools(mcp_manager)
42
57
  if mcp_tools_summary:
43
58
  tools.append({
44
59
  "name": "mcp_tools",
@@ -74,14 +89,10 @@ def handle_ai_chat(state, u):
74
89
  tools_info += f"{i}. {tool['name']}: {tool['description']}\n 可用命令: {', '.join(tool['commands'])}\n"
75
90
  # 注入 MCP 外部神通
76
91
  mcp_manager = getattr(state, "mcp", None)
77
- if mcp_manager and hasattr(mcp_manager, "get_server_tools_desc"):
78
- try:
79
- mcp_desc = mcp_manager.get_server_tools_desc()
80
- if isinstance(mcp_desc, str) and mcp_desc:
81
- tools_info += mcp_desc + "\n"
82
- tools_info += "\n调用 MCP 工具时,请使用格式:【调用:mcp_call({\"server\": \"服务器名\", \"tool\": \"工具名\", \"arguments\": {...}})】\n"
83
- except Exception:
84
- pass
92
+ mcp_desc = _fetch_mcp_desc(mcp_manager)
93
+ if mcp_desc:
94
+ tools_info += mcp_desc + "\n"
95
+ tools_info += "\n调用 MCP 工具时,请使用格式:【调用:mcp_call({\"server\": \"服务器名\", \"tool\": \"工具名\", \"arguments\": {...}})】\n"
85
96
  # 信息获取规范:当用户需要调用外部信息源时,采用双源回答模式
86
97
  if has_info_fetch_intent(u):
87
98
  tools_info += """\n
@@ -66,7 +66,7 @@ def should_force_tool(user_input):
66
66
  同时检测中英文关键词,不依赖当前界面语言。"""
67
67
  u = user_input.lower()
68
68
  for kw in _FORCE_TOOL_KEYWORDS:
69
- if kw.lower() in u:
69
+ if kw in u:
70
70
  return True
71
71
  return False
72
72
 
@@ -22,6 +22,18 @@ class BaseLLMClient(ABC):
22
22
  """
23
23
  pass
24
24
 
25
+ @staticmethod
26
+ def _yield_chunks(response) -> Iterator[dict]:
27
+ """通用 chunk 解析生成器,供各子类复用"""
28
+ for chunk in response:
29
+ content = ""
30
+ usage = None
31
+ if chunk.choices and chunk.choices[0].delta:
32
+ content = chunk.choices[0].delta.content or ""
33
+ if hasattr(chunk, 'usage') and chunk.usage:
34
+ usage = chunk.usage.model_dump() if hasattr(chunk.usage, 'model_dump') else vars(chunk.usage)
35
+ yield {"content": content, "usage": usage}
36
+
25
37
 
26
38
  class ZhipuLLMClient(BaseLLMClient):
27
39
  """智谱 AI 客户端 (zhipuai SDK)"""
@@ -38,14 +50,7 @@ class ZhipuLLMClient(BaseLLMClient):
38
50
  stream=True,
39
51
  max_tokens=max_tokens,
40
52
  )
41
- for chunk in response:
42
- content = ""
43
- usage = None
44
- if chunk.choices and chunk.choices[0].delta:
45
- content = chunk.choices[0].delta.content or ""
46
- if hasattr(chunk, 'usage') and chunk.usage:
47
- usage = chunk.usage.model_dump() if hasattr(chunk.usage, 'model_dump') else vars(chunk.usage)
48
- yield {"content": content, "usage": usage}
53
+ yield from self._yield_chunks(response)
49
54
 
50
55
 
51
56
  class OpenAICompatibleClient(BaseLLMClient):
@@ -66,14 +71,7 @@ class OpenAICompatibleClient(BaseLLMClient):
66
71
  stream=True,
67
72
  max_tokens=max_tokens,
68
73
  )
69
- for chunk in response:
70
- content = ""
71
- usage = None
72
- if chunk.choices and chunk.choices[0].delta:
73
- content = chunk.choices[0].delta.content or ""
74
- if hasattr(chunk, 'usage') and chunk.usage:
75
- usage = chunk.usage.model_dump() if hasattr(chunk.usage, 'model_dump') else vars(chunk.usage)
76
- yield {"content": content, "usage": usage}
74
+ yield from self._yield_chunks(response)
77
75
 
78
76
 
79
77
  # 提供商配置表
@@ -140,9 +138,10 @@ def create_llm_client(cfg: dict):
140
138
  pcfg = providers_cfg.get(provider, {})
141
139
 
142
140
  # 向后兼容:如果 providers 中没有当前 provider,从顶层读取 key/model
143
- api_key = pcfg.get("key", cfg.get("key", ""))
141
+ # 使用 'or' 确保空字符串也能正确回退到顶层 key
142
+ api_key = pcfg.get("key") or cfg.get("key", "")
144
143
  default_model = _PROVIDERS.get(provider, _PROVIDERS["zhipu"])["default_model"]
145
- model = pcfg.get("model", cfg.get("model", default_model))
144
+ model = pcfg.get("model") or cfg.get("model", default_model)
146
145
 
147
146
  info = _PROVIDERS.get(provider, _PROVIDERS["zhipu"])
148
147
  client_class = info["client_class"]
@@ -30,7 +30,6 @@ def _sync_manual_to_workspace(vfs):
30
30
  return
31
31
  manual_dst = Path(vfs.cwd) / "MANUAL.md"
32
32
  if not manual_dst.exists():
33
- import shutil
34
33
  shutil.copy2(manual_src, manual_dst)
35
34
  except Exception:
36
35
  pass
@@ -10,7 +10,7 @@ from fr_cli.ui.ui import (
10
10
  print_bye
11
11
  )
12
12
  from fr_cli.memory.history import save_sess, load_sess, del_sess, get_sessions
13
- from fr_cli.memory.context import load_context
13
+ from fr_cli.memory.context import load_context, extract_recent_turns, build_context_summary, save_context
14
14
  from fr_cli.memory.session import (
15
15
  list_sessions as list_auto_sessions,
16
16
  load_session as load_auto_session,
@@ -18,6 +18,7 @@ from fr_cli.memory.session import (
18
18
  )
19
19
  from fr_cli.addon.plugin import extract_code
20
20
  from fr_cli.core.stream import stream_cnt
21
+ from fr_cli.core.sysmon import get_sys_stats
21
22
  from fr_cli.agent.manager import (
22
23
  create_agent_dir, save_agent_code, save_persona, save_skills,
23
24
  save_memory, agent_exists, list_agents, delete_agent,
@@ -26,6 +27,16 @@ from fr_cli.agent.manager import (
26
27
  from fr_cli.agent.executor import run_agent
27
28
 
28
29
 
30
+ def _provider_has_key(state, provider_id):
31
+ """检查指定道统是否已配置 API Key(zhipu 向后兼容顶层 key)"""
32
+ providers_cfg = state.cfg.get("providers", {})
33
+ pcfg = providers_cfg.get(provider_id, {})
34
+ has_key = bool(pcfg.get("key"))
35
+ if not has_key and provider_id == "zhipu":
36
+ has_key = bool(state.cfg.get("key", ""))
37
+ return has_key
38
+
39
+
29
40
  def _print_help(state, topic):
30
41
  """打印修仙指南"""
31
42
  topic_map = {
@@ -104,13 +115,7 @@ def _cmd_model(state, parts):
104
115
  if ok:
105
116
  print(f"{GREEN}✅ 已切换: [{state.provider}] {state.model_name}{RESET}")
106
117
  # 检查新道统是否已配置 API Key,若未配置则引导输入
107
- providers_cfg = state.cfg.get("providers", {})
108
- pcfg = providers_cfg.get(state.provider, {})
109
- # 只检查该道统自己专属的 key;zhipu 向后兼容:未单独配置时回退顶层 key
110
- has_key = bool(pcfg.get("key"))
111
- if not has_key and state.provider == "zhipu":
112
- has_key = bool(state.cfg.get("key", ""))
113
- if not has_key:
118
+ if not _provider_has_key(state, state.provider):
114
119
  print(f"{YELLOW}⚠️ [{state.provider}] 尚未配置 API Key{RESET}")
115
120
  k = input(f"👉 请输入 [{state.provider}] 的 API Key: ").strip()
116
121
  if k:
@@ -128,10 +133,7 @@ def _cmd_model(state, parts):
128
133
  providers_cfg = state.cfg.get("providers", {})
129
134
  for p in list_providers():
130
135
  marker = " 👈 当前" if p["id"] == state.provider else ""
131
- pcfg = providers_cfg.get(p["id"], {})
132
- has_key = bool(pcfg.get("key"))
133
- if not has_key and p["id"] == "zhipu":
134
- has_key = bool(state.cfg.get("key", ""))
136
+ has_key = _provider_has_key(state, p["id"])
135
137
  key_status = f"{GREEN}✅ 已配置{RESET}" if has_key else f"{RED}❌ 未配置{RESET}"
136
138
  print(f" {CYAN}{p['id']}{RESET} — {p['name']}{DIM} (默认: {p['default_model']}){RESET} {key_status}{marker}")
137
139
  print(f"\n{DIM}用法:{RESET}")
@@ -195,10 +197,7 @@ def _cmd_providers(state, parts):
195
197
  from fr_cli.core.llm import list_providers, get_provider_info
196
198
  print(f"{CYAN}📜 道统配置总览{RESET}")
197
199
  for p in list_providers():
198
- pcfg = providers_cfg.get(p["id"], {})
199
- has_key = bool(pcfg.get("key"))
200
- if not has_key and p["id"] == "zhipu":
201
- has_key = bool(state.cfg.get("key", ""))
200
+ has_key = _provider_has_key(state, p["id"])
202
201
  key_status = f"{GREEN}✅{RESET}" if has_key else f"{RED}❌{RESET}"
203
202
  model = pcfg.get("model", p["default_model"])
204
203
  info = get_provider_info(p["id"])
@@ -263,11 +262,7 @@ def _cmd_providers(state, parts):
263
262
  if ok:
264
263
  print(f"{GREEN}✅ 已切换到: [{state.provider}] {state.model_name}{RESET}")
265
264
  # 检查新道统是否已配置 API Key
266
- pcfg = providers_cfg.get(state.provider, {})
267
- has_key = bool(pcfg.get("key"))
268
- if not has_key and state.provider == "zhipu":
269
- has_key = bool(state.cfg.get("key", ""))
270
- if not has_key:
265
+ if not _provider_has_key(state, state.provider):
271
266
  print(f"{YELLOW}⚠️ [{state.provider}] 尚未配置 API Key{RESET}")
272
267
  k = input(f"👉 请输入 [{state.provider}] 的 API Key: ").strip()
273
268
  if k:
@@ -7,8 +7,6 @@ import asyncio
7
7
  import json
8
8
  from typing import Dict, List, Any, Optional
9
9
 
10
- from fr_cli.ui.ui import CYAN, GREEN, YELLOW, RED, DIM, RESET
11
-
12
10
  try:
13
11
  from mcp import ClientSession, StdioServerParameters
14
12
  from mcp.client.stdio import stdio_client
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fr-cli
3
- Version: 2.2.4
3
+ Version: 2.2.6
4
4
  Summary: 凡人打字机 - 支持多模型(Zhipu/DeepSeek/Kimi/Qwen/StepFun/MiniMax/Spark)的终极全能终端工具
5
5
  Author: FANREN CLI Author
6
6
  License-Expression: MIT
@@ -37,7 +37,7 @@ Requires-Dist: pyodbc>=4.0.0; extra == "db"
37
37
  Requires-Dist: oracledb>=1.3.0; extra == "db"
38
38
  Provides-Extra: rag
39
39
  Requires-Dist: chromadb>=0.4.0; extra == "rag"
40
- Requires-Dist: sentence-transformers>=2.2.4; extra == "rag"
40
+ Requires-Dist: sentence-transformers>=2.2.0; extra == "rag"
41
41
  Provides-Extra: remote
42
42
  Requires-Dist: paramiko>=3.0.0; extra == "remote"
43
43
  Provides-Extra: spider
@@ -56,7 +56,7 @@ Requires-Dist: psycopg2-binary>=2.9.0; extra == "all"
56
56
  Requires-Dist: pyodbc>=4.0.0; extra == "all"
57
57
  Requires-Dist: oracledb>=1.3.0; extra == "all"
58
58
  Requires-Dist: chromadb>=0.4.0; extra == "all"
59
- Requires-Dist: sentence-transformers>=2.2.4; extra == "all"
59
+ Requires-Dist: sentence-transformers>=2.2.0; extra == "all"
60
60
  Requires-Dist: paramiko>=3.0.0; extra == "all"
61
61
  Requires-Dist: selenium>=4.10.0; extra == "all"
62
62
  Requires-Dist: bypy; extra == "all"
@@ -71,11 +71,11 @@ Dynamic: license-file
71
71
 
72
72
  # 凡人打字机 (fr-cli)
73
73
 
74
- 基于智谱 AI (ZhipuAI/GLM) 的终极全能终端工具。
74
+ 支持多模型(智谱/DeepSeek/Kimi/Qwen/StepFun/MiniMax/讯飞星火)的终极全能终端工具。
75
75
 
76
76
  ## ✨ 功能特性
77
77
 
78
- - 🤖 **AI 对话**:基于 GLM-4 系列模型的智能对话
78
+ - 🤖 **AI 对话**:支持多模型(智谱 GLM / DeepSeek / Kimi / 通义千问 / StepFun / MiniMax / 讯飞星火)
79
79
  - 🧠 **MasterAgent 主控**:自我进化的 ReAct 主控 Agent,自动规划、调用工具、反思进化
80
80
  - 🧩 **思维模式**:direct / CoT / ToT / ReAct 四种推理模式切换
81
81
  - 📁 **文件沙盒**:安全的虚拟文件系统,支持读写/目录操作
@@ -105,7 +105,7 @@ pip install fr-cli
105
105
  fr-cli
106
106
  ```
107
107
 
108
- 首次运行会引导输入智谱 API Key。
108
+ 首次运行会引导输入当前道统的 API Key。
109
109
 
110
110
  ## 📝 使用示例
111
111
 
@@ -66,17 +66,4 @@ fr_cli/weapon/loader.py
66
66
  fr_cli/weapon/mail.py
67
67
  fr_cli/weapon/mcp.py
68
68
  fr_cli/weapon/vision.py
69
- fr_cli/weapon/web.py
70
- tests/test_agent_client.py
71
- tests/test_agent_server.py
72
- tests/test_ai_save_file_with_verify.py
73
- tests/test_all.py
74
- tests/test_auto_session.py
75
- tests/test_builtins.py
76
- tests/test_dataframe.py
77
- tests/test_gatekeeper.py
78
- tests/test_integration.py
79
- tests/test_intent_classification.py
80
- tests/test_launcher.py
81
- tests/test_master_agent.py
82
- tests/test_structured_tools.py
69
+ fr_cli/weapon/web.py
@@ -11,7 +11,7 @@ psycopg2-binary>=2.9.0
11
11
  pyodbc>=4.0.0
12
12
  oracledb>=1.3.0
13
13
  chromadb>=0.4.0
14
- sentence-transformers>=2.2.4
14
+ sentence-transformers>=2.2.0
15
15
  paramiko>=3.0.0
16
16
  selenium>=4.10.0
17
17
  bypy
@@ -44,7 +44,7 @@ watchdog>=3.0.0
44
44
 
45
45
  [rag]
46
46
  chromadb>=0.4.0
47
- sentence-transformers>=2.2.4
47
+ sentence-transformers>=2.2.0
48
48
 
49
49
  [remote]
50
50
  paramiko>=3.0.0
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "fr-cli"
7
- version = "2.2.4"
7
+ version = "2.2.6"
8
8
  description = "凡人打字机 - 支持多模型(Zhipu/DeepSeek/Kimi/Qwen/StepFun/MiniMax/Spark)的终极全能终端工具"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -38,7 +38,7 @@ dependencies = [
38
38
  [project.optional-dependencies]
39
39
  data = ["pandas>=1.5.0", "openpyxl>=3.0.0"]
40
40
  db = ["pymysql>=1.0.0", "psycopg2-binary>=2.9.0", "pyodbc>=4.0.0", "oracledb>=1.3.0"]
41
- rag = ["chromadb>=0.4.0", "sentence-transformers>=2.2.4"]
41
+ rag = ["chromadb>=0.4.0", "sentence-transformers>=2.2.0"]
42
42
  remote = ["paramiko>=3.0.0"]
43
43
  spider = ["selenium>=4.10.0"]
44
44
  cloud = ["bypy", "aligo", "msal"]
@@ -46,7 +46,7 @@ monitor = ["watchdog>=3.0.0"]
46
46
  all = [
47
47
  "pandas>=1.5.0", "openpyxl>=3.0.0",
48
48
  "pymysql>=1.0.0", "psycopg2-binary>=2.9.0", "pyodbc>=4.0.0", "oracledb>=1.3.0",
49
- "chromadb>=0.4.0", "sentence-transformers>=2.2.4",
49
+ "chromadb>=0.4.0", "sentence-transformers>=2.2.0",
50
50
  "paramiko>=3.0.0",
51
51
  "selenium>=4.10.0",
52
52
  "bypy", "aligo", "msal",