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.

@@ -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
- logger.error(self.git_require_msg(source_dir=self.args.source_dir,error=str(e)))
163
- return
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
- logger.info(f"Upsert path: {file_path}")
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
- logger.info(f"Merged {total} files into the project.")
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 loguru import logger
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
- logger.error(self.git_require_msg(source_dir=self.args.source_dir,error=str(e)))
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
- logger.info(f"Merged {total} files into the project.")
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
- from loguru import logger
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
- logger.warning(f"Pylint check failed: {error_message}")
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
- logger.error(error_message)
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
- s = f"Found {len(unmerged_blocks)} unmerged blocks, the changes will not be applied. Please review them manually then try again."
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
- logger.warning(
359
- f"Pylint check failed for {file_path}. Changes not applied. Error: {error_message}"
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
- logger.error(
369
- self.git_require_msg(
370
- source_dir=self.args.source_dir, error=str(e))
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
- logger.error(
410
- self.git_require_msg(
411
- source_dir=self.args.source_dir, error=str(e)
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
- logger.info(
415
- f"Merged changes in {len(file_content_mapping.keys())} files {len(changes_to_make)}/{len(codes)} blocks."
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
- logger.warning("No changes were made to any files.")
419
+ self.printer.print_in_terminal("no_changes_made")
419
420
 
420
421
  def _print_unmerged_blocks(self, unmerged_blocks: List[tuple]):
421
- console = Console()
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
- console.print(f"\n[bold blue]File:[/bold blue] {file_path}")
425
- console.print(
426
- f"\n[bold green]Search Block({similarity}):[/bold green]")
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
- console.print("\n[bold yellow]Replace Block:[/bold yellow]")
430
- syntax = Syntax(update, "python", theme="monokai",
431
- line_numbers=True)
432
- console.print(Panel(syntax, expand=False))
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 loguru import logger
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
- logger.warning(f"Failed to apply patch to {full_path}: {str(e)}")
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
- logger.error(self.git_require_msg(source_dir=self.args.source_dir, error=str(e)))
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
- logger.info(f"Merged {total} files into the project.")
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 loguru import logger
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("generate_rerank_model") or [self.llm]
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] // id 为 edit_block 的 id,按质量从高到低排序
47
+ "rank_result": [id1, id2, id3]
44
48
  }
45
49
  ```
46
50
 
47
- 注意:
48
- 1. 只输出前面要求的 Json 格式就好,不要输出其他内容,Json 需要使用 ```json ```包裹
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
- logger.info("Only 1 candidate, skip ranking")
64
+ self.printer.print_in_terminal("ranking_skip", style="blue")
61
65
  return generate_result
62
-
63
- logger.info(f"Start ranking {len(generate_result.contents)} candidates")
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
- self._rank_modifications.with_llm(llm).with_return_type(RankResult).run,
76
- generate_result
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
- v = future.result()
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
- logger.warning(f"Ranking request failed: {str(e)}")
88
- logger.debug(traceback.format_exc())
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("All ranking requests failed")
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
- key=lambda x: candidate_scores[x],
104
- reverse=True)
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([f"candidate {i}: {candidate_scores[i]:.2f}" for i in sorted_candidates])
109
- logger.info(f"Ranking completed in {elapsed:.2f}s, total voters: {total_tasks}, best candidate index: {sorted_candidates[0]}, scores: {score_details}")
110
-
111
- rerank_contents = [generate_result.contents[i] for i in sorted_candidates]
112
- rerank_conversations = [generate_result.conversations[i] for i in sorted_candidates]
113
- return CodeGenerateResult(contents=rerank_contents,conversations=rerank_conversations)
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
- logger.error(f"Ranking process failed: {str(e)}")
117
- logger.debug(traceback.format_exc())
145
+ self.printer.print_in_terminal(
146
+ "ranking_process_failed", style="red", error=str(e))
118
147
  elapsed = time.time() - start_time
119
- logger.warning(f"Ranking failed in {elapsed:.2f}s, using original order")
148
+ self.printer.print_in_terminal(
149
+ "ranking_failed", style="yellow", elapsed=f"{elapsed:.2f}")
120
150
  return generate_result