jarvis-ai-assistant 0.1.134__py3-none-any.whl → 0.1.138__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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +201 -79
- jarvis/jarvis_agent/builtin_input_handler.py +16 -6
- jarvis/jarvis_agent/file_input_handler.py +9 -9
- jarvis/jarvis_agent/jarvis.py +10 -10
- jarvis/jarvis_agent/main.py +12 -11
- jarvis/jarvis_agent/output_handler.py +3 -3
- jarvis/jarvis_agent/patch.py +86 -62
- jarvis/jarvis_agent/shell_input_handler.py +5 -3
- jarvis/jarvis_code_agent/code_agent.py +134 -99
- jarvis/jarvis_code_agent/file_select.py +24 -24
- jarvis/jarvis_dev/main.py +45 -51
- jarvis/jarvis_git_details/__init__.py +0 -0
- jarvis/jarvis_git_details/main.py +179 -0
- jarvis/jarvis_git_squash/main.py +7 -7
- jarvis/jarvis_lsp/base.py +11 -11
- jarvis/jarvis_lsp/cpp.py +14 -14
- jarvis/jarvis_lsp/go.py +13 -13
- jarvis/jarvis_lsp/python.py +8 -8
- jarvis/jarvis_lsp/registry.py +21 -21
- jarvis/jarvis_lsp/rust.py +15 -15
- jarvis/jarvis_methodology/main.py +101 -0
- jarvis/jarvis_multi_agent/__init__.py +11 -11
- jarvis/jarvis_multi_agent/main.py +6 -6
- jarvis/jarvis_platform/__init__.py +1 -1
- jarvis/jarvis_platform/ai8.py +67 -89
- jarvis/jarvis_platform/base.py +14 -13
- jarvis/jarvis_platform/kimi.py +25 -28
- jarvis/jarvis_platform/ollama.py +24 -26
- jarvis/jarvis_platform/openai.py +15 -19
- jarvis/jarvis_platform/oyi.py +48 -50
- jarvis/jarvis_platform/registry.py +27 -28
- jarvis/jarvis_platform/yuanbao.py +38 -42
- jarvis/jarvis_platform_manager/main.py +81 -81
- jarvis/jarvis_platform_manager/openai_test.py +21 -21
- jarvis/jarvis_rag/file_processors.py +18 -18
- jarvis/jarvis_rag/main.py +261 -277
- jarvis/jarvis_smart_shell/main.py +12 -12
- jarvis/jarvis_tools/ask_codebase.py +28 -28
- jarvis/jarvis_tools/ask_user.py +8 -8
- jarvis/jarvis_tools/base.py +4 -4
- jarvis/jarvis_tools/chdir.py +9 -9
- jarvis/jarvis_tools/code_review.py +19 -19
- jarvis/jarvis_tools/create_code_agent.py +15 -15
- jarvis/jarvis_tools/execute_python_script.py +3 -3
- jarvis/jarvis_tools/execute_shell.py +11 -11
- jarvis/jarvis_tools/execute_shell_script.py +3 -3
- jarvis/jarvis_tools/file_analyzer.py +29 -29
- jarvis/jarvis_tools/file_operation.py +22 -20
- jarvis/jarvis_tools/find_caller.py +25 -25
- jarvis/jarvis_tools/find_methodolopy.py +65 -0
- jarvis/jarvis_tools/find_symbol.py +24 -24
- jarvis/jarvis_tools/function_analyzer.py +27 -27
- jarvis/jarvis_tools/git_commiter.py +9 -9
- jarvis/jarvis_tools/lsp_get_diagnostics.py +19 -19
- jarvis/jarvis_tools/methodology.py +23 -62
- jarvis/jarvis_tools/project_analyzer.py +29 -33
- jarvis/jarvis_tools/rag.py +15 -15
- jarvis/jarvis_tools/read_code.py +24 -22
- jarvis/jarvis_tools/read_webpage.py +31 -31
- jarvis/jarvis_tools/registry.py +72 -52
- jarvis/jarvis_tools/tool_generator.py +18 -18
- jarvis/jarvis_utils/config.py +23 -23
- jarvis/jarvis_utils/embedding.py +83 -83
- jarvis/jarvis_utils/git_utils.py +20 -20
- jarvis/jarvis_utils/globals.py +18 -6
- jarvis/jarvis_utils/input.py +10 -9
- jarvis/jarvis_utils/methodology.py +140 -136
- jarvis/jarvis_utils/output.py +11 -11
- jarvis/jarvis_utils/utils.py +22 -70
- {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/METADATA +1 -1
- jarvis_ai_assistant-0.1.138.dist-info/RECORD +85 -0
- {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/entry_points.txt +2 -0
- jarvis/jarvis_tools/select_code_files.py +0 -62
- jarvis_ai_assistant-0.1.134.dist-info/RECORD +0 -82
- {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.134.dist-info → jarvis_ai_assistant-0.1.138.dist-info}/top_level.txt +0 -0
jarvis/jarvis_agent/main.py
CHANGED
|
@@ -10,17 +10,17 @@ from jarvis.jarvis_utils.utils import init_env
|
|
|
10
10
|
|
|
11
11
|
def load_config(config_path: str) -> dict:
|
|
12
12
|
"""Load configuration from YAML file
|
|
13
|
-
|
|
13
|
+
|
|
14
14
|
Args:
|
|
15
15
|
config_path: Path to the YAML configuration file
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
Returns:
|
|
18
18
|
dict: Configuration dictionary
|
|
19
19
|
"""
|
|
20
20
|
if not os.path.exists(config_path):
|
|
21
21
|
PrettyOutput.print(f"配置文件 {config_path} 不存在,使用默认配置", OutputType.WARNING)
|
|
22
22
|
return {}
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
with open(config_path, 'r', encoding='utf-8', errors="ignore") as f:
|
|
25
25
|
try:
|
|
26
26
|
config = yaml.safe_load(f)
|
|
@@ -33,42 +33,43 @@ def main():
|
|
|
33
33
|
"""Main entry point for Jarvis agent"""
|
|
34
34
|
# Initialize environment
|
|
35
35
|
init_env()
|
|
36
|
-
|
|
36
|
+
|
|
37
37
|
# Set up argument parser
|
|
38
38
|
parser = argparse.ArgumentParser(description='Jarvis AI assistant')
|
|
39
|
-
parser.add_argument('-c', '--config', type=str, required=True,
|
|
39
|
+
parser.add_argument('-c', '--config', type=str, required=True,
|
|
40
40
|
help='Path to the YAML configuration file')
|
|
41
41
|
parser.add_argument('-t', '--task', type=str,
|
|
42
42
|
help='Initial task to execute')
|
|
43
43
|
args = parser.parse_args()
|
|
44
|
-
|
|
44
|
+
|
|
45
45
|
# Load configuration
|
|
46
46
|
config = load_config(args.config)
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
# Create and run agent
|
|
49
49
|
try:
|
|
50
50
|
agent = Agent(**config)
|
|
51
|
-
|
|
51
|
+
|
|
52
52
|
# Run agent with initial task if specified
|
|
53
53
|
if args.task:
|
|
54
54
|
PrettyOutput.print(f"执行初始任务: {args.task}", OutputType.INFO)
|
|
55
55
|
agent.run(args.task)
|
|
56
56
|
return 0
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
# Enter interactive mode if no initial task
|
|
59
59
|
while True:
|
|
60
60
|
try:
|
|
61
61
|
user_input = get_multiline_input("请输入你的任务(输入空行退出):")
|
|
62
62
|
if not user_input:
|
|
63
63
|
break
|
|
64
|
+
agent.set_addon_prompt("如果有必要,请先指定出行动计划,然后根据计划一步步执行")
|
|
64
65
|
agent.run(user_input)
|
|
65
66
|
except Exception as e:
|
|
66
67
|
PrettyOutput.print(f"错误: {str(e)}", OutputType.ERROR)
|
|
67
|
-
|
|
68
|
+
|
|
68
69
|
except Exception as e:
|
|
69
70
|
PrettyOutput.print(f"初始化错误: {str(e)}", OutputType.ERROR)
|
|
70
71
|
return 1
|
|
71
|
-
|
|
72
|
+
|
|
72
73
|
return 0
|
|
73
74
|
|
|
74
75
|
if __name__ == "__main__":
|
|
@@ -7,13 +7,13 @@ from typing import Any, Tuple
|
|
|
7
7
|
|
|
8
8
|
class OutputHandler(ABC):
|
|
9
9
|
@abstractmethod
|
|
10
|
-
def handle(self, response: str) -> Tuple[bool, Any]:
|
|
10
|
+
def handle(self, response: str, agent: Any) -> Tuple[bool, Any]:
|
|
11
11
|
pass
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
@abstractmethod
|
|
14
14
|
def can_handle(self, response: str) -> bool:
|
|
15
15
|
pass
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
@abstractmethod
|
|
18
18
|
def prompt(self) -> str:
|
|
19
19
|
pass
|
jarvis/jarvis_agent/patch.py
CHANGED
|
@@ -11,21 +11,24 @@ from jarvis.jarvis_tools.git_commiter import GitCommitTool
|
|
|
11
11
|
from jarvis.jarvis_tools.file_operation import FileOperationTool
|
|
12
12
|
from jarvis.jarvis_utils.config import is_confirm_before_apply_patch
|
|
13
13
|
from jarvis.jarvis_utils.git_utils import get_commits_between, get_latest_commit_hash
|
|
14
|
+
from jarvis.jarvis_utils.globals import add_read_file_record, has_read_file
|
|
14
15
|
from jarvis.jarvis_utils.input import get_multiline_input
|
|
15
16
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
|
16
17
|
from jarvis.jarvis_utils.utils import ct, ot, get_file_line_count, user_confirm
|
|
17
18
|
|
|
19
|
+
|
|
18
20
|
class PatchOutputHandler(OutputHandler):
|
|
19
21
|
def name(self) -> str:
|
|
20
22
|
return "PATCH"
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
|
|
24
|
+
def handle(self, response: str, agent: Any) -> Tuple[bool, Any]:
|
|
25
|
+
return False, apply_patch(response, agent)
|
|
26
|
+
|
|
24
27
|
def can_handle(self, response: str) -> bool:
|
|
25
28
|
if _parse_patch(response):
|
|
26
29
|
return True
|
|
27
30
|
return False
|
|
28
|
-
|
|
31
|
+
|
|
29
32
|
def prompt(self) -> str:
|
|
30
33
|
return f"""
|
|
31
34
|
# 代码补丁规范
|
|
@@ -44,18 +47,17 @@ Reason: [修改原因]
|
|
|
44
47
|
```
|
|
45
48
|
|
|
46
49
|
## 核心原则
|
|
47
|
-
1.
|
|
48
|
-
2.
|
|
49
|
-
3.
|
|
50
|
-
4. **格式严格保持**:
|
|
50
|
+
1. **精准修改**:只显示需要修改的代码部分,不需要展示整个文件内容
|
|
51
|
+
2. **最小补丁原则**:始终生成最小范围的补丁,只包含必要的上下文和实际修改
|
|
52
|
+
3. **格式严格保持**:
|
|
51
53
|
- 严格保持原始代码的缩进方式(空格或制表符)
|
|
52
54
|
- 保持原始代码的空行数量和位置
|
|
53
55
|
- 保持原始代码的行尾空格处理方式
|
|
54
56
|
- 不改变原始代码的换行风格
|
|
55
|
-
|
|
57
|
+
4. **新旧区分**:
|
|
56
58
|
- 对于新文件:提供完整的代码内容
|
|
57
|
-
-
|
|
58
|
-
|
|
59
|
+
- 对于现有文件:只提供修改部分,不要提供整个文件
|
|
60
|
+
5. **理由说明**:每个补丁必须包含清晰的修改理由,解释为什么需要此更改
|
|
59
61
|
|
|
60
62
|
## 格式兼容性要求
|
|
61
63
|
1. **缩进一致性**:
|
|
@@ -86,17 +88,21 @@ def add(a, b):
|
|
|
86
88
|
|
|
87
89
|
## 最佳实践
|
|
88
90
|
- 每个补丁专注于单一职责的修改
|
|
89
|
-
-
|
|
91
|
+
- 避免包含过多无关代码
|
|
90
92
|
- 确保修改理由清晰明确,便于理解变更目的
|
|
91
93
|
- 保持代码风格一致性,遵循项目现有的编码规范
|
|
92
94
|
- 在修改前仔细分析原代码的格式风格,确保补丁与之完全兼容
|
|
93
95
|
- 绝不提供完整文件内容,除非是新建文件
|
|
96
|
+
- 每个文件的修改是独立的,不能出现“参照xxx文件的修改”这样的描述
|
|
97
|
+
- 不要出现未实现的代码,如:TODO
|
|
94
98
|
"""
|
|
95
99
|
|
|
100
|
+
|
|
96
101
|
def _parse_patch(patch_str: str) -> Dict[str, str]:
|
|
97
102
|
"""解析新的上下文补丁格式"""
|
|
98
103
|
result = {}
|
|
99
|
-
patches = re.findall(ot("PATCH")+r'\n?(.*?)\n?'+
|
|
104
|
+
patches = re.findall(ot("PATCH")+r'\n?(.*?)\n?' +
|
|
105
|
+
ct("PATCH"), patch_str, re.DOTALL)
|
|
100
106
|
if patches:
|
|
101
107
|
for patch in patches:
|
|
102
108
|
first_line = patch.splitlines()[0]
|
|
@@ -111,7 +117,8 @@ def _parse_patch(patch_str: str) -> Dict[str, str]:
|
|
|
111
117
|
result[filepath] += "\n\n" + patch
|
|
112
118
|
return result
|
|
113
119
|
|
|
114
|
-
|
|
120
|
+
|
|
121
|
+
def apply_patch(output_str: str, agent: Any) -> str:
|
|
115
122
|
"""Apply patches to files"""
|
|
116
123
|
with yaspin(text="正在应用补丁...", color="cyan") as spinner:
|
|
117
124
|
try:
|
|
@@ -119,12 +126,17 @@ def apply_patch(output_str: str) -> str:
|
|
|
119
126
|
except Exception as e:
|
|
120
127
|
PrettyOutput.print(f"解析补丁失败: {str(e)}", OutputType.ERROR)
|
|
121
128
|
return ""
|
|
122
|
-
|
|
129
|
+
|
|
123
130
|
# 获取当前提交hash作为起始点
|
|
124
|
-
spinner.text= "开始获取当前提交hash..."
|
|
131
|
+
spinner.text = "开始获取当前提交hash..."
|
|
125
132
|
start_hash = get_latest_commit_hash()
|
|
126
133
|
spinner.write("✅ 当前提交hash获取完成")
|
|
127
|
-
|
|
134
|
+
|
|
135
|
+
not_read_file = [f for f in patches.keys() if not has_read_file(f)]
|
|
136
|
+
if not_read_file:
|
|
137
|
+
yaspin.write(f"❌ 以下文件未读取: {not_read_file},应用补丁存在风险,将先读取文件后再生成补丁", color="red")
|
|
138
|
+
return f"以下文件未读取: {not_read_file},应用补丁存在风险,请先读取文件后再生成补丁"
|
|
139
|
+
|
|
128
140
|
# 按文件逐个处理
|
|
129
141
|
for filepath, patch_content in patches.items():
|
|
130
142
|
try:
|
|
@@ -135,6 +147,7 @@ def apply_patch(output_str: str) -> str:
|
|
|
135
147
|
os.makedirs(os.path.dirname(filepath), exist_ok=True)
|
|
136
148
|
open(filepath, 'w', encoding='utf-8').close()
|
|
137
149
|
spinner.write("✅ 文件创建完成")
|
|
150
|
+
add_read_file_record(filepath)
|
|
138
151
|
with spinner.hidden():
|
|
139
152
|
while not handle_code_operation(filepath, patch_content):
|
|
140
153
|
if user_confirm("补丁应用失败,是否重试?", default=True):
|
|
@@ -146,7 +159,7 @@ def apply_patch(output_str: str) -> str:
|
|
|
146
159
|
spinner.text = f"文件 {filepath} 处理失败: {str(e)}, 回滚文件"
|
|
147
160
|
revert_file(filepath) # 回滚单个文件
|
|
148
161
|
spinner.write(f"✅ 文件 {filepath} 回滚完成")
|
|
149
|
-
|
|
162
|
+
|
|
150
163
|
final_ret = ""
|
|
151
164
|
diff = get_diff()
|
|
152
165
|
if diff:
|
|
@@ -157,30 +170,29 @@ def apply_patch(output_str: str) -> str:
|
|
|
157
170
|
# 获取提交信息
|
|
158
171
|
end_hash = get_latest_commit_hash()
|
|
159
172
|
commits = get_commits_between(start_hash, end_hash)
|
|
160
|
-
|
|
173
|
+
|
|
161
174
|
# 添加提交信息到final_ret
|
|
162
175
|
if commits:
|
|
163
176
|
final_ret += "✅ 补丁已应用\n"
|
|
164
177
|
final_ret += "# 提交信息:\n"
|
|
165
178
|
for commit_hash, commit_message in commits:
|
|
166
179
|
final_ret += f"- {commit_hash[:7]}: {commit_message}\n"
|
|
167
|
-
|
|
180
|
+
|
|
168
181
|
final_ret += f"# 应用补丁:\n```diff\n{diff}\n```"
|
|
169
|
-
|
|
182
|
+
|
|
170
183
|
# 增加代码变更分析和错误提示
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
+
|
|
185
|
+
addon_prompt = "1. 请调用静态检查工具(如有)检查以上变更是否引入了潜在错误\n"
|
|
186
|
+
addon_prompt += "2. 如果发现代码错误,请立即提出修复方案\n"
|
|
187
|
+
addon_prompt += "3. 如果错误并非是本次修改引入,要询问用户是否需要立即修复\n"
|
|
188
|
+
addon_prompt += "\n\n"
|
|
189
|
+
addon_prompt += "如果没有问题,请继续进行下一步修改\n"
|
|
190
|
+
addon_prompt += f"如果用户的需求已经完成,请终止,不要输出新的 {ot('PATCH')},不要实现任何超出用户需求外的内容\n"
|
|
191
|
+
addon_prompt += "如果有任何信息不清楚,调用工具获取信息\n"
|
|
192
|
+
addon_prompt += "每次响应必须且只能包含一个操作\n"
|
|
193
|
+
|
|
194
|
+
agent.set_addon_prompt(addon_prompt)
|
|
195
|
+
|
|
184
196
|
else:
|
|
185
197
|
final_ret += "✅ 补丁已应用(没有新的提交)"
|
|
186
198
|
else:
|
|
@@ -199,8 +211,10 @@ def apply_patch(output_str: str) -> str:
|
|
|
199
211
|
custom_reply = get_multiline_input("请输入自定义回复")
|
|
200
212
|
if not custom_reply.strip(): # 如果自定义回复为空,返回空字符串
|
|
201
213
|
return ""
|
|
202
|
-
|
|
203
|
-
|
|
214
|
+
agent.set_addon_prompt(custom_reply)
|
|
215
|
+
return final_ret
|
|
216
|
+
|
|
217
|
+
|
|
204
218
|
def revert_file(filepath: str):
|
|
205
219
|
"""增强版git恢复,处理新文件"""
|
|
206
220
|
import subprocess
|
|
@@ -211,7 +225,8 @@ def revert_file(filepath: str):
|
|
|
211
225
|
stderr=subprocess.PIPE
|
|
212
226
|
)
|
|
213
227
|
if result.returncode == 0:
|
|
214
|
-
subprocess.run(['git', 'checkout', 'HEAD',
|
|
228
|
+
subprocess.run(['git', 'checkout', 'HEAD',
|
|
229
|
+
'--', filepath], check=True)
|
|
215
230
|
else:
|
|
216
231
|
if os.path.exists(filepath):
|
|
217
232
|
os.remove(filepath)
|
|
@@ -219,11 +234,15 @@ def revert_file(filepath: str):
|
|
|
219
234
|
except subprocess.CalledProcessError as e:
|
|
220
235
|
PrettyOutput.print(f"恢复文件失败: {str(e)}", OutputType.ERROR)
|
|
221
236
|
# 修改后的恢复函数
|
|
237
|
+
|
|
238
|
+
|
|
222
239
|
def revert_change():
|
|
223
240
|
import subprocess
|
|
224
241
|
subprocess.run(['git', 'reset', '--hard', 'HEAD'], check=True)
|
|
225
242
|
subprocess.run(['git', 'clean', '-fd'], check=True)
|
|
226
243
|
# 修改后的获取差异函数
|
|
244
|
+
|
|
245
|
+
|
|
227
246
|
def get_diff() -> str:
|
|
228
247
|
"""使用git获取暂存区差异"""
|
|
229
248
|
import subprocess
|
|
@@ -241,9 +260,10 @@ def get_diff() -> str:
|
|
|
241
260
|
except subprocess.CalledProcessError as e:
|
|
242
261
|
return f"获取差异失败: {str(e)}"
|
|
243
262
|
|
|
244
|
-
|
|
263
|
+
|
|
264
|
+
def handle_commit_workflow() -> bool:
|
|
245
265
|
"""Handle the git commit workflow and return the commit details.
|
|
246
|
-
|
|
266
|
+
|
|
247
267
|
Returns:
|
|
248
268
|
tuple[bool, str, str]: (continue_execution, commit_id, commit_message)
|
|
249
269
|
"""
|
|
@@ -257,7 +277,7 @@ def handle_commit_workflow()->bool:
|
|
|
257
277
|
|
|
258
278
|
def handle_code_operation(filepath: str, patch_content: str) -> bool:
|
|
259
279
|
"""处理代码操作"""
|
|
260
|
-
if get_file_line_count(filepath) <
|
|
280
|
+
if get_file_line_count(filepath) < 5:
|
|
261
281
|
return handle_small_code_operation(filepath, patch_content)
|
|
262
282
|
else:
|
|
263
283
|
retry_count = 5
|
|
@@ -273,11 +293,12 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
|
|
|
273
293
|
with yaspin(text=f"正在修改文件 {filepath}...", color="cyan") as spinner:
|
|
274
294
|
try:
|
|
275
295
|
with spinner.hidden():
|
|
276
|
-
old_file_content = FileOperationTool().execute(
|
|
296
|
+
old_file_content = FileOperationTool().execute(
|
|
297
|
+
{"operation": "read", "files": [{"path": filepath}]})
|
|
277
298
|
if not old_file_content["success"]:
|
|
278
299
|
spinner.write("❌ 文件读取失败")
|
|
279
300
|
return False
|
|
280
|
-
|
|
301
|
+
|
|
281
302
|
prompt = f"""
|
|
282
303
|
# 代码合并专家指南
|
|
283
304
|
|
|
@@ -314,15 +335,16 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
|
|
|
314
335
|
{ct("MERGED_CODE")}
|
|
315
336
|
"""
|
|
316
337
|
model = PlatformRegistry().get_normal_platform()
|
|
317
|
-
model.set_suppress_output(
|
|
338
|
+
model.set_suppress_output(False)
|
|
318
339
|
count = 30
|
|
319
340
|
start_line = -1
|
|
320
341
|
end_line = -1
|
|
321
342
|
code = []
|
|
322
343
|
finished = False
|
|
323
|
-
while count>0:
|
|
344
|
+
while count > 0:
|
|
324
345
|
count -= 1
|
|
325
|
-
|
|
346
|
+
with spinner.hidden():
|
|
347
|
+
response = model.chat_until_success(prompt).splitlines()
|
|
326
348
|
try:
|
|
327
349
|
start_line = response.index(ot("MERGED_CODE")) + 1
|
|
328
350
|
try:
|
|
@@ -333,7 +355,7 @@ def handle_small_code_operation(filepath: str, patch_content: str) -> bool:
|
|
|
333
355
|
except:
|
|
334
356
|
pass
|
|
335
357
|
|
|
336
|
-
try:
|
|
358
|
+
try:
|
|
337
359
|
response.index(ot("!!!FINISHED!!!"))
|
|
338
360
|
finished = True
|
|
339
361
|
break
|
|
@@ -375,14 +397,15 @@ def handle_large_code_operation(filepath: str, patch_content: str, model: BasePl
|
|
|
375
397
|
with yaspin(text=f"正在处理文件 {filepath}...", color="cyan") as spinner:
|
|
376
398
|
try:
|
|
377
399
|
# 读取原始文件内容
|
|
378
|
-
old_file_content = FileOperationTool().execute(
|
|
400
|
+
old_file_content = FileOperationTool().execute(
|
|
401
|
+
{"operation": "read", "files": [{"path": filepath}]})
|
|
379
402
|
if not old_file_content["success"]:
|
|
380
403
|
spinner.text = "文件读取失败"
|
|
381
404
|
spinner.fail("❌")
|
|
382
405
|
return False
|
|
383
|
-
|
|
384
|
-
model.set_suppress_output(
|
|
385
|
-
|
|
406
|
+
|
|
407
|
+
model.set_suppress_output(False)
|
|
408
|
+
|
|
386
409
|
prompt = f"""
|
|
387
410
|
# 代码补丁生成专家指南
|
|
388
411
|
|
|
@@ -435,20 +458,21 @@ def handle_large_code_operation(filepath: str, patch_content: str, model: BasePl
|
|
|
435
458
|
{ct("DIFF")}
|
|
436
459
|
"""
|
|
437
460
|
# 获取补丁内容
|
|
438
|
-
|
|
439
|
-
|
|
461
|
+
with spinner.hidden():
|
|
462
|
+
response = model.chat_until_success(prompt)
|
|
463
|
+
|
|
440
464
|
# 解析差异化补丁
|
|
441
|
-
diff_blocks = re.finditer(ot("DIFF")+r'\s*>{4,} SEARCH\n?(.*?)\n?={4,}\n?(.*?)\s*<{4,} REPLACE\n?'+ct("DIFF"),
|
|
442
|
-
|
|
443
|
-
|
|
465
|
+
diff_blocks = re.finditer(ot("DIFF")+r'\s*>{4,} SEARCH\n?(.*?)\n?={4,}\n?(.*?)\s*<{4,} REPLACE\n?'+ct("DIFF"),
|
|
466
|
+
response, re.DOTALL)
|
|
467
|
+
|
|
444
468
|
# 读取原始文件内容
|
|
445
469
|
with open(filepath, 'r', encoding='utf-8', errors="ignore") as f:
|
|
446
470
|
file_content = f.read()
|
|
447
|
-
|
|
471
|
+
|
|
448
472
|
# 应用所有差异化补丁
|
|
449
473
|
modified_content = file_content
|
|
450
474
|
patch_count = 0
|
|
451
|
-
|
|
475
|
+
|
|
452
476
|
for match in diff_blocks:
|
|
453
477
|
search_text = match.group(1).strip()
|
|
454
478
|
replace_text = match.group(2).strip()
|
|
@@ -461,23 +485,23 @@ def handle_large_code_operation(filepath: str, patch_content: str, model: BasePl
|
|
|
461
485
|
spinner.fail("❌")
|
|
462
486
|
return False
|
|
463
487
|
# 应用替换
|
|
464
|
-
modified_content = modified_content.replace(
|
|
488
|
+
modified_content = modified_content.replace(
|
|
489
|
+
search_text, replace_text)
|
|
465
490
|
spinner.write(f"✅ 补丁 #{patch_count} 应用成功")
|
|
466
491
|
else:
|
|
467
492
|
spinner.text = f"补丁 #{patch_count} 应用失败:无法找到匹配的代码段"
|
|
468
493
|
spinner.fail("❌")
|
|
469
494
|
return False
|
|
470
|
-
|
|
495
|
+
|
|
471
496
|
# 写入修改后的内容
|
|
472
497
|
with open(filepath, 'w', encoding='utf-8', errors="ignore") as f:
|
|
473
498
|
f.write(modified_content)
|
|
474
|
-
|
|
499
|
+
|
|
475
500
|
spinner.text = f"文件 {filepath} 修改完成,应用了 {patch_count} 个补丁"
|
|
476
501
|
spinner.ok("✅")
|
|
477
502
|
return True
|
|
478
|
-
|
|
503
|
+
|
|
479
504
|
except Exception as e:
|
|
480
505
|
spinner.text = f"文件修改失败: {str(e)}"
|
|
481
506
|
spinner.fail("❌")
|
|
482
507
|
return False
|
|
483
|
-
|
|
@@ -21,7 +21,9 @@ def shell_input_handler(user_input: str, agent: Any) -> Tuple[str, bool]:
|
|
|
21
21
|
"arguments": {
|
|
22
22
|
"script_content": script
|
|
23
23
|
}
|
|
24
|
-
})
|
|
25
|
-
|
|
24
|
+
}, agent)
|
|
25
|
+
if user_confirm("是否将执行结果反馈给Agent?", default=True):
|
|
26
|
+
return f"{user_input}\n\n用户执行以下脚本:\n{script}\n\n执行结果:\n{output}", False
|
|
27
|
+
return "", True
|
|
26
28
|
return user_input, False
|
|
27
|
-
|
|
29
|
+
|