auto-coder 0.1.269__py3-none-any.whl → 0.1.271__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: auto-coder
3
- Version: 0.1.269
3
+ Version: 0.1.271
4
4
  Summary: AutoCoder: AutoCoder
5
5
  Author: allwefantasy
6
6
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
@@ -1,10 +1,10 @@
1
1
  autocoder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- autocoder/auto_coder.py,sha256=wAPeIawQCOxl8MfT_-PMaDI9ZNB8fNoI6FSSMnHRJeI,65206
2
+ autocoder/auto_coder.py,sha256=SgAAoSpUaPZiiNnEWRYIULEsKBJaKuQK_b7pEl_mKY8,65516
3
3
  autocoder/auto_coder_lang.py,sha256=Rtupq6N3_HT7JRhDKdgCBcwRaiAnyCOR_Gsp4jUomrI,3229
4
4
  autocoder/auto_coder_rag.py,sha256=nwgsXO2-scssWStjX3S910tDp-OZXZRddSYrpyC4Nq0,29021
5
5
  autocoder/auto_coder_rag_client_mcp.py,sha256=QRxUbjc6A8UmDMQ8lXgZkjgqtq3lgKYeatJbDY6rSo0,6270
6
6
  autocoder/auto_coder_rag_mcp.py,sha256=-RrjNwFaS2e5v8XDIrKR-zlUNUE8UBaeOtojffBrvJo,8521
7
- autocoder/auto_coder_runner.py,sha256=o5-Q7hdv-tnDz-GQuWOINRZGCG2xK06J3iBVIfxFuHY,100316
7
+ autocoder/auto_coder_runner.py,sha256=JGyLT231zu2cZgnf98Y-W5wDQ0xORZJef1GfL7SZQto,100725
8
8
  autocoder/auto_coder_server.py,sha256=6YQweNEKUrGAZ3yPvw8_qlNZJYLVSVUXGrn1K6udLts,20413
9
9
  autocoder/benchmark.py,sha256=Ypomkdzd1T3GE6dRICY3Hj547dZ6_inqJbBJIp5QMco,4423
10
10
  autocoder/chat_auto_coder.py,sha256=skujpqYqf4EvBLQARJELxj7Xwq9KQj2FGefUAiutF7c,16711
@@ -12,11 +12,12 @@ autocoder/chat_auto_coder_lang.py,sha256=ShOQVOnMA-WlT-fB9OrOer-xQkbcWxJGl-WMPuZ
12
12
  autocoder/command_args.py,sha256=9aYJ-AmPxP1sQh6ciw04FWHjSn31f2W9afXFwo8wgx4,30441
13
13
  autocoder/lang.py,sha256=U6AjVV8Rs1uLyjFCZ8sT6WWuNUxMBqkXXIOs4S120uk,14511
14
14
  autocoder/models.py,sha256=PlG1tKHSHwB57cKLOl5gTl5yTzFUDzCgeHPJU3N9F6Q,9106
15
- autocoder/version.py,sha256=S9yKKEg96OjIjypcL7XoP28mKfzSNjaNLrgAS0igsBw,23
15
+ autocoder/version.py,sha256=ZgM5wgwos-e45Kw9MewMvDsa_W_7THMB-EhDfus_ybw,23
16
16
  autocoder/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  autocoder/agent/auto_demand_organizer.py,sha256=NWSAEsEk94vT3lGjfo25kKLMwYdPcpy9e-i21txPasQ,6942
18
18
  autocoder/agent/auto_filegroup.py,sha256=CW7bqp0FW1GIEMnl-blyAc2UGT7O9Mom0q66ITz1ckM,6635
19
19
  autocoder/agent/auto_guess_query.py,sha256=rDSdhpPHcOGE5MuDXvIrhCXAPR4ARS1LqpyoLsx2Jhw,11374
20
+ autocoder/agent/auto_learn_from_commit.py,sha256=H_7i-73DZIOOKC9rfh1Uwb8SbWJ9XOJLwem3mhWSBEk,8240
20
21
  autocoder/agent/auto_review_commit.py,sha256=Ks5oCDp-82YXiRfcDCgYabKq0pcMJkMrP4CaWIgw_JE,9143
21
22
  autocoder/agent/auto_tool.py,sha256=DBzip-P_T6ZtT2eHexPcusmKYD0h7ufzp7TLwXAY10E,11554
22
23
  autocoder/agent/coder.py,sha256=x6bdJwDuETGg9ebQnYlUWCxCtQcDGg73LtI6McpWslQ,72034
@@ -25,15 +26,15 @@ autocoder/agent/planner.py,sha256=SZTSZHxHzDmuWZo3K5fs79RwvJLWurg-nbJRRNbX65o,91
25
26
  autocoder/agent/project_reader.py,sha256=tWLaPoLw1gI6kO_NzivQj28KbobU2ceOLuppHMbfGl8,18234
26
27
  autocoder/chat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
28
  autocoder/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
28
- autocoder/commands/auto_command.py,sha256=BbQoSmkzAv6EtVbJDYCgWqMrN8w3A6BdhlMTtEp1dXQ,51384
29
- autocoder/commands/tools.py,sha256=xDhGD1jRN67fGfeck33pM74TxXlAMeo49S3Q9K-VKco,20107
29
+ autocoder/commands/auto_command.py,sha256=ymAYnaxiKfYFegKxE5FhdD8XqXMMf4mbDHJsIOxPaUU,52396
30
+ autocoder/commands/tools.py,sha256=lanjoBGR6H8HDJSY3KrM6ibrtHZbgKX6mKJHSSE66dg,20493
30
31
  autocoder/common/JupyterClient.py,sha256=O-wi6pXeAEYhAY24kDa0BINrLYvKS6rKyWe98pDClS0,2816
31
32
  autocoder/common/ShellClient.py,sha256=fM1q8t_XMSbLBl2zkCNC2J9xuyKN3eXzGm6hHhqL2WY,2286
32
33
  autocoder/common/__init__.py,sha256=d1AmAAYhm4b17dVhAJFwV3Vv2r1lUhMejpjr32Poyg8,12948
33
34
  autocoder/common/anything2images.py,sha256=0ILBbWzY02M-CiWB-vzuomb_J1hVdxRcenAfIrAXq9M,25283
34
35
  autocoder/common/anything2img.py,sha256=4TREa-sOA-iargieUy7MpyCYVUE-9Mmq0wJtwomPqnE,7662
35
36
  autocoder/common/audio.py,sha256=Kn9nWKQddWnUrAz0a_ZUgjcu4VUU_IcZBigT7n3N3qc,7439
36
- autocoder/common/auto_coder_lang.py,sha256=AnWlvw3wCmfozxql5uwzMK1_drrPtzyBo6TpAINHq9Y,29919
37
+ autocoder/common/auto_coder_lang.py,sha256=WZm5XQaYeECiTiFiHjXWHRZKeV0ux2v64hwZqY8ZDDU,31709
37
38
  autocoder/common/auto_configure.py,sha256=L0wjvR-6wFNpP3c9ZxwDOunTtGrzwzjUwsS6BUnJ3W8,12470
38
39
  autocoder/common/buildin_tokenizer.py,sha256=L7d5t39ZFvUd6EoMPXUhYK1toD0FHlRH1jtjKRGokWU,1236
