jarvis-ai-assistant 0.2.0__py3-none-any.whl → 0.2.2__py3-none-any.whl
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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/jarvis.py +61 -59
- jarvis/jarvis_agent/main.py +42 -40
- jarvis/jarvis_code_agent/code_agent.py +35 -31
- jarvis/jarvis_code_analysis/code_review.py +73 -39
- jarvis/jarvis_git_squash/main.py +16 -12
- jarvis/jarvis_git_utils/git_commiter.py +25 -20
- jarvis/jarvis_methodology/main.py +34 -49
- jarvis/jarvis_multi_agent/__init__.py +7 -5
- jarvis/jarvis_multi_agent/main.py +28 -23
- jarvis/jarvis_platform/ai8.py +31 -22
- jarvis/jarvis_platform/kimi.py +31 -61
- jarvis/jarvis_platform/tongyi.py +62 -76
- jarvis/jarvis_platform/yuanbao.py +44 -50
- jarvis/jarvis_platform_manager/main.py +55 -90
- jarvis/jarvis_smart_shell/main.py +58 -87
- jarvis/jarvis_tools/cli/main.py +120 -153
- jarvis/jarvis_tools/registry.py +53 -21
- jarvis/jarvis_tools/search_web.py +12 -10
- jarvis/jarvis_utils/http.py +58 -79
- jarvis/jarvis_utils/output.py +1 -1
- jarvis_ai_assistant-0.2.2.dist-info/METADATA +228 -0
- {jarvis_ai_assistant-0.2.0.dist-info → jarvis_ai_assistant-0.2.2.dist-info}/RECORD +27 -30
- {jarvis_ai_assistant-0.2.0.dist-info → jarvis_ai_assistant-0.2.2.dist-info}/entry_points.txt +0 -2
- jarvis/jarvis_git_details/__init__.py +0 -0
- jarvis/jarvis_git_details/main.py +0 -265
- jarvis/jarvis_platform/oyi.py +0 -357
- jarvis_ai_assistant-0.2.0.dist-info/METADATA +0 -833
- {jarvis_ai_assistant-0.2.0.dist-info → jarvis_ai_assistant-0.2.2.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.2.0.dist-info → jarvis_ai_assistant-0.2.2.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.2.0.dist-info → jarvis_ai_assistant-0.2.2.dist-info}/top_level.txt +0 -0
@@ -1,15 +1,25 @@
|
|
1
1
|
#!/usr/bin/env python3
|
2
2
|
# -*- coding: utf-8 -*-
|
3
|
-
import argparse
|
4
3
|
import os
|
5
4
|
import sys
|
6
5
|
from typing import Optional, Tuple
|
7
6
|
|
7
|
+
import typer
|
8
|
+
|
8
9
|
from jarvis.jarvis_platform.registry import PlatformRegistry
|
9
10
|
from jarvis.jarvis_utils.config import get_shell_name, set_config
|
10
11
|
from jarvis.jarvis_utils.input import get_multiline_input
|
11
12
|
from jarvis.jarvis_utils.utils import init_env
|
12
13
|
|
14
|
+
app = typer.Typer(
|
15
|
+
help="将自然语言要求转换为shell命令",
|
16
|
+
epilog="""
|
17
|
+
Example:
|
18
|
+
jss request "Find all Python files in the current directory"
|
19
|
+
jss install
|
20
|
+
""",
|
21
|
+
)
|
22
|
+
|
13
23
|
|
14
24
|
def execute_command(command: str, should_run: bool) -> None:
|
15
25
|
"""Print command without execution"""
|
@@ -48,15 +58,18 @@ def _get_markers() -> Tuple[str, str]:
|
|
48
58
|
)
|
49
59
|
|
50
60
|
|
51
|
-
|
52
|
-
|
61
|
+
@app.command("install")
|
62
|
+
def install_jss_completion(
|
63
|
+
shell: str = typer.Option("fish", help="指定shell类型(仅支持fish)"),
|
64
|
+
) -> None:
|
65
|
+
"""为fish shell安装'命令未找到'处理器,实现自然语言命令执行"""
|
66
|
+
if shell != "fish":
|
67
|
+
print(f"错误: 不支持的shell类型: {shell}, 仅支持fish")
|
68
|
+
raise typer.Exit(code=1)
|
53
69
|
|
54
|
-
Returns:
|
55
|
-
int: 0 if success, 1 if failed
|
56
|
-
"""
|
57
70
|
if not _check_fish_shell():
|
58
71
|
print("当前不是fish shell,无需安装")
|
59
|
-
return
|
72
|
+
return
|
60
73
|
|
61
74
|
config_file = _get_config_file()
|
62
75
|
start_marker, end_marker = _get_markers()
|
@@ -64,13 +77,15 @@ def install_jss_completion() -> int:
|
|
64
77
|
if not os.path.exists(config_file):
|
65
78
|
print("未找到config.fish文件,将创建新文件")
|
66
79
|
os.makedirs(os.path.dirname(config_file), exist_ok=True)
|
80
|
+
with open(config_file, "w") as f:
|
81
|
+
f.write("")
|
67
82
|
|
68
83
|
with open(config_file, "r") as f:
|
69
84
|
content = f.read()
|
70
85
|
|
71
86
|
if start_marker in content:
|
72
87
|
print("JSS fish completion已安装,请执行: source ~/.config/fish/config.fish")
|
73
|
-
return
|
88
|
+
return
|
74
89
|
|
75
90
|
with open(config_file, "a") as f:
|
76
91
|
f.write(
|
@@ -90,32 +105,34 @@ end
|
|
90
105
|
"""
|
91
106
|
)
|
92
107
|
print("JSS fish completion已安装,请执行: source ~/.config/fish/config.fish")
|
93
|
-
return 0
|
94
108
|
|
95
109
|
|
96
|
-
|
97
|
-
|
110
|
+
@app.command("uninstall")
|
111
|
+
def uninstall_jss_completion(
|
112
|
+
shell: str = typer.Option("fish", help="指定shell类型(仅支持fish)"),
|
113
|
+
) -> None:
|
114
|
+
"""卸载JSS fish shell'命令未找到'处理器"""
|
115
|
+
if shell != "fish":
|
116
|
+
print(f"错误: 不支持的shell类型: {shell}, 仅支持fish")
|
117
|
+
raise typer.Exit(code=1)
|
98
118
|
|
99
|
-
Returns:
|
100
|
-
int: 0 if success, 1 if failed
|
101
|
-
"""
|
102
119
|
if not _check_fish_shell():
|
103
120
|
print("当前不是fish shell,无需卸载")
|
104
|
-
return
|
121
|
+
return
|
105
122
|
|
106
123
|
config_file = _get_config_file()
|
107
124
|
start_marker, end_marker = _get_markers()
|
108
125
|
|
109
126
|
if not os.path.exists(config_file):
|
110
127
|
print("未找到JSS fish completion配置,无需卸载")
|
111
|
-
return
|
128
|
+
return
|
112
129
|
|
113
130
|
with open(config_file, "r") as f:
|
114
131
|
content = f.read()
|
115
132
|
|
116
133
|
if start_marker not in content:
|
117
134
|
print("未找到JSS fish completion配置,无需卸载")
|
118
|
-
return
|
135
|
+
return
|
119
136
|
|
120
137
|
new_content = content.split(start_marker)[0] + content.split(end_marker)[-1]
|
121
138
|
|
@@ -123,7 +140,6 @@ def uninstall_jss_completion() -> int:
|
|
123
140
|
f.write(new_content)
|
124
141
|
|
125
142
|
print("JSS fish completion已卸载,请执行: source ~/.config/fish/config.fish")
|
126
|
-
return 0
|
127
143
|
|
128
144
|
|
129
145
|
def process_request(request: str) -> Optional[str]:
|
@@ -175,85 +191,40 @@ def process_request(request: str) -> Optional[str]:
|
|
175
191
|
return None
|
176
192
|
|
177
193
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
set_config("JARVIS_PRINT_PROMPT", "false")
|
183
|
-
|
184
|
-
parser = argparse.ArgumentParser(
|
185
|
-
description="将自然语言要求转换为shell命令",
|
186
|
-
formatter_class=argparse.RawDescriptionHelpFormatter,
|
187
|
-
epilog="""
|
188
|
-
Example:
|
189
|
-
%(prog)s request "Find all Python files in the current directory"
|
190
|
-
%(prog)s install
|
191
|
-
""",
|
194
|
+
@app.command("request")
|
195
|
+
def request_command(
|
196
|
+
request: Optional[str] = typer.Argument(
|
197
|
+
None, help="描述您想要执行的操作(用自然语言描述),如果未提供则从标准输入读取"
|
192
198
|
)
|
193
|
-
|
194
|
-
|
195
|
-
subparsers = parser.add_subparsers(dest="command", required=True)
|
196
|
-
|
197
|
-
# request子命令
|
198
|
-
request_parser = subparsers.add_parser(
|
199
|
-
"request", help="描述您想要执行的操作(用自然语言描述)"
|
200
|
-
)
|
201
|
-
request_parser.add_argument(
|
202
|
-
"request",
|
203
|
-
nargs="?", # 设置为可选参数
|
204
|
-
help="描述您想要执行的操作(用自然语言描述),如果未提供则从标准输入读取",
|
205
|
-
)
|
206
|
-
|
207
|
-
# install子命令
|
208
|
-
install_parser = subparsers.add_parser(
|
209
|
-
"install", help="安装JSS fish shell命令补全功能"
|
210
|
-
)
|
211
|
-
install_parser.add_argument(
|
212
|
-
"--shell", choices=["fish"], default="fish", help="指定shell类型(仅支持fish)"
|
213
|
-
)
|
214
|
-
|
215
|
-
# 添加uninstall子命令
|
216
|
-
uninstall_parser = subparsers.add_parser(
|
217
|
-
"uninstall", help="卸载JSS fish shell命令补全功能"
|
218
|
-
)
|
219
|
-
uninstall_parser.add_argument(
|
220
|
-
"--shell", choices=["fish"], default="fish", help="指定shell类型(仅支持fish)"
|
221
|
-
)
|
222
|
-
|
223
|
-
# 解析参数
|
224
|
-
args = parser.parse_args()
|
225
|
-
|
199
|
+
):
|
200
|
+
"""描述您想要执行的操作(用自然语言描述)"""
|
226
201
|
should_run = False
|
227
|
-
|
228
|
-
# 处理install命令
|
229
|
-
if args.command == "install":
|
230
|
-
if args.shell != "fish":
|
231
|
-
print(f"错误: 不支持的shell类型: {args.shell}, 仅支持fish")
|
232
|
-
return 1
|
233
|
-
return install_jss_completion()
|
234
|
-
|
235
|
-
# 处理uninstall命令
|
236
|
-
if args.command == "uninstall":
|
237
|
-
if args.shell != "fish":
|
238
|
-
print(f"错误: 不支持的shell类型: {args.shell}, 仅支持fish")
|
239
|
-
return 1
|
240
|
-
return uninstall_jss_completion()
|
241
|
-
|
242
|
-
# 处理request命令
|
243
|
-
if not args.request:
|
202
|
+
if not request:
|
244
203
|
# 检查是否在交互式终端中运行
|
245
|
-
|
204
|
+
request = get_multiline_input(tip="请输入您要执行的功能:")
|
246
205
|
should_run = True
|
206
|
+
|
247
207
|
# 处理请求
|
248
|
-
command = process_request(
|
208
|
+
command = process_request(request)
|
249
209
|
|
250
210
|
# 输出结果
|
251
211
|
if command:
|
252
212
|
execute_command(command, should_run) # 显示并执行命令
|
253
|
-
return 0
|
254
213
|
else:
|
255
|
-
|
214
|
+
raise typer.Exit(code=1)
|
215
|
+
|
216
|
+
|
217
|
+
def cli():
|
218
|
+
"""Typer application entry point"""
|
219
|
+
init_env("")
|
220
|
+
set_config("JARVIS_PRINT_PROMPT", "false")
|
221
|
+
app()
|
222
|
+
|
223
|
+
|
224
|
+
def main():
|
225
|
+
"""Main entry point for the script"""
|
226
|
+
cli()
|
256
227
|
|
257
228
|
|
258
229
|
if __name__ == "__main__":
|
259
|
-
|
230
|
+
main()
|
jarvis/jarvis_tools/cli/main.py
CHANGED
@@ -1,178 +1,145 @@
|
|
1
1
|
import sys
|
2
|
+
import json
|
3
|
+
from typing import Optional
|
4
|
+
|
5
|
+
import typer
|
6
|
+
from tabulate import tabulate
|
2
7
|
|
3
8
|
from jarvis.jarvis_tools.registry import ToolRegistry
|
4
9
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
5
10
|
from jarvis.jarvis_utils.utils import init_env
|
6
11
|
|
12
|
+
app = typer.Typer(help="Jarvis 工具系统命令行界面")
|
7
13
|
|
8
|
-
def main() -> int:
|
9
|
-
"""
|
10
|
-
命令行工具入口,提供工具列表查看和工具调用功能
|
11
|
-
|
12
|
-
功能:
|
13
|
-
1. 列出所有可用工具 (list命令)
|
14
|
-
2. 调用指定工具 (call命令)
|
15
|
-
|
16
|
-
参数:
|
17
|
-
通过命令行参数传递,包括:
|
18
|
-
- list: 列出工具
|
19
|
-
--json: 以JSON格式输出
|
20
|
-
--detailed: 显示详细信息
|
21
|
-
- call: 调用工具
|
22
|
-
tool_name: 工具名称
|
23
|
-
--args: 工具参数(JSON格式)
|
24
|
-
--args-file: 从文件加载工具参数
|
25
|
-
|
26
|
-
返回值:
|
27
|
-
int: 0表示成功,非0表示错误
|
28
|
-
"""
|
29
|
-
import argparse
|
30
|
-
import json
|
31
|
-
|
32
|
-
init_env("欢迎使用 Jarvis-Tools,您的工具系统已准备就绪!")
|
33
|
-
|
34
|
-
parser = argparse.ArgumentParser(description="Jarvis 工具系统命令行界面")
|
35
|
-
subparsers = parser.add_subparsers(dest="command", help="命令")
|
36
14
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
call_parser = subparsers.add_parser("call", help="调用指定工具")
|
44
|
-
call_parser.add_argument("tool_name", help="要调用的工具名称")
|
45
|
-
call_parser.add_argument("--args", type=str, help="工具参数 (JSON格式)")
|
46
|
-
call_parser.add_argument(
|
47
|
-
"--args-file", type=str, help="从文件加载工具参数 (JSON格式)"
|
48
|
-
)
|
49
|
-
|
50
|
-
# 统计子命令
|
51
|
-
stat_parser = subparsers.add_parser("stat", help="显示工具调用统计信息")
|
52
|
-
stat_parser.add_argument("--json", action="store_true", help="以JSON格式输出")
|
53
|
-
|
54
|
-
args = parser.parse_args()
|
55
|
-
|
56
|
-
# 初始化工具注册表
|
15
|
+
@app.command("list")
|
16
|
+
def list_tools(
|
17
|
+
as_json: bool = typer.Option(False, "--json", help="以JSON格式输出"),
|
18
|
+
detailed: bool = typer.Option(False, "--detailed", help="显示详细信息"),
|
19
|
+
):
|
20
|
+
"""列出所有可用工具"""
|
57
21
|
registry = ToolRegistry()
|
22
|
+
tools = registry.get_all_tools()
|
58
23
|
|
59
|
-
if
|
60
|
-
|
61
|
-
|
62
|
-
if args.json:
|
63
|
-
if args.detailed:
|
64
|
-
print(
|
65
|
-
json.dumps(tools, indent=2, ensure_ascii=False)
|
66
|
-
) # 输出完整JSON格式
|
67
|
-
else:
|
68
|
-
simple_tools = [
|
69
|
-
{"name": t["name"], "description": t["description"]} for t in tools
|
70
|
-
] # 简化工具信息
|
71
|
-
print(json.dumps(simple_tools, indent=2, ensure_ascii=False))
|
24
|
+
if as_json:
|
25
|
+
if detailed:
|
26
|
+
print(json.dumps(tools, indent=2, ensure_ascii=False))
|
72
27
|
else:
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
print(tool["parameters"]) # 显示详细参数信息
|
80
|
-
|
81
|
-
elif args.command == "stat":
|
82
|
-
from tabulate import tabulate
|
83
|
-
|
84
|
-
stats = registry._get_tool_stats()
|
85
|
-
tools = registry.get_all_tools()
|
86
|
-
|
87
|
-
# 构建统计表格数据
|
88
|
-
table_data = []
|
28
|
+
simple_tools = [
|
29
|
+
{"name": t["name"], "description": t["description"]} for t in tools
|
30
|
+
]
|
31
|
+
print(json.dumps(simple_tools, indent=2, ensure_ascii=False))
|
32
|
+
else:
|
33
|
+
PrettyOutput.section("可用工具列表", OutputType.SYSTEM)
|
89
34
|
for tool in tools:
|
90
|
-
|
91
|
-
|
92
|
-
|
35
|
+
print(f"\n✅ {tool['name']}")
|
36
|
+
print(f" 描述: {tool['description']}")
|
37
|
+
if detailed:
|
38
|
+
print(" 参数:")
|
39
|
+
print(tool["parameters"])
|
93
40
|
|
94
|
-
# 按调用次数降序排序
|
95
|
-
table_data.sort(key=lambda x: x[1], reverse=True)
|
96
41
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
42
|
+
@app.command("stat")
|
43
|
+
def stat_tools(as_json: bool = typer.Option(False, "--json", help="以JSON格式输出")):
|
44
|
+
"""显示工具调用统计信息"""
|
45
|
+
registry = ToolRegistry()
|
46
|
+
stats = registry._get_tool_stats()
|
47
|
+
tools = registry.get_all_tools()
|
48
|
+
|
49
|
+
table_data = []
|
50
|
+
for tool in tools:
|
51
|
+
name = tool["name"]
|
52
|
+
count = stats.get(name, 0)
|
53
|
+
table_data.append([name, count])
|
104
54
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
55
|
+
table_data.sort(key=lambda x: x[1], reverse=True)
|
56
|
+
|
57
|
+
if as_json:
|
58
|
+
print(json.dumps(dict(table_data), indent=2))
|
59
|
+
else:
|
60
|
+
PrettyOutput.section("工具调用统计", OutputType.SYSTEM)
|
61
|
+
print(tabulate(table_data, headers=["工具名称", "调用次数"], tablefmt="grid"))
|
62
|
+
|
63
|
+
|
64
|
+
@app.command("call")
|
65
|
+
def call_tool(
|
66
|
+
tool_name: str = typer.Argument(..., help="要调用的工具名称"),
|
67
|
+
args: Optional[str] = typer.Option(None, "--args", help="工具参数 (JSON格式)"),
|
68
|
+
args_file: Optional[str] = typer.Option(
|
69
|
+
None, "--args-file", help="从文件加载工具参数 (JSON格式)"
|
70
|
+
),
|
71
|
+
):
|
72
|
+
"""调用指定工具"""
|
73
|
+
registry = ToolRegistry()
|
74
|
+
tool_obj = registry.get_tool(tool_name)
|
75
|
+
|
76
|
+
if not tool_obj:
|
77
|
+
PrettyOutput.print(f"错误: 工具 '{tool_name}' 不存在", OutputType.ERROR)
|
78
|
+
available_tools = ", ".join([t["name"] for t in registry.get_all_tools()])
|
79
|
+
print(f"可用工具: {available_tools}")
|
80
|
+
raise typer.Exit(code=1)
|
81
|
+
|
82
|
+
tool_args = {}
|
83
|
+
if args:
|
84
|
+
try:
|
85
|
+
tool_args = json.loads(args)
|
86
|
+
except json.JSONDecodeError:
|
87
|
+
PrettyOutput.print("错误: 参数必须是有效的JSON格式", OutputType.ERROR)
|
88
|
+
raise typer.Exit(code=1)
|
89
|
+
elif args_file:
|
90
|
+
try:
|
91
|
+
with open(args_file, "r", encoding="utf-8") as f:
|
92
|
+
tool_args = json.load(f)
|
93
|
+
except (json.JSONDecodeError, FileNotFoundError) as e:
|
141
94
|
PrettyOutput.print(
|
142
|
-
f"错误:
|
95
|
+
f"错误: 无法从文件加载参数: {str(e)}", OutputType.ERROR
|
143
96
|
)
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
97
|
+
raise typer.Exit(code=1)
|
98
|
+
|
99
|
+
required_params = tool_obj.parameters.get("required", [])
|
100
|
+
missing_params = [p for p in required_params if p not in tool_args]
|
101
|
+
|
102
|
+
if missing_params:
|
103
|
+
PrettyOutput.print(
|
104
|
+
f"错误: 缺少必需参数: {', '.join(missing_params)}", OutputType.ERROR
|
105
|
+
)
|
106
|
+
print("\n参数说明:")
|
107
|
+
params = tool_obj.parameters.get("properties", {})
|
108
|
+
for param_name in required_params:
|
109
|
+
param_info = params.get(param_name, {})
|
110
|
+
desc = param_info.get("description", "无描述")
|
111
|
+
print(f" - {param_name}: {desc}")
|
112
|
+
raise typer.Exit(code=1)
|
113
|
+
|
114
|
+
result = registry.execute_tool(tool_name, tool_args)
|
115
|
+
|
116
|
+
if result["success"]:
|
117
|
+
PrettyOutput.section(f"工具 {tool_name} 执行成功", OutputType.SUCCESS)
|
118
|
+
else:
|
119
|
+
PrettyOutput.section(f"工具 {tool_name} 执行失败", OutputType.ERROR)
|
160
120
|
|
161
|
-
|
162
|
-
|
163
|
-
|
121
|
+
if result.get("stdout"):
|
122
|
+
print("\n输出:")
|
123
|
+
print(result["stdout"])
|
164
124
|
|
165
|
-
|
166
|
-
|
167
|
-
|
125
|
+
if result.get("stderr"):
|
126
|
+
PrettyOutput.print("\n错误:", OutputType.ERROR)
|
127
|
+
print(result["stderr"])
|
168
128
|
|
169
|
-
|
129
|
+
if not result["success"]:
|
130
|
+
raise typer.Exit(code=1)
|
131
|
+
|
132
|
+
|
133
|
+
def cli():
|
134
|
+
"""Typer application entry point"""
|
135
|
+
init_env("欢迎使用 Jarvis-Tools,您的工具系统已准备就绪!")
|
136
|
+
app()
|
170
137
|
|
171
|
-
else:
|
172
|
-
parser.print_help()
|
173
138
|
|
174
|
-
|
139
|
+
def main():
|
140
|
+
"""Main entry point for the script"""
|
141
|
+
cli()
|
175
142
|
|
176
143
|
|
177
144
|
if __name__ == "__main__":
|
178
|
-
|
145
|
+
main()
|
jarvis/jarvis_tools/registry.py
CHANGED
@@ -15,6 +15,7 @@ from jarvis.jarvis_mcp.stdio_mcp_client import StdioMcpClient
|
|
15
15
|
from jarvis.jarvis_mcp.streamable_mcp_client import StreamableMcpClient
|
16
16
|
from jarvis.jarvis_tools.base import Tool
|
17
17
|
from jarvis.jarvis_utils.config import get_data_dir, get_tool_load_dirs
|
18
|
+
from jarvis.jarvis_utils.input import user_confirm
|
18
19
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
19
20
|
from jarvis.jarvis_utils.tag import ct, ot
|
20
21
|
from jarvis.jarvis_utils.utils import is_context_overflow, daily_check_git_updates
|
@@ -106,14 +107,20 @@ arguments:
|
|
106
107
|
|
107
108
|
|
108
109
|
class OutputHandlerProtocol(Protocol):
|
109
|
-
def name(self) -> str:
|
110
|
-
|
111
|
-
def prompt(self) -> str: ...
|
112
|
-
def handle(self, response: str, agent: Any) -> Tuple[bool, Any]: ...
|
110
|
+
def name(self) -> str:
|
111
|
+
...
|
113
112
|
|
113
|
+
def can_handle(self, response: str) -> bool:
|
114
|
+
...
|
114
115
|
|
115
|
-
|
116
|
+
def prompt(self) -> str:
|
117
|
+
...
|
116
118
|
|
119
|
+
def handle(self, response: str, agent: Any) -> Tuple[bool, Any]:
|
120
|
+
...
|
121
|
+
|
122
|
+
|
123
|
+
class ToolRegistry(OutputHandlerProtocol):
|
117
124
|
def name(self) -> str:
|
118
125
|
return "TOOL_CALL"
|
119
126
|
|
@@ -124,14 +131,16 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
124
131
|
"""加载工具"""
|
125
132
|
tools = self.get_all_tools()
|
126
133
|
if tools:
|
127
|
-
tools_prompt = f"
|
134
|
+
tools_prompt = f"<tools_section>\n"
|
128
135
|
tools_prompt += " <header>## 可用工具:</header>\n"
|
129
136
|
tools_prompt += " <tools_list>\n"
|
130
137
|
for tool in tools:
|
131
138
|
try:
|
132
139
|
tools_prompt += " <tool>\n"
|
133
140
|
tools_prompt += f" <name>名称: {tool['name']}</name>\n"
|
134
|
-
tools_prompt +=
|
141
|
+
tools_prompt += (
|
142
|
+
f" <description>描述: {tool['description']}</description>\n"
|
143
|
+
)
|
135
144
|
tools_prompt += " <parameters>\n"
|
136
145
|
tools_prompt += " <yaml>|\n"
|
137
146
|
|
@@ -197,9 +206,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
197
206
|
with open(stats_file, "r", encoding="utf-8") as f:
|
198
207
|
return yaml.safe_load(f) or {}
|
199
208
|
except Exception as e:
|
200
|
-
PrettyOutput.print(
|
201
|
-
f"加载工具调用统计失败: {str(e)}", OutputType.WARNING
|
202
|
-
)
|
209
|
+
PrettyOutput.print(f"加载工具调用统计失败: {str(e)}", OutputType.WARNING)
|
203
210
|
return {}
|
204
211
|
|
205
212
|
def _update_tool_stats(self, name: str) -> None:
|
@@ -269,9 +276,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
269
276
|
config = yaml.safe_load(open(file_path, "r", encoding="utf-8"))
|
270
277
|
self.register_mcp_tool_by_config(config)
|
271
278
|
except Exception as e:
|
272
|
-
PrettyOutput.print(
|
273
|
-
f"文件 {file_path} 加载失败: {str(e)}", OutputType.WARNING
|
274
|
-
)
|
279
|
+
PrettyOutput.print(f"文件 {file_path} 加载失败: {str(e)}", OutputType.WARNING)
|
275
280
|
|
276
281
|
def _load_builtin_tools(self) -> None:
|
277
282
|
"""从内置工具目录加载工具"""
|
@@ -508,7 +513,6 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
508
513
|
and hasattr(item, "execute")
|
509
514
|
and item.name == module_name
|
510
515
|
):
|
511
|
-
|
512
516
|
if hasattr(item, "check"):
|
513
517
|
if not item.check():
|
514
518
|
continue
|
@@ -567,10 +571,40 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
567
571
|
ot("TOOL_CALL") + r"(.*?)" + ct("TOOL_CALL"), content, re.DOTALL
|
568
572
|
)
|
569
573
|
if not data:
|
570
|
-
|
571
|
-
|
572
|
-
|
573
|
-
|
574
|
+
# can_handle 确保 ot("TOOL_CALL") 在内容中。
|
575
|
+
# 如果数据为空,则表示 ct("TOOL_CALL") 可能丢失。
|
576
|
+
if ot("TOOL_CALL") in content and ct("TOOL_CALL") not in content:
|
577
|
+
# 尝试通过附加结束标签来修复它
|
578
|
+
fixed_content = content.strip() + f"\n{ct('TOOL_CALL')}"
|
579
|
+
|
580
|
+
# 再次提取,并检查YAML是否有效
|
581
|
+
temp_data = re.findall(
|
582
|
+
ot("TOOL_CALL") + r"(.*?)" + ct("TOOL_CALL"),
|
583
|
+
fixed_content,
|
584
|
+
re.DOTALL,
|
585
|
+
)
|
586
|
+
|
587
|
+
if temp_data:
|
588
|
+
try:
|
589
|
+
yaml.safe_load(temp_data[0]) # Check if valid YAML
|
590
|
+
|
591
|
+
# Ask user for confirmation
|
592
|
+
PrettyOutput.print(
|
593
|
+
f"检测到缺失的 {ct('TOOL_CALL')} 标签,已自动修复。修复后的内容如下:",
|
594
|
+
OutputType.INFO,
|
595
|
+
)
|
596
|
+
PrettyOutput.print(fixed_content, OutputType.TOOL)
|
597
|
+
data = temp_data
|
598
|
+
except (yaml.YAMLError, EOFError, KeyboardInterrupt):
|
599
|
+
# Even after fixing, it's not valid YAML, or user cancelled.
|
600
|
+
# Fall through to the original error.
|
601
|
+
pass
|
602
|
+
|
603
|
+
if not data:
|
604
|
+
return (
|
605
|
+
{},
|
606
|
+
f"只有{ot('TOOL_CALL')}标签,未找到{ct('TOOL_CALL')}标签,调用格式错误,请检查工具调用格式。\n{tool_call_help}",
|
607
|
+
)
|
574
608
|
ret = []
|
575
609
|
for item in data:
|
576
610
|
try:
|
@@ -685,9 +719,7 @@ class ToolRegistry(OutputHandlerProtocol):
|
|
685
719
|
"""
|
686
720
|
if len(output.splitlines()) > 60:
|
687
721
|
lines = output.splitlines()
|
688
|
-
return "\n".join(
|
689
|
-
lines[:30] + ["\n...内容太长,已截取前后30行...\n"] + lines[-30:]
|
690
|
-
)
|
722
|
+
return "\n".join(lines[:30] + ["\n...内容太长,已截取前后30行...\n"] + lines[-30:])
|
691
723
|
return output
|
692
724
|
|
693
725
|
def handle_tool_calls(self, tool_call: Dict[str, Any], agent: Any) -> str:
|