jarvis-ai-assistant 0.1.207__py3-none-any.whl → 0.1.209__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 +63 -103
- jarvis/jarvis_agent/edit_file_handler.py +43 -47
- jarvis/jarvis_agent/jarvis.py +33 -39
- jarvis/jarvis_code_agent/code_agent.py +74 -30
- jarvis/jarvis_code_agent/lint.py +6 -6
- jarvis/jarvis_code_analysis/code_review.py +164 -175
- jarvis/jarvis_data/config_schema.json +0 -25
- jarvis/jarvis_git_utils/git_commiter.py +148 -153
- jarvis/jarvis_methodology/main.py +70 -81
- jarvis/jarvis_platform/base.py +21 -17
- jarvis/jarvis_platform/kimi.py +59 -64
- jarvis/jarvis_platform/tongyi.py +118 -131
- jarvis/jarvis_platform/yuanbao.py +117 -122
- jarvis/jarvis_platform_manager/main.py +102 -502
- jarvis/jarvis_platform_manager/service.py +432 -0
- jarvis/jarvis_smart_shell/main.py +99 -33
- jarvis/jarvis_tools/ask_user.py +0 -1
- jarvis/jarvis_tools/edit_file.py +64 -55
- jarvis/jarvis_tools/file_analyzer.py +17 -28
- jarvis/jarvis_tools/read_code.py +80 -81
- jarvis/jarvis_utils/builtin_replace_map.py +1 -36
- jarvis/jarvis_utils/config.py +13 -48
- jarvis/jarvis_utils/embedding.py +6 -51
- jarvis/jarvis_utils/git_utils.py +93 -43
- jarvis/jarvis_utils/http.py +104 -0
- jarvis/jarvis_utils/methodology.py +12 -17
- jarvis/jarvis_utils/utils.py +186 -63
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/METADATA +4 -19
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/RECORD +34 -40
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/entry_points.txt +1 -1
- jarvis/jarvis_data/huggingface.tar.gz +0 -0
- jarvis/jarvis_dev/main.py +0 -1247
- jarvis/jarvis_tools/chdir.py +0 -72
- jarvis/jarvis_tools/code_plan.py +0 -218
- jarvis/jarvis_tools/create_code_agent.py +0 -95
- jarvis/jarvis_tools/create_sub_agent.py +0 -82
- jarvis/jarvis_tools/file_operation.py +0 -238
- jarvis/jarvis_utils/jarvis_history.py +0 -98
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/top_level.txt +0 -0
@@ -1,238 +0,0 @@
|
|
1
|
-
# -*- coding: utf-8 -*-
|
2
|
-
import os
|
3
|
-
from pathlib import Path
|
4
|
-
from typing import Any, Dict
|
5
|
-
|
6
|
-
from yaspin import yaspin # type: ignore
|
7
|
-
|
8
|
-
# 导入文件处理器
|
9
|
-
from jarvis.jarvis_utils.file_processors import TextFileProcessor
|
10
|
-
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
11
|
-
|
12
|
-
|
13
|
-
class FileOperationTool:
|
14
|
-
name = "file_operation"
|
15
|
-
description = "文件批量操作工具,可批量读写多个文件,仅支持文本文件,适用于需要同时处理多个文件的场景(读取配置文件、保存生成内容等)"
|
16
|
-
parameters = {
|
17
|
-
"type": "object",
|
18
|
-
"properties": {
|
19
|
-
"operation": {
|
20
|
-
"type": "string",
|
21
|
-
"enum": ["read", "write"],
|
22
|
-
"description": "要执行的文件操作类型(读取或写入多个文件)",
|
23
|
-
},
|
24
|
-
"files": {
|
25
|
-
"type": "array",
|
26
|
-
"items": {
|
27
|
-
"type": "object",
|
28
|
-
"properties": {
|
29
|
-
"path": {"type": "string"},
|
30
|
-
"content": {"type": "string"},
|
31
|
-
},
|
32
|
-
"required": ["path"],
|
33
|
-
},
|
34
|
-
"description": "要操作的文件列表",
|
35
|
-
},
|
36
|
-
},
|
37
|
-
"required": ["operation", "files"],
|
38
|
-
}
|
39
|
-
|
40
|
-
def _get_file_processor(self, file_path: str):
|
41
|
-
"""获取适合处理指定文件的处理器"""
|
42
|
-
processors = [TextFileProcessor] # 文本文件处理器(放在最后作为兜底)
|
43
|
-
|
44
|
-
for processor in processors:
|
45
|
-
if processor.can_handle(file_path):
|
46
|
-
return processor
|
47
|
-
|
48
|
-
return None # 如果没有合适的处理器,返回None
|
49
|
-
|
50
|
-
def _handle_single_file(
|
51
|
-
self,
|
52
|
-
operation: str,
|
53
|
-
filepath: str,
|
54
|
-
content: str = "",
|
55
|
-
start_line: int = 1,
|
56
|
-
end_line: int = -1,
|
57
|
-
agent: Any = None,
|
58
|
-
) -> Dict[str, Any]:
|
59
|
-
"""Handle operations for a single file"""
|
60
|
-
try:
|
61
|
-
abs_path = os.path.abspath(filepath)
|
62
|
-
|
63
|
-
if operation == "read":
|
64
|
-
with yaspin(
|
65
|
-
text=f"正在读取文件: {abs_path}...", color="cyan"
|
66
|
-
) as spinner:
|
67
|
-
if not os.path.exists(abs_path):
|
68
|
-
return {
|
69
|
-
"success": False,
|
70
|
-
"stdout": "",
|
71
|
-
"stderr": f"文件不存在: {abs_path}",
|
72
|
-
}
|
73
|
-
|
74
|
-
# 检查文件大小
|
75
|
-
if os.path.getsize(abs_path) > 30 * 1024 * 1024: # 30MB
|
76
|
-
return {
|
77
|
-
"success": False,
|
78
|
-
"stdout": "",
|
79
|
-
"stderr": "文件过大 (>30MB),无法处理",
|
80
|
-
}
|
81
|
-
|
82
|
-
file_extension = Path(abs_path).suffix.lower()
|
83
|
-
|
84
|
-
# 获取文件处理器
|
85
|
-
processor = self._get_file_processor(abs_path)
|
86
|
-
|
87
|
-
if processor is None:
|
88
|
-
return {
|
89
|
-
"success": False,
|
90
|
-
"stdout": "",
|
91
|
-
"stderr": f"不支持的文件类型: {file_extension}",
|
92
|
-
}
|
93
|
-
|
94
|
-
# 特殊处理纯文本文件,支持行范围选择
|
95
|
-
if processor == TextFileProcessor:
|
96
|
-
try:
|
97
|
-
with open(
|
98
|
-
abs_path, "r", encoding="utf-8", errors="ignore"
|
99
|
-
) as f:
|
100
|
-
lines = f.readlines()
|
101
|
-
|
102
|
-
total_lines = len(lines)
|
103
|
-
start_line = (
|
104
|
-
start_line
|
105
|
-
if start_line >= 0
|
106
|
-
else total_lines + start_line + 1
|
107
|
-
)
|
108
|
-
end_line = (
|
109
|
-
end_line
|
110
|
-
if end_line >= 0
|
111
|
-
else total_lines + end_line + 1
|
112
|
-
)
|
113
|
-
start_line = max(1, min(start_line, total_lines))
|
114
|
-
end_line = max(1, min(end_line, total_lines))
|
115
|
-
if end_line == -1:
|
116
|
-
end_line = total_lines
|
117
|
-
|
118
|
-
if start_line > end_line:
|
119
|
-
spinner.text = "无效的行范围"
|
120
|
-
spinner.fail("❌")
|
121
|
-
error_msg = f"无效的行范围 [{start_line, end_line}] (文件总行数: {total_lines})"
|
122
|
-
return {
|
123
|
-
"success": False,
|
124
|
-
"stdout": "",
|
125
|
-
"stderr": error_msg,
|
126
|
-
}
|
127
|
-
|
128
|
-
content = "".join(lines[start_line - 1 : end_line])
|
129
|
-
file_info = f"\n文件: {abs_path} (文本文件)\n行: [{start_line}-{end_line}]/{total_lines}"
|
130
|
-
except Exception as e:
|
131
|
-
return {
|
132
|
-
"success": False,
|
133
|
-
"stdout": "",
|
134
|
-
"stderr": f"读取文本文件失败: {str(e)}",
|
135
|
-
}
|
136
|
-
else:
|
137
|
-
return {
|
138
|
-
"success": False,
|
139
|
-
"stdout": "",
|
140
|
-
"stderr": f"不支持的文件类型: {file_extension}",
|
141
|
-
}
|
142
|
-
|
143
|
-
# 构建输出信息
|
144
|
-
output = f"{file_info}\n{content}" + "\n\n"
|
145
|
-
|
146
|
-
spinner.text = f"文件读取完成: {abs_path}"
|
147
|
-
spinner.ok("✅")
|
148
|
-
|
149
|
-
if agent:
|
150
|
-
files = agent.get_user_data("files")
|
151
|
-
if files:
|
152
|
-
files.append(abs_path)
|
153
|
-
else:
|
154
|
-
files = [abs_path]
|
155
|
-
agent.set_user_data("files", files)
|
156
|
-
|
157
|
-
return {"success": True, "stdout": output, "stderr": ""}
|
158
|
-
elif operation == "write":
|
159
|
-
with yaspin(
|
160
|
-
text=f"正在写入文件: {abs_path}...", color="cyan"
|
161
|
-
) as spinner:
|
162
|
-
os.makedirs(
|
163
|
-
os.path.dirname(os.path.abspath(abs_path)), exist_ok=True
|
164
|
-
)
|
165
|
-
with open(abs_path, "w", encoding="utf-8", errors="ignore") as f:
|
166
|
-
f.write(content)
|
167
|
-
spinner.text = f"文件写入完成: {abs_path}"
|
168
|
-
spinner.ok("✅")
|
169
|
-
return {
|
170
|
-
"success": True,
|
171
|
-
"stdout": f"文件写入成功: {abs_path}",
|
172
|
-
"stderr": "",
|
173
|
-
}
|
174
|
-
return {"success": False, "stdout": "", "stderr": f"未知操作: {operation}"}
|
175
|
-
|
176
|
-
except Exception as e:
|
177
|
-
PrettyOutput.print(str(e), OutputType.ERROR)
|
178
|
-
return {
|
179
|
-
"success": False,
|
180
|
-
"stdout": "",
|
181
|
-
"stderr": f"文件操作失败 {abs_path}: {str(e)}",
|
182
|
-
}
|
183
|
-
|
184
|
-
def execute(self, args: Dict) -> Dict[str, Any]:
|
185
|
-
"""Execute file operations for multiple files
|
186
|
-
|
187
|
-
Args:
|
188
|
-
args: Dictionary containing operation and files list
|
189
|
-
|
190
|
-
Returns:
|
191
|
-
Dict containing:
|
192
|
-
- success: Boolean indicating overall success
|
193
|
-
- stdout: Combined output of all operations as string
|
194
|
-
- stderr: Error message if any
|
195
|
-
"""
|
196
|
-
try:
|
197
|
-
operation = args["operation"].strip()
|
198
|
-
agent = args.get("agent", None)
|
199
|
-
if "files" not in args or not isinstance(args["files"], list):
|
200
|
-
return {
|
201
|
-
"success": False,
|
202
|
-
"stdout": "",
|
203
|
-
"stderr": "files参数是必需的,且必须是一个列表",
|
204
|
-
}
|
205
|
-
|
206
|
-
all_outputs = []
|
207
|
-
success = True
|
208
|
-
|
209
|
-
for file_info in args["files"]:
|
210
|
-
if not isinstance(file_info, dict) or "path" not in file_info:
|
211
|
-
continue
|
212
|
-
|
213
|
-
content = file_info.get("content", "") if operation == "write" else ""
|
214
|
-
result = self._handle_single_file(
|
215
|
-
operation,
|
216
|
-
file_info["path"].strip(),
|
217
|
-
content,
|
218
|
-
file_info.get("start_line", 1),
|
219
|
-
file_info.get("end_line", -1),
|
220
|
-
agent,
|
221
|
-
)
|
222
|
-
|
223
|
-
if result["success"]:
|
224
|
-
all_outputs.append(result["stdout"])
|
225
|
-
else:
|
226
|
-
all_outputs.append(
|
227
|
-
f"处理文件 {file_info['path']} 时出错: {result['stderr']}"
|
228
|
-
)
|
229
|
-
success = success and result["success"]
|
230
|
-
|
231
|
-
# Combine all outputs with separators
|
232
|
-
combined_output = "\n\n" + "=" * 80 + "\n\n".join(all_outputs)
|
233
|
-
|
234
|
-
return {"success": success, "stdout": combined_output, "stderr": ""}
|
235
|
-
|
236
|
-
except Exception as e:
|
237
|
-
PrettyOutput.print(str(e), OutputType.ERROR)
|
238
|
-
return {"success": False, "stdout": "", "stderr": f"文件操作失败: {str(e)}"}
|
@@ -1,98 +0,0 @@
|
|
1
|
-
import glob
|
2
|
-
import os
|
3
|
-
from datetime import datetime
|
4
|
-
from typing import Dict, List, Optional, Union
|
5
|
-
|
6
|
-
import yaml
|
7
|
-
|
8
|
-
|
9
|
-
class JarvisHistory:
|
10
|
-
def __init__(self):
|
11
|
-
self.records: List[Dict[str, str]] = []
|
12
|
-
self.current_file: Optional[str] = None
|
13
|
-
|
14
|
-
def start_record(self, data_dir: str) -> None:
|
15
|
-
"""Start a new recording session with timestamped filename"""
|
16
|
-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
17
|
-
self.current_file = os.path.join(data_dir, f"history_{timestamp}.yaml")
|
18
|
-
self.records = []
|
19
|
-
|
20
|
-
def append_msg(self, role: str, msg: str) -> None:
|
21
|
-
"""Append a message to current recording session"""
|
22
|
-
if not self.current_file:
|
23
|
-
raise RuntimeError("Recording not started. Call start_record first.")
|
24
|
-
self.records.append({"role": role, "message": msg})
|
25
|
-
|
26
|
-
def save_history(self, filename: str) -> None:
|
27
|
-
"""Save recorded messages to YAML file"""
|
28
|
-
|
29
|
-
# Skip saving if records is empty
|
30
|
-
if not self.records:
|
31
|
-
return
|
32
|
-
|
33
|
-
# Ensure directory exists
|
34
|
-
os.makedirs(os.path.dirname(filename), exist_ok=True)
|
35
|
-
|
36
|
-
with open(filename, "w") as f:
|
37
|
-
yaml.safe_dump({"conversation": self.records}, f, allow_unicode=True)
|
38
|
-
|
39
|
-
def stop_record(self) -> None:
|
40
|
-
"""Stop recording session and save messages"""
|
41
|
-
if not self.current_file:
|
42
|
-
raise RuntimeError("No recording session to stop.")
|
43
|
-
|
44
|
-
self.save_history(self.current_file)
|
45
|
-
self.current_file = None
|
46
|
-
self.records = []
|
47
|
-
|
48
|
-
@staticmethod
|
49
|
-
def export_history_to_markdown(
|
50
|
-
input_dir: str, output_file: str, max_files: Optional[int] = None
|
51
|
-
) -> None:
|
52
|
-
"""
|
53
|
-
Export all history files in the directory to a single markdown file
|
54
|
-
|
55
|
-
Args:
|
56
|
-
input_dir: Directory containing history YAML files
|
57
|
-
output_file: Path to output markdown file
|
58
|
-
max_files: Maximum number of history files to export (None for all)
|
59
|
-
"""
|
60
|
-
# Find all history files in the directory
|
61
|
-
history_files = glob.glob(os.path.join(input_dir, "history_*.yaml"))
|
62
|
-
|
63
|
-
if not history_files:
|
64
|
-
raise FileNotFoundError(f"No history files found in {input_dir}")
|
65
|
-
|
66
|
-
# Sort files by modification time (newest first) and limit to max_files
|
67
|
-
history_files.sort(key=os.path.getmtime, reverse=True)
|
68
|
-
if max_files is not None:
|
69
|
-
history_files = history_files[:max_files]
|
70
|
-
|
71
|
-
# Ensure output directory exists
|
72
|
-
os.makedirs(os.path.dirname(output_file), exist_ok=True)
|
73
|
-
|
74
|
-
with open(output_file, "w", encoding="utf-8") as md_file:
|
75
|
-
md_file.write("# Jarvis Conversation History\n\n")
|
76
|
-
|
77
|
-
for history_file in sorted(history_files):
|
78
|
-
# Read YAML file
|
79
|
-
with open(history_file, "r", encoding="utf-8") as f:
|
80
|
-
data = yaml.safe_load(f)
|
81
|
-
|
82
|
-
if not data or "conversation" not in data:
|
83
|
-
continue
|
84
|
-
|
85
|
-
# Write file header with timestamp from filename
|
86
|
-
timestamp = os.path.basename(history_file)[
|
87
|
-
8:-5
|
88
|
-
] # Extract timestamp from "history_YYYYMMDD_HHMMSS.yaml"
|
89
|
-
md_file.write(
|
90
|
-
f"## Conversation at {timestamp[:4]}-{timestamp[4:6]}-{timestamp[6:8]} "
|
91
|
-
f"{timestamp[9:11]}:{timestamp[11:13]}:{timestamp[13:15]}\n\n"
|
92
|
-
)
|
93
|
-
|
94
|
-
# Write conversation messages
|
95
|
-
for msg in data["conversation"]:
|
96
|
-
md_file.write(f"**{msg['role']}**: {msg['message']}\n\n")
|
97
|
-
|
98
|
-
md_file.write("\n---\n\n")
|
File without changes
|
{jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/licenses/LICENSE
RENAMED
File without changes
|
{jarvis_ai_assistant-0.1.207.dist-info → jarvis_ai_assistant-0.1.209.dist-info}/top_level.txt
RENAMED
File without changes
|