39
40
  autocoder/common/chunk_validation.py,sha256=BrR_ZWavW8IANuueEE7hS8NFAwEvm8TX34WnPx_1hs8,3030
@@ -47,16 +48,16 @@ autocoder/common/code_auto_merge.py,sha256=cMEX44QT59iMPEo5B8OQKU9fAzn4iIahEPk2n
47
48
  autocoder/common/code_auto_merge_diff.py,sha256=jl6rYvioAuN5CWaYRPY4uBFKsDB0_yW6g6KeVlIxPJA,17948
48
49
  autocoder/common/code_auto_merge_editblock.py,sha256=D8petP0X74lScsVktFXEQ4_9u5UDVBRDFF16nkjWGZg,20310
49
50
  autocoder/common/code_auto_merge_strict_diff.py,sha256=VxasaD8IcxOEEilXmxT0WeLLA16JDlvvI7cV5GyyHAI,12086
50
- autocoder/common/code_modification_ranker.py,sha256=sRNIPIsQP8FucxSuBeAHKJ7RWB_iB5Pb_cd0BAhvgGc,10926
51
+ autocoder/common/code_modification_ranker.py,sha256=e1i8oNPN_PfG4O8HQnQWxh9tyOd-ur6jFTXfkHPVXYo,13152
51
52
  autocoder/common/command_completer.py,sha256=Nw_EFXLDuVXbOKGlmzTODNRJUV9mSUEWV73GUmO_WLw,35166
52
- autocoder/common/command_generator.py,sha256=Je7OKJvyzeb4krdEspUJx2tyM2tjk-PkNpkc6bZ7PbA,2563
53
+ autocoder/common/command_generator.py,sha256=kxdkRtnPl4O51ChUmvLTXJqJlto5G02eG6xyx8fcLdA,2669
53
54
  autocoder/common/command_templates.py,sha256=WAixVjue5QmCFAD13K4ElfcOEjdeGr8tFb0atDAbEoo,8658
54
55
  autocoder/common/conf_import_export.py,sha256=w__WsIobe6nmsGns2pV-laU7R5ZvtQNuIbXebxhbY7A,3967
55
56
  autocoder/common/conf_validator.py,sha256=EzSmadpZ22D9e8iWmfeWodUeYJt0IgMoaAOmCleXliI,8795
56
57
  autocoder/common/const.py,sha256=eTjhjh4Aj4CUzviJ81jaf3Y5cwqsLATySn2wJxaS6RQ,2911
57
- autocoder/common/context_pruner.py,sha256=i3DeVG3WABnTS30XyOGFSoJpQDquLDugDkOoT8zkZl0,20517
58
+ autocoder/common/context_pruner.py,sha256=_JUpN8naoGNF2JupirM-5xn8Bl9uQgIVmZP3dXH4F1g,21725
58
59
  autocoder/common/conversation_pruner.py,sha256=pzmrQEa7pFzA66eYSS_h7VqP6ZwUABeooDQzm0PGu0A,5770
59
- autocoder/common/files.py,sha256=2-9CJwOZtyWkk2TQM4gPSkpJ3_cwb-l_3sdsCd1H5GQ,3380
60
+ autocoder/common/files.py,sha256=nPiKcnUcYZbSUn3TskKeTVnAxCJRtuehPuB_5d2imX8,4618
60
61
  autocoder/common/git_utils.py,sha256=qeuF_IB3G3M72asHxWokROU3hINCuFA1nar-UtF9wIU,26022
61
62
  autocoder/common/global_cancel.py,sha256=hT7J7J5ChThIhk2x11_v4v9ASIn4HtwyPD26t2s-fwc,418
62
63
  autocoder/common/image_to_page.py,sha256=yWiTJQ49Lm3j0FngiJhQ9u7qayqE_bOGb8Rk0TmSWx0,14123
@@ -74,7 +75,7 @@ autocoder/common/result_manager.py,sha256=nBcFRj5reBC7vp13M91f4B8iPW8B8OehayHlUd
74
75
  autocoder/common/screenshots.py,sha256=_gA-z1HxGjPShBrtgkdideq58MG6rqFB2qMUJKjrycs,3769
75
76
  autocoder/common/search.py,sha256=245iPFgWhMldoUK3CqCP89ltaxZiNPK73evoG6Fp1h8,16518
76
77
  autocoder/common/search_replace.py,sha256=GphFkc57Hb673CAwmbiocqTbw8vrV7TrZxtOhD0332g,22147
77
- autocoder/common/shells.py,sha256=xSyQChybfClMgp8o07oS5CipME-XQgU564sjrRYK6fM,15977
78
+ autocoder/common/shells.py,sha256=NOgsKbAJVBti_5lSp7kEKAzjH0AJWufWpgXQdJYQF7E,18312
78
79
  autocoder/common/stats_panel.py,sha256=wGl9O45pjVVDxhNumLv4_NfLYSlUP_18Tw4hcJSjw50,4596
79
80
  autocoder/common/sys_prompt.py,sha256=JlexfjZt554faqbgkCmzOJqYUzDHfbnxly5ugFfHfEE,26403
80
81
  autocoder/common/text.py,sha256=KGRQq314GHBmY4MWG8ossRoQi1_DTotvhxchpn78c-k,1003
@@ -94,9 +95,9 @@ autocoder/dispacher/actions/plugins/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQ
94
95
  autocoder/dispacher/actions/plugins/action_regex_project.py,sha256=AqGIkjbqV1eOS3vBoZUTSOpyOlkv1p5h35mI2Kcvekw,6906
95
96
  autocoder/dispacher/actions/plugins/action_translate.py,sha256=GEn7dZA22jy5WyzINomjmzzB795p2Olg-CJla97lRF8,7744
96
97
  autocoder/index/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
97
- autocoder/index/entry.py,sha256=nRKV_T_u3_0HL3xURjd3zisrm-WR4an6_vbUFHaPKJE,14396
98
+ autocoder/index/entry.py,sha256=l-RVlKSLDZkWaWi3jDzMbyWpZ97ZHIEZufsF5gae4Lo,14430
98
99
  autocoder/index/for_command.py,sha256=BFvljE4t6VaMBGboZAuhUCzVK0EitCy_n5D_7FEnihw,3204
99
- autocoder/index/index.py,sha256=1ZNw4s_Fq_ARqjnfU0Vmo1mvdzM6sjaLspjcfinTEh4,27350
100
+ autocoder/index/index.py,sha256=1wUYfK9s0IHmisQL0bzd42PKdFg6WhYgglyMFEcrx6Y,27362
100
101
  autocoder/index/symbols_utils.py,sha256=_EP7E_qWXxluAxq3FGZLlLfdrfwx3FmxCdulI8VGuac,2244
101
102
  autocoder/index/types.py,sha256=a2s_KV5FJlq7jqA2ELSo9E1sjuLwDB-JJYMhSpzBAhU,596
102
103
  autocoder/index/filter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -163,9 +164,9 @@ autocoder/utils/types.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
163
164
  autocoder/utils/auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
164
165
  autocoder/utils/auto_coder_utils/chat_stream_out.py,sha256=lkJ_A-sYU36JMzjFWkk3pR6uos8oZHYt9GPsPe_CPAo,11766
165
166
  autocoder/utils/chat_auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
