jarvis-ai-assistant 0.1.222__py3-none-any.whl → 0.7.0__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/__init__.py +1143 -245
- jarvis/jarvis_agent/agent_manager.py +97 -0
- jarvis/jarvis_agent/builtin_input_handler.py +12 -10
- jarvis/jarvis_agent/config_editor.py +57 -0
- jarvis/jarvis_agent/edit_file_handler.py +392 -99
- jarvis/jarvis_agent/event_bus.py +48 -0
- jarvis/jarvis_agent/events.py +157 -0
- jarvis/jarvis_agent/file_context_handler.py +79 -0
- jarvis/jarvis_agent/file_methodology_manager.py +117 -0
- jarvis/jarvis_agent/jarvis.py +1117 -147
- jarvis/jarvis_agent/main.py +78 -34
- jarvis/jarvis_agent/memory_manager.py +195 -0
- jarvis/jarvis_agent/methodology_share_manager.py +174 -0
- jarvis/jarvis_agent/prompt_manager.py +82 -0
- jarvis/jarvis_agent/prompts.py +46 -9
- jarvis/jarvis_agent/protocols.py +4 -1
- jarvis/jarvis_agent/rewrite_file_handler.py +141 -0
- jarvis/jarvis_agent/run_loop.py +146 -0
- jarvis/jarvis_agent/session_manager.py +9 -9
- jarvis/jarvis_agent/share_manager.py +228 -0
- jarvis/jarvis_agent/shell_input_handler.py +23 -3
- jarvis/jarvis_agent/stdio_redirect.py +295 -0
- jarvis/jarvis_agent/task_analyzer.py +212 -0
- jarvis/jarvis_agent/task_manager.py +154 -0
- jarvis/jarvis_agent/task_planner.py +496 -0
- jarvis/jarvis_agent/tool_executor.py +8 -4
- jarvis/jarvis_agent/tool_share_manager.py +139 -0
- jarvis/jarvis_agent/user_interaction.py +42 -0
- jarvis/jarvis_agent/utils.py +54 -0
- jarvis/jarvis_agent/web_bridge.py +189 -0
- jarvis/jarvis_agent/web_output_sink.py +53 -0
- jarvis/jarvis_agent/web_server.py +751 -0
- jarvis/jarvis_c2rust/__init__.py +26 -0
- jarvis/jarvis_c2rust/cli.py +613 -0
- jarvis/jarvis_c2rust/collector.py +258 -0
- jarvis/jarvis_c2rust/library_replacer.py +1122 -0
- jarvis/jarvis_c2rust/llm_module_agent.py +1300 -0
- jarvis/jarvis_c2rust/optimizer.py +960 -0
- jarvis/jarvis_c2rust/scanner.py +1681 -0
- jarvis/jarvis_c2rust/transpiler.py +2325 -0
- jarvis/jarvis_code_agent/build_validation_config.py +133 -0
- jarvis/jarvis_code_agent/code_agent.py +1605 -178
- jarvis/jarvis_code_agent/code_analyzer/__init__.py +62 -0
- jarvis/jarvis_code_agent/code_analyzer/base_language.py +74 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/__init__.py +44 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/base.py +102 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/cmake.py +59 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/detector.py +125 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/fallback.py +69 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/go.py +38 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_gradle.py +44 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/java_maven.py +38 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/makefile.py +50 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/nodejs.py +93 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/python.py +129 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/rust.py +54 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator/validator.py +154 -0
- jarvis/jarvis_code_agent/code_analyzer/build_validator.py +43 -0
- jarvis/jarvis_code_agent/code_analyzer/context_manager.py +363 -0
- jarvis/jarvis_code_agent/code_analyzer/context_recommender.py +18 -0
- jarvis/jarvis_code_agent/code_analyzer/dependency_analyzer.py +132 -0
- jarvis/jarvis_code_agent/code_analyzer/file_ignore.py +330 -0
- jarvis/jarvis_code_agent/code_analyzer/impact_analyzer.py +781 -0
- jarvis/jarvis_code_agent/code_analyzer/language_registry.py +185 -0
- jarvis/jarvis_code_agent/code_analyzer/language_support.py +89 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/__init__.py +31 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/c_cpp_language.py +231 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/go_language.py +183 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/python_language.py +219 -0
- jarvis/jarvis_code_agent/code_analyzer/languages/rust_language.py +209 -0
- jarvis/jarvis_code_agent/code_analyzer/llm_context_recommender.py +451 -0
- jarvis/jarvis_code_agent/code_analyzer/symbol_extractor.py +77 -0
- jarvis/jarvis_code_agent/code_analyzer/tree_sitter_extractor.py +48 -0
- jarvis/jarvis_code_agent/lint.py +275 -13
- jarvis/jarvis_code_agent/utils.py +142 -0
- jarvis/jarvis_code_analysis/checklists/loader.py +20 -6
- jarvis/jarvis_code_analysis/code_review.py +583 -548
- jarvis/jarvis_data/config_schema.json +339 -28
- jarvis/jarvis_git_squash/main.py +22 -13
- jarvis/jarvis_git_utils/git_commiter.py +171 -55
- jarvis/jarvis_mcp/sse_mcp_client.py +22 -15
- jarvis/jarvis_mcp/stdio_mcp_client.py +4 -4
- jarvis/jarvis_mcp/streamable_mcp_client.py +36 -16
- jarvis/jarvis_memory_organizer/memory_organizer.py +753 -0
- jarvis/jarvis_methodology/main.py +48 -63
- jarvis/jarvis_multi_agent/__init__.py +302 -43
- jarvis/jarvis_multi_agent/main.py +70 -24
- jarvis/jarvis_platform/ai8.py +40 -23
- jarvis/jarvis_platform/base.py +210 -49
- jarvis/jarvis_platform/human.py +11 -1
- jarvis/jarvis_platform/kimi.py +82 -76
- jarvis/jarvis_platform/openai.py +73 -1
- jarvis/jarvis_platform/registry.py +8 -15
- jarvis/jarvis_platform/tongyi.py +115 -101
- jarvis/jarvis_platform/yuanbao.py +89 -63
- jarvis/jarvis_platform_manager/main.py +194 -132
- jarvis/jarvis_platform_manager/service.py +122 -86
- jarvis/jarvis_rag/cli.py +156 -53
- jarvis/jarvis_rag/embedding_manager.py +155 -12
- jarvis/jarvis_rag/llm_interface.py +10 -13
- jarvis/jarvis_rag/query_rewriter.py +63 -12
- jarvis/jarvis_rag/rag_pipeline.py +222 -40
- jarvis/jarvis_rag/reranker.py +26 -3
- jarvis/jarvis_rag/retriever.py +270 -14
- jarvis/jarvis_sec/__init__.py +3605 -0
- jarvis/jarvis_sec/checkers/__init__.py +32 -0
- jarvis/jarvis_sec/checkers/c_checker.py +2680 -0
- jarvis/jarvis_sec/checkers/rust_checker.py +1108 -0
- jarvis/jarvis_sec/cli.py +116 -0
- jarvis/jarvis_sec/report.py +257 -0
- jarvis/jarvis_sec/status.py +264 -0
- jarvis/jarvis_sec/types.py +20 -0
- jarvis/jarvis_sec/workflow.py +219 -0
- jarvis/jarvis_smart_shell/main.py +405 -137
- jarvis/jarvis_stats/__init__.py +13 -0
- jarvis/jarvis_stats/cli.py +387 -0
- jarvis/jarvis_stats/stats.py +711 -0
- jarvis/jarvis_stats/storage.py +612 -0
- jarvis/jarvis_stats/visualizer.py +282 -0
- jarvis/jarvis_tools/ask_user.py +1 -0
- jarvis/jarvis_tools/base.py +18 -2
- jarvis/jarvis_tools/clear_memory.py +239 -0
- jarvis/jarvis_tools/cli/main.py +220 -144
- jarvis/jarvis_tools/execute_script.py +52 -12
- jarvis/jarvis_tools/file_analyzer.py +17 -12
- jarvis/jarvis_tools/generate_new_tool.py +46 -24
- jarvis/jarvis_tools/read_code.py +277 -18
- jarvis/jarvis_tools/read_symbols.py +141 -0
- jarvis/jarvis_tools/read_webpage.py +86 -13
- jarvis/jarvis_tools/registry.py +294 -90
- jarvis/jarvis_tools/retrieve_memory.py +227 -0
- jarvis/jarvis_tools/save_memory.py +194 -0
- jarvis/jarvis_tools/search_web.py +62 -28
- jarvis/jarvis_tools/sub_agent.py +205 -0
- jarvis/jarvis_tools/sub_code_agent.py +217 -0
- jarvis/jarvis_tools/virtual_tty.py +330 -62
- jarvis/jarvis_utils/builtin_replace_map.py +4 -5
- jarvis/jarvis_utils/clipboard.py +90 -0
- jarvis/jarvis_utils/config.py +607 -50
- jarvis/jarvis_utils/embedding.py +3 -0
- jarvis/jarvis_utils/fzf.py +57 -0
- jarvis/jarvis_utils/git_utils.py +251 -29
- jarvis/jarvis_utils/globals.py +174 -17
- jarvis/jarvis_utils/http.py +58 -79
- jarvis/jarvis_utils/input.py +899 -153
- jarvis/jarvis_utils/methodology.py +210 -83
- jarvis/jarvis_utils/output.py +220 -137
- jarvis/jarvis_utils/utils.py +1906 -135
- jarvis_ai_assistant-0.7.0.dist-info/METADATA +465 -0
- jarvis_ai_assistant-0.7.0.dist-info/RECORD +192 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/entry_points.txt +8 -2
- jarvis/jarvis_git_details/main.py +0 -265
- jarvis/jarvis_platform/oyi.py +0 -357
- jarvis/jarvis_tools/edit_file.py +0 -255
- jarvis/jarvis_tools/rewrite_file.py +0 -195
- jarvis_ai_assistant-0.1.222.dist-info/METADATA +0 -767
- jarvis_ai_assistant-0.1.222.dist-info/RECORD +0 -110
- /jarvis/{jarvis_git_details → jarvis_memory_organizer}/__init__.py +0 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.222.dist-info → jarvis_ai_assistant-0.7.0.dist-info}/top_level.txt +0 -0
jarvis/jarvis_tools/cli/main.py
CHANGED
|
@@ -1,178 +1,254 @@
|
|
|
1
|
-
import
|
|
1
|
+
import json
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
import typer
|
|
5
|
+
from tabulate import tabulate
|
|
2
6
|
|
|
3
7
|
from jarvis.jarvis_tools.registry import ToolRegistry
|
|
4
8
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
5
9
|
from jarvis.jarvis_utils.utils import init_env
|
|
6
10
|
|
|
11
|
+
app = typer.Typer(help="Jarvis 工具系统命令行界面")
|
|
7
12
|
|
|
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
|
-
|
|
37
|
-
# 列出工具子命令
|
|
38
|
-
list_parser = subparsers.add_parser("list", help="列出所有可用工具")
|
|
39
|
-
list_parser.add_argument("--json", action="store_true", help="以JSON格式输出")
|
|
40
|
-
list_parser.add_argument("--detailed", action="store_true", help="显示详细信息")
|
|
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
13
|
|
|
54
|
-
args = parser.parse_args()
|
|
55
14
|
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
+
PrettyOutput.print(
|
|
27
|
+
json.dumps(tools, indent=2, ensure_ascii=False),
|
|
28
|
+
OutputType.CODE,
|
|
29
|
+
lang="json",
|
|
30
|
+
)
|
|
72
31
|
else:
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
32
|
+
simple_tools = [
|
|
33
|
+
{"name": t["name"], "description": t["description"]} for t in tools
|
|
34
|
+
]
|
|
35
|
+
PrettyOutput.print(
|
|
36
|
+
json.dumps(simple_tools, indent=2, ensure_ascii=False),
|
|
37
|
+
OutputType.CODE,
|
|
38
|
+
lang="json",
|
|
39
|
+
)
|
|
40
|
+
else:
|
|
41
|
+
PrettyOutput.section("可用工具列表", OutputType.SYSTEM)
|
|
42
|
+
# 为避免 PrettyOutput 对每行加框造成信息稀疏,先拼接字符串再统一打印
|
|
43
|
+
lines = []
|
|
44
|
+
import json as _json # local import to ensure available
|
|
45
|
+
for tool in tools:
|
|
46
|
+
lines.append(f"\n{tool['name']}")
|
|
47
|
+
lines.append(f" 描述: {tool['description']}")
|
|
48
|
+
if detailed:
|
|
49
|
+
lines.append(" 参数:")
|
|
50
|
+
# 使用 Markdown 代码块统一展示参数
|
|
51
|
+
lines.append("```json")
|
|
52
|
+
try:
|
|
53
|
+
lines.append(_json.dumps(tool["parameters"], ensure_ascii=False, indent=2))
|
|
54
|
+
except Exception:
|
|
55
|
+
lines.append(str(tool.get("parameters")))
|
|
56
|
+
lines.append("```")
|
|
57
|
+
PrettyOutput.print("\n".join(lines), OutputType.INFO, lang="markdown")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@app.command("stat")
|
|
61
|
+
def stat_tools(
|
|
62
|
+
as_json: bool = typer.Option(False, "--json", help="以JSON格式输出"),
|
|
63
|
+
last_days: Optional[int] = typer.Option(
|
|
64
|
+
None, "--days", help="显示最近N天的统计(默认显示所有历史数据)"
|
|
65
|
+
),
|
|
66
|
+
format: str = typer.Option(
|
|
67
|
+
"table", "--format", help="显示格式: table, chart, summary"
|
|
68
|
+
),
|
|
69
|
+
):
|
|
70
|
+
"""显示工具调用统计信息"""
|
|
71
|
+
from jarvis.jarvis_stats.stats import StatsManager
|
|
72
|
+
|
|
73
|
+
if format == "table":
|
|
74
|
+
registry = ToolRegistry()
|
|
84
75
|
stats = registry._get_tool_stats()
|
|
85
76
|
tools = registry.get_all_tools()
|
|
86
77
|
|
|
87
|
-
# 构建统计表格数据
|
|
88
78
|
table_data = []
|
|
89
79
|
for tool in tools:
|
|
90
80
|
name = tool["name"]
|
|
91
81
|
count = stats.get(name, 0)
|
|
92
|
-
|
|
82
|
+
if count > 0: # 只显示有调用记录的工具
|
|
83
|
+
table_data.append([name, count])
|
|
93
84
|
|
|
94
|
-
# 按调用次数降序排序
|
|
95
85
|
table_data.sort(key=lambda x: x[1], reverse=True)
|
|
96
86
|
|
|
97
|
-
if
|
|
98
|
-
print(
|
|
99
|
-
|
|
100
|
-
PrettyOutput.section("工具调用统计", OutputType.SYSTEM)
|
|
101
|
-
print(
|
|
102
|
-
tabulate(table_data, headers=["工具名称", "调用次数"], tablefmt="grid")
|
|
87
|
+
if as_json:
|
|
88
|
+
PrettyOutput.print(
|
|
89
|
+
json.dumps(dict(table_data), indent=2), OutputType.CODE, lang="json"
|
|
103
90
|
)
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
return 1
|
|
116
|
-
|
|
117
|
-
# 获取参数: 支持从命令行直接传入或从文件加载
|
|
118
|
-
tool_args = {}
|
|
119
|
-
if args.args:
|
|
120
|
-
try:
|
|
121
|
-
tool_args = json.loads(args.args) # 解析JSON格式参数
|
|
122
|
-
except json.JSONDecodeError:
|
|
123
|
-
PrettyOutput.print("错误: 参数必须是有效的JSON格式", OutputType.ERROR)
|
|
124
|
-
return 1
|
|
125
|
-
|
|
126
|
-
elif args.args_file:
|
|
127
|
-
try:
|
|
128
|
-
with open(args.args_file, "r", encoding="utf-8") as f:
|
|
129
|
-
tool_args = json.load(f) # 从文件加载JSON参数
|
|
130
|
-
except (json.JSONDecodeError, FileNotFoundError) as e:
|
|
91
|
+
else:
|
|
92
|
+
time_desc = f"最近{last_days}天" if last_days else "所有历史"
|
|
93
|
+
PrettyOutput.section(f"工具调用统计 ({time_desc})", OutputType.SYSTEM)
|
|
94
|
+
if table_data:
|
|
95
|
+
PrettyOutput.print(
|
|
96
|
+
tabulate(
|
|
97
|
+
table_data, headers=["工具名称", "调用次数"], tablefmt="grid"
|
|
98
|
+
),
|
|
99
|
+
OutputType.CODE,
|
|
100
|
+
lang="text",
|
|
101
|
+
)
|
|
131
102
|
PrettyOutput.print(
|
|
132
|
-
f"
|
|
103
|
+
f"\n总计: {len(table_data)} 个工具被使用,共 {sum(x[1] for x in table_data)} 次调用",
|
|
104
|
+
OutputType.INFO,
|
|
133
105
|
)
|
|
134
|
-
|
|
106
|
+
else:
|
|
107
|
+
PrettyOutput.print("暂无工具调用记录", OutputType.INFO)
|
|
108
|
+
else:
|
|
109
|
+
# 使用 stats 系统的高级功能
|
|
110
|
+
PrettyOutput.section("工具组统计", OutputType.SYSTEM)
|
|
111
|
+
# 显示所有标记为 tool 组的指标
|
|
112
|
+
metrics = StatsManager.list_metrics()
|
|
113
|
+
tool_metrics = []
|
|
114
|
+
|
|
115
|
+
for metric in metrics:
|
|
116
|
+
# 检查是否是工具组的指标
|
|
117
|
+
if last_days:
|
|
118
|
+
stats_data = StatsManager.get_stats(
|
|
119
|
+
metric_name=metric, last_days=last_days, tags={"group": "tool"}
|
|
120
|
+
)
|
|
121
|
+
else:
|
|
122
|
+
# 获取所有历史数据
|
|
123
|
+
from datetime import datetime
|
|
124
|
+
|
|
125
|
+
stats_data = StatsManager.get_stats(
|
|
126
|
+
metric_name=metric,
|
|
127
|
+
start_time=datetime(2000, 1, 1),
|
|
128
|
+
end_time=datetime.now(),
|
|
129
|
+
tags={"group": "tool"},
|
|
130
|
+
)
|
|
131
|
+
if stats_data and stats_data.get("records"):
|
|
132
|
+
tool_metrics.append(metric)
|
|
133
|
+
|
|
134
|
+
if tool_metrics:
|
|
135
|
+
for metric in tool_metrics:
|
|
136
|
+
if format == "chart":
|
|
137
|
+
if last_days:
|
|
138
|
+
StatsManager.plot(
|
|
139
|
+
metric, last_days=last_days, tags={"group": "tool"}
|
|
140
|
+
)
|
|
141
|
+
else:
|
|
142
|
+
from datetime import datetime
|
|
143
|
+
|
|
144
|
+
StatsManager.plot(
|
|
145
|
+
metric,
|
|
146
|
+
start_time=datetime(2000, 1, 1),
|
|
147
|
+
end_time=datetime.now(),
|
|
148
|
+
tags={"group": "tool"},
|
|
149
|
+
)
|
|
150
|
+
elif format == "summary":
|
|
151
|
+
if last_days:
|
|
152
|
+
StatsManager.show(
|
|
153
|
+
metric,
|
|
154
|
+
last_days=last_days,
|
|
155
|
+
format="summary",
|
|
156
|
+
tags={"group": "tool"},
|
|
157
|
+
)
|
|
158
|
+
else:
|
|
159
|
+
from datetime import datetime
|
|
160
|
+
|
|
161
|
+
StatsManager.show(
|
|
162
|
+
metric,
|
|
163
|
+
start_time=datetime(2000, 1, 1),
|
|
164
|
+
end_time=datetime.now(),
|
|
165
|
+
format="summary",
|
|
166
|
+
tags={"group": "tool"},
|
|
167
|
+
)
|
|
168
|
+
else:
|
|
169
|
+
PrettyOutput.print("暂无工具调用记录", OutputType.INFO)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
@app.command("call")
|
|
173
|
+
def call_tool(
|
|
174
|
+
tool_name: str = typer.Argument(..., help="要调用的工具名称"),
|
|
175
|
+
args: Optional[str] = typer.Option(None, "--args", help="工具参数 (JSON格式)"),
|
|
176
|
+
args_file: Optional[str] = typer.Option(
|
|
177
|
+
None, "--args-file", help="从文件加载工具参数 (JSON格式)"
|
|
178
|
+
),
|
|
179
|
+
):
|
|
180
|
+
"""调用指定工具"""
|
|
181
|
+
registry = ToolRegistry()
|
|
182
|
+
tool_obj = registry.get_tool(tool_name)
|
|
183
|
+
|
|
184
|
+
if not tool_obj:
|
|
185
|
+
PrettyOutput.print(f"错误: 工具 '{tool_name}' 不存在", OutputType.ERROR)
|
|
186
|
+
available_tools = ", ".join([t["name"] for t in registry.get_all_tools()])
|
|
187
|
+
PrettyOutput.print(f"可用工具: {available_tools}", OutputType.INFO)
|
|
188
|
+
raise typer.Exit(code=1)
|
|
189
|
+
|
|
190
|
+
tool_args = {}
|
|
191
|
+
if args:
|
|
192
|
+
try:
|
|
193
|
+
tool_args = json.loads(args)
|
|
194
|
+
except json.JSONDecodeError:
|
|
195
|
+
PrettyOutput.print("错误: 参数必须是有效的JSON格式", OutputType.ERROR)
|
|
196
|
+
raise typer.Exit(code=1)
|
|
197
|
+
elif args_file:
|
|
198
|
+
try:
|
|
199
|
+
with open(args_file, "r", encoding="utf-8") as f:
|
|
200
|
+
tool_args = json.load(f)
|
|
201
|
+
except (json.JSONDecodeError, FileNotFoundError) as e:
|
|
202
|
+
PrettyOutput.print(f"错误: 无法从文件加载参数: {str(e)}", OutputType.ERROR)
|
|
203
|
+
raise typer.Exit(code=1)
|
|
204
|
+
|
|
205
|
+
required_params = tool_obj.parameters.get("required", [])
|
|
206
|
+
missing_params = [p for p in required_params if p not in tool_args]
|
|
207
|
+
|
|
208
|
+
if missing_params:
|
|
209
|
+
# 先拼接提示与参数说明,再统一打印,避免循环中逐条打印
|
|
210
|
+
params = tool_obj.parameters.get("properties", {})
|
|
211
|
+
lines = [
|
|
212
|
+
f"错误: 缺少必需参数: {', '.join(missing_params)}",
|
|
213
|
+
"",
|
|
214
|
+
"参数说明:",
|
|
215
|
+
]
|
|
216
|
+
for param_name in required_params:
|
|
217
|
+
param_info = params.get(param_name, {})
|
|
218
|
+
desc = param_info.get("description", "无描述")
|
|
219
|
+
lines.append(f" - {param_name}: {desc}")
|
|
220
|
+
PrettyOutput.print("\n".join(lines), OutputType.ERROR)
|
|
221
|
+
raise typer.Exit(code=1)
|
|
222
|
+
|
|
223
|
+
result = registry.execute_tool(tool_name, tool_args)
|
|
224
|
+
|
|
225
|
+
if result["success"]:
|
|
226
|
+
PrettyOutput.section(f"工具 {tool_name} 执行成功", OutputType.SUCCESS)
|
|
227
|
+
else:
|
|
228
|
+
PrettyOutput.section(f"工具 {tool_name} 执行失败", OutputType.ERROR)
|
|
135
229
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
230
|
+
if result.get("stdout"):
|
|
231
|
+
PrettyOutput.print("\n输出:", OutputType.INFO)
|
|
232
|
+
PrettyOutput.print(result["stdout"], OutputType.CODE, lang="text")
|
|
139
233
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
)
|
|
144
|
-
print("\n参数说明:")
|
|
145
|
-
params = tool_obj.parameters.get("properties", {})
|
|
146
|
-
for param_name in required_params:
|
|
147
|
-
param_info = params.get(param_name, {})
|
|
148
|
-
desc = param_info.get("description", "无描述")
|
|
149
|
-
print(f" - {param_name}: {desc}")
|
|
150
|
-
return 1
|
|
151
|
-
|
|
152
|
-
# 执行工具并处理结果
|
|
153
|
-
result = registry.execute_tool(tool_name, tool_args)
|
|
154
|
-
|
|
155
|
-
# 显示执行结果
|
|
156
|
-
if result["success"]:
|
|
157
|
-
PrettyOutput.section(f"工具 {tool_name} 执行成功", OutputType.SUCCESS)
|
|
158
|
-
else:
|
|
159
|
-
PrettyOutput.section(f"工具 {tool_name} 执行失败", OutputType.ERROR)
|
|
234
|
+
if result.get("stderr"):
|
|
235
|
+
PrettyOutput.print("\n错误:", OutputType.ERROR)
|
|
236
|
+
PrettyOutput.print(result["stderr"], OutputType.ERROR, lang="text")
|
|
160
237
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
print(result["stdout"])
|
|
238
|
+
if not result["success"]:
|
|
239
|
+
raise typer.Exit(code=1)
|
|
164
240
|
|
|
165
|
-
if result.get("stderr"):
|
|
166
|
-
PrettyOutput.print("\n错误:", OutputType.ERROR)
|
|
167
|
-
print(result["stderr"])
|
|
168
241
|
|
|
169
|
-
|
|
242
|
+
def cli():
|
|
243
|
+
"""Typer application entry point"""
|
|
244
|
+
init_env("欢迎使用 Jarvis-Tools,您的工具系统已准备就绪!")
|
|
245
|
+
app()
|
|
170
246
|
|
|
171
|
-
else:
|
|
172
|
-
parser.print_help()
|
|
173
247
|
|
|
174
|
-
|
|
248
|
+
def main():
|
|
249
|
+
"""Main entry point for the script"""
|
|
250
|
+
cli()
|
|
175
251
|
|
|
176
252
|
|
|
177
253
|
if __name__ == "__main__":
|
|
178
|
-
|
|
254
|
+
main()
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import os
|
|
3
3
|
import tempfile
|
|
4
4
|
from pathlib import Path
|
|
5
|
-
from typing import Any, Dict
|
|
5
|
+
from typing import Any, Dict, List
|
|
6
6
|
|
|
7
7
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
8
8
|
|
|
@@ -19,6 +19,7 @@ class ScriptTool:
|
|
|
19
19
|
+ "注意:由于模型上下文长度限制,请避免在脚本中输出大量信息,应该使用rg过滤输出。"
|
|
20
20
|
+ "与virtual_tty不同,此工具会创建一个临时的脚本文件,并使用脚本命令执行脚本,不具备交互式操作的能力,"
|
|
21
21
|
+ "适用于需要执行脚本并获取结果的场景。不适合需要交互式操作的场景(如:ssh连接、sftp传输、gdb/dlv调试等)。"
|
|
22
|
+
+ "在非交互模式(JARVIS_NON_INTERACTIVE=true)下:脚本执行时间限制为5分钟,超时将被终止并返回超时信息;用户无法与命令交互,请不要执行需要交互的命令。"
|
|
22
23
|
)
|
|
23
24
|
parameters = {
|
|
24
25
|
"type": "object",
|
|
@@ -74,8 +75,7 @@ class ScriptTool:
|
|
|
74
75
|
stream.feed(data)
|
|
75
76
|
|
|
76
77
|
# 清理每行右侧空格,并过滤空行
|
|
77
|
-
cleaned = []
|
|
78
|
-
cleaned = []
|
|
78
|
+
cleaned: List[str] = []
|
|
79
79
|
for y in range(screen.lines):
|
|
80
80
|
line = screen.buffer[y]
|
|
81
81
|
stripped = "".join(char.data for char in line.values()).rstrip()
|
|
@@ -116,8 +116,37 @@ class ScriptTool:
|
|
|
116
116
|
f"script -q -c '{interpreter} {script_path}' {output_file}"
|
|
117
117
|
)
|
|
118
118
|
|
|
119
|
-
# Execute command
|
|
120
|
-
|
|
119
|
+
# Execute command with optional timeout in non-interactive mode
|
|
120
|
+
from jarvis.jarvis_utils.config import get_script_execution_timeout, is_non_interactive
|
|
121
|
+
import subprocess
|
|
122
|
+
|
|
123
|
+
timed_out = False
|
|
124
|
+
if is_non_interactive():
|
|
125
|
+
try:
|
|
126
|
+
proc = subprocess.Popen(tee_command, shell=True)
|
|
127
|
+
try:
|
|
128
|
+
proc.wait(timeout=get_script_execution_timeout())
|
|
129
|
+
except subprocess.TimeoutExpired:
|
|
130
|
+
timed_out = True
|
|
131
|
+
try:
|
|
132
|
+
proc.kill()
|
|
133
|
+
except Exception:
|
|
134
|
+
pass
|
|
135
|
+
except Exception as e:
|
|
136
|
+
PrettyOutput.print(str(e), OutputType.ERROR)
|
|
137
|
+
# Attempt to read any partial output if available
|
|
138
|
+
try:
|
|
139
|
+
output = self.get_display_output(output_file)
|
|
140
|
+
except Exception as ee:
|
|
141
|
+
output = f"读取输出文件失败: {str(ee)}"
|
|
142
|
+
return {
|
|
143
|
+
"success": False,
|
|
144
|
+
"stdout": output,
|
|
145
|
+
"stderr": f"执行脚本失败: {str(e)}",
|
|
146
|
+
}
|
|
147
|
+
else:
|
|
148
|
+
# Execute command and capture return code
|
|
149
|
+
os.system(tee_command)
|
|
121
150
|
|
|
122
151
|
# Read and process output file
|
|
123
152
|
try:
|
|
@@ -126,12 +155,19 @@ class ScriptTool:
|
|
|
126
155
|
except Exception as e:
|
|
127
156
|
output = f"读取输出文件失败: {str(e)}"
|
|
128
157
|
|
|
129
|
-
# Return
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
158
|
+
# Return result (handle timeout in non-interactive mode)
|
|
159
|
+
if is_non_interactive() and timed_out:
|
|
160
|
+
return {
|
|
161
|
+
"success": False,
|
|
162
|
+
"stdout": output,
|
|
163
|
+
"stderr": f"执行超时(超过{get_script_execution_timeout()}秒),进程已被终止(非交互模式)。",
|
|
164
|
+
}
|
|
165
|
+
else:
|
|
166
|
+
return {
|
|
167
|
+
"success": True,
|
|
168
|
+
"stdout": output,
|
|
169
|
+
"stderr": "",
|
|
170
|
+
}
|
|
135
171
|
|
|
136
172
|
finally:
|
|
137
173
|
# Clean up temporary files
|
|
@@ -173,4 +209,8 @@ class ScriptTool:
|
|
|
173
209
|
|
|
174
210
|
if __name__ == "__main__":
|
|
175
211
|
script_tool = ScriptTool()
|
|
176
|
-
print(
|
|
212
|
+
PrettyOutput.print(
|
|
213
|
+
script_tool.get_display_output("/path/to/a.txt"),
|
|
214
|
+
OutputType.CODE,
|
|
215
|
+
lang="text",
|
|
216
|
+
)
|
|
@@ -30,7 +30,7 @@ class FileAnalyzerTool:
|
|
|
30
30
|
|
|
31
31
|
@staticmethod
|
|
32
32
|
def check() -> bool:
|
|
33
|
-
return PlatformRegistry().
|
|
33
|
+
return PlatformRegistry().get_normal_platform().support_upload_files()
|
|
34
34
|
|
|
35
35
|
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
36
36
|
"""执行文件分析操作
|
|
@@ -45,25 +45,31 @@ class FileAnalyzerTool:
|
|
|
45
45
|
file_paths = args["file_paths"]
|
|
46
46
|
prompt = args["prompt"]
|
|
47
47
|
|
|
48
|
-
#
|
|
48
|
+
# 验证文件路径(先收集不存在的文件,统一打印一次)
|
|
49
49
|
valid_files = []
|
|
50
|
+
missing_files = []
|
|
50
51
|
for file_path in file_paths:
|
|
51
52
|
if os.path.exists(file_path):
|
|
52
53
|
valid_files.append(file_path)
|
|
53
54
|
else:
|
|
54
|
-
|
|
55
|
+
missing_files.append(file_path)
|
|
56
|
+
if missing_files:
|
|
57
|
+
PrettyOutput.print(
|
|
58
|
+
"以下文件不存在:\n" + "\n".join(f" - {p}" for p in missing_files),
|
|
59
|
+
OutputType.WARNING,
|
|
60
|
+
)
|
|
55
61
|
|
|
56
62
|
if not valid_files:
|
|
57
63
|
return {"success": False, "stdout": "", "stderr": "没有找到有效的文件"}
|
|
58
64
|
|
|
59
|
-
#
|
|
60
|
-
platform = PlatformRegistry().
|
|
65
|
+
# 创建平台实例
|
|
66
|
+
platform = PlatformRegistry().get_normal_platform()
|
|
61
67
|
|
|
62
68
|
if not platform:
|
|
63
69
|
return {
|
|
64
70
|
"success": False,
|
|
65
71
|
"stdout": "",
|
|
66
|
-
"stderr": "
|
|
72
|
+
"stderr": "无法创建平台实例",
|
|
67
73
|
}
|
|
68
74
|
|
|
69
75
|
# 设置系统消息
|
|
@@ -73,19 +79,19 @@ class FileAnalyzerTool:
|
|
|
73
79
|
platform.set_system_prompt(system_message)
|
|
74
80
|
|
|
75
81
|
# 上传文件
|
|
76
|
-
|
|
82
|
+
|
|
77
83
|
try:
|
|
78
84
|
upload_result = platform.upload_files(valid_files)
|
|
79
85
|
if not upload_result:
|
|
80
|
-
print(
|
|
86
|
+
PrettyOutput.print("文件上传失败", OutputType.ERROR)
|
|
81
87
|
return {
|
|
82
88
|
"success": False,
|
|
83
89
|
"stdout": "",
|
|
84
90
|
"stderr": "文件上传失败",
|
|
85
91
|
}
|
|
86
|
-
|
|
92
|
+
|
|
87
93
|
except Exception as e:
|
|
88
|
-
print(f"
|
|
94
|
+
PrettyOutput.print(f"文件上传失败: {str(e)}", OutputType.ERROR)
|
|
89
95
|
return {
|
|
90
96
|
"success": False,
|
|
91
97
|
"stdout": "",
|
|
@@ -102,9 +108,8 @@ class FileAnalyzerTool:
|
|
|
102
108
|
请提供详细的分析结果和理由。"""
|
|
103
109
|
|
|
104
110
|
# 发送请求并获取分析结果
|
|
105
|
-
|
|
111
|
+
|
|
106
112
|
analysis_result = platform.chat_until_success(analysis_request)
|
|
107
|
-
print(f"✅ 分析完成")
|
|
108
113
|
|
|
109
114
|
# 清理会话
|
|
110
115
|
platform.delete_chat()
|