jarvis-ai-assistant 0.1.200__py3-none-any.whl → 0.1.202__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 CHANGED
@@ -1,4 +1,4 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  """Jarvis AI Assistant"""
3
3
 
4
- __version__ = "0.1.200"
4
+ __version__ = "0.1.202"
@@ -0,0 +1,412 @@
1
+ import os
2
+ import re
3
+ from abc import ABC, abstractmethod
4
+ from typing import Any, Dict, List, Tuple
5
+
6
+ from yaspin import yaspin
7
+ from yaspin.core import Yaspin
8
+
9
+ from jarvis.jarvis_agent.output_handler import OutputHandler
10
+ from jarvis.jarvis_platform.registry import PlatformRegistry
11
+ from jarvis.jarvis_tools.file_operation import FileOperationTool
12
+ from jarvis.jarvis_utils.git_utils import revert_file
13
+ from jarvis.jarvis_utils.output import OutputType, PrettyOutput
14
+ from jarvis.jarvis_utils.tag import ct, ot
15
+ from jarvis.jarvis_utils.utils import is_context_overflow
16
+
17
+
18
+ class EditFileHandler(OutputHandler):
19
+ def __init__(self):
20
+ self.patch_pattern = re.compile(
21
+ ot("PATCH file=([^>]+)") + r"\s*"
22
+ r"(?:"
23
+ + ot("DIFF")
24
+ + r"\s*"
25
+ + ot("SEARCH")
26
+ + r"(.*?)"
27
+ + ct("SEARCH")
28
+ + r"\s*"
29
+ + ot("REPLACE")
30
+ + r"(.*?)"
31
+ + ct("REPLACE")
32
+ + r"\s*"
33
+ + ct("DIFF")
34
+ + r"\s*)+"
35
+ + ct("PATCH"),
36
+ re.DOTALL,
37
+ )
38
+ self.diff_pattern = re.compile(
39
+ ot("DIFF")
40
+ + r"\s*"
41
+ + ot("SEARCH")
42
+ + r"(.*?)"
43
+ + ct("SEARCH")
44
+ + r"\s*"
45
+ + ot("REPLACE")
46
+ + r"(.*?)"
47
+ + ct("REPLACE")
48
+ + r"\s*"
49
+ + ct("DIFF"),
50
+ re.DOTALL,
51
+ )
52
+
53
+ def handle(self, response: str, agent: Any) -> Tuple[bool, str]:
54
+ """处理文件编辑响应
55
+
56
+ Args:
57
+ response: 包含文件编辑指令的响应字符串
58
+ agent: 执行处理的agent实例
59
+
60
+ Returns:
61
+ Tuple[bool, str]: 返回处理结果元组,第一个元素表示是否处理成功,第二个元素为处理结果汇总字符串
62
+ """
63
+ patches = self._parse_patches(response)
64
+ if not patches:
65
+ return False, "未找到有效的文件编辑指令"
66
+
67
+ results = []
68
+
69
+ for file_path, diffs in patches.items():
70
+ file_path = os.path.abspath(file_path)
71
+ file_patches = [
72
+ {"search": diff["search"], "replace": diff["replace"]} for diff in diffs
73
+ ]
74
+
75
+ with yaspin(text=f"正在处理文件 {file_path}...", color="cyan") as spinner:
76
+ # 首先尝试fast_edit模式
77
+ success, result = self._fast_edit(file_path, file_patches, spinner)
78
+ if not success:
79
+ # 如果fast_edit失败,尝试slow_edit模式
80
+ success, result = EditFileHandler._slow_edit(
81
+ file_path, file_patches, spinner, agent
82
+ )
83
+
84
+ if success:
85
+ results.append(f"✅ 文件 {file_path} 修改成功")
86
+ else:
87
+ results.append(f"❌ 文件 {file_path} 修改失败: {result}")
88
+
89
+ summary = "\n".join(results)
90
+ return False, summary
91
+
92
+ def can_handle(self, response: str) -> bool:
93
+ """判断是否能处理给定的响应
94
+
95
+ Args:
96
+ response: 需要判断的响应字符串
97
+
98
+ Returns:
99
+ bool: 返回是否能处理该响应
100
+ """
101
+ return bool(self.patch_pattern.search(response))
102
+
103
+ def prompt(self) -> str:
104
+ """获取处理器的提示信息
105
+
106
+ Returns:
107
+ str: 返回处理器的提示字符串
108
+ """
109
+ return f"""文件编辑指令格式:
110
+ {ot("PATCH file=文件路径")}
111
+ {ot("DIFF")}
112
+ {ot("SEARCH")}原始代码{ct("SEARCH")}
113
+ {ot("REPLACE")}新代码{ct("REPLACE")}
114
+ {ct("DIFF")}
115
+ {ct("PATCH")}
116
+
117
+ 可以返回多个PATCH块用于同时修改多个文件
118
+ 每个PATCH块可以包含多个DIFF块,每个DIFF块包含一组搜索和替换内容。
119
+ 搜索文本必须能在文件中唯一匹配,否则编辑将失败。"""
120
+
121
+ def name(self) -> str:
122
+ """获取处理器的名称
123
+
124
+ Returns:
125
+ str: 返回处理器的名称字符串
126
+ """
127
+ return "PATCH"
128
+
129
+ def _parse_patches(self, response: str) -> Dict[str, List[Dict[str, str]]]:
130
+ """解析响应中的补丁信息
131
+
132
+ 该方法使用正则表达式从响应文本中提取文件编辑指令(PATCH块),
133
+ 每个PATCH块可以包含多个DIFF块,每个DIFF块包含一组搜索和替换内容。
134
+ 解析后会返回一个字典,键是文件路径,值是该文件对应的补丁列表。
135
+
136
+ Args:
137
+ response: 包含补丁信息的响应字符串,格式应符合PATCH指令规范
138
+
139
+ Returns:
140
+ Dict[str, List[Dict[str, str]]]:
141
+ 返回解析后的补丁信息字典,结构为:
142
+ {
143
+ "文件路径1": [
144
+ {"search": "搜索文本1", "replace": "替换文本1"},
145
+ {"search": "搜索文本2", "replace": "替换文本2"}
146
+ ],
147
+ "文件路径2": [...]
148
+ }
149
+ """
150
+ patches = {}
151
+ for match in self.patch_pattern.finditer(response):
152
+ file_path = match.group(1)
153
+ diffs = []
154
+ for diff_match in self.diff_pattern.finditer(match.group(0)):
155
+ diffs.append(
156
+ {
157
+ "search": diff_match.group(1).strip(),
158
+ "replace": diff_match.group(2).strip(),
159
+ }
160
+ )
161
+ if diffs:
162
+ patches[file_path] = diffs
163
+ return patches
164
+
165
+ @staticmethod
166
+ def _fast_edit(
167
+ file_path: str, patches: List[Dict[str, str]], spinner: Yaspin
168
+ ) -> Tuple[bool, str]:
169
+ """快速应用补丁到文件
170
+
171
+ 该方法直接尝试将补丁应用到目标文件,适用于简单、明确的修改场景。
172
+ 特点:
173
+ 1. 直接进行字符串替换,效率高
174
+ 2. 会自动处理缩进问题,尝试匹配不同缩进级别的代码
175
+ 3. 确保搜索文本在文件中唯一匹配
176
+ 4. 如果失败会自动回滚修改
177
+
178
+ Args:
179
+ file_path: 要修改的文件路径,支持绝对路径和相对路径
180
+ patches: 补丁列表,每个补丁包含search(搜索文本)和replace(替换文本)
181
+ spinner: 进度显示对象,用于显示处理状态和结果
182
+
183
+ Returns:
184
+ Tuple[bool, str]:
185
+ 返回处理结果元组,第一个元素表示是否成功(True/False),
186
+ 第二个元素为结果信息,成功时为修改后的文件内容,失败时为错误信息
187
+ """
188
+ try:
189
+ # 确保目录存在
190
+ os.makedirs(os.path.dirname(file_path), exist_ok=True)
191
+
192
+ # 读取原始文件内容
193
+ file_content = ""
194
+ if os.path.exists(file_path):
195
+ with open(file_path, "r", encoding="utf-8") as f:
196
+ file_content = f.read()
197
+
198
+ # 应用所有补丁
199
+ modified_content = file_content
200
+ patch_count = 0
201
+ for patch in patches:
202
+ search_text = patch["search"]
203
+ replace_text = patch["replace"]
204
+ patch_count += 1
205
+
206
+ if search_text in modified_content:
207
+ if modified_content.count(search_text) > 1:
208
+ return False, f"搜索文本在文件中存在多处匹配:\n{search_text}"
209
+ modified_content = modified_content.replace(
210
+ search_text, replace_text
211
+ )
212
+ spinner.write(f"✅ 补丁 #{patch_count} 应用成功")
213
+ else:
214
+ # 尝试增加缩进重试
215
+ found = False
216
+ for space_count in range(1, 17):
217
+ indented_search = "\n".join(
218
+ " " * space_count + line if line.strip() else line
219
+ for line in search_text.split("\n")
220
+ )
221
+ indented_replace = "\n".join(
222
+ " " * space_count + line if line.strip() else line
223
+ for line in replace_text.split("\n")
224
+ )
225
+ if indented_search in modified_content:
226
+ if modified_content.count(indented_search) > 1:
227
+ return (
228
+ False,
229
+ f"搜索文本在文件中存在多处匹配:\n{indented_search}",
230
+ )
231
+ modified_content = modified_content.replace(
232
+ indented_search, indented_replace
233
+ )
234
+ spinner.write(
235
+ f"✅ 补丁 #{patch_count} 应用成功 (自动增加 {space_count} 个空格缩进)"
236
+ )
237
+ found = True
238
+ break
239
+
240
+ if not found:
241
+ return False, f"搜索文本在文件中不存在:\n{search_text}"
242
+
243
+ # 写入修改后的内容
244
+ with open(file_path, "w", encoding="utf-8") as f:
245
+ f.write(modified_content)
246
+
247
+ spinner.text = f"文件 {file_path} 修改完成,应用了 {patch_count} 个补丁"
248
+ spinner.ok("✅")
249
+ return True, modified_content
250
+
251
+ except Exception as e:
252
+ spinner.text = f"文件修改失败: {str(e)}"
253
+ spinner.fail("❌")
254
+ revert_file(file_path)
255
+ return False, f"文件修改失败: {str(e)}"
256
+
257
+ @staticmethod
258
+ def _slow_edit(
259
+ file_path: str, patches: List[Dict[str, str]], spinner: Yaspin, agent: Any
260
+ ) -> Tuple[bool, str]:
261
+ """使用AI模型生成补丁并应用到文件
262
+
263
+ 当_fast_edit方法失败时调用此方法,使用AI模型生成更精确的补丁。
264
+ 特点:
265
+ 1. 适用于复杂修改场景或需要上下文理解的修改
266
+ 2. 会自动处理大文件上传问题
267
+ 3. 会尝试最多3次生成有效的补丁
268
+ 4. 生成的补丁会再次通过_fast_edit方法应用
269
+ 5. 如果失败会自动回滚修改
270
+
271
+ Args:
272
+ file_path: 要修改的文件路径,支持绝对路径和相对路径
273
+ patches: 补丁列表,每个补丁包含search(搜索文本)和replace(替换文本)
274
+ spinner: 进度显示对象,用于显示处理状态和结果
275
+ agent: 执行处理的agent实例,用于访问AI模型平台
276
+
277
+ Returns:
278
+ Tuple[bool, str]:
279
+ 返回处理结果元组,第一个元素表示是否成功(True/False),
280
+ 第二个元素为结果信息,成功时为修改后的文件内容,失败时为错误信息
281
+ """
282
+ try:
283
+ model = PlatformRegistry().get_normal_platform()
284
+
285
+ # 读取原始文件内容
286
+ file_content = ""
287
+ if os.path.exists(file_path):
288
+ with open(file_path, "r", encoding="utf-8") as f:
289
+ file_content = f.read()
290
+
291
+ is_large_context = is_context_overflow(file_content)
292
+ upload_success = False
293
+
294
+ # 如果是大文件,尝试上传到模型平台
295
+ with spinner.hidden():
296
+ if (
297
+ is_large_context
298
+ and model.support_upload_files()
299
+ and model.upload_files([file_path])
300
+ ):
301
+ upload_success = True
302
+
303
+ model.set_suppress_output(True)
304
+
305
+ # 构建补丁内容
306
+ patch_content = []
307
+ for patch in patches:
308
+ patch_content.append(
309
+ {
310
+ "reason": "根据用户指令修改代码",
311
+ "search": patch["search"],
312
+ "replace": patch["replace"],
313
+ }
314
+ )
315
+
316
+ # 构建提示词
317
+ main_prompt = f"""
318
+ # 代码补丁生成专家指南
319
+
320
+ ## 任务描述
321
+ 你是一位精确的代码补丁生成专家,需要根据补丁描述生成精确的代码差异。
322
+
323
+ ### 补丁内容
324
+ ```
325
+ {str(patch_content)}
326
+ ```
327
+
328
+ ## 补丁生成要求
329
+ 1. **精确性**:严格按照补丁的意图修改代码
330
+ 2. **格式一致性**:严格保持原始代码的格式风格,如果补丁中缩进或者空行与原代码不一致,则需要修正补丁中的缩进或者空行
331
+ 3. **最小化修改**:只修改必要的代码部分,保持其他部分不变
332
+ 4. **上下文完整性**:提供足够的上下文,确保补丁能准确应用
333
+
334
+ ## 输出格式规范
335
+ - 使用{ot("DIFF")}块包围每个需要修改的代码段
336
+ - 每个{ot("DIFF")}块必须包含SEARCH部分和REPLACE部分
337
+ - SEARCH部分是需要查找的原始代码
338
+ - REPLACE部分是替换后的新代码
339
+ - 确保SEARCH部分能在原文件中**唯一匹配**
340
+ - 如果修改较大,可以使用多个{ot("DIFF")}块
341
+
342
+ ## 输出模板
343
+ {ot("DIFF")}
344
+ {ot("SEARCH")}[需要查找的原始代码,包含足够上下文,避免出现可匹配多处的情况]{ct("SEARCH")}
345
+ {ot("REPLACE")}[替换后的新代码]{ct("REPLACE")}
346
+ {ct("DIFF")}
347
+
348
+ {ot("DIFF")}
349
+ {ot("SEARCH")}[另一处需要查找的原始代码,包含足够上下文,避免出现可匹配多处的情况]{ct("SEARCH")}
350
+ {ot("REPLACE")}[另一处替换后的新代码]{ct("REPLACE")}
351
+ {ct("DIFF")}
352
+ """
353
+
354
+ # 尝试最多3次生成补丁
355
+ for _ in range(3):
356
+ if is_large_context:
357
+ if upload_success:
358
+ response = model.chat_until_success(main_prompt)
359
+ else:
360
+ file_prompt = f"""
361
+ # 原始代码
362
+ {file_content}
363
+ """
364
+ response = model.chat_until_success(main_prompt + file_prompt)
365
+ else:
366
+ file_prompt = f"""
367
+ # 原始代码
368
+ {file_content}
369
+ """
370
+ response = model.chat_until_success(main_prompt + file_prompt)
371
+
372
+ # 解析生成的补丁
373
+ diff_blocks = re.finditer(
374
+ ot("DIFF")
375
+ + r"\s*"
376
+ + ot("SEARCH")
377
+ + r"(.*?)"
378
+ + ct("SEARCH")
379
+ + r"\s*"
380
+ + ot("REPLACE")
381
+ + r"(.*?)"
382
+ + ct("REPLACE")
383
+ + r"\s*"
384
+ + ct("DIFF"),
385
+ response,
386
+ re.DOTALL,
387
+ )
388
+
389
+ generated_patches = []
390
+ for match in diff_blocks:
391
+ generated_patches.append(
392
+ {
393
+ "search": match.group(1).strip(),
394
+ "replace": match.group(2).strip(),
395
+ }
396
+ )
397
+
398
+ if generated_patches:
399
+ # 尝试应用生成的补丁
400
+ success, result = EditFileHandler._fast_edit(
401
+ file_path, generated_patches, spinner
402
+ )
403
+ if success:
404
+ return True, result
405
+
406
+ return False, "AI模型无法生成有效的补丁"
407
+
408
+ except Exception as e:
409
+ spinner.text = f"文件修改失败: {str(e)}"
410
+ spinner.fail("❌")
411
+ revert_file(file_path)
412
+ return False, f"文件修改失败: {str(e)}"
@@ -14,6 +14,7 @@ from yaspin import yaspin # type: ignore
14
14
 