166
- auto_coder-0.1.269.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
167
- auto_coder-0.1.269.dist-info/METADATA,sha256=nsZmASYIQdIDIfvDFGnIIS8iRDCt5Ex0Q4YHteRoZAU,2643
168
- auto_coder-0.1.269.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
169
- auto_coder-0.1.269.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
170
- auto_coder-0.1.269.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
171
- auto_coder-0.1.269.dist-info/RECORD,,
167
+ auto_coder-0.1.271.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
168
+ auto_coder-0.1.271.dist-info/METADATA,sha256=eqiFY5uN-v89P1pH5YtnxnghXFABqjgmm4pPOqRUOpE,2643
169
+ auto_coder-0.1.271.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
170
+ auto_coder-0.1.271.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
171
+ auto_coder-0.1.271.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
172
+ auto_coder-0.1.271.dist-info/RECORD,,
@@ -0,0 +1,209 @@
1
+ from typing import Generator, List, Dict, Union, Tuple, Optional
2
+ import os
3
+ import yaml
4
+ import byzerllm
5
+ import pydantic
6
+ import git
7
+ from rich.console import Console
8
+ from autocoder.common.printer import Printer
9
+ from autocoder.common import AutoCoderArgs
10
+ from autocoder.common.utils_code_auto_generate import stream_chat_with_continue
11
+ import hashlib
12
+
13
+
14
+ def load_yaml_config(yaml_file: str) -> Dict:
15
+ """加载YAML配置文件"""
16
+ try:
17
+ with open(yaml_file, 'r', encoding='utf-8') as f:
18
+ return yaml.safe_load(f)
19
+ except Exception as e:
20
+ printer = Printer()
21
+ printer.print_in_terminal("yaml_load_error", style="red", yaml_file=yaml_file, error=str(e))
22
+ return {}
23
+
24
+
25
+ class AutoLearnFromCommit:
26
+ def __init__(self, llm: Union[byzerllm.ByzerLLM,byzerllm.SimpleByzerLLM],
27
+ args:AutoCoderArgs,
28
+ skip_diff: bool = False,
29
+ console: Optional[Console] = None):
30
+ """
31
+ 初始化 AutoLearnFromCommit
32
+
33
+ Args:
34
+ llm: ByzerLLM 实例,用于代码学习
35
+ project_dir: 项目根目录
36
+ skip_diff: 是否跳过获取 diff 信息
37
+ """
38
+ self.project_dir = args.source_dir
39
+ self.actions_dir = os.path.join(args.source_dir, "actions")
40
+ self.llm = llm
41
+ self.skip_diff = skip_diff
42
+ self.console = console or Console()
43
+
44
+ @byzerllm.prompt()
45
+ def learn(self, querie_with_urls_and_changes: List[Tuple[str, List[str], Dict[str, Tuple[str, str]]]], query: str) -> Generator[str,None,None]:
46
+ """
47
+ 请根据下面的代码变更,总结出通用的代码调整模式,以帮助实现类似的需求。
48
+
49
+ 下面用户对本次的目标以及你需要总结的要求:
50
+ <goal>
51
+ {{ query }}
52
+ </goal>
53
+
54
+ 下面是本次提交的代码变更:
55
+ <changes>
56
+ {% for query,urls,changes in querie_with_urls_and_changes %}
57
+ ## 原始的任务需求
58
+ {{ query }}
59
+
60
+ 修改的文件:
61
+ {% for url in urls %}
62
+ - {{ url }}
63
+ {% endfor %}
64
+
65
+ 代码变更:
66
+ {% for file_path, (before, after) in changes.items() %}
67
+ ##File: {{ file_path }}
68
+ ##修改前:
69
+
70
+ {{ before or "New file" }}
71
+
72
+ ##File: {{ file_path }}
73
+ ##修改后:
74
+
75
+ {{ after or "File deleted" }}
76
+
77
+ {% endfor %}
78
+ {% endfor %}
79
+ </changes>
80
+
81
+ 请总结以下内容:
82
+ 1. 代码调整模式:描述为了实现这个目标,通常需要对代码做出哪些通用调整
83
+ 2. 关键修改点:指出本次提交中最关键或最具代表性的修改点
84
+ 3. 潜在扩展:基于这些修改,未来类似需求可能需要进行哪些扩展
85
+ 4. 注意事项:在实现类似功能时需要注意哪些问题
86
+
87
+ 总结要求:
88
+ 1. 总结应该抽象化,不要局限于具体实现细节
89
+ 2. 对于每个模式都应该提供明确的适用场景
90
+ 3. 应该考虑代码的可维护性和可扩展性
91
+ 4. 应该指出在不同技术栈或框架下的通用性
92
+ """
93
+ pass
94
+
95
+ def parse_history_tasks(self) -> List[Dict]:
96
+ """
97
+ 解析历史任务信息
98
+
99
+ Returns:
100
+ List[Dict]: 每个字典包含一个历史任务的信息
101
+ """
102
+ # 获取所有YAML文件
103
+ action_files = [
104
+ f for f in os.listdir(self.actions_dir)
105
+ if f[:3].isdigit() and "_" in f and f.endswith('.yml')
106
+ ]
107
+
108
+ # 按序号排序
109
+ def get_seq(name):
110
+ return int(name.split("_")[0])
111
+
112
+ # 获取最新的action文件列表
113
+ action_files = sorted(action_files, key=get_seq)
114
+ action_files.reverse()
115
+
116
+ action_file = action_files[0]
117
+
118
+ querie_with_urls_and_changes = []
119
+ repo = git.Repo(self.project_dir)
120
+
121
+ # 收集所有query、urls和对应的文件变化
122
+ for yaml_file in [action_file]:
123
+ yaml_path = os.path.join(self.actions_dir, yaml_file)
124
+ config = load_yaml_config(yaml_path)
125
+
126
+ if not config:
127
+ continue
128
+
129
+ query = config.get('query', '')
130
+ urls = config.get('urls', [])
131
+
132
+ if query:
133
+ changes = {}
134
+ if not self.skip_diff:
135
+ # 计算文件的MD5用于匹配commit
136
+ with open(yaml_path, 'r', encoding='utf-8') as f:
137
+ yaml_content = f.read()
138
+ file_md5 = hashlib.md5(yaml_content.encode("utf-8")).hexdigest()
139
+ response_id = f"auto_coder_{yaml_file}_{file_md5}"
140
+ # 查找对应的commit
141
+ try:
142
+ for commit in repo.iter_commits():
143
+ if response_id in commit.message:
144
+ if commit.parents:
145
+ parent = commit.parents[0]
146
+ # 获取所有文件的前后内容
147
+ for diff_item in parent.diff(commit):
148
+ file_path = diff_item.a_path if diff_item.a_path else diff_item.b_path
149
+
150
+ # 获取变更前内容
151
+ before_content = None
152
+ try:
153
+ if diff_item.a_blob:
154
+ before_content = repo.git.show(f"{parent.hexsha}:{file_path}")
155
+ except git.exc.GitCommandError:
156
+ pass # 文件可能是新增的
157
+
158
+ # 获取变更后内容
159
+ after_content = None
160
+ try:
161
+ if diff_item.b_blob:
162
+ after_content = repo.git.show(f"{commit.hexsha}:{file_path}")
163
+ except git.exc.GitCommandError:
164
+ pass # 文件可能被删除
165
+
166
+ changes[file_path] = (before_content, after_content)
167
+ break
168
+ except git.exc.GitCommandError as e:
169
+ printer = Printer()
170
+ printer.print_in_terminal("git_command_error", style="red", error=str(e))
171
+ except Exception as e:
172
+ printer = Printer()
173
+ printer.print_in_terminal("get_commit_changes_error", style="red", error=str(e))
174
+
175
+ querie_with_urls_and_changes.append((query, urls, changes))
176
+
177
+ return querie_with_urls_and_changes
178
+
179
+
180
+ def learn_from_commit(self,query: str, conversations: List[Dict]) -> Generator[str,None,None]:
181
+ """
182
+ 从最新的代码提交中学习通用模式
183
+
184
+ Returns:
185
+ Optional[Generator]: 学习结果生成器,如果出错则返回None
186
+ """
187
+ printer = Printer()
188
+ # 获取最新的提交信息
189
+ changes = self.parse_history_tasks()
190
+ if not changes:
191
+ printer.print_in_terminal("no_latest_commit", style="red")
192
+ return None
193
+
194
+ # 调用LLM进行代码学习
195
+ try:
196
+ # 获取 prompt 内容
197
+ query = self.learn.prompt(changes, query)
198
+ new_conversations = conversations.copy()[0:-1]
199
+ new_conversations.append({"role": "user", "content": query})
200
+ # 构造对话消息
201
+ v = stream_chat_with_continue(
202
+ llm=self.llm,
203
+ conversations=new_conversations,
204
+ llm_config={}
205
+ )
206
+ return v
207
+ except Exception as e:
208
+ printer.print_in_terminal("code_learn_error", style="red", error=str(e))
209
+ return None
autocoder/auto_coder.py CHANGED
@@ -1339,6 +1339,10 @@ def main(input_args: Optional[List[str]] = None):
1339
1339
  from autocoder.agent.auto_review_commit import AutoReviewCommit
