auto-coder 0.1.233__py3-none-any.whl → 0.1.237__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.233.dist-info → auto_coder-0.1.237.dist-info}/METADATA +2 -3
- {auto_coder-0.1.233.dist-info → auto_coder-0.1.237.dist-info}/RECORD +24 -21
- autocoder/auto_coder.py +68 -35
- autocoder/chat_auto_coder.py +107 -84
- autocoder/chat_auto_coder_lang.py +69 -17
- autocoder/common/__init__.py +3 -0
- autocoder/common/auto_coder_lang.py +141 -0
- autocoder/common/code_auto_merge.py +12 -9
- autocoder/common/code_auto_merge_diff.py +5 -4
- autocoder/common/code_auto_merge_editblock.py +35 -33
- autocoder/common/code_auto_merge_strict_diff.py +5 -4
- autocoder/common/code_modification_ranker.py +74 -44
- autocoder/common/printer.py +49 -0
- autocoder/dispacher/actions/action.py +68 -19
- autocoder/dispacher/actions/plugins/action_regex_project.py +5 -0
- autocoder/index/entry.py +57 -14
- autocoder/index/filter/quick_filter.py +7 -6
- autocoder/index/index.py +54 -16
- autocoder/utils/types.py +0 -0
- autocoder/version.py +1 -1
- {auto_coder-0.1.233.dist-info → auto_coder-0.1.237.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.233.dist-info → auto_coder-0.1.237.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.233.dist-info → auto_coder-0.1.237.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.233.dist-info → auto_coder-0.1.237.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import locale
|
|
2
|
+
|
|
3
|
+
MESSAGES = {
|
|
4
|
+
"en": {
|
|
5
|
+
"memory_save_success": "✅ Saved to your memory",
|
|
6
|
+
"index_file_too_large": "⚠️ File {{ file_path }} is too large ({{ file_size }} > {{ max_length }}), splitting into chunks...",
|
|
7
|
+
"index_update_success": "✅ Successfully updated index for {{ file_path }} (md5: {{ md5 }}) in {{ duration }}s",
|
|
8
|
+
"index_build_error": "❌ Error building index for {{ file_path }}: {{ error }}",
|
|
9
|
+
"index_build_summary": "📊 Total Files: {{ total_files }}, Need to Build Index: {{ num_files }}",
|
|
10
|
+
"building_index_progress": "⏳ Building Index: {{ counter }}/{{ num_files }}...",
|
|
11
|
+
"index_source_dir_mismatch": "⚠️ Source directory mismatch (file_path: {{ file_path }}, source_dir: {{ source_dir }})",
|
|
12
|
+
"index_related_files_fail": "⚠️ Failed to find related files for chunk {{ chunk_count }}",
|
|
13
|
+
"index_threads_completed": "✅ Completed {{ completed_threads }}/{{ total_threads }} threads",
|
|
14
|
+
"index_related_files_fail": "⚠️ Failed to find related files for chunk {{ chunk_count }}",
|
|
15
|
+
"human_as_model_instructions": (
|
|
16
|
+
"You are now in Human as Model mode. The content has been copied to your clipboard.\n"
|
|
17
|
+
"The system is waiting for your input. When finished, enter 'EOF' on a new line to submit.\n"
|
|
18
|
+
"Use '/break' to exit this mode. If you have issues with copy-paste, use '/clear' to clean and paste again."
|
|
19
|
+
),
|
|
20
|
+
"clipboard_not_supported": (
|
|
21
|
+
"pyperclip not installed or clipboard is not supported, instruction will not be copied to clipboard."
|
|
22
|
+
),
|
|
23
|
+
"human_as_model_instructions_no_clipboard": (
|
|
24
|
+
"You are now in Human as Model mode. [bold red]The content could not be copied to your clipboard.[/bold red]\n"
|
|
25
|
+
"but you can copy prompt from output.txt file.\n"
|
|
26
|
+
"The system is waiting for your input. When finished, enter 'EOF' on a new line to submit.\n"
|
|
27
|
+
"Use '/break' to exit this mode. If you have issues with copy-paste, use '/clear' to clean and paste again."
|
|
28
|
+
),
|
|
29
|
+
"phase1_processing_sources": "Phase 1: Processing REST/RAG/Search sources...",
|
|
30
|
+
"phase2_building_index": "Phase 2: Building index for all files...",
|
|
31
|
+
"phase6_file_selection": "Phase 6: Processing file selection and limits...",
|
|
32
|
+
"phase7_preparing_output": "Phase 7: Preparing final output...",
|
|
33
|
+
"chat_human_as_model_instructions": (
|
|
34
|
+
"Chat is now in Human as Model mode.\n"
|
|
35
|
+
"The question has been copied to your clipboard.\n"
|
|
36
|
+
"Please use Web version model to get the answer.\n"
|
|
37
|
+
"Or use /conf human_as_model:false to close this mode and get the answer in terminal directly."
|
|
38
|
+
"Paste the answer to the input box below, use '/break' to exit, '/clear' to clear the screen, '/eof' to submit."
|
|
39
|
+
),
|
|
40
|
+
"code_generation_start": "Auto generate the code...",
|
|
41
|
+
"code_generation_complete": "Code generation completed in {{ duration }} seconds, input_tokens_count: {{ input_tokens }}, generated_tokens_count: {{ output_tokens }}",
|
|
42
|
+
"code_merge_start": "Auto merge the code...",
|
|
43
|
+
"code_execution_warning": "Content(send to model) is {{ content_length }} tokens (you may collect too much files), which is larger than the maximum input length {{ max_length }}",
|
|
44
|
+
"quick_filter_start": "Starting filter context(quick_filter)...",
|
|
45
|
+
"normal_filter_start": "Starting filter context(normal_filter)...",
|
|
46
|
+
"pylint_check_failed": "⚠️ Pylint check failed: {{ error_message }}",
|
|
47
|
+
"pylint_error": "❌ Error running pylint: {{ error_message }}",
|
|
48
|
+
"unmerged_blocks_warning": "⚠️ Found {{ num_blocks }} unmerged blocks, the changes will not be applied. Please review them manually then try again.",
|
|
49
|
+
"pylint_file_check_failed": "⚠️ Pylint check failed for {{ file_path }}. Changes not applied. Error: {{ error_message }}",
|
|
50
|
+
"merge_success": "✅ Merged changes in {{ num_files }} files {{ num_changes }}/{{ total_blocks }} blocks.",
|
|
51
|
+
"no_changes_made": "⚠️ No changes were made to any files.",
|
|
52
|
+
"files_merged": "✅ Merged {{ total }} files into the project.",
|
|
53
|
+
"merge_failed": "❌ Merge file {{ path }} failed: {{ error }}",
|
|
54
|
+
"files_merged_total": "✅ Merged {{ total }} files into the project.",
|
|
55
|
+
"ranking_skip": "Only 1 candidate, skip ranking",
|
|
56
|
+
"ranking_start": "Start ranking {{ count }} candidates",
|
|
57
|
+
"ranking_failed_request": "Ranking request failed: {{ error }}",
|
|
58
|
+
"ranking_all_failed": "All ranking requests failed",
|
|
59
|
+
"ranking_complete": "Ranking completed in {{ elapsed }}s, total voters: {{ total_tasks }}, best candidate index: {{ best_candidate }}, scores: {{ scores }}, input_tokens: {{ input_tokens }}, output_tokens: {{ output_tokens }}",
|
|
60
|
+
"ranking_process_failed": "Ranking process failed: {{ error }}",
|
|
61
|
+
"ranking_failed": "Ranking failed in {{ elapsed }}s, using original order"
|
|
62
|
+
},
|
|
63
|
+
"zh": {
|
|
64
|
+
"memory_save_success": "✅ 已保存到您的记忆中",
|
|
65
|
+
"index_file_too_large": "⚠️ 文件 {{ file_path }} 过大 ({{ file_size }} > {{ max_length }}), 正在分块处理...",
|
|
66
|
+
"index_update_success": "✅ 成功更新 {{ file_path }} 的索引 (md5: {{ md5 }}), 耗时 {{ duration }} 秒",
|
|
67
|
+
"index_build_error": "❌ 构建 {{ file_path }} 索引时出错: {{ error }}",
|
|
68
|
+
"index_build_summary": "📊 总文件数: {{ total_files }}, 需要构建索引: {{ num_files }}",
|
|
69
|
+
"building_index_progress": "⏳ 正在构建索引: {{ counter }}/{{ num_files }}...",
|
|
70
|
+
"index_source_dir_mismatch": "⚠️ 源目录不匹配 (文件路径: {{ file_path }}, 源目录: {{ source_dir }})",
|
|
71
|
+
"index_related_files_fail": "⚠️ 无法为块 {{ chunk_count }} 找到相关文件",
|
|
72
|
+
"index_threads_completed": "✅ 已完成 {{ completed_threads }}/{{ total_threads }} 个线程",
|
|
73
|
+
"index_related_files_fail": "⚠️ 无法为块 {{ chunk_count }} 找到相关文件",
|
|
74
|
+
"human_as_model_instructions": (
|
|
75
|
+
"您现在处于人类作为模型模式。内容已复制到您的剪贴板。\n"
|
|
76
|
+
"系统正在等待您的输入。完成后,在新行输入'EOF'提交。\n"
|
|
77
|
+
"使用'/break'退出此模式。如果复制粘贴有问题,使用'/clear'清理并重新粘贴。"
|
|
78
|
+
),
|
|
79
|
+
"clipboard_not_supported": (
|
|
80
|
+
"未安装pyperclip或不支持剪贴板,指令将不会被复制到剪贴板。"
|
|
81
|
+
),
|
|
82
|
+
"human_as_model_instructions_no_clipboard": (
|
|
83
|
+
"您现在处于人类作为模型模式。[bold red]内容无法复制到您的剪贴板。[/bold red]\n"
|
|
84
|
+
"但您可以从output.txt文件复制提示。\n"
|
|
85
|
+
"系统正在等待您的输入。完成后,在新行输入'EOF'提交。\n"
|
|
86
|
+
"使用'/break'退出此模式。如果复制粘贴有问题,使用'/clear'清理并重新粘贴。"
|
|
87
|
+
),
|
|
88
|
+
"phase1_processing_sources": "阶段 1: 正在处理 REST/RAG/Search 源...",
|
|
89
|
+
"phase2_building_index": "阶段 2: 正在为所有文件构建索引...",
|
|
90
|
+
"phase6_file_selection": "阶段 6: 正在处理文件选择和限制...",
|
|
91
|
+
"phase7_preparing_output": "阶段 7: 正在准备最终输出...",
|
|
92
|
+
"chat_human_as_model_instructions": (
|
|
93
|
+
"\n============= Chat 处于 Human as Model 模式 =============\n"
|
|
94
|
+
"问题已复制到剪贴板\n"
|
|
95
|
+
"请使用Web版本模型获取答案\n"
|
|
96
|
+
"或者使用 /conf human_as_model:false 关闭该模式直接在终端获得答案。"
|
|
97
|
+
"将获得答案黏贴到下面的输入框,换行后,使用 '/break' 退出,'/clear' 清屏,'/eof' 提交。"
|
|
98
|
+
),
|
|
99
|
+
"code_generation_start": "正在自动生成代码...",
|
|
100
|
+
"code_generation_complete": "代码生成完成,耗时 {{ duration }} 秒,输入token数: {{ input_tokens }}, 输出token数: {{ output_tokens }}",
|
|
101
|
+
"code_merge_start": "正在自动合并代码...",
|
|
102
|
+
"code_execution_warning": "发送给模型的内容长度为 {{ content_length }} tokens(您可能收集了太多文件),超过了最大输入长度 {{ max_length }}",
|
|
103
|
+
"quick_filter_start": "开始查找上下文(quick_filter)...",
|
|
104
|
+
"normal_filter_start": "开始查找上下文(normal_filter)...",
|
|
105
|
+
"pylint_check_failed": "⚠️ Pylint 检查失败: {{ error_message }}",
|
|
106
|
+
"pylint_error": "❌ 运行 Pylint 时出错: {{ error_message }}",
|
|
107
|
+
"unmerged_blocks_warning": "⚠️ 发现 {{ num_blocks }} 个未合并的代码块,更改将不会被应用。请手动检查后重试。",
|
|
108
|
+
"pylint_file_check_failed": "⚠️ {{ file_path }} 的 Pylint 检查失败。更改未应用。错误: {{ error_message }}",
|
|
109
|
+
"merge_success": "✅ 成功合并了 {{ num_files }} 个文件中的更改 {{ num_changes }}/{{ total_blocks }} 个代码块。",
|
|
110
|
+
"no_changes_made": "⚠️ 未对任何文件进行更改。",
|
|
111
|
+
"unmerged_blocks_title": "Unmerged Blocks",
|
|
112
|
+
"unmerged_file_path": "File: {file_path}",
|
|
113
|
+
"unmerged_search_block": "Search Block({similarity}):",
|
|
114
|
+
"unmerged_replace_block": "Replace Block:",
|
|
115
|
+
"unmerged_blocks_total": "Total unmerged blocks: {num_blocks}",
|
|
116
|
+
"git_init_required": "⚠️ auto_merge 仅适用于 git 仓库。\n\n请尝试在源目录中使用 git init:\n\n```shell\ncd {{ source_dir }}\ngit init .\n```\n\n然后再次运行 auto-coder。\n错误: {{ error }}",
|
|
117
|
+
"upsert_file": "✅ 更新文件: {{ file_path }}",
|
|
118
|
+
"files_merged": "✅ 成功合并了 {{ total }} 个文件到项目中。",
|
|
119
|
+
"merge_failed": "❌ 合并文件 {{ path }} 失败: {{ error }}",
|
|
120
|
+
"files_merged_total": "✅ 合并了 {{ total }} 个文件到项目中。",
|
|
121
|
+
"ranking_skip": "只有1个候选项,跳过排序",
|
|
122
|
+
"ranking_start": "开始对 {{ count }} 个候选项进行排序",
|
|
123
|
+
"ranking_failed_request": "排序请求失败: {{ error }}",
|
|
124
|
+
"ranking_all_failed": "所有排序请求都失败",
|
|
125
|
+
"ranking_complete": "排序完成,耗时 {{ elapsed }} 秒,总投票数: {{ total_tasks }},最佳候选索引: {{ best_candidate }},得分: {{ scores }},输入token数: {{ input_tokens }},输出token数: {{ output_tokens }}",
|
|
126
|
+
"ranking_process_failed": "排序过程失败: {{ error }}",
|
|
127
|
+
"ranking_failed": "排序失败,耗时 {{ elapsed }} 秒,使用原始顺序"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def get_system_language():
|
|
133
|
+
try:
|
|
134
|
+
return locale.getdefaultlocale()[0][:2]
|
|
135
|
+
except:
|
|
136
|
+
return 'en'
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def get_message(key):
|
|
140
|
+
lang = get_system_language()
|
|
141
|
+
return MESSAGES.get(lang, MESSAGES['en']).get(key, MESSAGES['en'][key])
|
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
|
|
2
2
|
import os
|
|
3
3
|
from byzerllm.utils.client import code_utils
|
|
4
|
-
from autocoder.common import AutoCoderArgs,git_utils
|
|
5
|
-
from typing import List,Union,Tuple
|
|
4
|
+
from autocoder.common import AutoCoderArgs, git_utils
|
|
5
|
+
from typing import List, Union, Tuple
|
|
6
6
|
import pydantic
|
|
7
7
|
import byzerllm
|
|
8
|
-
from loguru import logger
|
|
9
8
|
from autocoder.common.types import CodeGenerateResult, MergeCodeWithoutEffect
|
|
10
9
|
from autocoder.common.code_modification_ranker import CodeModificationRanker
|
|
11
10
|
import hashlib
|
|
12
11
|
from autocoder.common import files as FileUtils
|
|
12
|
+
from autocoder.common.printer import Printer
|
|
13
|
+
from autocoder.common.auto_coder_lang import get_message
|
|
13
14
|
|
|
14
15
|
class PathAndCode(pydantic.BaseModel):
|
|
15
16
|
path: str
|
|
16
17
|
content: str
|
|
17
18
|
|
|
18
19
|
class CodeAutoMerge:
|
|
19
|
-
def __init__(self, llm:byzerllm.ByzerLLM,args:AutoCoderArgs):
|
|
20
|
+
def __init__(self, llm: byzerllm.ByzerLLM, args: AutoCoderArgs):
|
|
20
21
|
self.llm = llm
|
|
21
|
-
self.args = args
|
|
22
|
+
self.args = args
|
|
23
|
+
self.printer = Printer()
|
|
22
24
|
|
|
23
25
|
|
|
24
26
|
def parse_whole_text_v2(self,text: str) -> List[PathAndCode]:
|
|
@@ -159,8 +161,9 @@ class CodeAutoMerge:
|
|
|
159
161
|
try:
|
|
160
162
|
git_utils.commit_changes(self.args.source_dir, f"auto_coder_pre_{file_name}_{md5}")
|
|
161
163
|
except Exception as e:
|
|
162
|
-
|
|
163
|
-
|
|
164
|
+
self.printer.print_in_terminal("git_init_required",
|
|
165
|
+
source_dir=self.args.source_dir, error=str(e))
|
|
166
|
+
return
|
|
164
167
|
|
|
165
168
|
codes = self.parse_whole_text_v2(content)
|
|
166
169
|
for block in codes:
|
|
@@ -168,11 +171,11 @@ class CodeAutoMerge:
|
|
|
168
171
|
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
|
169
172
|
|
|
170
173
|
with open(file_path, "w") as f:
|
|
171
|
-
|
|
174
|
+
self.printer.print_in_terminal("upsert_file", file_path=file_path)
|
|
172
175
|
total += 1
|
|
173
176
|
f.write(block.content)
|
|
174
177
|
|
|
175
|
-
|
|
178
|
+
self.printer.print_in_terminal("files_merged", total=total)
|
|
176
179
|
if not force_skip_git:
|
|
177
180
|
commit_result = git_utils.commit_changes(self.args.source_dir, f"auto_coder_{file_name}_{md5}")
|
|
178
181
|
git_utils.print_commit_info(commit_result=commit_result)
|
|
@@ -4,7 +4,7 @@ from autocoder.common import AutoCoderArgs,git_utils
|
|
|
4
4
|
from typing import List,Union,Tuple
|
|
5
5
|
import pydantic
|
|
6
6
|
import byzerllm
|
|
7
|
-
from
|
|
7
|
+
from autocoder.common.printer import Printer
|
|
8
8
|
import hashlib
|
|
9
9
|
from pathlib import Path
|
|
10
10
|
from itertools import groupby
|
|
@@ -361,7 +361,8 @@ other_hunks_applied = (
|
|
|
361
361
|
class CodeAutoMergeDiff:
|
|
362
362
|
def __init__(self, llm:byzerllm.ByzerLLM,args:AutoCoderArgs):
|
|
363
363
|
self.llm = llm
|
|
364
|
-
self.args = args
|
|
364
|
+
self.args = args
|
|
365
|
+
self.printer = Printer()
|
|
365
366
|
|
|
366
367
|
def get_edits(self,content:str):
|
|
367
368
|
# might raise ValueError for malformed ORIG/UPD blocks
|
|
@@ -514,13 +515,13 @@ class CodeAutoMergeDiff:
|
|
|
514
515
|
try:
|
|
515
516
|
git_utils.commit_changes(self.args.source_dir, f"auto_coder_pre_{file_name}_{md5}")
|
|
516
517
|
except Exception as e:
|
|
517
|
-
|
|
518
|
+
self.printer.print_in_terminal("git_init_required", style="red", source_dir=self.args.source_dir, error=str(e))
|
|
518
519
|
return
|
|
519
520
|
|
|
520
521
|
edits = self.get_edits(content)
|
|
521
522
|
self.apply_edits(edits)
|
|
522
523
|
|
|
523
|
-
|
|
524
|
+
self.printer.print_in_terminal("files_merged_total", total=total)
|
|
524
525
|
if not force_skip_git:
|
|
525
526
|
commit_result = git_utils.commit_changes(self.args.source_dir, f"auto_coder_{file_name}_{md5}")
|
|
526
527
|
git_utils.print_commit_info(commit_result=commit_result)
|
|
@@ -9,7 +9,7 @@ from autocoder.utils.queue_communicate import (
|
|
|
9
9
|
)
|
|
10
10
|
import pydantic
|
|
11
11
|
import byzerllm
|
|
12
|
-
|
|
12
|
+
|
|
13
13
|
import hashlib
|
|
14
14
|
import subprocess
|
|
15
15
|
import tempfile
|
|
@@ -21,6 +21,7 @@ from typing import Union, List, Tuple
|
|
|
21
21
|
from autocoder.common.types import CodeGenerateResult, MergeCodeWithoutEffect
|
|
22
22
|
from autocoder.common.code_modification_ranker import CodeModificationRanker
|
|
23
23
|
from autocoder.common import files as FileUtils
|
|
24
|
+
from autocoder.common.printer import Printer
|
|
24
25
|
|
|
25
26
|
class PathAndCode(pydantic.BaseModel):
|
|
26
27
|
path: str
|
|
@@ -39,6 +40,7 @@ class CodeAutoMergeEditBlock:
|
|
|
39
40
|
self.args = args
|
|
40
41
|
self.fence_0 = fence_0
|
|
41
42
|
self.fence_1 = fence_1
|
|
43
|
+
self.printer = Printer()
|
|
42
44
|
|
|
43
45
|
def run_pylint(self, code: str) -> tuple[bool, str]:
|
|
44
46
|
with tempfile.NamedTemporaryFile(
|
|
@@ -62,12 +64,12 @@ class CodeAutoMergeEditBlock:
|
|
|
62
64
|
os.unlink(temp_file_path)
|
|
63
65
|
if result.returncode != 0:
|
|
64
66
|
error_message = result.stdout.strip() or result.stderr.strip()
|
|
65
|
-
|
|
67
|
+
self.printer.print_in_terminal("pylint_check_failed", error_message=error_message)
|
|
66
68
|
return False, error_message
|
|
67
69
|
return True, ""
|
|
68
70
|
except subprocess.CalledProcessError as e:
|
|
69
71
|
error_message = f"Error running pylint: {str(e)}"
|
|
70
|
-
|
|
72
|
+
self.printer.print_in_terminal("pylint_error", error_message=error_message)
|
|
71
73
|
os.unlink(temp_file_path)
|
|
72
74
|
return False, error_message
|
|
73
75
|
|
|
@@ -344,9 +346,8 @@ class CodeAutoMergeEditBlock:
|
|
|
344
346
|
),
|
|
345
347
|
)
|
|
346
348
|
return
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
logger.warning(s)
|
|
349
|
+
|
|
350
|
+
self.printer.print_in_terminal("unmerged_blocks_warning", num_blocks=len(unmerged_blocks))
|
|
350
351
|
self._print_unmerged_blocks(unmerged_blocks)
|
|
351
352
|
return
|
|
352
353
|
|
|
@@ -355,9 +356,9 @@ class CodeAutoMergeEditBlock:
|
|
|
355
356
|
if file_path.endswith(".py"):
|
|
356
357
|
pylint_passed, error_message = self.run_pylint(new_content)
|
|
357
358
|
if not pylint_passed:
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
359
|
+
self.printer.print_in_terminal("pylint_file_check_failed",
|
|
360
|
+
file_path=file_path,
|
|
361
|
+
error_message=error_message)
|
|
361
362
|
|
|
362
363
|
if changes_made and not force_skip_git:
|
|
363
364
|
try:
|
|
@@ -365,9 +366,9 @@ class CodeAutoMergeEditBlock:
|
|
|
365
366
|
self.args.source_dir, f"auto_coder_pre_{file_name}_{md5}"
|
|
366
367
|
)
|
|
367
368
|
except Exception as e:
|
|
368
|
-
|
|
369
|
-
self.git_require_msg(
|
|
370
|
-
|
|
369
|
+
self.printer.print_str_in_terminal(
|
|
370
|
+
self.git_require_msg(source_dir=self.args.source_dir, error=str(e)),
|
|
371
|
+
style="red"
|
|
371
372
|
)
|
|
372
373
|
return
|
|
373
374
|
# Now, apply the changes
|
|
@@ -406,30 +407,31 @@ class CodeAutoMergeEditBlock:
|
|
|
406
407
|
)
|
|
407
408
|
git_utils.print_commit_info(commit_result=commit_result)
|
|
408
409
|
except Exception as e:
|
|
409
|
-
|
|
410
|
-
self.git_require_msg(
|
|
411
|
-
|
|
412
|
-
)
|
|
410
|
+
self.printer.print_str_in_terminal(
|
|
411
|
+
self.git_require_msg(source_dir=self.args.source_dir, error=str(e)),
|
|
412
|
+
style="red"
|
|
413
413
|
)
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
414
|
+
self.printer.print_in_terminal("merge_success",
|
|
415
|
+
num_files=len(file_content_mapping.keys()),
|
|
416
|
+
num_changes=len(changes_to_make),
|
|
417
|
+
total_blocks=len(codes))
|
|
417
418
|
else:
|
|
418
|
-
|
|
419
|
+
self.printer.print_in_terminal("no_changes_made")
|
|
419
420
|
|
|
420
421
|
def _print_unmerged_blocks(self, unmerged_blocks: List[tuple]):
|
|
421
|
-
|
|
422
|
-
console.print("\n[bold red]Unmerged Blocks:[/bold red]")
|
|
422
|
+
self.printer.print_in_terminal("unmerged_blocks_title", style="bold red")
|
|
423
423
|
for file_path, head, update, similarity in unmerged_blocks:
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
424
|
+
self.printer.print_str_in_terminal(
|
|
425
|
+
f"\n{self.printer.get_message_from_key('unmerged_file_path').format(file_path=file_path)}",
|
|
426
|
+
style="bold blue"
|
|
427
|
+
)
|
|
428
|
+
self.printer.print_str_in_terminal(
|
|
429
|
+
f"\n{self.printer.get_message_from_key('unmerged_search_block').format(similarity=similarity)}",
|
|
430
|
+
style="bold green"
|
|
431
|
+
)
|
|
427
432
|
syntax = Syntax(head, "python", theme="monokai", line_numbers=True)
|
|
428
|
-
console.print(Panel(syntax, expand=False))
|
|
429
|
-
|
|
430
|
-
syntax = Syntax(update, "python", theme="monokai",
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
console.print(
|
|
434
|
-
f"\n[bold red]Total unmerged blocks: {len(unmerged_blocks)}[/bold red]"
|
|
435
|
-
)
|
|
433
|
+
self.printer.console.print(Panel(syntax, expand=False))
|
|
434
|
+
self.printer.print_in_terminal("unmerged_replace_block", style="bold yellow")
|
|
435
|
+
syntax = Syntax(update, "python", theme="monokai", line_numbers=True)
|
|
436
|
+
self.printer.console.print(Panel(syntax, expand=False))
|
|
437
|
+
self.printer.print_in_terminal("unmerged_blocks_total", num_blocks=len(unmerged_blocks), style="bold red")
|
|
@@ -5,7 +5,7 @@ from autocoder.common import AutoCoderArgs, git_utils
|
|
|
5
5
|
from typing import List,Tuple
|
|
6
6
|
import pydantic
|
|
7
7
|
import byzerllm
|
|
8
|
-
from
|
|
8
|
+
from autocoder.common.printer import Printer
|
|
9
9
|
import hashlib
|
|
10
10
|
from pathlib import Path
|
|
11
11
|
from autocoder.common.types import CodeGenerateResult, MergeCodeWithoutEffect
|
|
@@ -84,6 +84,7 @@ class CodeAutoMergeStrictDiff:
|
|
|
84
84
|
def __init__(self, llm: byzerllm.ByzerLLM, args: AutoCoderArgs):
|
|
85
85
|
self.llm = llm
|
|
86
86
|
self.args = args
|
|
87
|
+
self.printer = Printer()
|
|
87
88
|
|
|
88
89
|
|
|
89
90
|
def parse_diff_block(self,text: str) -> List[PathAndCode]:
|
|
@@ -192,7 +193,7 @@ class CodeAutoMergeStrictDiff:
|
|
|
192
193
|
else:
|
|
193
194
|
failed_blocks.append((full_path, content))
|
|
194
195
|
except Exception as e:
|
|
195
|
-
|
|
196
|
+
self.printer.print_in_terminal("merge_failed", style="yellow", path=full_path, error=str(e))
|
|
196
197
|
failed_blocks.append((full_path, content))
|
|
197
198
|
|
|
198
199
|
return MergeCodeWithoutEffect(
|
|
@@ -212,7 +213,7 @@ class CodeAutoMergeStrictDiff:
|
|
|
212
213
|
try:
|
|
213
214
|
git_utils.commit_changes(self.args.source_dir, f"auto_coder_pre_{file_name}_{md5}")
|
|
214
215
|
except Exception as e:
|
|
215
|
-
|
|
216
|
+
self.printer.print_in_terminal("git_init_required", style="red", source_dir=self.args.source_dir, error=str(e))
|
|
216
217
|
return
|
|
217
218
|
|
|
218
219
|
diff_blocks = self.parse_diff_block(content)
|
|
@@ -230,7 +231,7 @@ class CodeAutoMergeStrictDiff:
|
|
|
230
231
|
if not success:
|
|
231
232
|
raise Exception("Error applying diff to file: " + path)
|
|
232
233
|
|
|
233
|
-
|
|
234
|
+
self.printer.print_in_terminal("files_merged_total", total=total)
|
|
234
235
|
if not force_skip_git:
|
|
235
236
|
commit_result = git_utils.commit_changes(self.args.source_dir, f"auto_coder_{file_name}_{md5}")
|
|
236
237
|
git_utils.print_commit_info(commit_result=commit_result)
|
|
@@ -1,70 +1,79 @@
|
|
|
1
1
|
import byzerllm
|
|
2
|
-
from typing import List,Union
|
|
2
|
+
from typing import List, Union
|
|
3
3
|
from autocoder.common import AutoCoderArgs
|
|
4
4
|
from autocoder.common.types import CodeGenerateResult
|
|
5
5
|
from pydantic import BaseModel
|
|
6
|
-
from
|
|
6
|
+
from autocoder.common.printer import Printer
|
|
7
7
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
8
8
|
import traceback
|
|
9
|
-
|
|
9
|
+
from autocoder.common.utils_code_auto_generate import chat_with_continue
|
|
10
|
+
from byzerllm.utils.str2model import to_model
|
|
10
11
|
class RankResult(BaseModel):
|
|
11
|
-
rank_result:List[int]
|
|
12
|
+
rank_result: List[int]
|
|
13
|
+
|
|
12
14
|
|
|
13
15
|
class CodeModificationRanker:
|
|
14
16
|
def __init__(self, llm: byzerllm.ByzerLLM, args: AutoCoderArgs):
|
|
15
17
|
self.llm = llm
|
|
16
|
-
self.args = args
|
|
17
|
-
self.llms = self.llm.get_sub_client(
|
|
18
|
+
self.args = args
|
|
19
|
+
self.llms = self.llm.get_sub_client(
|
|
20
|
+
"generate_rerank_model") or [self.llm]
|
|
18
21
|
if not isinstance(self.llms, list):
|
|
19
22
|
self.llms = [self.llms]
|
|
20
|
-
|
|
23
|
+
self.printer = Printer()
|
|
24
|
+
|
|
21
25
|
@byzerllm.prompt()
|
|
22
|
-
def _rank_modifications(self, s:CodeGenerateResult) -> str:
|
|
26
|
+
def _rank_modifications(self, s: CodeGenerateResult) -> str:
|
|
23
27
|
'''
|
|
24
28
|
对一组代码修改进行质量评估并排序。
|
|
25
29
|
|
|
26
30
|
下面是修改需求:
|
|
27
|
-
|
|
31
|
+
|
|
28
32
|
<edit_requirement>
|
|
29
33
|
{{ s.conversations[0][-2]["content"] }}
|
|
30
34
|
</edit_requirement>
|
|
31
|
-
|
|
35
|
+
|
|
32
36
|
下面是相应的代码修改:
|
|
33
37
|
{% for content in s.contents %}
|
|
34
38
|
<edit_block id="{{ loop.index0 }}">
|
|
35
39
|
{{content}}
|
|
36
40
|
</edit_block>
|
|
37
41
|
{% endfor %}
|
|
38
|
-
|
|
42
|
+
|
|
39
43
|
请输出如下格式的评估结果,只包含 JSON 数据:
|
|
40
44
|
|
|
41
45
|
```json
|
|
42
46
|
{
|
|
43
|
-
"rank_result": [id1, id2, id3]
|
|
47
|
+
"rank_result": [id1, id2, id3]
|
|
44
48
|
}
|
|
45
49
|
```
|
|
46
50
|
|
|
47
|
-
注意:
|
|
48
|
-
1.
|
|
51
|
+
注意:
|
|
52
|
+
1. id 为 edit_block 的 id,按质量从高到低排序,并且 id 必须是数字
|
|
53
|
+
2. 只输出前面要求的 Json 格式就好,不要输出其他内容,Json 需要使用 ```json ```包裹
|
|
49
54
|
'''
|
|
50
|
-
|
|
51
55
|
|
|
52
56
|
def rank_modifications(self, generate_result: CodeGenerateResult) -> CodeGenerateResult:
|
|
53
57
|
import time
|
|
54
58
|
from collections import defaultdict
|
|
55
59
|
|
|
56
60
|
start_time = time.time()
|
|
57
|
-
|
|
61
|
+
|
|
58
62
|
# 如果只有一个候选,直接返回
|
|
59
63
|
if len(generate_result.contents) == 1:
|
|
60
|
-
|
|
64
|
+
self.printer.print_in_terminal("ranking_skip", style="blue")
|
|
61
65
|
return generate_result
|
|
62
|
-
|
|
63
|
-
|
|
66
|
+
|
|
67
|
+
self.printer.print_in_terminal(
|
|
68
|
+
"ranking_start", style="blue", count=len(generate_result.contents))
|
|
64
69
|
generate_times = self.args.generate_times_same_model
|
|
65
70
|
total_tasks = len(self.llms) * generate_times
|
|
71
|
+
|
|
72
|
+
query = self._rank_modifications.prompt(generate_result)
|
|
73
|
+
input_tokens_count = 0
|
|
74
|
+
generated_tokens_count = 0
|
|
66
75
|
try:
|
|
67
|
-
# Create a thread pool with (number of models * generate_times) workers
|
|
76
|
+
# Create a thread pool with (number of models * generate_times) workers
|
|
68
77
|
with ThreadPoolExecutor(max_workers=total_tasks) as executor:
|
|
69
78
|
# Submit tasks for each model and generate_times
|
|
70
79
|
futures = []
|
|
@@ -72,49 +81,70 @@ class CodeModificationRanker:
|
|
|
72
81
|
for _ in range(generate_times):
|
|
73
82
|
futures.append(
|
|
74
83
|
executor.submit(
|
|
75
|
-
|
|
76
|
-
|
|
84
|
+
chat_with_continue,
|
|
85
|
+
llm,
|
|
86
|
+
[{"role": "user", "content": query}],
|
|
87
|
+
{}
|
|
77
88
|
)
|
|
78
89
|
)
|
|
79
|
-
|
|
90
|
+
|
|
80
91
|
# Collect all results
|
|
81
92
|
results = []
|
|
82
93
|
for future in as_completed(futures):
|
|
83
94
|
try:
|
|
84
|
-
|
|
95
|
+
result = future.result()
|
|
96
|
+
input_tokens_count += result.input_tokens_count
|
|
97
|
+
generated_tokens_count += result.generated_tokens_count
|
|
98
|
+
v = to_model(result.content,RankResult)
|
|
85
99
|
results.append(v.rank_result)
|
|
86
100
|
except Exception as e:
|
|
87
|
-
|
|
88
|
-
|
|
101
|
+
self.printer.print_in_terminal(
|
|
102
|
+
"ranking_failed_request", style="yellow", error=str(e))
|
|
103
|
+
if self.args.debug:
|
|
104
|
+
print(traceback.format_exc())
|
|
89
105
|
continue
|
|
90
|
-
|
|
106
|
+
|
|
91
107
|
if not results:
|
|
92
|
-
raise Exception(
|
|
93
|
-
|
|
108
|
+
raise Exception(
|
|
109
|
+
self.printer.get_message_from_key("ranking_all_failed"))
|
|
110
|
+
|
|
94
111
|
# Calculate scores for each candidate
|
|
95
112
|
candidate_scores = defaultdict(float)
|
|
96
113
|
for rank_result in results:
|
|
97
114
|
for idx, candidate_id in enumerate(rank_result):
|
|
98
115
|
# Score is 1/(position + 1) since position starts from 0
|
|
99
116
|
candidate_scores[candidate_id] += 1.0 / (idx + 1)
|
|
100
|
-
|
|
117
|
+
|
|
101
118
|
# Sort candidates by score in descending order
|
|
102
|
-
sorted_candidates = sorted(candidate_scores.keys(),
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
119
|
+
sorted_candidates = sorted(candidate_scores.keys(),
|
|
120
|
+
key=lambda x: candidate_scores[x],
|
|
121
|
+
reverse=True)
|
|
122
|
+
|
|
106
123
|
elapsed = time.time() - start_time
|
|
107
124
|
# Format scores for logging
|
|
108
|
-
score_details = ", ".join(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
125
|
+
score_details = ", ".join(
|
|
126
|
+
[f"candidate {i}: {candidate_scores[i]:.2f}" for i in sorted_candidates])
|
|
127
|
+
self.printer.print_in_terminal(
|
|
128
|
+
"ranking_complete",
|
|
129
|
+
style="green",
|
|
130
|
+
elapsed=f"{elapsed:.2f}",
|
|
131
|
+
total_tasks=total_tasks,
|
|
132
|
+
best_candidate=sorted_candidates[0],
|
|
133
|
+
scores=score_details,
|
|
134
|
+
input_tokens=input_tokens_count,
|
|
135
|
+
output_tokens=generated_tokens_count
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
rerank_contents = [generate_result.contents[i]
|
|
139
|
+
for i in sorted_candidates]
|
|
140
|
+
rerank_conversations = [
|
|
141
|
+
generate_result.conversations[i] for i in sorted_candidates]
|
|
142
|
+
return CodeGenerateResult(contents=rerank_contents, conversations=rerank_conversations)
|
|
143
|
+
|
|
115
144
|
except Exception as e:
|
|
116
|
-
|
|
117
|
-
|
|
145
|
+
self.printer.print_in_terminal(
|
|
146
|
+
"ranking_process_failed", style="red", error=str(e))
|
|
118
147
|
elapsed = time.time() - start_time
|
|
119
|
-
|
|
148
|
+
self.printer.print_in_terminal(
|
|
149
|
+
"ranking_failed", style="yellow", elapsed=f"{elapsed:.2f}")
|
|
120
150
|
return generate_result
|