auto-coder 0.1.264__py3-none-any.whl → 0.1.266__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 auto-coder might be problematic. Click here for more details.
- {auto_coder-0.1.264.dist-info → auto_coder-0.1.266.dist-info}/METADATA +2 -2
- {auto_coder-0.1.264.dist-info → auto_coder-0.1.266.dist-info}/RECORD +53 -51
- autocoder/agent/planner.py +4 -4
- autocoder/auto_coder.py +26 -21
- autocoder/auto_coder_server.py +7 -7
- autocoder/chat_auto_coder.py +150 -49
- autocoder/commands/auto_command.py +83 -5
- autocoder/commands/tools.py +48 -50
- autocoder/common/__init__.py +0 -1
- autocoder/common/auto_coder_lang.py +43 -3
- autocoder/common/code_auto_generate.py +3 -3
- autocoder/common/code_auto_generate_diff.py +3 -6
- autocoder/common/code_auto_generate_editblock.py +3 -3
- autocoder/common/code_auto_generate_strict_diff.py +3 -3
- autocoder/common/code_auto_merge_diff.py +37 -3
- autocoder/common/code_auto_merge_editblock.py +43 -1
- autocoder/common/code_auto_merge_strict_diff.py +39 -4
- autocoder/common/command_completer.py +3 -0
- autocoder/common/command_generator.py +24 -8
- autocoder/common/command_templates.py +2 -2
- autocoder/common/conf_import_export.py +105 -0
- autocoder/common/conf_validator.py +1 -1
- autocoder/common/files.py +41 -2
- autocoder/common/image_to_page.py +11 -11
- autocoder/common/index_import_export.py +38 -18
- autocoder/common/mcp_hub.py +3 -3
- autocoder/common/mcp_server.py +2 -2
- autocoder/common/shells.py +254 -13
- autocoder/common/stats_panel.py +126 -0
- autocoder/dispacher/actions/action.py +6 -18
- autocoder/dispacher/actions/copilot.py +2 -2
- autocoder/dispacher/actions/plugins/action_regex_project.py +1 -3
- autocoder/dispacher/actions/plugins/action_translate.py +1 -1
- autocoder/index/entry.py +2 -2
- autocoder/index/filter/normal_filter.py +1 -1
- autocoder/index/filter/quick_filter.py +1 -1
- autocoder/index/index.py +5 -5
- autocoder/models.py +2 -2
- autocoder/pyproject/__init__.py +5 -5
- autocoder/rag/cache/byzer_storage_cache.py +4 -4
- autocoder/rag/cache/file_monitor_cache.py +2 -2
- autocoder/rag/cache/simple_cache.py +4 -4
- autocoder/rag/long_context_rag.py +2 -2
- autocoder/regexproject/__init__.py +3 -2
- autocoder/suffixproject/__init__.py +3 -2
- autocoder/tsproject/__init__.py +3 -2
- autocoder/utils/conversation_store.py +1 -1
- autocoder/utils/operate_config_api.py +3 -3
- autocoder/version.py +1 -1
- {auto_coder-0.1.264.dist-info → auto_coder-0.1.266.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.264.dist-info → auto_coder-0.1.266.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.264.dist-info → auto_coder-0.1.266.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.264.dist-info → auto_coder-0.1.266.dist-info}/top_level.txt +0 -0
autocoder/commands/tools.py
CHANGED
|
@@ -27,6 +27,7 @@ from autocoder.utils.queue_communicate import (
|
|
|
27
27
|
)
|
|
28
28
|
import sys
|
|
29
29
|
import io
|
|
30
|
+
from autocoder.common import files as files_utils
|
|
30
31
|
|
|
31
32
|
@byzerllm.prompt()
|
|
32
33
|
def detect_rm_command(command: str) -> Bool:
|
|
@@ -286,34 +287,33 @@ class AutoCommandTools:
|
|
|
286
287
|
|
|
287
288
|
result = []
|
|
288
289
|
try:
|
|
289
|
-
|
|
290
|
-
|
|
290
|
+
|
|
291
|
+
lines = files_utils.read_lines(absolute_path)
|
|
292
|
+
# Find all lines containing the keyword
|
|
293
|
+
keyword_lines = []
|
|
294
|
+
for i, line in enumerate(lines):
|
|
295
|
+
if keyword.lower() in line.lower():
|
|
296
|
+
keyword_lines.append(i)
|
|
297
|
+
|
|
298
|
+
# Process each keyword line and its surrounding range
|
|
299
|
+
processed_ranges = set()
|
|
300
|
+
for line_num in keyword_lines:
|
|
301
|
+
# Calculate range boundaries
|
|
302
|
+
start = max(0, line_num - before_size)
|
|
303
|
+
end = min(len(lines), line_num + after_size + 1)
|
|
291
304
|
|
|
292
|
-
#
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
keyword_lines.append(i)
|
|
305
|
+
# Check if this range overlaps with any previously processed range
|
|
306
|
+
range_key = (start, end)
|
|
307
|
+
if range_key in processed_ranges:
|
|
308
|
+
continue
|
|
297
309
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
# Check if this range overlaps with any previously processed range
|
|
306
|
-
range_key = (start, end)
|
|
307
|
-
if range_key in processed_ranges:
|
|
308
|
-
continue
|
|
309
|
-
|
|
310
|
-
processed_ranges.add(range_key)
|
|
311
|
-
|
|
312
|
-
# Format the content block
|
|
313
|
-
content = f"##File: {absolute_path}\n"
|
|
314
|
-
content += f"##Line: {start+1}-{end}\n\n"
|
|
315
|
-
content += "".join(lines[start:end])
|
|
316
|
-
result.append(content)
|
|
310
|
+
processed_ranges.add(range_key)
|
|
311
|
+
|
|
312
|
+
# Format the content block
|
|
313
|
+
content = f"##File: {absolute_path}\n"
|
|
314
|
+
content += f"##Line: {start+1}-{end}\n\n"
|
|
315
|
+
content += "".join(lines[start:end])
|
|
316
|
+
result.append(content)
|
|
317
317
|
|
|
318
318
|
except Exception as e:
|
|
319
319
|
v = f"Error reading file {absolute_path}: {str(e)}"
|
|
@@ -403,23 +403,22 @@ class AutoCommandTools:
|
|
|
403
403
|
if path in os.path.join(root, file):
|
|
404
404
|
absolute_path = os.path.join(root, file)
|
|
405
405
|
break
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
source_code = f"##File: {absolute_path}\n\n{content}"
|
|
406
|
+
|
|
407
|
+
if path in file_line_ranges:
|
|
408
|
+
# Read specific line ranges
|
|
409
|
+
lines = files_utils.read_lines(absolute_path)
|
|
410
|
+
filtered_lines = []
|
|
411
|
+
for start, end in file_line_ranges[path]:
|
|
412
|
+
# Adjust for 0-based indexing
|
|
413
|
+
start = max(0, start - 1)
|
|
414
|
+
end = min(len(lines), end)
|
|
415
|
+
content = "".join(lines[start:end])
|
|
416
|
+
filtered_lines.extend(f"##File: {absolute_path}\n##Line: {start}-{end}\n\n{content}")
|
|
417
|
+
source_code = "".join(filtered_lines)
|
|
418
|
+
else:
|
|
419
|
+
# Read entire file if no range specified
|
|
420
|
+
content = files_utils.read_file(absolute_path)
|
|
421
|
+
source_code = f"##File: {absolute_path}\n\n{content}"
|
|
423
422
|
|
|
424
423
|
sc = SourceCode(module_name=absolute_path, source_code=source_code)
|
|
425
424
|
source_code_str += f"{sc.source_code}\n\n"
|
|
@@ -510,13 +509,12 @@ class AutoCommandTools:
|
|
|
510
509
|
for file in files:
|
|
511
510
|
file_path = os.path.join(root, file)
|
|
512
511
|
try:
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
break
|
|
512
|
+
content = files_utils.read_file(file_path)
|
|
513
|
+
if keyword.lower() in content.lower():
|
|
514
|
+
matched_files.append(file_path)
|
|
515
|
+
# Limit to first 10 matches
|
|
516
|
+
if len(matched_files) >= 10:
|
|
517
|
+
break
|
|
520
518
|
except Exception:
|
|
521
519
|
# Skip files that can't be read
|
|
522
520
|
pass
|
autocoder/common/__init__.py
CHANGED
|
@@ -3,6 +3,7 @@ from byzerllm.utils import format_str_jinja2
|
|
|
3
3
|
|
|
4
4
|
MESSAGES = {
|
|
5
5
|
"en": {
|
|
6
|
+
"invalid_file_pattern": "Invalid file pattern: {{file_pattern}}. e.g. regex://.*/package-lock\\.json",
|
|
6
7
|
"config_validation_error": "Config validation error: {{error}}",
|
|
7
8
|
"invalid_boolean_value": "Value '{{value}}' is not a valid boolean(true/false)",
|
|
8
9
|
"invalid_integer_value": "Value '{{value}}' is not a valid integer",
|
|
@@ -116,6 +117,7 @@ MESSAGES = {
|
|
|
116
117
|
"quick_filter_stats": "{{ model_names }} Quick filter completed in {{ elapsed_time }} seconds, input tokens: {{ input_tokens }}, output tokens: {{ output_tokens }}, input cost: {{ input_cost }}, output cost: {{ output_cost }} speed: {{ speed }} tokens/s",
|
|
117
118
|
"upsert_file": "✅ Updated file: {{ file_path }}",
|
|
118
119
|
"unmerged_blocks_title": "Unmerged Blocks",
|
|
120
|
+
"merged_blocks_title": "Merged Changes",
|
|
119
121
|
"quick_filter_title": "{{ model_name }} is analyzing how to filter context...",
|
|
120
122
|
"quick_filter_failed": "❌ Quick filter failed: {{ error }}. ",
|
|
121
123
|
"unmerged_file_path": "File: {{file_path}}",
|
|
@@ -139,9 +141,42 @@ MESSAGES = {
|
|
|
139
141
|
"conversation_pruning_start": "⚠️ Conversation pruning started, total tokens: {{total_tokens}}, safe zone: {{safe_zone}}",
|
|
140
142
|
"invalid_file_number": "⚠️ Invalid file number {{file_number}}, total files: {{total_files}}",
|
|
141
143
|
"all_merge_results_failed": "⚠️ All merge attempts failed, returning first candidate",
|
|
142
|
-
"only_one_merge_result_success": "✅ Only one merge result succeeded, returning that candidate"
|
|
143
|
-
},
|
|
144
|
+
"only_one_merge_result_success": "✅ Only one merge result succeeded, returning that candidate",
|
|
145
|
+
"conf_import_success": "Successfully imported configuration: {{path}}",
|
|
146
|
+
"conf_export_success": "Successfully exported configuration: {{path}}",
|
|
147
|
+
"conf_import_error": "Error importing configuration: {{error}}",
|
|
148
|
+
"conf_export_error": "Error exporting configuration: {{error}}",
|
|
149
|
+
"conf_import_invalid_format": "Invalid import configuration format, expected 'key:value'",
|
|
150
|
+
"conf_export_invalid_format": "Invalid export configuration format, expected 'key:value'",
|
|
151
|
+
"conf_import_file_not_found": "Import configuration file not found: {{file_path}}",
|
|
152
|
+
"conf_export_file_not_found": "Export configuration file not found: {{file_path}}",
|
|
153
|
+
"conf_import_file_empty": "Import configuration file is empty: {{file_path}}",
|
|
154
|
+
"conf_export_file_empty": "Export configuration file is empty: {{file_path}}",
|
|
155
|
+
"generated_shell_script": "Generated Shell Script",
|
|
156
|
+
"confirm_execute_shell_script": "Do you want to execute this shell script?",
|
|
157
|
+
"shell_script_not_executed": "Shell script was not executed",
|
|
158
|
+
"conf_not_found": "Configuration file not found: {{path}}",
|
|
159
|
+
"index_export_success": "Index exported successfully: {{path}}",
|
|
160
|
+
"index_import_success": "Index imported successfully: {{path}}",
|
|
161
|
+
"edits_title": "edits",
|
|
162
|
+
"diff_blocks_title":"diff blocks"
|
|
163
|
+
},
|
|
144
164
|
"zh": {
|
|
165
|
+
"invalid_file_pattern": "无效的文件模式: {{file_pattern}}. 例如: regex://.*/package-lock\\.json",
|
|
166
|
+
"conf_not_found": "未找到配置文件: {{path}}",
|
|
167
|
+
"conf_import_success": "成功导入配置: {{path}}",
|
|
168
|
+
"conf_export_success": "成功导出配置: {{path}}",
|
|
169
|
+
"conf_import_error": "导入配置出错: {{error}}",
|
|
170
|
+
"conf_export_error": "导出配置出错: {{error}}",
|
|
171
|
+
"conf_import_invalid_format": "导入配置格式无效, 应为 'key:value' 格式",
|
|
172
|
+
"conf_export_invalid_format": "导出配置格式无效, 应为 'key:value' 格式",
|
|
173
|
+
"conf_import_file_not_found": "未找到导入配置文件: {{file_path}}",
|
|
174
|
+
"conf_export_file_not_found": "未找到导出配置文件: {{file_path}}",
|
|
175
|
+
"conf_import_file_empty": "导入配置文件为空: {{file_path}}",
|
|
176
|
+
"conf_export_file_empty": "导出配置文件为空: {{file_path}}",
|
|
177
|
+
"generated_shell_script": "生成的 Shell 脚本",
|
|
178
|
+
"confirm_execute_shell_script": "您要执行此 shell 脚本吗?",
|
|
179
|
+
"shell_script_not_executed": "Shell 脚本未执行",
|
|
145
180
|
"config_validation_error": "配置验证错误: {{error}}",
|
|
146
181
|
"invalid_boolean_value": "值 '{{value}}' 不是有效的布尔值(true/false)",
|
|
147
182
|
"invalid_integer_value": "值 '{{value}}' 不是有效的整数",
|
|
@@ -241,6 +276,7 @@ MESSAGES = {
|
|
|
241
276
|
"merge_success": "✅ 成功合并了 {{ num_files }} 个文件中的更改 {{ num_changes }}/{{ total_blocks }} 个代码块。",
|
|
242
277
|
"no_changes_made": "⚠️ 未对任何文件进行更改。这个原因可能是因为coding函数生成的文本块格式有问题,导致无法合并进项目",
|
|
243
278
|
"unmerged_blocks_title": "未合并代码块",
|
|
279
|
+
"merged_blocks_title": "合并的更改",
|
|
244
280
|
"unmerged_file_path": "文件: {{file_path}}",
|
|
245
281
|
"unmerged_search_block": "Search Block({{similarity}}):",
|
|
246
282
|
"unmerged_replace_block": "Replace Block:",
|
|
@@ -276,7 +312,11 @@ MESSAGES = {
|
|
|
276
312
|
"conversation_pruning_start": "⚠️ 对话长度 {{total_tokens}} tokens 超过安全阈值 {{safe_zone}},开始修剪对话。",
|
|
277
313
|
"invalid_file_number": "⚠️ 无效的文件编号 {{file_number}},总文件数为 {{total_files}}",
|
|
278
314
|
"all_merge_results_failed": "⚠️ 所有合并尝试都失败,返回第一个候选",
|
|
279
|
-
"only_one_merge_result_success": "✅ 只有一个合并结果成功,返回该候选"
|
|
315
|
+
"only_one_merge_result_success": "✅ 只有一个合并结果成功,返回该候选",
|
|
316
|
+
"index_export_success": "索引导出成功: {{path}}",
|
|
317
|
+
"index_import_success": "索引导入成功: {{path}}",
|
|
318
|
+
"edits_title": "编辑块",
|
|
319
|
+
"diff_blocks_title": "差异块",
|
|
280
320
|
}}
|
|
281
321
|
|
|
282
322
|
|
|
@@ -197,7 +197,7 @@ class CodeAutoGenerate:
|
|
|
197
197
|
instruction=query, content=source_content
|
|
198
198
|
)
|
|
199
199
|
|
|
200
|
-
with open(self.args.target_file, "w") as file:
|
|
200
|
+
with open(self.args.target_file, "w",encoding="utf-8") as file:
|
|
201
201
|
file.write(init_prompt)
|
|
202
202
|
|
|
203
203
|
conversations = []
|
|
@@ -298,7 +298,7 @@ class CodeAutoGenerate:
|
|
|
298
298
|
|
|
299
299
|
conversations = [{"role": "user", "content": init_prompt}]
|
|
300
300
|
|
|
301
|
-
with open(self.args.target_file, "w") as file:
|
|
301
|
+
with open(self.args.target_file, "w",encoding="utf-8") as file:
|
|
302
302
|
file.write(init_prompt)
|
|
303
303
|
|
|
304
304
|
t = self.llm.chat_oai(conversations=conversations, llm_config=llm_config)
|
|
@@ -320,7 +320,7 @@ class CodeAutoGenerate:
|
|
|
320
320
|
|
|
321
321
|
conversations.append({"role": "user", "content": "继续"})
|
|
322
322
|
|
|
323
|
-
with open(self.args.target_file, "w") as file:
|
|
323
|
+
with open(self.args.target_file, "w",encoding="utf-8") as file:
|
|
324
324
|
file.write("继续")
|
|
325
325
|
|
|
326
326
|
t = self.llm.chat_oai(conversations=conversations, llm_config=llm_config)
|
|
@@ -315,10 +315,7 @@ class CodeAutoGenerateDiff:
|
|
|
315
315
|
elif self.args.template == "auto_implement":
|
|
316
316
|
init_prompt = self.auto_implement_function.prompt(
|
|
317
317
|
instruction=query, content=source_content
|
|
318
|
-
)
|
|
319
|
-
|
|
320
|
-
with open(self.args.target_file, "w") as file:
|
|
321
|
-
file.write(init_prompt)
|
|
318
|
+
)
|
|
322
319
|
|
|
323
320
|
conversations = []
|
|
324
321
|
|
|
@@ -447,7 +444,7 @@ class CodeAutoGenerateDiff:
|
|
|
447
444
|
# conversations.append({"role": "system", "content": sys_prompt.prompt()})
|
|
448
445
|
conversations.append({"role": "user", "content": init_prompt})
|
|
449
446
|
|
|
450
|
-
with open(self.args.target_file, "w") as file:
|
|
447
|
+
with open(self.args.target_file, "w",encoding="utf-8") as file:
|
|
451
448
|
file.write(init_prompt)
|
|
452
449
|
|
|
453
450
|
code_llm = self.llms[0]
|
|
@@ -467,7 +464,7 @@ class CodeAutoGenerateDiff:
|
|
|
467
464
|
|
|
468
465
|
conversations.append({"role": "user", "content": "继续"})
|
|
469
466
|
|
|
470
|
-
with open(self.args.target_file, "w") as file:
|
|
467
|
+
with open(self.args.target_file, "w",encoding="utf-8") as file:
|
|
471
468
|
file.write("继续")
|
|
472
469
|
|
|
473
470
|
t = code_llm.chat_oai(
|
|
@@ -418,7 +418,7 @@ class CodeAutoGenerateEditBlock:
|
|
|
418
418
|
instruction=query, content=source_content
|
|
419
419
|
)
|
|
420
420
|
|
|
421
|
-
with open(self.args.target_file, "w") as file:
|
|
421
|
+
with open(self.args.target_file, "w",encoding="utf-8") as file:
|
|
422
422
|
file.write(init_prompt)
|
|
423
423
|
|
|
424
424
|
conversations = []
|
|
@@ -538,7 +538,7 @@ class CodeAutoGenerateEditBlock:
|
|
|
538
538
|
# conversations.append({"role": "system", "content": sys_prompt.prompt()})
|
|
539
539
|
conversations.append({"role": "user", "content": init_prompt})
|
|
540
540
|
|
|
541
|
-
with open(self.args.target_file, "w") as file:
|
|
541
|
+
with open(self.args.target_file, "w",encoding="utf-8") as file:
|
|
542
542
|
file.write(init_prompt)
|
|
543
543
|
|
|
544
544
|
code_llm = self.llms[0]
|
|
@@ -558,7 +558,7 @@ class CodeAutoGenerateEditBlock:
|
|
|
558
558
|
|
|
559
559
|
conversations.append({"role": "user", "content": "继续"})
|
|
560
560
|
|
|
561
|
-
with open(self.args.target_file, "w") as file:
|
|
561
|
+
with open(self.args.target_file, "w",encoding="utf-8") as file:
|
|
562
562
|
file.write("继续")
|
|
563
563
|
|
|
564
564
|
t = code_llm.chat_oai(
|
|
@@ -287,7 +287,7 @@ class CodeAutoGenerateStrictDiff:
|
|
|
287
287
|
instruction=query, content=source_content
|
|
288
288
|
)
|
|
289
289
|
|
|
290
|
-
with open(self.args.target_file, "w") as file:
|
|
290
|
+
with open(self.args.target_file, "w",encoding="utf-8") as file:
|
|
291
291
|
file.write(init_prompt)
|
|
292
292
|
|
|
293
293
|
conversations = []
|
|
@@ -417,7 +417,7 @@ class CodeAutoGenerateStrictDiff:
|
|
|
417
417
|
# conversations.append({"role": "system", "content": sys_prompt.prompt()})
|
|
418
418
|
conversations.append({"role": "user", "content": init_prompt})
|
|
419
419
|
|
|
420
|
-
with open(self.args.target_file, "w") as file:
|
|
420
|
+
with open(self.args.target_file, "w",encoding="utf-8") as file:
|
|
421
421
|
file.write(init_prompt)
|
|
422
422
|
|
|
423
423
|
code_llm = self.llms[0]
|
|
@@ -437,7 +437,7 @@ class CodeAutoGenerateStrictDiff:
|
|
|
437
437
|
|
|
438
438
|
conversations.append({"role": "user", "content": "继续"})
|
|
439
439
|
|
|
440
|
-
with open(self.args.target_file, "w") as file:
|
|
440
|
+
with open(self.args.target_file, "w",encoding="utf-8") as file:
|
|
441
441
|
file.write("继续")
|
|
442
442
|
|
|
443
443
|
t = code_llm.chat_oai(
|
|
@@ -462,7 +462,7 @@ class CodeAutoMergeDiff:
|
|
|
462
462
|
full_path = self.abs_root_path(path)
|
|
463
463
|
|
|
464
464
|
if not os.path.exists(full_path):
|
|
465
|
-
with open(full_path, "w"
|
|
465
|
+
with open(full_path, "w") as f:
|
|
466
466
|
f.write("")
|
|
467
467
|
|
|
468
468
|
content = FileUtils.read_file(full_path)
|
|
@@ -528,10 +528,41 @@ class CodeAutoMergeDiff:
|
|
|
528
528
|
failed_blocks=failed_blocks
|
|
529
529
|
)
|
|
530
530
|
|
|
531
|
+
def print_edits(self, edits: List[Tuple[str, List[str]]]):
|
|
532
|
+
"""Print diffs for user review using rich library"""
|
|
533
|
+
from rich.syntax import Syntax
|
|
534
|
+
from rich.panel import Panel
|
|
535
|
+
|
|
536
|
+
# Group edits by file path
|
|
537
|
+
file_edits = {}
|
|
538
|
+
for path, hunk in edits:
|
|
539
|
+
if path not in file_edits:
|
|
540
|
+
file_edits[path] = []
|
|
541
|
+
file_edits[path].append(hunk)
|
|
542
|
+
|
|
543
|
+
# Generate formatted text for each file
|
|
544
|
+
formatted_text = ""
|
|
545
|
+
for path, hunks in file_edits.items():
|
|
546
|
+
formatted_text += f"##File: {path}\n"
|
|
547
|
+
for hunk in hunks:
|
|
548
|
+
formatted_text += "".join(hunk)
|
|
549
|
+
formatted_text += "\n"
|
|
550
|
+
|
|
551
|
+
# Print with rich panel
|
|
552
|
+
self.printer.print_in_terminal("edits_title", style="bold green")
|
|
553
|
+
self.printer.console.print(
|
|
554
|
+
Panel(
|
|
555
|
+
Syntax(formatted_text, "diff", theme="monokai"),
|
|
556
|
+
title="Edits",
|
|
557
|
+
border_style="green",
|
|
558
|
+
expand=False
|
|
559
|
+
)
|
|
560
|
+
)
|
|
561
|
+
|
|
531
562
|
def _merge_code(self, content: str,force_skip_git:bool=False):
|
|
532
563
|
total = 0
|
|
533
564
|
|
|
534
|
-
file_content =
|
|
565
|
+
file_content = FileUtils.read_file(self.args.file)
|
|
535
566
|
md5 = hashlib.md5(file_content.encode('utf-8')).hexdigest()
|
|
536
567
|
# get the file name
|
|
537
568
|
file_name = os.path.basename(self.args.file)
|
|
@@ -544,9 +575,12 @@ class CodeAutoMergeDiff:
|
|
|
544
575
|
return
|
|
545
576
|
|
|
546
577
|
edits = self.get_edits(content)
|
|
547
|
-
self.apply_edits(edits)
|
|
578
|
+
self.apply_edits(edits)
|
|
548
579
|
|
|
549
580
|
self.printer.print_in_terminal("files_merged_total", total=total)
|
|
550
581
|
if not force_skip_git and not self.args.skip_commit:
|
|
551
582
|
commit_result = git_utils.commit_changes(self.args.source_dir, f"auto_coder_{file_name}_{md5}\n{self.args.query}")
|
|
552
583
|
git_utils.print_commit_info(commit_result=commit_result)
|
|
584
|
+
else:
|
|
585
|
+
# Print edits for review
|
|
586
|
+
self.print_edits(edits)
|
|
@@ -287,9 +287,10 @@ class CodeAutoMergeEditBlock:
|
|
|
287
287
|
for path, content in file_content_mapping.items()],
|
|
288
288
|
failed_blocks=failed_blocks
|
|
289
289
|
)
|
|
290
|
+
|
|
290
291
|
|
|
291
292
|
def _merge_code(self, content: str, force_skip_git: bool = False):
|
|
292
|
-
file_content =
|
|
293
|
+
file_content = FileUtils.read_file(self.args.file)
|
|
293
294
|
md5 = hashlib.md5(file_content.encode("utf-8")).hexdigest()
|
|
294
295
|
file_name = os.path.basename(self.args.file)
|
|
295
296
|
|
|
@@ -391,6 +392,7 @@ class CodeAutoMergeEditBlock:
|
|
|
391
392
|
style="red"
|
|
392
393
|
)
|
|
393
394
|
return
|
|
395
|
+
|
|
394
396
|
# Now, apply the changes
|
|
395
397
|
for file_path, new_content in file_content_mapping.items():
|
|
396
398
|
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
|
@@ -431,10 +433,14 @@ class CodeAutoMergeEditBlock:
|
|
|
431
433
|
self.git_require_msg(source_dir=self.args.source_dir, error=str(e)),
|
|
432
434
|
style="red"
|
|
433
435
|
)
|
|
436
|
+
else:
|
|
437
|
+
self.print_merged_blocks(merged_blocks)
|
|
438
|
+
|
|
434
439
|
self.printer.print_in_terminal("merge_success",
|
|
435
440
|
num_files=len(file_content_mapping.keys()),
|
|
436
441
|
num_changes=len(changes_to_make),
|
|
437
442
|
total_blocks=len(codes))
|
|
443
|
+
|
|
438
444
|
else:
|
|
439
445
|
self.printer.print_in_terminal("no_changes_made")
|
|
440
446
|
|
|
@@ -455,3 +461,39 @@ class CodeAutoMergeEditBlock:
|
|
|
455
461
|
syntax = Syntax(update, "python", theme="monokai", line_numbers=True)
|
|
456
462
|
self.printer.console.print(Panel(syntax, expand=False))
|
|
457
463
|
self.printer.print_in_terminal("unmerged_blocks_total", num_blocks=len(unmerged_blocks), style="bold red")
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def print_merged_blocks(self, merged_blocks: List[tuple]):
|
|
467
|
+
"""Print search/replace blocks for user review using rich library"""
|
|
468
|
+
from rich.syntax import Syntax
|
|
469
|
+
from rich.panel import Panel
|
|
470
|
+
|
|
471
|
+
# Group blocks by file path
|
|
472
|
+
file_blocks = {}
|
|
473
|
+
for file_path, head, update, similarity in merged_blocks:
|
|
474
|
+
if file_path not in file_blocks:
|
|
475
|
+
file_blocks[file_path] = []
|
|
476
|
+
file_blocks[file_path].append((head, update, similarity))
|
|
477
|
+
|
|
478
|
+
# Generate formatted text for each file
|
|
479
|
+
formatted_text = ""
|
|
480
|
+
for file_path, blocks in file_blocks.items():
|
|
481
|
+
formatted_text += f"##File: {file_path}\n"
|
|
482
|
+
for head, update, similarity in blocks:
|
|
483
|
+
formatted_text += "<<<<<<< SEARCH\n"
|
|
484
|
+
formatted_text += head + "\n"
|
|
485
|
+
formatted_text += "=======\n"
|
|
486
|
+
formatted_text += update + "\n"
|
|
487
|
+
formatted_text += ">>>>>>> REPLACE\n"
|
|
488
|
+
formatted_text += "\n"
|
|
489
|
+
|
|
490
|
+
# Print with rich panel
|
|
491
|
+
self.printer.print_in_terminal("merged_blocks_title", style="bold green")
|
|
492
|
+
self.printer.console.print(
|
|
493
|
+
Panel(
|
|
494
|
+
Syntax(formatted_text, "diff", theme="monokai"),
|
|
495
|
+
title="Merged Changes",
|
|
496
|
+
border_style="green",
|
|
497
|
+
expand=False
|
|
498
|
+
)
|
|
499
|
+
)
|
|
@@ -10,6 +10,7 @@ import hashlib
|
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from autocoder.common.types import CodeGenerateResult, MergeCodeWithoutEffect
|
|
12
12
|
from autocoder.common.code_modification_ranker import CodeModificationRanker
|
|
13
|
+
from autocoder.common import files as FileUtils
|
|
13
14
|
|
|
14
15
|
class PathAndCode(pydantic.BaseModel):
|
|
15
16
|
path: str
|
|
@@ -195,8 +196,7 @@ class CodeAutoMergeStrictDiff:
|
|
|
195
196
|
continue
|
|
196
197
|
|
|
197
198
|
if full_path not in file_content_mapping:
|
|
198
|
-
|
|
199
|
-
file_content_mapping[full_path] = f.read()
|
|
199
|
+
file_content_mapping[full_path] = FileUtils.read_file(full_path)
|
|
200
200
|
|
|
201
201
|
try:
|
|
202
202
|
import patch
|
|
@@ -221,10 +221,41 @@ class CodeAutoMergeStrictDiff:
|
|
|
221
221
|
failed_blocks=failed_blocks
|
|
222
222
|
)
|
|
223
223
|
|
|
224
|
+
def print_diff_blocks(self, diff_blocks: List[PathAndCode]):
|
|
225
|
+
"""Print diff blocks for user review using rich library"""
|
|
226
|
+
from rich.syntax import Syntax
|
|
227
|
+
from rich.panel import Panel
|
|
228
|
+
|
|
229
|
+
# Group blocks by file path
|
|
230
|
+
file_blocks = {}
|
|
231
|
+
for block in diff_blocks:
|
|
232
|
+
if block.path not in file_blocks:
|
|
233
|
+
file_blocks[block.path] = []
|
|
234
|
+
file_blocks[block.path].append(block.content)
|
|
235
|
+
|
|
236
|
+
# Generate formatted text for each file
|
|
237
|
+
formatted_text = ""
|
|
238
|
+
for path, contents in file_blocks.items():
|
|
239
|
+
formatted_text += f"##File: {path}\n"
|
|
240
|
+
for content in contents:
|
|
241
|
+
formatted_text += content + "\n"
|
|
242
|
+
formatted_text += "\n"
|
|
243
|
+
|
|
244
|
+
# Print with rich panel
|
|
245
|
+
self.printer.print_in_terminal("diff_blocks_title", style="bold green")
|
|
246
|
+
self.printer.console.print(
|
|
247
|
+
Panel(
|
|
248
|
+
Syntax(formatted_text, "diff", theme="monokai"),
|
|
249
|
+
title="Diff Blocks",
|
|
250
|
+
border_style="green",
|
|
251
|
+
expand=False
|
|
252
|
+
)
|
|
253
|
+
)
|
|
254
|
+
|
|
224
255
|
def _merge_code(self, content: str, force_skip_git: bool = False):
|
|
225
256
|
total = 0
|
|
226
257
|
|
|
227
|
-
file_content =
|
|
258
|
+
file_content = FileUtils.read_file(self.args.file)
|
|
228
259
|
md5 = hashlib.md5(file_content.encode('utf-8')).hexdigest()
|
|
229
260
|
# get the file name
|
|
230
261
|
file_name = os.path.basename(self.args.file)
|
|
@@ -236,7 +267,8 @@ class CodeAutoMergeStrictDiff:
|
|
|
236
267
|
self.printer.print_in_terminal("git_init_required", style="red", source_dir=self.args.source_dir, error=str(e))
|
|
237
268
|
return
|
|
238
269
|
|
|
239
|
-
diff_blocks = self.parse_diff_block(content)
|
|
270
|
+
diff_blocks = self.parse_diff_block(content)
|
|
271
|
+
|
|
240
272
|
for diff_blocks in diff_blocks:
|
|
241
273
|
path = diff_blocks.path
|
|
242
274
|
content = diff_blocks.content
|
|
@@ -255,6 +287,9 @@ class CodeAutoMergeStrictDiff:
|
|
|
255
287
|
if not force_skip_git and not self.args.skip_commit:
|
|
256
288
|
commit_result = git_utils.commit_changes(self.args.source_dir, f"auto_coder_{file_name}_{md5}\n{self.args.query}")
|
|
257
289
|
git_utils.print_commit_info(commit_result=commit_result)
|
|
290
|
+
else:
|
|
291
|
+
# Print diff blocks for review
|
|
292
|
+
self.print_diff_blocks(diff_blocks)
|
|
258
293
|
|
|
259
294
|
@byzerllm.prompt(render="jinja2")
|
|
260
295
|
def git_require_msg(self, source_dir: str, error: str) -> str:
|
|
@@ -4,6 +4,7 @@ from autocoder.utils.auto_coder_utils.chat_stream_out import stream_out
|
|
|
4
4
|
from autocoder.common import detect_env
|
|
5
5
|
from autocoder.common import shells
|
|
6
6
|
from autocoder.common.printer import Printer
|
|
7
|
+
from typing import Dict,Union
|
|
7
8
|
|
|
8
9
|
@byzerllm.prompt()
|
|
9
10
|
def _generate_shell_script(user_input: str) -> str:
|
|
@@ -14,6 +15,11 @@ def _generate_shell_script(user_input: str) -> str:
|
|
|
14
15
|
Python版本: {{ env_info.python_version }}
|
|
15
16
|
终端类型: {{ env_info.shell_type }}
|
|
16
17
|
终端编码: {{ env_info.shell_encoding }}
|
|
18
|
+
|
|
19
|
+
{%- if shell_type %}
|
|
20
|
+
脚本类型:{{ shell_type }}
|
|
21
|
+
{%- endif %}
|
|
22
|
+
|
|
17
23
|
{%- if env_info.conda_env %}
|
|
18
24
|
Conda环境: {{ env_info.conda_env }}
|
|
19
25
|
{%- endif %}
|
|
@@ -21,29 +27,39 @@ def _generate_shell_script(user_input: str) -> str:
|
|
|
21
27
|
虚拟环境: {{ env_info.virtualenv }}
|
|
22
28
|
{%- endif %}
|
|
23
29
|
|
|
24
|
-
|
|
30
|
+
根据用户的输入以及当前的操作系统和终端类型以及脚本类型生成脚本,
|
|
31
|
+
注意只能生成一个shell脚本,不要生成多个。
|
|
25
32
|
|
|
26
33
|
用户输入: {{ user_input }}
|
|
27
34
|
|
|
28
|
-
|
|
35
|
+
请生成一个适当的脚本来执行用户的请求。确保脚本是安全的,并且可以在当前Shell环境中运行。
|
|
29
36
|
脚本应该包含必要的注释来解释每个步骤。
|
|
30
37
|
脚本内容请用如下方式返回:
|
|
31
38
|
|
|
32
|
-
```
|
|
33
|
-
# 你的
|
|
39
|
+
```script
|
|
40
|
+
# 你的 script 脚本内容
|
|
34
41
|
```
|
|
35
42
|
"""
|
|
36
|
-
env_info = detect_env()
|
|
43
|
+
env_info = detect_env()
|
|
44
|
+
shell_type = "bash"
|
|
45
|
+
if shells.is_running_in_cmd():
|
|
46
|
+
shell_type = "cmd"
|
|
47
|
+
elif shells.is_running_in_powershell():
|
|
48
|
+
shell_type = "powershell"
|
|
37
49
|
return {
|
|
38
50
|
"env_info": env_info,
|
|
39
|
-
"shell_type":
|
|
51
|
+
"shell_type": shell_type,
|
|
40
52
|
"shell_encoding": shells.get_terminal_encoding()
|
|
41
53
|
}
|
|
42
54
|
|
|
43
55
|
|
|
44
|
-
def generate_shell_script(user_input: str, llm: byzerllm.ByzerLLM) -> str:
|
|
56
|
+
def generate_shell_script(user_input: str, llm: Union[byzerllm.ByzerLLM,byzerllm.SimpleByzerLLM]) -> str:
|
|
45
57
|
# 获取 prompt 内容
|
|
46
58
|
prompt = _generate_shell_script.prompt(user_input=user_input)
|
|
59
|
+
if llm.get_sub_client("chat_model"):
|
|
60
|
+
shell_llm = llm.get_sub_client("chat_model")
|
|
61
|
+
else:
|
|
62
|
+
shell_llm = llm
|
|
47
63
|
|
|
48
64
|
# 构造对话上下文
|
|
49
65
|
conversations = [{"role": "user", "content": prompt}]
|
|
@@ -52,7 +68,7 @@ def generate_shell_script(user_input: str, llm: byzerllm.ByzerLLM) -> str:
|
|
|
52
68
|
printer = Printer()
|
|
53
69
|
title = printer.get_message_from_key("generating_shell_script")
|
|
54
70
|
result, _ = stream_out(
|
|
55
|
-
|
|
71
|
+
shell_llm.stream_chat_oai(conversations=conversations, delta_mode=True),
|
|
56
72
|
model_name=llm.default_model_name,
|
|
57
73
|
title=title
|
|
58
74
|
)
|
|
@@ -139,7 +139,7 @@ def create_actions(source_dir:str,params:Dict[str,str]):
|
|
|
139
139
|
"000_example": base_000_example.prompt(),
|
|
140
140
|
}
|
|
141
141
|
init_file_path = os.path.join(source_dir, "actions", "101_current_work.yml")
|
|
142
|
-
with open(init_file_path, "w") as f:
|
|
142
|
+
with open(init_file_path, "w", encoding="utf-8") as f:
|
|
143
143
|
f.write(init_command_template.prompt(source_dir=source_dir))
|
|
144
144
|
|
|
145
145
|
for k,v in mapping.items():
|
|
@@ -152,7 +152,7 @@ def create_actions(source_dir:str,params:Dict[str,str]):
|
|
|
152
152
|
if k == "000_example":
|
|
153
153
|
file_path = os.path.join(source_dir, "actions", f"{k}.yml")
|
|
154
154
|
|
|
155
|
-
with open(file_path, "w") as f:
|
|
155
|
+
with open(file_path, "w", encoding="utf-8") as f:
|
|
156
156
|
f.write(v)
|
|
157
157
|
|
|
158
158
|
@byzerllm.prompt()
|