1340
1340
  reviewer = AutoReviewCommit(llm=chat_llm, args=args)
1341
1341
  v = reviewer.review_commit(query=args.query,conversations=loaded_conversations)
1342
+ elif "learn_from_commit" in args.action:
1343
+ from autocoder.agent.auto_learn_from_commit import AutoLearnFromCommit
1344
+ learner = AutoLearnFromCommit(llm=chat_llm, args=args)
1345
+ v = learner.learn_from_commit(query=args.query,conversations=loaded_conversations)
1342
1346
  else:
1343
1347
  # 预估token数量
1344
1348
  estimated_input_tokens = count_tokens(json.dumps(loaded_conversations, ensure_ascii=False))
@@ -1,4 +1,6 @@
1
1
  from itertools import product
2
+ from rich.console import Console
3
+ from rich.panel import Panel
2
4
  from prompt_toolkit.formatted_text import HTML
3
5
  from prompt_toolkit.shortcuts import radiolist_dialog
4
6
  from prompt_toolkit import prompt
@@ -646,21 +648,28 @@ def revert():
646
648
  with redirect_stdout() as output:
647
649
  auto_coder_main(["revert", "--file", file_path])
648
650
  s = output.getvalue()
649
- print(s, flush=True)
651
+
652
+ console = Console()
653
+ panel = Panel(
654
+ Markdown(s),
655
+ title="Revert Result",
656
+ border_style="green" if "Successfully reverted changes" in s else "red",
657
+ padding=(1, 2),
658
+ expand=False
659
+ )
660
+ console.print(panel)
661
+
650
662
  if "Successfully reverted changes" in s:
651
663
  result_manager.append(content=s, meta={"action": "revert","success":False, "input":{
652
664
  }})
653
- print(
654
- "Reverted the last chat action successfully. Remove the yaml file {file_path}"
655
- )
665
+
656
666
  os.remove(file_path)
657
667
  else:
658
668
  result_manager.append(content=s, meta={"action": "revert","success":False, "input":{
659
669
  }})
660
670
  else:
661
671
  result_manager.append(content="No previous chat action found to revert.", meta={"action": "revert","success":False, "input":{
662
- }})
663
- print("No previous chat action found to revert.")
672
+ }})
664
673
 
665
674
 
666
675
  def add_files(args: List[str]):
@@ -1155,6 +1164,7 @@ def mcp(query: str):
1155
1164
  "silence": conf.get("silence", "true") == "true",
1156
1165
  "include_project_structure": conf.get("include_project_structure", "true")
1157
1166
  == "true",
1167
+ "exclude_files": memory.get("exclude_files", []),
1158
1168
  }
1159
1169
  for key, value in conf.items():
1160
1170
  converted_value = convert_config_value(key, value)
@@ -1224,6 +1234,7 @@ def code_next(query: str):
1224
1234
  "silence": conf.get("silence", "true") == "true",
1225
1235
  "include_project_structure": conf.get("include_project_structure", "true")
1226
1236
  == "true",
1237
+ "exclude_files": memory.get("exclude_files", []),
1227
1238
  }
1228
1239
  for key, value in conf.items():
1229
1240
  converted_value = convert_config_value(key, value)
@@ -1595,6 +1606,9 @@ def chat(query: str):
1595
1606
  if "/review" in query and "/commit" in query:
1596
1607
  yaml_config["action"].append("review_commit")
1597
1608
  query = query.replace("/review", "", 1).replace("/commit", "", 1).strip()
1609
+ elif "/learn" in query:
1610
+ yaml_config["action"].append("learn_from_commit")
1611
+ query = query.replace("/learn", "", 1).strip()
1598
1612
  else:
1599
1613
  is_review = query.strip().startswith("/review")
1600
1614
  if is_review:
@@ -2283,6 +2297,7 @@ def index_build():
2283
2297
  conf = memory.get("conf", {})
2284
2298
  yaml_config = {
2285
2299
  "include_file": ["./base/base.yml"],
2300
+ "exclude_files": memory.get("exclude_files", []),
2286
2301
  }
2287
2302
 
2288
2303
  for key, value in conf.items():
@@ -2295,10 +2310,8 @@ def index_build():
2295
2310
 
2296
2311
  with open(yaml_file, "w",encoding="utf-8") as f:
2297
2312
  f.write(yaml_content)
2298
- try:
2299
- with redirect_stdout() as output:
2300
- auto_coder_main(["index", "--file", yaml_file])
2301
- print(output.getvalue(), flush=True)
2313
+ try:
2314
+ auto_coder_main(["index", "--file", yaml_file])
2302
2315
  completer.refresh_files()
2303
2316
  finally:
2304
2317
  os.remove(yaml_file)
@@ -2315,6 +2328,7 @@ def get_final_config()->AutoCoderArgs:
2315
2328
  "silence": conf.get("silence", "true") == "true",
2316
2329
  "include_project_structure": conf.get("include_project_structure", "true")
2317
2330
  == "true",
2331
+ "exclude_files": memory.get("exclude_files", []),
2318
2332
  }
2319
2333
  for key, value in conf.items():
2320
2334
  converted_value = convert_config_value(key, value)
@@ -2380,10 +2394,8 @@ def index_query(query: str):
2380
2394
 
2381
2395
  with open(yaml_file, "w",encoding="utf-8") as f:
2382
2396
  f.write(yaml_content)
