jarvis-ai-assistant 0.1.89__py3-none-any.whl → 0.1.90__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.

Potentially problematic release.


This version of jarvis-ai-assistant might be problematic. Click here for more details.

@@ -1,390 +0,0 @@
1
- import re
2
- import os
3
- from typing import List, Tuple, Dict
4
- from jarvis.utils import OutputType, PrettyOutput
5
- from .model_utils import call_model_with_retry
6
-
7
- class PatchHandler:
8
- def __init__(self, model):
9
- self.model = model
10
-
11
- def _extract_patches(self, response: str) -> List[Tuple[str, str, str]]:
12
- """从响应中提取补丁
13
-
14
- Args:
15
- response: 模型响应内容
16
-
17
- Returns:
18
- List[Tuple[str, str, str]]: 补丁列表,每个补丁是 (格式, 文件路径, 补丁内容) 的元组
19
- """
20
- patches = []
21
-
22
- # 匹配两种格式的补丁
23
- fmt1_patches = re.finditer(r'<PATCH_FMT1>\n?(.*?)\n?</PATCH_FMT1>', response, re.DOTALL)
24
- fmt2_patches = re.finditer(r'<PATCH_FMT2>\n?(.*?)\n?</PATCH_FMT2>', response, re.DOTALL)
25
-
26
- # 处理 FMT1 格式的补丁
27
- for match in fmt1_patches:
28
- patch_content = match.group(1)
29
- if not patch_content:
30
- continue
31
-
32
- # 提取文件路径和补丁内容
33
- lines = patch_content.split('\n')
34
- file_path_match = re.search(r'> (.*)', lines[0])
35
- if not file_path_match:
36
- continue
37
-
38
- file_path = file_path_match.group(1).strip()
39
- patch_content = '\n'.join(lines[1:])
40
- patches.append(("FMT1", file_path, patch_content))
41
-
42
- # 处理 FMT2 格式的补丁
43
- for match in fmt2_patches:
44
- patch_content = match.group(1)
45
- if not patch_content:
46
- continue
47
-
48
- # 提取文件路径和补丁内容
49
- lines = patch_content.split('\n')
50
- file_path_match = re.search(r'> (.*)', lines[0])
51
- if not file_path_match:
52
- continue
53
-
54
- file_path = file_path_match.group(1).strip()
55
- patch_content = '\n'.join(lines[1:])
56
- patches.append(("FMT2", file_path, patch_content))
57
-
58
- return patches
59
-
60
- def make_patch(self, related_files: List[Dict], feature: str) -> List[Tuple[str, str, str]]:
61
- """生成修改方案"""
62
- prompt = """你是一个资深程序员,请根据需求描述,修改文件内容。
63
-
64
- 修改格式说明:
65
- 1. 第一种格式 - 完整代码块替换:
66
- <PATCH_FMT1>
67
- > path/to/file
68
- old_content
69
- @@@@@@
70
- new_content
71
- </PATCH_FMT1>
72
-
73
- 例:
74
- <PATCH_FMT1>
75
- > src/main.py
76
- def old_function():
77
- print("old code")
78
- return False
79
- @@@@@@
80
- def old_function():
81
- print("new code")
82
- return True
83
- </PATCH_FMT1>
84
-
85
- 2. 第二种格式 - 通过首尾行定位要修改的代码范围:
86
- <PATCH_FMT2>
87
- > path/to/file
88
- start_line_content
89
- end_line_content
90
- new_content
91
- ...
92
- </PATCH_FMT2>
93
-
94
- 例:
95
- <PATCH_FMT2>
96
- > src/main.py
97
- def old_function():
98
- return False
99
- def new_function():
100
- print("new code")
101
- return True
102
- </PATCH_FMT2>
103
-
104
- 例子中 `def old_function():` 是首行内容,`return False` 是尾行内容,第三行开始是新的代码内容,将替换第一行到最后一行之间的所有内容
105
-
106
- 注意事项:
107
- 1、仅输出补丁内容,不要输出任何其他内容
108
- 2、如果在大段代码中有零星修改,生成多个补丁
109
- 3、要替换的内容,一定要与文件内容完全一致,不要有任何多余或者缺失的内容
110
- 4、每个patch不超过20行,超出20行,请生成多个patch
111
- 5、务必保留原始文件的缩进和格式
112
- 6、优先使用第二种格式(PATCH_FMT2),因为它更准确地定位要修改的代码范围
113
- 7、第二种格式(PATCH_FMT2)的前两行必须完全匹配文件中要修改的代码块的首尾行
114
- 8、如果第二种格式无法准确定位到要修改的代码(比如有重复的行),请使用第一种格式(PATCH_FMT1)
115
- """
116
- # 添加文件内容到提示
117
- for i, file in enumerate(related_files):
118
- prompt += f"""\n{i}. {file["file_path"]}\n"""
119
- prompt += f"""文件内容:\n"""
120
- prompt += f"<FILE_CONTENT>\n"
121
- prompt += f'{file["file_content"]}\n'
122
- prompt += f"</FILE_CONTENT>\n"
123
-
124
- prompt += f"\n需求描述: {feature}\n"
125
-
126
- # 调用模型生成补丁
127
- success, response = call_model_with_retry(self.model, prompt)
128
- if not success:
129
- PrettyOutput.print("生成补丁失败", OutputType.ERROR)
130
- return []
131
-
132
- try:
133
- patches = self._extract_patches(response)
134
-
135
- if not patches:
136
- PrettyOutput.print("未生成任何有效补丁", OutputType.WARNING)
137
- return []
138
-
139
- PrettyOutput.print(f"生成了 {len(patches)} 个补丁", OutputType.SUCCESS)
140
- return patches
141
-
142
- except Exception as e:
143
- PrettyOutput.print(f"解析patch失败: {str(e)}", OutputType.WARNING)
144
- return []
145
-
146
- def apply_patch(self, related_files: List[Dict], patches: List[Tuple[str, str, str]]) -> Tuple[bool, str]:
147
- """应用补丁
148
-
149
- Args:
150
- related_files: 相关文件列表
151
- patches: 补丁列表,每个补丁是 (格式, 文件路径, 补丁内容) 的元组
152
-
153
- Returns:
154
- Tuple[bool, str]: (是否成功, 错误信息)
155
- """
156
- error_info = []
157
- modified_files = set()
158
-
159
- # 创建文件内容映射
160
- file_map = {file["file_path"]: file["file_content"] for file in related_files}
161
- temp_map = file_map.copy() # 创建临时映射用于尝试应用
162
-
163
- # 尝试应用所有补丁
164
- for i, (fmt, file_path, patch_content) in enumerate(patches):
165
- PrettyOutput.print(f"正在应用补丁 {i+1}/{len(patches)}", OutputType.INFO)
166
-
167
- try:
168
- # 处理文件修改
169
- if file_path not in temp_map:
170
- error_info.append(f"文件不存在: {file_path}")
171
- return False, "\n".join(error_info)
172
-
173
- current_content = temp_map[file_path]
174
-
175
- if fmt == "FMT1": # 完整代码块替换格式
176
- parts = patch_content.split("@@@@@@")
177
- if len(parts) != 2:
178
- error_info.append(f"FMT1补丁格式错误: {file_path},缺少分隔符")
179
- return False, "\n".join(error_info)
180
-
181
- old_content, new_content = parts
182
-
183
- # 处理新文件
184
- if not old_content:
185
- temp_map[file_path] = new_content
186
- modified_files.add(file_path)
187
- continue
188
-
189
- # 查找并替换代码块
190
- if old_content not in current_content:
191
- error_info.append(
192
- f"补丁应用失败: {file_path}\n"
193
- f"原因: 未找到要替换的代码\n"
194
- f"期望找到的代码:\n{old_content}\n"
195
- f"实际文件内容:\n{current_content[:200]}..."
196
- )
197
- return False, "\n".join(error_info)
198
-
199
- # 应用更改
200
- temp_map[file_path] = current_content.replace(old_content, new_content)
201
-
202
- else: # FMT2 - 首尾行定位格式
203
- lines = patch_content.splitlines()
204
- if len(lines) < 3:
205
- error_info.append(f"FMT2补丁格式错误: {file_path},行数不足")
206
- return False, "\n".join(error_info)
207
-
208
- first_line = lines[0]
209
- last_line = lines[1]
210
- new_content = '\n'.join(lines[2:])
211
-
212
- # 在文件内容中定位要替换的区域
213
- content_lines = current_content.splitlines()
214
- start_idx = -1
215
- end_idx = -1
216
-
217
- # 查找匹配的起始行和结束行
218
- for idx, line in enumerate(content_lines):
219
- if line.rstrip() == first_line.rstrip():
220
- start_idx = idx
221
- if start_idx != -1 and line.rstrip() == last_line.rstrip():
222
- end_idx = idx
223
- break
224
-
225
- if start_idx == -1 or end_idx == -1:
226
- error_info.append(
227
- f"补丁应用失败: {file_path}\n"
228
- f"原因: 未找到匹配的代码范围\n"
229
- f"起始行: {first_line}\n"
230
- f"结束行: {last_line}"
231
- )
232
- return False, "\n".join(error_info)
233
-
234
- # 替换内容
235
- content_lines[start_idx:end_idx + 1] = new_content.splitlines()
236
- temp_map[file_path] = "\n".join(content_lines)
237
-
238
- modified_files.add(file_path)
239
-
240
- except Exception as e:
241
- error_info.append(f"处理补丁时发生错误: {str(e)}")
242
- return False, "\n".join(error_info)
243
-
244
- # 所有补丁都应用成功,更新实际文件
245
- for file_path in modified_files:
246
- try:
247
- dir_path = os.path.dirname(file_path)
248
- if dir_path and not os.path.exists(dir_path):
249
- os.makedirs(dir_path, exist_ok=True)
250
-
251
- with open(file_path, "w", encoding="utf-8") as f:
252
- f.write(temp_map[file_path])
253
-
254
- PrettyOutput.print(f"成功修改文件: {file_path}", OutputType.SUCCESS)
255
-
256
- except Exception as e:
257
- error_info.append(f"写入文件失败 {file_path}: {str(e)}")
258
- return False, "\n".join(error_info)
259
-
260
- return True, ""
261
-
262
- def handle_patch_feedback(self, error_msg: str, feature: str) -> List[Tuple[str, str, str]]:
263
- """处理补丁应用失败的反馈
264
-
265
- Args:
266
- error_msg: 错误信息
267
- feature: 功能描述
268
-
269
- Returns:
270
- List[Tuple[str, str, str]]: 新的补丁列表
271
- """
272
- PrettyOutput.print("补丁应用失败,尝试重新生成", OutputType.WARNING)
273
-
274
- # 获取用户补充信息
275
- additional_info = input("\n请输入补充信息(直接回车跳过):")
276
- PrettyOutput.print(f"开始重新生成补丁", OutputType.INFO)
277
-
278
- # 构建重试提示
279
- retry_prompt = f"""补丁应用失败,请根据以下信息重新生成补丁:
280
-
281
- 错误信息:
282
- {error_msg}
283
-
284
- 原始需求:
285
- {feature}
286
-
287
- 用户补充信息:
288
- {additional_info}
289
-
290
- 请重新生成补丁,确保:
291
- 1. 代码匹配完全准确
292
- 2. 保持正确的缩进和格式
293
- 3. 避免之前的错误
294
- """
295
- success, response = call_model_with_retry(self.model, retry_prompt)
296
- if not success:
297
- return []
298
-
299
- try:
300
- patches = self._extract_patches(response)
301
- return patches
302
-
303
- except Exception as e:
304
- PrettyOutput.print(f"解析patch失败: {str(e)}", OutputType.WARNING)
305
- return []
306
-
307
- def monitor_patch_result(self, success: bool, error_msg: str) -> bool:
308
- """监控补丁应用结果
309
-
310
- Args:
311
- success: 是否成功
312
- error_msg: 错误信息
313
-
314
- Returns:
315
- bool: 是否继续尝试
316
- """
317
- if success:
318
- PrettyOutput.print("补丁应用成功", OutputType.SUCCESS)
319
- return False
320
-
321
- PrettyOutput.print(f"补丁应用失败: {error_msg}", OutputType.ERROR)
322
-
323
- # 询问是否继续尝试
324
- retry = input("\n是否重新尝试?(y/n) [y]: ").lower() or "y"
325
- return retry == "y"
326
-
327
- def handle_patch_application(self, related_files: List[Dict], feature: str) -> bool:
328
- """处理补丁应用流程
329
-
330
- Args:
331
- related_files: 相关文件列表
332
- feature: 功能描述
333
-
334
- Returns:
335
- bool: 是否成功应用补丁
336
- """
337
- max_attempts = 3
338
- attempt = 0
339
-
340
- while attempt < max_attempts:
341
- attempt += 1
342
-
343
- while True: # 在当前尝试中循环,直到成功或用户放弃
344
- # 1. 生成补丁
345
- patches = self.make_patch(related_files, feature)
346
- if not patches:
347
- return False
348
-
349
- # 2. 显示补丁内容
350
- PrettyOutput.print("\n将要应用以下补丁:", OutputType.INFO)
351
- for fmt, file_path, patch_content in patches:
352
- PrettyOutput.print(f"\n文件: {file_path}", OutputType.INFO)
353
- PrettyOutput.print(f"格式: {fmt}", OutputType.INFO)
354
- PrettyOutput.print("补丁内容:", OutputType.INFO)
355
- print(patch_content)
356
-
357
- # 3. 应用补丁
358
- success, error_msg = self.apply_patch(related_files, patches)
359
- if not success:
360
- # 4. 如果应用失败,询问是否重试
361
- should_retry = self.monitor_patch_result(success, error_msg)
362
- if not should_retry:
363
- break # 退出内层循环,尝试下一次完整的迭代
364
-
365
- # 5. 处理失败反馈
366
- patches = self.handle_patch_feedback(error_msg, feature)
367
- if not patches:
368
- return False
369
- continue # 继续当前迭代
370
-
371
- # 6. 应用成功,让用户确认修改
372
- PrettyOutput.print("\n补丁已应用,请检查修改效果。", OutputType.SUCCESS)
373
- confirm = input("\n是否保留这些修改?(y/n) [y]: ").lower() or "y"
374
- if confirm != "y":
375
- PrettyOutput.print("用户取消修改,正在回退", OutputType.WARNING)
376
- os.system("git reset --hard") # 回退所有修改
377
-
378
- # 询问是否要在当前迭代中重试
379
- retry = input("\n是否要重新生成补丁?(y/n) [y]: ").lower() or "y"
380
- if retry != "y":
381
- break # 退出内层循环,尝试下一次完整的迭代
382
- continue # 继续当前迭代
383
-
384
- return True # 用户确认修改,返回成功
385
-
386
- # 如果内层循环正常退出(非return),继续外层循环
387
- continue
388
-
389
- PrettyOutput.print(f"达到最大重试次数 ({max_attempts})", OutputType.WARNING)
390
- return False
jarvis/tools/coder.py DELETED
@@ -1,69 +0,0 @@
1
- import os
2
- from typing import Dict, Any, Optional
3
- from jarvis.jarvis_coder.main import JarvisCoder
4
- from jarvis.utils import PrettyOutput, OutputType
5
-
6
- class CoderTool:
7
- """代码修改工具"""
8
-
9
- name = "coder"
10
- description = "分析并修改现有代码,用于实现新功能、修复bug、重构代码等。能理解代码上下文并进行精确的代码编辑。"
11
- parameters = {
12
- "feature": {
13
- "type": "string",
14
- "description": "要实现的功能描述或需要修改的内容,例如:'添加日志功能'、'修复内存泄漏'、'优化性能'等",
15
- "required": True
16
- },
17
- "dir": {
18
- "type": "string",
19
- "description": "项目根目录,默认为当前目录",
20
- "required": False
21
- },
22
- "language": {
23
- "type": "string",
24
- "description": "项目的主要编程语言,默认为python",
25
- "required": False
26
- }
27
- }
28
-
29
- def __init__(self):
30
- self._coder = None
31
-
32
-
33
- def _init_coder(self, dir: Optional[str] = None, language: Optional[str] = "python") -> None:
34
- """初始化JarvisCoder实例"""
35
- if not self._coder:
36
- import os
37
- work_dir = dir or os.getcwd()
38
- self._coder = JarvisCoder(work_dir, language)
39
-
40
- def execute(self, args: Dict) -> Dict[str, Any]:
41
- """执行代码修改
42
-
43
- Args:
44
- feature: 要实现的功能描述
45
- dir: 可选,项目根目录
46
- language: 可选,编程语言
47
-
48
- Returns:
49
- Dict[str, Any]: 执行结果
50
- """
51
- feature = args.get("feature")
52
- dir = args.get("dir")
53
- language = args.get("language", "python")
54
-
55
- try:
56
- self.current_dir = os.getcwd()
57
- self._init_coder(dir, language)
58
- result = self._coder.execute(feature)
59
- return result
60
- except Exception as e:
61
- PrettyOutput.print(f"代码修改失败: {str(e)}", OutputType.ERROR)
62
- return {
63
- "success": False,
64
- "stdout": "",
65
- "stderr": f"执行失败: {str(e)}",
66
- "error": e
67
- }
68
- finally:
69
- os.chdir(self.current_dir)
@@ -1,43 +0,0 @@
1
- jarvis/__init__.py,sha256=_3MqMWMf232eVTukktJrSn128MkK9jyds0HMhYHti94,50
2
- jarvis/agent.py,sha256=_qh4mSojAgClOEz5pTiRIfRJU-5_3QGzBAU09roCjtk,19095
3
- jarvis/main.py,sha256=72od8757A3bhe0ncE38S7u-YZsAh0y50w9ozMhgqIU8,5423
4
- jarvis/utils.py,sha256=Y5zig7AgIzdWHF31qHaMUziezythfjVKjxFRtMzd1m4,10357
5
- jarvis/jarvis_codebase/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- jarvis/jarvis_codebase/main.py,sha256=hYdDwREfFW5KPbIbLZ75MFCth6VqrwxSeNfXiQavdzE,26473
7
- jarvis/jarvis_coder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
8
- jarvis/jarvis_coder/git_utils.py,sha256=tJ25kIzglGaPBqu42rZZSsXk0tpOFTiaG8q-bq4CSF0,2343
9
- jarvis/jarvis_coder/main.py,sha256=5uxexKziA5kMZLNkGPEzz8hKQvarKVflNqRcXZVWnT8,13406
10
- jarvis/jarvis_coder/model_utils.py,sha256=XXg5ZPlgRsq9K6iJb4vPoZqSiAJbAUIZffmgaLFnLCw,1104
11
- jarvis/jarvis_coder/patch_handler.py,sha256=U8I19Aq2U3LAU3FE94C3tlFQNtGYA_luwnBU7OnX7pw,15253
12
- jarvis/jarvis_rag/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- jarvis/jarvis_rag/main.py,sha256=a8TtPVCh5Xd6W1AaRFGeXvU_1hEnHQdoMElxnMuq0ew,24773
14
- jarvis/jarvis_smart_shell/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
15
- jarvis/jarvis_smart_shell/main.py,sha256=QgR1CZRcTVfC8a5hMso3onH3pFdDoniRjr0YQvY2jXQ,3809
16
- jarvis/models/__init__.py,sha256=mrOt67nselz_H1gX9wdAO4y2DY5WPXzABqJbr5Des8k,63
17
- jarvis/models/ai8.py,sha256=Te-zqUVTTsismsoS8yXljAMwxKY5BVHdfwJyzPrIYSc,12216
18
- jarvis/models/base.py,sha256=vQmgr-l0fRzVTUX4orbQZIKsXEInagjFdYv1cL9Hp7U,1511
19
- jarvis/models/kimi.py,sha256=ltYoHQDn9vfZyhZ25eUVKMBpxKKlqlw4kManozVF7uo,16135
20
- jarvis/models/ollama.py,sha256=iPCsJKZs3kXtuJyVBo6d6Ls5qBkSRgtuqF38PDFadso,6097
21
- jarvis/models/openai.py,sha256=Ns_kpJcWoQuxdKScOFlfkSGjrL2dVGzgmvcnP44sEgs,4044
22
- jarvis/models/oyi.py,sha256=vV3IMsdegxQqhS2vvG6MB648fec6bVopdNZC5xcdY_c,14678
23
- jarvis/models/registry.py,sha256=Lt8IdVBAEx_CCFtfZJPgw3nxSEjfFcqI47I-U64kIbg,8257
24
- jarvis/tools/__init__.py,sha256=7Rqyj5hBAv5cWDVr5T9ZTZASO7ssBHeQNm2_4ZARdkA,72
25
- jarvis/tools/ask_user.py,sha256=OELDt7oTCjI2G-CebvnBSxFJhqkIWcugLStU-XxouzE,1998
26
- jarvis/tools/base.py,sha256=EGRGbdfbLXDLwtyoWdvp9rlxNX7bzc20t0Vc2VkwIEY,652
27
- jarvis/tools/chdir.py,sha256=TjfPbX8yvNKgUNJEMXh3ZlVDEIse_Fo8xMoVsiK7_dA,2688
28
- jarvis/tools/codebase_qa.py,sha256=LsowsgL7HBmdBwa7zXcYi_OkwOok4qbnzYWYsuZxHtU,2413
29
- jarvis/tools/coder.py,sha256=kmotT2Klsug44S51QoSW9DzkxLzcF-XonyYAEoWZV6c,2295
30
- jarvis/tools/file_ops.py,sha256=h8g0eT9UvlJf4kt0DLXvdSsjcPj7x19lxWdDApeDfpg,3842
31
- jarvis/tools/generator.py,sha256=TB1zcw_JmRL2W9w6L4IxtrLF3gjnNw5Jj2Zrowj0eSg,5763
32
- jarvis/tools/methodology.py,sha256=UG6s5VYRcd9wrKX4cg6f7zJhet5AIcthFGMOAdevBiw,5175
33
- jarvis/tools/registry.py,sha256=AbADf8pcjHqfNoQNJkWqEuVg6zHRdryhJyDQ5w4O2sc,9177
34
- jarvis/tools/search.py,sha256=c9dXtyICdl8Lm8shNPNyIx9k67uY0rMF8xnIKu2RsnE,8787
35
- jarvis/tools/shell.py,sha256=UPKshPyOaUwTngresUw-ot1jHjQIb4wCY5nkJqa38lU,2520
36
- jarvis/tools/sub_agent.py,sha256=rEtAmSVY2ZjFOZEKr5m5wpACOQIiM9Zr_3dT92FhXYU,2621
37
- jarvis/tools/webpage.py,sha256=d3w3Jcjcu1ESciezTkz3n3Zf-rp_l91PrVoDEZnckOo,2391
38
- jarvis_ai_assistant-0.1.89.dist-info/LICENSE,sha256=AGgVgQmTqFvaztRtCAXsAMryUymB18gZif7_l2e1XOg,1063
39
- jarvis_ai_assistant-0.1.89.dist-info/METADATA,sha256=aG0WNMCBnK4O5fPKrNiq7Nj3wlsU5mx2xy1tQUSlmAU,12589
40
- jarvis_ai_assistant-0.1.89.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
41
- jarvis_ai_assistant-0.1.89.dist-info/entry_points.txt,sha256=sdmIO86MrIUepJTGyHs0i_Ho9VGf1q9YRP4RgQvGWcI,280
42
- jarvis_ai_assistant-0.1.89.dist-info/top_level.txt,sha256=1BOxyWfzOP_ZXj8rVTDnNCJ92bBGB0rwq8N1PCpoMIs,7
43
- jarvis_ai_assistant-0.1.89.dist-info/RECORD,,
File without changes