15
15
  from jarvis.jarvis_agent import Agent
16
16
  from jarvis.jarvis_agent.builtin_input_handler import builtin_input_handler
17
+ from jarvis.jarvis_agent.edit_file_handler import EditFileHandler
17
18
  from jarvis.jarvis_agent.shell_input_handler import shell_input_handler
18
19
  # 忽略yaspin的类型检查
19
20
  from jarvis.jarvis_code_agent.lint import get_lint_tools
@@ -54,7 +55,7 @@ class CodeAgent:
54
55
  "read_code",
55
56
  "methodology",
56
57
  "chdir",
57
- "edit_file",
58
+ # "edit_file",
58
59
  "rewrite_file",
59
60
  ]
60
61
  )
@@ -94,7 +95,7 @@ class CodeAgent:
94
95
  - 仅在命令行工具不足时使用专用工具
95
96
 
96
97
  ## 文件编辑工具使用规范
97
- - 对于部分文件内容修改,使用edit_file工具
98
+ - 对于部分文件内容修改,使用PATCH
98
99
  - 对于需要重写整个文件内容,使用rewrite_file工具
99
100
  - 对于简单的修改,可以使用execute_script工具执行shell命令完成
100
101
  </code_engineer_guide>
@@ -112,7 +113,7 @@ class CodeAgent:
112
113
  system_prompt=code_system_prompt,