2383
- try:
2384
- with redirect_stdout() as output:
2385
- auto_coder_main(["index-query", "--file", yaml_file])
2386
- print(output.getvalue(), flush=True)
2397
+ try:
2398
+ auto_coder_main(["index-query", "--file", yaml_file])
2387
2399
  finally:
2388
2400
  os.remove(yaml_file)
2389
2401
 
@@ -171,15 +171,21 @@ class CommandAutoTuner:
171
171
  当前用户环境信息如下:
172
172
  <os_info>
173
173
  操作系统: {{ env_info.os_name }} {{ env_info.os_version }}
174
+ 操作系统发行版: {{ os_distribution }}
174
175
  Python版本: {{ env_info.python_version }}
175
176
  终端类型: {{ env_info.shell_type }}
176
177
  终端编码: {{ env_info.shell_encoding }}
178
+
179
+ {%- if shell_type %}
180
+ 脚本类型:{{ shell_type }}
181
+ {%- endif %}
182
+
177
183
  {%- if env_info.conda_env %}
178
184
  Conda环境: {{ env_info.conda_env }}
179
185
  {%- endif %}
180
186
  {%- if env_info.virtualenv %}
181
187
  虚拟环境: {{ env_info.virtualenv }}
182
- {%- endif %}
188
+ {%- endif %}
183
189
  </os_info>
184
190
 
185
191
  我们的目标是根据用户输入和当前上下文,组合多个函数来完成用户的需求。
@@ -221,6 +227,7 @@ class CommandAutoTuner:
221
227
  每个文件的tokens数,你可以根据这个信息来决定如何读取这个文件。比如对于很小的文件,那么可以直接全部读取,
222
228
  而对于分析一个超大文件推荐组合 read_files 带上 line_ranges 参数来读取,或者组合 read_file_withread_file_with_keyword_ranges 等来读取,
223
229
  每个函数你还可以使用多次来获取更多信息。
230
+ 9. 根据操作系统,终端类型,脚本类型等各种信息,在涉及到路径或者脚本的时候,需要考虑平台差异性。
224
231
  </function_combination_readme>
225
232
 
226
233
 
@@ -260,7 +267,13 @@ class CommandAutoTuner:
260
267
  满足需求。
261
268
  """
262
269
 
263
- env_info = detect_env()
270
+ env_info = detect_env()
271
+ shell_type = "bash"
272
+ if shells.is_running_in_cmd():
273
+ shell_type = "cmd"
274
+ elif shells.is_running_in_powershell():
275
+ shell_type = "powershell"
276
+
264
277
  return {
265
278
  "user_input": request.user_input,
266
279
  "current_files": self.memory_config.memory["current_files"]["files"],
@@ -268,9 +281,10 @@ class CommandAutoTuner:
268
281
  "available_commands": self._command_readme.prompt(),
269
282
  "current_conf": json.dumps(self.memory_config.memory["conf"], indent=2),
270
283
  "env_info": env_info,
271
- "shell_type": shells.get_terminal_name(),
284
+ "shell_type": shell_type,
272
285
  "shell_encoding": shells.get_terminal_encoding(),
273
- "conversation_safe_zone_tokens": self.args.conversation_prune_safe_zone_tokens
286
+ "conversation_safe_zone_tokens": self.args.conversation_prune_safe_zone_tokens,
287
+ "os_distribution": shells.get_os_distribution()
274
288
  }
275
289
 
276
290
  @byzerllm.prompt()
@@ -1179,6 +1193,18 @@ class CommandAutoTuner:
1179
1193
  此时会返回诸如 "ts,py,java,go,js,ts" 这样的字符串,表示项目类型。
1180
1194
  </usage>
1181
1195
  </command>
1196
+
1197
+ <command>
1198
+ <name>response_user</name>
1199
+ <description>响应用户。</description>
1200
+ <usage>
1201
+ 如果你需要直接发送信息给用户,那么可以通过 response_user 函数来直接回复用户。
1202
+
1203
+ 比如用户问你是谁?
1204
+ 你可以通过如下方式来回答:
1205
+ response_user(response="你好,我是 auto-coder")
1206
+ </usage>
1207
+ </command>
1182
1208
  </commands>
1183
1209
 
1184
1210
 
@@ -1224,7 +1250,9 @@ class CommandAutoTuner:
1224
1250
  "get_project_related_files": self.tools.get_project_related_files,
1225
1251
  "ask_user":self.tools.ask_user,
1226
1252
  "read_file_with_keyword_ranges": self.tools.read_file_with_keyword_ranges,
1227
- "get_project_type": self.project_type_analyzer.analyze,
1253
+ "get_project_type": self.project_type_analyzer.analyze,
1254
+ "response_user": self.tools.response_user,
1255
+
1228
1256
  }
1229
1257
 
1230
1258
  if command not in command_map:
@@ -28,6 +28,10 @@ from autocoder.utils.queue_communicate import (
28
28
  import sys
29
29
  import io
30
30
  from autocoder.common import files as files_utils
31
+ from autocoder.common.printer import Printer
32
+ from prompt_toolkit import PromptSession
33
+ from prompt_toolkit.styles import Style
34
+
31
35
 
32
36
  @byzerllm.prompt()
33
37
  def detect_rm_command(command: str) -> Bool:
@@ -56,6 +60,7 @@ class AutoCommandTools:
56
60
  self.args = args
57
61
  self.llm = llm
58
62
  self.result_manager = ResultManager()
63
+ self.printer = Printer()
59
64
 
60
65
  def ask_user(self,question:str) -> str:
61
66
  '''
@@ -91,35 +96,43 @@ class AutoCommandTools:
91
96
  )
92
97
 
93
98
  # 显示问题面板
94
- console.print(question_panel)
99
+ console.print(question_panel)
95
100
 
96
- # 创建一个自定义提示符
97
- prompt = Prompt.ask(
98
- "\n[bold green]Your Answer[/bold green]",
99
- console=console
100
- )
101
+ session = PromptSession( message=self.printer.get_message_from_key('tool_ask_user'))
102
+ try:
103
+ answer = session.prompt()
104
+ except KeyboardInterrupt:
105
+ answer = ""
101
106
 
102
- # 获取用户的回答
103
- answer = prompt
107
+ self.result_manager.append(content=answer, meta = {
108
+ "action": "ask_user",
109
+ "input": {
110
+ "question": question
111
+ }
112
+ })
104
113
 
105
- # 显示用户的回答
106
- answer_text = Text(answer, style="italic")
114
+ return answer
115
+
116
+ def response_user(self, response: str):
117
+ console = Console()
118
+ answer_text = Text(response, style="italic")
107
119
  answer_panel = Panel(
108
120
  answer_text,
109
- title="[bold yellow]Your Response[/bold yellow]",
121
+ title="",
110
122
  border_style="green",
111
123
  expand=False
112
124
  )
113
125
  console.print(answer_panel)
114
126
 
115
- self.result_manager.append(content=answer, meta = {
116
- "action": "ask_user",
127
+ self.result_manager.append(content=response, meta = {
128
+ "action": "response_user",
117
129
  "input": {
118
- "question": question
130
+ "response": response
119
131
  }
120
132
  })
121
133
 
122
- return answer
134
+ return response
135
+
123
136
 
124
137
  def run_python_code(self, code: str) -> str:
125
138
  """
@@ -130,7 +130,7 @@ MESSAGES = {
130
130
  "quick_filter_too_long": "⚠️ index file is too large ({{ tokens_len }}/{{ max_tokens }}). The query will be split into {{ split_size }} chunks.",
131
131
  "quick_filter_tokens_len": "📊 Current index size: {{ tokens_len }} tokens",
132
132
  "estimated_chat_input_tokens": "Estimated chat input tokens: {{ estimated_input_tokens }}",
133
- "estimated_input_tokens_in_generate": "Estimated input tokens in generate ({{ generate_mode }}): {{ estimated_input_tokens }}",
133
+ "estimated_input_tokens_in_generate": "Estimated input tokens in generate ({{ generate_mode }}): {{ estimated_input_tokens }}",
134
134
  "model_has_access_restrictions": "{{model_name}} has access restrictions, cannot use the current function",
135
135
  "auto_command_not_found": "Auto command not found: {{command}}. Please check your input and try again.",
136
136
  "auto_command_failed": "Auto command failed: {{error}}. Please check your input and try again.",
@@ -163,11 +163,22 @@ MESSAGES = {
163
163
  "diff_blocks_title":"diff blocks",
164
164
  "index_exclude_files_error": "index filter exclude files fail: {{ error }}",
165
165
  "file_sliding_window_processing": "File {{ file_path }} is too large ({{ tokens }} tokens), processing with sliding window...",
166
- "file_snippet_processing": "Processing file {{ file_path }} with code snippet extraction..."
166
+ "file_snippet_processing": "Processing file {{ file_path }} with code snippet extraction...",
167
+ "context_pruning_start": "⚠️ Context pruning started. Total tokens: {{ total_tokens }} (max allowed: {{ max_tokens }}). Applying strategy: {{ strategy }}.",
168
+ "context_pruning_reason": "Context length exceeds maximum limit ({{ total_tokens }} > {{ max_tokens }}). Pruning is required to fit within the model's context window.",
169
+ "rank_code_modification_title": "{{model_name}} ranking codes",
170
+ "sorted_files_message": "Reordered files:\n{% for file in files %}- {{ file }}\n{% endfor %}",
171
+ "estimated_input_tokens_in_ranking": "estimate input token {{ estimated_input_tokens }} when ranking",
172
+ "file_snippet_procesed": "{{ file_path }} processed with tokens: {{ tokens }} => {{ snippet_tokens }}. Current total tokens: {{ total_tokens }}",
173
+ "tool_ask_user": "Your Reply: ",
174
+ "tool_ask_user_accept":"Your Response received",
175
+
167
176
  },
168
177
  "zh": {
169
178
  "file_sliding_window_processing": "文件 {{ file_path }} 过大 ({{ tokens }} tokens),正在使用滑动窗口处理...",
170
179
  "file_snippet_processing": "正在对文件 {{ file_path }} 进行代码片段提取...",
180
+ "context_pruning_start": "⚠️ 开始上下文剪枝。总token数: {{ total_tokens }} (最大允许: {{ max_tokens }})。正在应用策略: {{ strategy }}。",
181
+ "context_pruning_reason": "上下文长度超过最大限制 ({{ total_tokens }} > {{ max_tokens }})。需要进行剪枝以适配模型的上下文窗口。",
171
182
  "file_scored_message": "文件评分: {{file_path}} - 分数: {{score}}",
172
183
  "invalid_file_pattern": "无效的文件模式: {{file_pattern}}. 例如: regex://.*/package-lock\\.json",
173
184
  "conf_not_found": "未找到配置文件: {{path}}",
@@ -308,7 +319,7 @@ MESSAGES = {
308
319
  "quick_filter_title": "{{ model_name }} 正在分析如何筛选上下文...",
309
320
  "quick_filter_failed": "❌ 快速过滤器失败: {{ error }}. ",
310
321
  "estimated_chat_input_tokens": "对话输入token预估为: {{ estimated_input_tokens }}",
311
- "estimated_input_tokens_in_generate": "生成代码({{ generate_mode }})预计输入token数: {{ estimated_input_tokens_in_generate }}",
322
+ "estimated_input_tokens_in_generate": "生成代码({{ generate_mode }})预计输入token数: {{ estimated_input_tokens }}",
312
323
  "model_has_access_restrictions": "{{model_name}} 有访问限制,无法使用当前功能",
313
324
  "auto_command_not_found": "未找到自动命令: {{command}}。请检查您的输入并重试。",
314
325
  "auto_command_failed": "自动命令执行失败: {{error}}。请检查您的输入并重试。",
@@ -324,7 +335,13 @@ MESSAGES = {
324
335
  "index_import_success": "索引导入成功: {{path}}",
325
336
  "edits_title": "编辑块",
326
337
  "diff_blocks_title": "差异块",
327
- "index_exclude_files_error": "索引排除文件时出错: {{error}}"
338
+ "index_exclude_files_error": "索引排除文件时出错: {{error}}",
339
+ "rank_code_modification_title": "模型{{model_name}}对代码打分",
340
+ "sorted_files_message": "重新排序后的文件路径:\n{% for file in files %}- {{ file }}\n{% endfor %}",
341
+ "estimated_input_tokens_in_ranking": "排序预计输入token数: {{ estimated_input_tokens }}",
342
+ "file_snippet_procesed": "文件 {{ file_path }} 处理后token数: {{ tokens }} => {{ snippet_tokens }} 当前总token数: {{ total_tokens }}",
343
+ "tool_ask_user": "您的回复: ",
344
+ "tool_ask_user_accept":"收到您的回复",
328
345
  }}
329
346
 
330
347
 
@@ -6,11 +6,13 @@ from pydantic import BaseModel
6
6
  from autocoder.common.printer import Printer
7
7
  from concurrent.futures import ThreadPoolExecutor, as_completed
8
8
  import traceback
9
- from autocoder.common.utils_code_auto_generate import chat_with_continue
9
+ from autocoder.common.utils_code_auto_generate import chat_with_continue,stream_chat_with_continue
10
10
  from byzerllm.utils.str2model import to_model
11
+ from autocoder.utils.auto_coder_utils.chat_stream_out import stream_out
11
12
  from autocoder.utils.llms import get_llm_names, get_model_info
12
13
  from autocoder.common.types import CodeGenerateResult, MergeCodeWithoutEffect
13
14
  import os
15
+ from autocoder.rag.token_counter import count_tokens
14
16
 
15
17
  class RankResult(BaseModel):
16
18
  rank_result: List[int]
@@ -133,6 +135,15 @@ class CodeModificationRanker:
133
135
  else:
134
136
  raise Exception(f"Invalid rank strategy: {self.args.rank_strategy}")
135
137
 
138
+ # 计算 query 的 token 数量
139
+ token_count = count_tokens(query)
140
+
141
+ # 打印 token 统计信息
142
+ self.printer.print_in_terminal(
143
+ "estimated_input_tokens_in_ranking",
144
+ estimated_input_tokens=token_count
145
+ )
146
+
136
147
  input_tokens_count = 0
137
148
  generated_tokens_count = 0
138
149
  try:
@@ -145,16 +156,25 @@ class CodeModificationRanker:
145
156
  self.printer.print_in_terminal(
146
157
  "ranking_start", style="blue", count=len(generate_result.contents), model_name=model_name)
147
158
 
148
- for _ in range(rank_times):
149
-
150
- futures.append(
151
- executor.submit(
152
- chat_with_continue,
153
- llm,
154
- [{"role": "user", "content": query}],
155
- {}
159
+ for i in range(rank_times):
160
+ if i == 0:
161
+ futures.append(
162
+ executor.submit(
163
+ stream_chat_with_continue,
164
+ llm,
165
+ [{"role": "user", "content": query}],
166
+ {}
167
+ )
168
+ )
169
+ else:
170
+ futures.append(
171
+ executor.submit(
172
+ chat_with_continue,
173
+ llm,
174
+ [{"role": "user", "content": query}],
175
+ {}
176
+ )
156
177
  )
157
- )
158
178
 
159
179
  # Collect all results
160
180
  results = []
@@ -180,7 +200,31 @@ class CodeModificationRanker:
180
200
  total_input_cost = 0.0
181
201
  total_output_cost = 0.0
182
202
 
183
- for future, model_name in zip(futures, model_names):
203
+ # 第一个future使用流式输出
204
+ stream_future = futures[0]
205
+ model_name = model_names[0]
206
+ stream_generator = stream_future.result()
207
+ full_response, last_meta = stream_out(
208
+ stream_generator,
209
+ model_name=model_name,
210
+ title=self.printer.get_message_from_key_with_format(
211
+ "rank_code_modification_title", model_name=model_name),
212
+ args=self.args
213
+ )
214
+
215
+ if last_meta:
216
+ input_tokens_count += last_meta.input_tokens_count
217
+ generated_tokens_count += last_meta.generated_tokens_count
218
+ # 计算成本
219
+ info = model_info_map.get(model_name, {})
220
+ # 计算公式:token数 * 单价 / 1000000
221
+ total_input_cost += (last_meta.input_tokens_count * info.get("input_cost", 0.0)) / 1000000
222
+ total_output_cost += (last_meta.generated_tokens_count * info.get("output_cost", 0.0)) / 1000000
223
+
224
+ v = to_model(full_response,RankResult)
225
+ results.append(v.rank_result)
226
+
227
+ for future, model_name in zip(futures[1:], model_names[1:]):
184
228
  try:
185
229
  result = future.result()
186
230
  input_tokens_count += result.input_tokens_count
@@ -12,6 +12,7 @@ def _generate_shell_script(user_input: str) -> str:
12
12
  环境信息如下:
13
13
 
14
14
  操作系统: {{ env_info.os_name }} {{ env_info.os_version }}
15
+ 操作系统发行版: {{ os_distribution }}
15
16
  Python版本: {{ env_info.python_version }}
16
17
  终端类型: {{ env_info.shell_type }}
17
18
  终端编码: {{ env_info.shell_encoding }}
@@ -49,7 +50,8 @@ def _generate_shell_script(user_input: str) -> str:
49
50
  return {
50
51
  "env_info": env_info,
51
52
  "shell_type": shell_type,
52
- "shell_encoding": shells.get_terminal_encoding()
53
+ "shell_encoding": shells.get_terminal_encoding(),
54
+ "os_distribution": shells.get_os_distribution()
53
55
  }
54
56
 
55
57
 
@@ -287,8 +287,12 @@ class PruneContext:
287
287
  content_snippets = self._build_snippet_content(file_path, content, merged_snippets)
288
288
  snippet_tokens = count_tokens(content_snippets)
289
289
  if token_count + snippet_tokens <= self.max_tokens:
290
- selected_files.append(SourceCode(module_name=file_path,source_code=content_snippets,tokens=snippet_tokens))
290
+ selected_files.append(SourceCode(module_name=file_path,source_code=content_snippets,tokens=snippet_tokens))
291
291
  token_count += snippet_tokens
292
+ self.printer.print_in_terminal("file_snippet_procesed", file_path=file_path,
293
+ total_tokens=token_count,
294
+ tokens=tokens,
295
+ snippet_tokens=snippet_tokens)
292
296
  continue
293
297
  else:
294
298
  break
@@ -315,11 +319,15 @@ class PruneContext:
315
319
  content_snippets = self._build_snippet_content(file_path, content, snippets)
316
320
 
317
321
  snippet_tokens = count_tokens(content_snippets)
318
- if token_count + snippet_tokens <= self.max_tokens:
322
+ if token_count + snippet_tokens <= self.max_tokens:
319
323
  selected_files.append(SourceCode(module_name=file_path,
320
324
  source_code=content_snippets,
321
325
  tokens=snippet_tokens))
322
326
  token_count += snippet_tokens
327
+ self.printer.print_in_terminal("file_snippet_procesed", file_path=file_path,
328
+ total_tokens = token_count,
329
+ tokens=tokens,
330
+ snippet_tokens=snippet_tokens)
323
331
  else:
324
332
  break
325
333
  except Exception as e:
@@ -379,7 +387,26 @@ class PruneContext:
379
387
  total_tokens,sources = self._count_tokens(file_paths)
380
388
  if total_tokens <= self.max_tokens:
381
389
  return sources
382
- # print(f"total_tokens: {total_tokens} {self.max_tokens}, 进行策略: {strategy}")
390
+
391
+ self.printer.print_in_terminal(
392
+ "context_pruning_reason",
393
+ total_tokens=total_tokens,
394
+ max_tokens=self.max_tokens,
395
+ style="yellow"
396
+ )
397
+
398
+ self.printer.print_in_terminal(
399
+ "sorted_files_message",
400
+ files=file_paths
401
+ )
402
+
403
+ self.printer.print_in_terminal(
404
+ "context_pruning_start",
405
+ total_tokens=total_tokens,
406
+ max_tokens=self.max_tokens,
407
+ strategy=strategy
408
+ )
409
+
383
410
  if strategy == "score":
384
411
  return self._score_and_filter_files(file_paths, conversations)
385
412
  if strategy == "delete":
autocoder/common/files.py CHANGED
@@ -4,8 +4,7 @@ from typing import List, Dict, Union, Generator, Tuple
4
4
  def read_file(file_path):
5
5
  """Read a file with automatic encoding detection.
6
6
 
7
- Tries common encodings in sequence (UTF-8 > GBK > UTF-16 > Latin-1) to handle
8
- cross-platform encoding issues between Windows and Linux systems.
7
+ Tries common encodings in sequence to handle cross-platform encoding issues.
9
8
 
10
9
  Args:
11
10
  file_path (str): Path to the file to read
@@ -16,7 +15,10 @@ def read_file(file_path):
16
15
  Raises:
17
16
  ValueError: If the file cannot be decoded with any of the tried encodings
18
17
  """
19
- encodings = ['utf-8', 'gbk', 'utf-16', 'latin-1']
18
+ # Expanded list of encodings to try
19
+ encodings = ['utf-8', 'gbk', 'utf-16', 'latin-1',
20
+ 'big5', 'shift_jis', 'euc-jp', 'iso-8859-1',
21
+ 'cp1252', 'ascii']
20
22
 
21
23
  for encoding in encodings:
22
24
  try:
@@ -25,22 +27,54 @@ def read_file(file_path):
25
27
  return content
26
28
  except UnicodeDecodeError:
27
29
  continue
30
+ except Exception as e:
31
+ # Log other errors but continue trying other encodings
32
+ continue
28
33
 
29
- raise ValueError(get_message_with_format("file_decode_error",
30
- file_path=file_path,
31
- encodings=", ".join(encodings)))
34
+ # If all encodings fail, try reading as binary and decode with 'replace'
35
+ try:
36
+ with open(file_path, 'rb') as f:
37
+ return f.read().decode('utf-8', errors='replace')
38
+ except Exception as e:
39
+ raise ValueError(get_message_with_format("file_decode_error",
40
+ file_path=file_path,
41
+ encodings=", ".join(encodings)))
32
42
 
33
43
  def read_lines(file_path:str):
34
- encodings = ['utf-8', 'gbk', 'utf-16', 'latin-1']
44
+ """Read a file line by line with automatic encoding detection.
45
+
46
+ Args:
47
+ file_path (str): Path to the file to read
48
+
49
+ Returns:
50
+ List[str]: List of lines in the file
51
+
52
+ Raises:
53
+ ValueError: If the file cannot be decoded with any of the tried encodings
54
+ """
55
+ # Expanded list of encodings to try
56
+ encodings = ['utf-8', 'gbk', 'utf-16', 'latin-1',
57
+ 'big5', 'shift_jis', 'euc-jp', 'iso-8859-1',
58
+ 'cp1252', 'ascii']
59
+
35
60
  for encoding in encodings:
36
61
  try:
37
62
  with open(file_path, 'r', encoding=encoding) as f:
38
63
  return f.readlines()
39
64
  except UnicodeDecodeError:
40
65
  continue
41
- raise ValueError(get_message_with_format("file_decode_error",
42
- file_path=file_path,
43
- encodings=", ".join(encodings)))
66
+ except Exception as e:
67
+ # Log other errors but continue trying other encodings
68
+ continue
69
+
70
+ # If all encodings fail, try reading as binary and decode with 'replace'
71
+ try:
72
+ with open(file_path, 'rb') as f:
73
+ return f.read().decode('utf-8', errors='replace').splitlines()
74
+ except Exception as e:
75
+ raise ValueError(get_message_with_format("file_decode_error",
76
+ file_path=file_path,
77
+ encodings=", ".join(encodings)))
44
78
 
45
79
 
46
80
 
@@ -5,6 +5,7 @@ import subprocess
5
5
  import platform
6
6
  import tempfile
7
7
  import uuid
8
+ import re
8
9
  from rich.console import Console
9
10
  from rich.panel import Panel
10
11
  from rich.text import Text
@@ -187,6 +188,71 @@ def _get_windows_terminal_name() -> str:
187
188
  # 默认返回 cmd.exe
188
189
  return 'cmd'
189
190
 
191
+ def get_os_distribution() -> str:
192
+ """
193
+ 获取操作系统发行版名称
194
+
195
+ Returns:
196
+ str: 操作系统发行版名称,如 'Ubuntu 22.04', 'CentOS 7', 'Windows 10' 等
197
+ """
198
+ system = platform.system()
199
+
200
+ if system == 'Windows':
201
+ # Windows 系统版本
202
+ version = sys.getwindowsversion()
203
+ if version.major == 10 and version.build >= 22000:
204
+ return 'Windows 11'
205
+ elif version.major == 10:
206
+ return 'Windows 10'
207
+ elif version.major == 6:
208
+ if version.minor == 3:
209
+ return 'Windows 8.1'
210
+ elif version.minor == 2:
211
+ return 'Windows 8'
212
+ elif version.minor == 1:
213
+ return 'Windows 7'
214
+ elif version.minor == 0:
215
+ return 'Windows Vista'
216
+ elif version.major == 5:
217
+ if version.minor == 2:
218
+ return 'Windows Server 2003'
219
+ elif version.minor == 1:
220
+ return 'Windows XP'
221
+ return f'Windows {version.major}.{version.minor}'
222
+
223
+ elif system == 'Linux':
224
+ # Linux 发行版检测
225
+ try:
226
+ # 尝试读取 /etc/os-release
227
+ with open('/etc/os-release', 'r') as f:
228
+ os_release = f.read()
229
+
230
+ # 解析 NAME 和 VERSION_ID
231
+ name = re.search(r'NAME="([^"]+)"', os_release)
232
+ version = re.search(r'VERSION_ID="([^"]+)"', os_release)
233
+
234
+ if name and version:
235
+ return f"{name.group(1)} {version.group(1)}"
236
+
237
+ # 如果无法解析,尝试 lsb_release
238
+ try:
239
+ output = subprocess.check_output(['lsb_release', '-d'], stderr=subprocess.STDOUT)
240
+ return output.decode('utf-8').split(':')[1].strip()
241
+ except (subprocess.CalledProcessError, FileNotFoundError):
242
+ pass
243
+
244
+ # 最后尝试 uname
245
+ return platform.platform()
246
+ except Exception:
247
+ return platform.platform()
248
+
249
+ elif system == 'Darwin':
250
+ # macOS 版本
251
+ version = platform.mac_ver()[0]
252
+ return f'macOS {version}'
253
+
254
+ return platform.platform()
255
+
190
256
  def _get_unix_terminal_name() -> str:
191
257
  """Linux/Mac 系统终端检测"""
192
258
  # 从环境变量获取终端名称
@@ -238,6 +304,8 @@ def get_terminal_encoding() -> str:
238
304
  return _get_unix_terminal_encoding()
239
305
 
240
306
  def _get_windows_terminal_encoding() -> str:
307
+ # 添加发行版信息到环境变量
308
+ # os.environ['OS_DISTRIBUTION'] = get_os_distribution()
241
309
  """Windows 系统编码检测"""
242
310
  try:
243
311
  # 通过 chcp 命令获取代码页
autocoder/index/entry.py CHANGED
@@ -291,10 +291,12 @@ def build_index_and_filter_files(
291
291
  # 提取排序后的文件路径列表
292
292
  sorted_file_paths = [file_path for _, file_path in position_file_pairs]
293
293
  # 根据 sorted_file_paths 重新排序 temp_sources
294
- temp_sources.sort(key=lambda x: sorted_file_paths.index(x.module_name) if x.module_name in sorted_file_paths else len(sorted_file_paths))
295
- pruned_files = context_pruner.handle_overflow([source.module_name for source in temp_sources], [{"role":"user","content":args.query}], args.context_prune_strategy)
294
+ temp_sources.sort(key=lambda x: sorted_file_paths.index(x.module_name) if x.module_name in sorted_file_paths else len(sorted_file_paths))
295
+
296
+ pruned_files = context_pruner.handle_overflow([source.module_name for source in temp_sources], [{"role":"user","content":args.query}], args.context_prune_strategy)
296
297
  source_code_list.sources = pruned_files
297
298
 
299
+
298
300
  if args.request_id and not args.skip_events:
299
301
  queue_communicate.send_event(
300
302
  request_id=args.request_id,
autocoder/index/index.py CHANGED
@@ -400,7 +400,7 @@ class IndexManager:
400
400
 
401
401
  # 删除被排除的文件
402
402
  try:
403
- exclude_patterns = self.parse_exclude_files(self.args.exclude_files)
403
+ exclude_patterns = self.parse_exclude_files(self.args.exclude_files)
404
404
  for file_path in index_data:
405
405
  if self.filter_exclude_files(file_path, exclude_patterns):
406
406
  keys_to_remove.append(file_path)
autocoder/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.269"
1
+ __version__ = "0.1.271"