113
114
  name="CodeAgent",
114
115
  auto_complete=False,
115
- output_handler=[tool_registry],
116
+ output_handler=[tool_registry, EditFileHandler()],
116
117
  platform=platform_instance,
117
118
  input_handler=[shell_input_handler, builtin_input_handler],
118
119
  need_summary=need_summary,
@@ -82,7 +82,7 @@ class BasePlatform(ABC):
82
82
  while_true(
83
83
  lambda: while_success(
84
84
  lambda: self.chat(
85
- f"<part_content>{input}</part_content>请返回已收到"
85
+ f"<part_content>{input}</part_content>\n\n请返回已收到"
86
86
  ),
87
87
  5,
88
88
  ),
@@ -149,6 +149,9 @@ class BasePlatform(ABC):
149
149
  response = re.sub(
150
150
  ot("think") + r".*?" + ct("think"), "", response, flags=re.DOTALL
151
151
  )
152
+ response = re.sub(
153
+ ot("thinking") + r".*?" + ct("thinking"), "", response, flags=re.DOTALL
154
+ )
152
155
  return response
153
156
 
154
157
  def chat_until_success(self, message: str) -> str:
@@ -477,7 +477,6 @@ class YuanbaoPlatform(BasePlatform):
477
477
  if self.first_chat and self.system_message:
478
478
  payload["prompt"] = f"{self.system_message}\n\n{message}"
479
479
  payload["displayPrompt"] = payload["prompt"]
480
- self.first_chat = False
481
480
 
482
481
  try:
483
482
  # 发送消息请求,获取流式响应
@@ -532,6 +531,7 @@ class YuanbaoPlatform(BasePlatform):
532
531
  # 检测结束标志
533
532
  elif line_str == "data: [DONE]":
534
533
  return None
534
+ self.first_chat = False
535
535
  return None
536
536
 
537
537
  except Exception as e:
@@ -17,30 +17,21 @@
17
17
  - 支持大文件处理(自动上传到模型平台)
18
18
  - 提供3次重试机制确保操作可靠性
19
19
  """
20
- import re
21
- from typing import Any, Dict, List, Tuple
20
+ from typing import Any, Dict
22
21
 
23
- import yaml
24
22
  from yaspin import yaspin
25
- from yaspin.core import Yaspin
26
23
 
27
- from jarvis.jarvis_platform.registry import PlatformRegistry
28
- from jarvis.jarvis_tools.file_operation import FileOperationTool
29
- from jarvis.jarvis_utils.git_utils import revert_file
30
- from jarvis.jarvis_utils.output import OutputType, PrettyOutput
31
- from jarvis.jarvis_utils.tag import ct, ot
32
- from jarvis.jarvis_utils.utils import is_context_overflow
24
+ from jarvis.jarvis_agent.edit_file_handler import EditFileHandler
33
25
 
34
26
 
35
27
  class FileSearchReplaceTool:
36
28
  name = "edit_file"
37
- description = """代码编辑工具,用于精确修改单个文件
29
+ description = """代码编辑工具,用于精确修改一个或多个文件
38
30
 
39
31
  # 文件编辑工具使用指南
40
32
 
41
-
42
33
  ## 基本使用
43
- 1. 指定需要修改的文件路径
34
+ 1. 指定需要修改的文件路径(单个或多个)
44
35
  2. 提供一组或多组修改,每个修改包含:
45
36
  - reason: 修改原因描述
46
37
  - search: 需要查找的原始代码(必须包含足够上下文)
@@ -52,59 +43,68 @@ class FileSearchReplaceTool:
52
43
  2. **最小补丁原则**: 生成最小范围的补丁,包含必要的上下文
53
44
  3. **唯一匹配**: 确保搜索文本在文件中唯一匹配
54
45
  4. **格式保持**: 严格保持原始代码的格式风格
55
-
46
+ 5. **部分成功**: 支持多个文件编辑,允许部分文件编辑成功
56
47
 
57
48
  """
58
49
  parameters = {
59
50
  "type": "object",
60
51
  "properties": {
61
- "file": {"type": "string", "description": "需要修改的文件路径"},
62
- "changes": {
52
+ "files": {
63
53
  "type": "array",
64
- "description": "一组或多组修改,每个修改必须包含1-2行上下文用于精确定位",
54
+ "description": "需要修改的文件路径列表",
65
55
  "items": {
66
56
  "type": "object",
67
57
  "properties": {
68
- "reason": {"type": "string", "description": "修改的原因"},
69
- "search": {
70
- "type": "string",
71
- "description": "需要查找的原始代码",
58
+ "path": {"type": "string", "description": "文件路径"},
59
+ "changes": {
60
+ "type": "array",
61
+ "description": "一组或多组修改,每个修改必须包含1-2行上下文用于精确定位",
62
+ "items": {
63
+ "type": "object",
64
+ "properties": {
65
+ "reason": {"type": "string", "description": "修改的原因"},
66
+ "search": {
67
+ "type": "string",
68
+ "description": "需要查找的原始代码",
69
+ },
70
+ "replace": {"type": "string", "description": "替换后的新代码"},
71
+ },
72
+ },
72
73
  },
73
- "replace": {"type": "string", "description": "替换后的新代码"},
74
74
  },
75
+ "required": ["path", "changes"],
75
76
  },
76
77
  },
77
78
  },
78
- "required": ["file", "changes"],
79
+ "required": ["files"],
79
80
  }
80
81
 
81
- def __init__(self):
82
- """初始化文件搜索替换工具"""
83
- pass
84
82
 
85
83
  def execute(self, args: Dict) -> Dict[str, Any]:
86
84
  """执行文件编辑操作,支持快速编辑和AI辅助编辑两种模式。
87
85
 
88
86
  主要功能:
89
- 1. 处理文件创建或修改,支持不存在的文件
90
- 2. 原子操作:所有修改要么全部成功,要么全部回滚
87
+ 1. 处理多个文件的创建或修改,支持不存在的文件
88
+ 2. 每个文件独立处理,允许部分文件编辑成功
91
89
  3. 自动选择编辑模式(fast_edit或slow_edit)
92
90
  4. 保存修改前后的文件状态以便回滚
93
91
  5. 提供详细的执行状态输出
94
92
 
95
93
  参数:
96
94
  args: 包含以下键的字典:
97
- - file: 要修改的文件路径(必填)
98
- - changes: 修改列表,每个修改包含(必填):
99
- - reason: 修改原因描述
100
- - search: 需要查找的原始代码(必须包含足够上下文)
101
- - replace: 替换后的新代码
95
+ - files: 文件列表,每个文件包含(必填):
96
+ - path: 要修改的文件路径
97
+ - changes: 修改列表,每个修改包含:
98
+ - reason: 修改原因描述
99
+ - search: 需要查找的原始代码(必须包含足够上下文)
100
+ - replace: 替换后的新代码
102
101
 
103
102
  返回:
104
103
  Dict[str, Any] 包含:
105
- - success: 操作是否成功(True/False)
104
+ - success: 是否至少有一个文件编辑成功(True/False)
106
105
  - stdout: 成功时的输出消息
107
106
  - stderr: 失败时的错误消息
107
+ - results: 每个文件的处理结果列表
108
108
 
109
109
  异常处理:
110
110
  1. 捕获并记录文件操作异常
@@ -125,365 +125,147 @@ class FileSearchReplaceTool:
125
125
 
126
126
  stdout_messages = []
127
127
  stderr_messages = []
128
- success = True
129
-
130
- file_path = os.path.abspath(args["file"])
131
- changes = args["changes"]
132
- agent = args.get("agent", None)
128
+ overall_success = False
129
+ file_results = []
133
130
 
134
- # 创建已处理文件变量,用于失败时回滚
135
- original_content = None
136
- processed = False
131
+ for file_info in args["files"]:
132
+ file_path = os.path.abspath(file_info["path"])
133
+ changes = file_info["changes"]
134
+ agent = args.get("agent", None)
137
135
 
138
- try:
139
- file_exists = os.path.exists(file_path)
140
- content = ""
136
+ # 创建已处理文件变量,用于失败时回滚
137
+ original_content = None
138
+ processed = False
139
+ file_success = True
141
140
 
142
141
  try:
143
- # 如果文件存在,则读取内容
144
- if file_exists:
145
- with open(file_path, "r", encoding="utf-8") as f:
146
- content = f.read()
147
- original_content = content
148
-
149
- if file_exists and agent:
150
- files = agent.get_user_data("files")
151
- if not files or file_path not in files:
152
- return {
153
- "success": False,
154
- "stdout": "",
155
- "stderr": f"请先读取文件 {file_path} 的内容后再编辑",
156
- }
157
-
158
- with yaspin(
159
- text=f"正在处理文件 {file_path}...", color="cyan"
160
- ) as spinner:
161
- success, temp_content = fast_edit(file_path, changes, spinner)
162
- if not success:
163
- success, temp_content = slow_edit(
164
- file_path,
165
- yaml.safe_dump(changes, allow_unicode=True),
166
- spinner,
167
- )
168
- if not success:
169
- spinner.text = f"文件 {file_path} 处理失败"
170
- spinner.fail("❌")
171
- return {
142
+ file_exists = os.path.exists(file_path)
143
+ content = ""
144
+
145
+ try:
146
+ # 如果文件存在,则读取内容
147
+ if file_exists:
148
+ with open(file_path, "r", encoding="utf-8") as f:
149
+ content = f.read()
150
+ original_content = content
151
+
152
+ if file_exists and agent:
153
+ files = agent.get_user_data("files")
154
+ if not files or file_path not in files:
155
+ file_results.append({
156
+ "file": file_path,
172
157
  "success": False,
173
158
  "stdout": "",
174
- "stderr": temp_content,
175
- }
159
+ "stderr": f"请先读取文件 {file_path} 的内容后再编辑"
160
+ })
161
+ continue
162
+
163
+ with yaspin(
164
+ text=f"正在处理文件 {file_path}...", color="cyan"
165
+ ) as spinner:
166
+ # 首先尝试fast_edit模式
167
+ success, temp_content = EditFileHandler._fast_edit(file_path, changes, spinner)
168
+ if not success:
169
+ # 如果fast_edit失败,尝试slow_edit模式
170
+ success, temp_content = EditFileHandler._slow_edit(
171
+ file_path,
172
+ changes,
173
+ spinner,
174
+ agent
175
+ )
176
+ if not success:
177
+ spinner.text = f"文件 {file_path} 处理失败"
178
+ spinner.fail("❌")
179
+ file_results.append({
180
+ "file": file_path,
181
+ "success": False,
182
+ "stdout": "",
183
+ "stderr": temp_content
184
+ })
185
+ continue
186
+ else:
187
+ spinner.text = f"文件 {file_path} 内容生成完成"
188
+ spinner.ok("✅")
176
189
  else:
177
190
  spinner.text = f"文件 {file_path} 内容生成完成"
178
191
  spinner.ok("✅")
179
- else:
180
- spinner.text = f"文件 {file_path} 内容生成完成"
181
- spinner.ok("✅")
182
-
183
- # 只有当所有替换操作都成功时,才写回文件
184
- if success and (temp_content != original_content or not file_exists):
185
- # 确保目录存在
186
- os.makedirs(
187
- os.path.dirname(os.path.abspath(file_path)), exist_ok=True
188
- )
189
-
190
- with open(file_path, "w", encoding="utf-8") as f:
191
- f.write(temp_content)
192
192
 
193
- processed = True
194
-
195
- action = "创建并写入" if not file_exists else "成功修改"
196
- stdout_message = f"文件 {file_path} {action} 完成"
197
- stdout_messages.append(stdout_message)
198
- PrettyOutput.print(stdout_message, OutputType.SUCCESS)
199
-
200
- except Exception as e:
201
- stderr_message = f"处理文件 {file_path} 时出错: {str(e)}"
202
- stderr_messages.append(stderr_message)
203
- PrettyOutput.print(stderr_message, OutputType.WARNING)
204
- success = False
205
-
206
- return {
207
- "success": success,
208
- "stdout": "\n".join(stdout_messages) if success else "",
209
- "stderr": "\n".join(stderr_messages) if not success else "",
210
- }
211
-
212
- except Exception as e:
213
- error_msg = f"文件搜索替换操作失败: {str(e)}"
214
- PrettyOutput.print(error_msg, OutputType.WARNING)
215
-
216
- # 如果有已修改的文件,尝试回滚
217
- if processed:
218
- rollback_message = "操作失败,正在回滚修改..."
219
- stderr_messages.append(rollback_message)
220
- PrettyOutput.print(rollback_message, OutputType.WARNING)
193
+ # 只有当所有替换操作都成功时,才写回文件
194
+ if success and (temp_content != original_content or not file_exists):
195
+ # 确保目录存在
196
+ os.makedirs(
197
+ os.path.dirname(os.path.abspath(file_path)), exist_ok=True
198
+ )
221
199
 
222
- try:
223
- if original_content is None:
224
- # 如果是新创建的文件,则删除
225
- if os.path.exists(file_path):
226
- os.remove(file_path)
227
- stderr_messages.append(f"已删除新创建的文件: {file_path}")
228
- else:
229
- # 如果是修改的文件,则恢复原内容
230
200
  with open(file_path, "w", encoding="utf-8") as f:
231
- f.write(original_content)
232
- stderr_messages.append(f"已回滚文件: {file_path}")
233
- except:
234
- stderr_messages.append(f"回滚文件失败: {file_path}")
235
-
236
- return {
237
- "success": False,
238
- "stdout": "",
239
- "stderr": error_msg + "\n" + "\n".join(stderr_messages),
240
- }
241
-
242
-
243
- def slow_edit(filepath: str, patch_content: str, spinner: Yaspin) -> Tuple[bool, str]:
244
- """执行精确的文件编辑操作,使用AI模型生成差异补丁并应用。
245
-
246
- 核心功能:
247
- 1. 使用AI模型分析补丁内容并生成精确的代码差异
248
- 2. 应用生成的差异补丁到目标文件
249
- 3. 提供3次重试机制确保操作可靠性
250
- 4. 支持大文件处理(自动上传到模型平台)
251
- 5. 严格的格式一致性检查
252
-
253
- 参数:
254
- filepath: 要编辑的文件路径(绝对或相对路径)
255
- patch_content: YAML格式的补丁内容,包含:
256
- - reason: 修改原因描述
257
- - search: 需要查找的原始代码(必须包含足够上下文)
258
- - replace: 替换后的新代码
259
- spinner: Yaspin实例,用于显示处理状态
260
-
261
- 返回值:
262
- Tuple[bool, str]:
263
- - 第一个元素表示操作是否成功(True/False)
264
- - 第二个元素是修改后的文件内容(成功时)或空字符串(失败时)
265
-
266
- 异常处理:
267
- 1. 文件不存在或权限不足时会捕获异常并返回失败
268
- 2. 模型生成补丁失败时会自动重试最多3次
269
- 3. 补丁应用失败时会自动回滚文件修改
270
-
271
- 实现细节:
272
- 1. 检查文件是否在工作目录下(影响版本控制)
273
- 2. 根据文件大小决定是否上传到模型平台
274
- 3. 使用精确的DIFF格式解析模型生成的补丁
275
- 4. 确保补丁应用前进行唯一性匹配检查
276
- """
277
- import os
278
-
279
- work_dir = os.path.abspath(os.curdir)
280
- filepath = os.path.abspath(filepath)
281
- if not filepath.startswith(work_dir):
282
- PrettyOutput.print(
283
- f"文件 {filepath} 不在工作目录 {work_dir} 下,不会进行版本控制管理",
284
- OutputType.WARNING,
285
- )
286
- model = PlatformRegistry().get_normal_platform()
287
- try:
288
- file_content = FileOperationTool().execute(
289
- {"operation": "read", "files": [{"path": filepath}]}
290
- )["stdout"]
291
- is_large_context = is_context_overflow(file_content)
292
- upload_success = False
293
- # 读取原始文件内容
294
- with spinner.hidden():
295
- if (
296
- is_large_context
297
- and model.support_upload_files()
298
- and model.upload_files([filepath])
299
- ):
300
- upload_success = True
301
-
302
- model.set_suppress_output(True)
303
-
304
- main_prompt = f"""
305
- # 代码补丁生成专家指南
306
-
307
- ## 任务描述
308
- 你是一位精确的代码补丁生成专家,需要根据补丁描述生成精确的代码差异。
309
-
310
- ### 补丁内容
311
- ```
312
- {patch_content}
313
- ```
314
-
315
- ## 补丁生成要求
316
- 1. **精确性**:严格按照补丁的意图修改代码
317
- 2. **格式一致性**:严格保持原始代码的格式风格,如果补丁中缩进或者空行与原代码不一致,则需要修正补丁中的缩进或者空行
318
- 3. **最小化修改**:只修改必要的代码部分,保持其他部分不变
319
- 4. **上下文完整性**:提供足够的上下文,确保补丁能准确应用
320
-
321
- ## 输出格式规范
322
- - 使用{ot("DIFF")}块包围每个需要修改的代码段
323
- - 每个{ot("DIFF")}块必须包含SEARCH部分和REPLACE部分
324
- - SEARCH部分是需要查找的原始代码
325
- - REPLACE部分是替换后的新代码
326
- - 确保SEARCH部分能在原文件中**唯一匹配**
327
- - 如果修改较大,可以使用多个{ot("DIFF")}块
328
-
329
- ## 输出模板
330
- {ot("DIFF")}
331
- {">" * 5} SEARCH
332
- [需要查找的原始代码,包含足够上下文,避免出现可匹配多处的情况]
333
- {'='*5}
334
- [替换后的新代码]
335
- {"<" * 5} REPLACE
336
- {ct("DIFF")}
337
-
338
- {ot("DIFF")}
339
- {">" * 5} SEARCH
340
- [另一处需要查找的原始代码,包含足够上下文,避免出现可匹配多处的情况]
341
- {'='*5}
342
- [另一处替换后的新代码]
343
- {"<" * 5} REPLACE
344
- {ct("DIFF")}
345
- """
201
+ f.write(temp_content)
202
+
203
+ processed = True
204
+
205
+ action = "创建并写入" if not file_exists else "成功修改"
206
+ stdout_message = f"文件 {file_path} {action} 完成"
207
+ stdout_messages.append(stdout_message)
208
+ PrettyOutput.print(stdout_message, OutputType.SUCCESS)
209
+ overall_success = True
210
+
211
+ file_results.append({
212
+ "file": file_path,
213
+ "success": True,
214
+ "stdout": stdout_message,
215
+ "stderr": ""
216
+ })
217
+
218
+ except Exception as e:
219
+ stderr_message = f"处理文件 {file_path} 时出错: {str(e)}"
220
+ stderr_messages.append(stderr_message)
221
+ PrettyOutput.print(stderr_message, OutputType.WARNING)
222
+ file_success = False
223
+ file_results.append({
224
+ "file": file_path,
225
+ "success": False,
226
+ "stdout": "",
227
+ "stderr": stderr_message
228
+ })
346
229
 
347
- for _ in range(3):
348
- if is_large_context:
349
- if upload_success:
350
- response = model.chat_until_success(main_prompt)
351
- else:
352
- file_prompt = f"""
353
- # 原始代码
354
- {file_content}
355
- """
356
- response = model.chat_until_success(main_prompt + file_prompt)
357
- else:
358
- file_prompt = f"""
359
- # 原始代码
360
- {file_content}
361
- """
362
- response = model.chat_until_success(main_prompt + file_prompt)
363
-
364
- # 解析差异化补丁
365
- diff_blocks = re.finditer(
366
- ot("DIFF")
367
- + r"\s*>{4,} SEARCH\n?(.*?)\n?={4,}\n?(.*?)\s*<{4,} REPLACE\n?"
368
- + ct("DIFF"),
369
- response,
370
- re.DOTALL,
371
- )
372
-
373
- patches = []
374
- for match in diff_blocks:
375
- patches.append(
376
- {
377
- "search": match.group(1).strip(),
378
- "replace": match.group(2).strip(),
379
- }
380
- )
381
-
382
- success, modified_content_or_err = fast_edit(filepath, patches, spinner)
383
- if success:
384
- return True, modified_content_or_err
385
- spinner.text = f"文件 {filepath} 修改失败"
386
- spinner.fail("")
387
- return False, f"文件修改失败: {modified_content_or_err}"
388
-
389
- except Exception as e:
390
- spinner.text = f"文件修改失败: {str(e)}"
391
- spinner.fail("❌")
392
- return False, f"文件修改失败: {str(e)}"
393
-
394
-
395
- def fast_edit(
396
- filepath: str, patches: List[Dict[str, str]], spinner: Yaspin
397
- ) -> Tuple[bool, str]:
398
- """快速应用预先生成的补丁到目标文件。
399
-
400
- 核心功能:
401
- 1. 直接应用已生成的代码补丁
402
- 2. 执行严格的唯一匹配检查
403
- 3. 提供详细的补丁应用状态反馈
404
- 4. 失败时自动回滚文件修改
405
-
406
- 参数:
407
- filepath: 要编辑的文件路径(绝对或相对路径)
408
- patches: 补丁列表,每个补丁包含:
409
- - search: 需要查找的原始代码
410
- - replace: 替换后的新代码
411
- spinner: Yaspin实例,用于显示处理状态
412
-
413
- 返回值:
414
- Tuple[bool, str]:
415
- - 第一个元素表示操作是否成功(True/False)
416
- - 第二个元素是修改后的文件内容(成功时)或空字符串(失败时)
417
-
418
- 异常处理:
419
- 1. 文件不存在或权限不足时会捕获异常并返回失败
420
- 2. 补丁不匹配或有多处匹配时会返回失败
421
- 3. 失败时会自动回滚文件修改
422
-
423
- 实现细节:
424
- 1. 读取文件内容到内存
425
- 2. 依次应用每个补丁,检查唯一匹配性
426
- 3. 记录每个补丁的应用状态
427
- 4. 所有补丁成功应用后才写入文件
428
- """
429
- # 读取原始文件内容
430
- with open(filepath, "r", encoding="utf-8", errors="ignore") as f:
431
- file_content = f.read()
432
-
433
- # 应用所有差异化补丁
434
- modified_content = file_content
435
- patch_count = 0
436
- success = True
437
- err_msg = ""
438
- for patch in patches:
439
- search_text = patch["search"]
440
- replace_text = patch["replace"]
441
- patch_count += 1
442
- # 检查搜索文本是否存在于文件中
443
- if search_text in modified_content:
444
- # 如果有多处,报错
445
- if modified_content.count(search_text) > 1:
446
- success = False
447
- err_msg = f"搜索文本 ```\n{search_text}\n``` 在文件中存在多处,请检查补丁内容"
448
- break
449
- # 应用替换
450
- modified_content = modified_content.replace(search_text, replace_text)
451
- spinner.write(f"✅ 补丁 #{patch_count} 应用成功")
452
- else:
453
- # 尝试增加缩进重试
454
- found = False
455
- for space_count in range(1, 17):
456
- # 跳过空行不增加空格
457
- indented_search = "\n".join(
458
- " " * space_count + line if line.strip() else line
459
- for line in search_text.split("\n")
460
- )
461
- indented_replace = "\n".join(
462
- " " * space_count + line if line.strip() else line
463
- for line in replace_text.split("\n")
464
- )
465
- if indented_search in modified_content:
466
- if modified_content.count(indented_search) > 1:
467
- success = False
468
- err_msg = f"搜索文本 ```\n{indented_search}\n``` 在文件中存在多处,请检查补丁内容"
469
- break
470
- modified_content = modified_content.replace(
471
- indented_search, indented_replace
472
- )
473
- spinner.write(
474
- f"✅ 补丁 #{patch_count} 应用成功 (自动增加 {space_count} 个空格缩进)"
475
- )
476
- found = True
477
- break
478
-
479
- if not found:
480
- success = False
481
- err_msg = f"搜索文本 ```\n{search_text}\n``` 在文件中不存在,尝试增加1-16个空格缩进后仍未找到匹配"
482
- break
483
- if not success:
484
- revert_file(filepath)
485
- return False, err_msg
486
-
487
- spinner.text = f"文件 {filepath} 修改完成,应用了 {patch_count} 个补丁"
488
- spinner.ok("✅")
489
- return True, modified_content
230
+ except Exception as e:
231
+ error_msg = f"文件搜索替换操作失败: {str(e)}"
232
+ PrettyOutput.print(error_msg, OutputType.WARNING)
233
+
234
+ # 如果有已修改的文件,尝试回滚
235
+ if processed:
236
+ rollback_message = "操作失败,正在回滚修改..."
237
+ stderr_messages.append(rollback_message)
238
+ PrettyOutput.print(rollback_message, OutputType.WARNING)
239
+
240
+ try:
241
+ if original_content is None:
242
+ # 如果是新创建的文件,则删除
243
+ if os.path.exists(file_path):
244
+ os.remove(file_path)
245
+ stderr_messages.append(f"已删除新创建的文件: {file_path}")
246
+ else:
247
+ # 如果是修改的文件,则恢复原内容
248
+ with open(file_path, "w", encoding="utf-8") as f:
249
+ f.write(original_content)
250
+ stderr_messages.append(f"已回滚文件: {file_path}")
251
+ except:
252
+ stderr_messages.append(f"回滚文件失败: {file_path}")
253
+
254
+ file_results.append({
255
+ "file": file_path,
256
+ "success": False,
257
+ "stdout": "",
258
+ "stderr": error_msg
259
+ })
260
+
261
+ # 整合所有错误信息到stderr
262
+ all_stderr = []
263
+ for result in file_results:
264
+ if not result["success"]:
265
+ all_stderr.append(f"文件 {result['file']} 处理失败: {result['stderr']}")
266
+
267
+ return {
268
+ "success": overall_success,
269
+ "stdout": "\n".join(stdout_messages) if overall_success else "",
270
+ "stderr": "\n".join(all_stderr) if not overall_success else ""
271
+ }
@@ -553,24 +553,24 @@ class ToolRegistry(OutputHandlerProtocol):
553
553
  for item in data:
554
554
  try:
555
555
  msg = yaml.safe_load(item)
556
- if "name" in msg and "arguments" in msg and "want" in msg:
557
- ret.append(msg)
558
- else:
559
- return (
560
- {},
561
- f"""工具调用格式错误,请检查工具调用格式(缺少name、arguments、want字段)。
562
-
563
- {tool_call_help}""",
564
- )
565
556
  except Exception as e:
566
557
  return (
567
558
  {},
568
- f"""工具调用格式错误,请检查工具调用格式。
569
-
559
+ f"""yaml 解析失败,请检查工具调用格式。
570
560
  {e}
571
561
 
572
562
  {tool_call_help}""",
573
563
  )
564
+
565
+ if "name" in msg and "arguments" in msg and "want" in msg:
566
+ ret.append(msg)
567
+ else:
568
+ return (
569
+ {},
570
+ f"""工具调用格式错误,请检查工具调用格式(缺少name、arguments、want字段)。
571
+
572
+ {tool_call_help}""",
573
+ )
574
574
  if len(ret) > 1:
575
575
  return {}, "检测到多个工具调用,请一次只处理一个工具调用。"
576
576
  return ret[0] if ret else {}, ""
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jarvis-ai-assistant
3
- Version: 0.1.200
3
+ Version: 0.1.202
4
4
  Summary: Jarvis: An AI assistant that uses tools to interact with the system
5
5
  Home-page: https://github.com/skyfireitdiy/Jarvis
6
6
  Author: skyfire
@@ -1,12 +1,13 @@
1
- jarvis/__init__.py,sha256=ePqWF92FI0LF1Btg_GrnhZEoxtiseFY4-wiB5EEsh-U,75
1
+ jarvis/__init__.py,sha256=1uYZEHP44b3jLJZ5d-Ydy94aetzbjTE6KoKfy3CdhSc,75
2
2
  jarvis/jarvis_agent/__init__.py,sha256=X5BWIOzxXUWtCbpDkTFfUYskf6sbNzb1qQu8nQ4NN1k,34371
3
3
  jarvis/jarvis_agent/builtin_input_handler.py,sha256=1V7kV5Zhw2HE3Xgjs1R-43RZ2huq3Kg-32NCdNnyZmA,2216
4
+ jarvis/jarvis_agent/edit_file_handler.py,sha256=ZDHjtggwFrHAOcfOAScKyPHSw9a3NvbKRqRtHjp2w9Q,15545
4
5
  jarvis/jarvis_agent/jarvis.py,sha256=GH2zi8eXNpW8twiY3LKDEZgGmFC5geB0jlkwFrm7hOQ,6279
5
6
  jarvis/jarvis_agent/main.py,sha256=c6bQe-8LXvW2-NBn9Rn_yPYdrwnkJ8KQaSFY2cPvkxw,2775
6
7
  jarvis/jarvis_agent/output_handler.py,sha256=P7oWpXBGFfOsWq7cIhS_z9crkQ19ES7qU5pM92KKjAs,1172
7
8
  jarvis/jarvis_agent/shell_input_handler.py,sha256=zVaKNthIHJh1j4g8_-d3w5ahNH9aH-ZNRSOourQpHR4,1328
8
9
  jarvis/jarvis_code_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- jarvis/jarvis_code_agent/code_agent.py,sha256=aNAjj7EqabaMQg8Ngf26TMLpQJpBVceE-_FPTpXbRs8,15389
10
+ jarvis/jarvis_code_agent/code_agent.py,sha256=fr2_hqFuTyLqVB_huLgbz-UB0GIJM4YIQoi6s_X-WzE,15466
10
11
  jarvis/jarvis_code_agent/lint.py,sha256=j1kS-wFYigmkXyxOuUiaJ9cknYkraikQSSf51VWturE,4038
11
12
  jarvis/jarvis_code_analysis/code_review.py,sha256=jwvGYwwTM7UG6cESw6I7vCp6FimESKvowCbz6u28ikE,31439
12
13
  jarvis/jarvis_code_analysis/checklists/__init__.py,sha256=LIXAYa1sW3l7foP6kohLWnE98I_EQ0T7z5bYKHq6rJA,78
@@ -45,13 +46,13 @@ jarvis/jarvis_methodology/main.py,sha256=NMtd6DRn-Q8NcYtQ3qgTKUp9RW0cDGJod8ZXebl
45
46
  jarvis/jarvis_multi_agent/__init__.py,sha256=sDd3sK88dS7_qAz2ywIAaEWdQ4iRVCiuBu2rQQmrKbU,4512
46
47
  jarvis/jarvis_multi_agent/main.py,sha256=h7VUSwoPrES0XTK8z5kt3XLX1mmcm8UEuFEHQOUWPH4,1696
47
48
  jarvis/jarvis_platform/__init__.py,sha256=WLQHSiE87PPket2M50_hHzjdMIgPIBx2VF8JfB_NNRk,105
48
- jarvis/jarvis_platform/base.py,sha256=HsqEtgVF_ZUdXUgeod6IRDWPRggKkovkGCJxOwEtRe8,7759
49
+ jarvis/jarvis_platform/base.py,sha256=dv_O3IcUTRhi_jfoRIG2g_FwnYpFRZo8g8tWIanOgVo,7884
49
50
  jarvis/jarvis_platform/human.py,sha256=r8Vlltp_LirJZeZh1Mmi30iJr9tl1JaNFoqthSRHF6o,2826
50
51
  jarvis/jarvis_platform/kimi.py,sha256=uIpSWQ3MqDBYYMROeXin_YqM2LFrovMICKpwLWO1ODo,12784
51
52
  jarvis/jarvis_platform/openai.py,sha256=uEjBikfFj7kp5wondLvOx4WdkmTX0aqF6kixxAufcHg,4806
52
53
  jarvis/jarvis_platform/registry.py,sha256=Sz4ADAaxuufpAQG0KSQZuL1yALzH-aF3FDapkNn5foE,8107
53
54
  jarvis/jarvis_platform/tongyi.py,sha256=zu1bE-LdF_Sohwb5FxoIkDOTI0Lv5G_XhuSOPI20Gf8,21190
54
- jarvis/jarvis_platform/yuanbao.py,sha256=nzdizTNM1d9OWfxkAZjBkhA_hllNPekpcv2vhJiPUB0,21446
55
+ jarvis/jarvis_platform/yuanbao.py,sha256=_lJFnxu5ZK3ihtG8JW4bWDu2VGVCZPm19CdmU4vqAS8,21446
55
56
  jarvis/jarvis_platform_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
56
57
  jarvis/jarvis_platform_manager/main.py,sha256=BdBH2tPzq7p9Mvii7abu6M7uj4lfG05gwjYfMqU5HUA,29567
57
58
  jarvis/jarvis_smart_shell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -63,7 +64,7 @@ jarvis/jarvis_tools/chdir.py,sha256=BlRvggcOeBM7UwHGL8U8hjuNm_WGkH5BdhxmPVJXejw,
63
64
  jarvis/jarvis_tools/code_plan.py,sha256=XRXHdkN4O1A4qbShLa4VBHFdRl6YdyMgZxbPPotrqNs,7358
64
65
  jarvis/jarvis_tools/create_code_agent.py,sha256=wls2B2iLrIEeG9kSWnJHv1wQ4TNqdD9T5OPoAyN53N0,3384
65
66
  jarvis/jarvis_tools/create_sub_agent.py,sha256=ilnApbz7ptNz5o3IrhBTTiNMJGtdeUmkpYL2BFqyI6c,2874
66
- jarvis/jarvis_tools/edit_file.py,sha256=Awe_2zZxRl_2u81pu4VKb9Rom4xzbYpZV07_s7BEhLM,18747
67
+ jarvis/jarvis_tools/edit_file.py,sha256=lXF4fetuXtIoLfxQyxHR3QqxvNOVN4oEFbafxUvyZDw,11517
67
68
  jarvis/jarvis_tools/execute_script.py,sha256=gMarE5yCCSPU6Dp6HlcL2KT-2xCzR-1p-oQNlYOJK58,6157
68
69
  jarvis/jarvis_tools/file_analyzer.py,sha256=SvNLve5AL2d3oQsWSoHouIsAsYanksBnl5RCCsrw2PQ,4604
69
70
  jarvis/jarvis_tools/file_operation.py,sha256=b488jf0_l7aP0RkntaNE9WqZXtbdrQTJdmWawJbfC-A,9529
@@ -71,7 +72,7 @@ jarvis/jarvis_tools/generate_new_tool.py,sha256=2YAs8DC7fJnxOkjSmhmSAwqSpBlicVhY
71
72
  jarvis/jarvis_tools/methodology.py,sha256=_K4GIDUodGEma3SvNRo7Qs5rliijgNespVLyAPN35JU,5233
72
73
  jarvis/jarvis_tools/read_code.py,sha256=6mQ3KNGc43FTiLFxMbAN28FTSVhVfkFJ6mob4heXBNM,6663
73
74
  jarvis/jarvis_tools/read_webpage.py,sha256=NmDUboVZd4CGHBPRFK6dp3uqVhuGopW1bOi3TcaLDF4,2092
74
- jarvis/jarvis_tools/registry.py,sha256=ZuD6x4znAGwB-Z7-43f-SAlrFPIVaLiHqzpZp6zc5r4,25729
75
+ jarvis/jarvis_tools/registry.py,sha256=0SdgBi-b1qnd3QX0VvQy7UVU7Pq9BZGYLiAe7t0DjpQ,25690
75
76
  jarvis/jarvis_tools/rewrite_file.py,sha256=eG_WKg6cVAXmuGwUqlWkcuyay5S8DOzEi8vZCmX3O8w,7255
76
77
  jarvis/jarvis_tools/search_web.py,sha256=C-TuBFy25WOSiwRCe7YiL0hnMx0DuHj6upHO5OtkHTA,925
77
78
  jarvis/jarvis_tools/virtual_tty.py,sha256=KKr3jpvQWWMPr2o40hlmN6fuXJCN8H4_ma5QU40Citc,16089
@@ -90,9 +91,9 @@ jarvis/jarvis_utils/methodology.py,sha256=MhPrMxMqElyAn54BDfpQdUqrRr7IbSlrLvAI39
90
91
  jarvis/jarvis_utils/output.py,sha256=PRCgudPOB8gMEP3u-g0FGD2c6tBgJhLXUMqNPglfjV8,10813
91
92
  jarvis/jarvis_utils/tag.py,sha256=f211opbbbTcSyzCDwuIK_oCnKhXPNK-RknYyGzY1yD0,431
92
93
  jarvis/jarvis_utils/utils.py,sha256=RYFQx7OkOu3fe_QhS-5jx7O06k_58X14S-9PFJgwOa4,12178
93
- jarvis_ai_assistant-0.1.200.dist-info/licenses/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
94
- jarvis_ai_assistant-0.1.200.dist-info/METADATA,sha256=ZCSRI6f3QMXnALhhPRM0ZjlnWQUYn0AhoqbtsA0xzRk,20215
95
- jarvis_ai_assistant-0.1.200.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
96
- jarvis_ai_assistant-0.1.200.dist-info/entry_points.txt,sha256=Gy3DOP1PYLMK0GCj4rrP_9lkOyBQ39EK_lKGUSwn41E,869
97
- jarvis_ai_assistant-0.1.200.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
98
- jarvis_ai_assistant-0.1.200.dist-info/RECORD,,
94
+ jarvis_ai_assistant-0.1.202.dist-info/licenses/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
95
+ jarvis_ai_assistant-0.1.202.dist-info/METADATA,sha256=7i5-nQ4XDVbIBrfSebmIaqMOKN6GzZot2a4pJ1OJ_AQ,20215
96
+ jarvis_ai_assistant-0.1.202.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
97
+ jarvis_ai_assistant-0.1.202.dist-info/entry_points.txt,sha256=Gy3DOP1PYLMK0GCj4rrP_9lkOyBQ39EK_lKGUSwn41E,869
98
+ jarvis_ai_assistant-0.1.202.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
99
+ jarvis_ai_assistant-0.1.202.dist-info/RECORD,,