auto-coder 0.1.358__py3-none-any.whl → 0.1.360__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.358.dist-info → auto_coder-0.1.360.dist-info}/METADATA +1 -1
- {auto_coder-0.1.358.dist-info → auto_coder-0.1.360.dist-info}/RECORD +21 -18
- autocoder/agent/auto_review_commit.py +5 -3
- autocoder/common/code_auto_generate.py +21 -0
- autocoder/common/code_auto_generate_diff.py +26 -2
- autocoder/common/code_auto_generate_editblock.py +25 -0
- autocoder/common/code_auto_generate_strict_diff.py +26 -1
- autocoder/common/directory_cache/__init__.py +1 -0
- autocoder/common/directory_cache/cache.py +192 -0
- autocoder/common/directory_cache/test_cache.py +190 -0
- autocoder/common/file_monitor/monitor.py +1 -1
- autocoder/common/v2/code_auto_generate.py +83 -0
- autocoder/common/v2/code_auto_generate_diff.py +26 -3
- autocoder/common/v2/code_auto_generate_editblock.py +24 -2
- autocoder/common/v2/code_auto_generate_strict_diff.py +25 -3
- autocoder/rag/api_server.py +7 -0
- autocoder/version.py +1 -1
- {auto_coder-0.1.358.dist-info → auto_coder-0.1.360.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.358.dist-info → auto_coder-0.1.360.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.358.dist-info → auto_coder-0.1.360.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.358.dist-info → auto_coder-0.1.360.dist-info}/top_level.txt +0 -0
|
@@ -14,7 +14,7 @@ autocoder/command_parser.py,sha256=fx1g9E6GaM273lGTcJqaFQ-hoksS_Ik2glBMnVltPCE,1
|
|
|
14
14
|
autocoder/lang.py,sha256=PFtATuOhHRnfpqHQkXr6p4C893JvpsgwTMif3l-GEi0,14321
|
|
15
15
|
autocoder/models.py,sha256=Gu50IATQtZtgEir1PpCfwgH6o4ygw6XqqbQRj3lx5dU,13798
|
|
16
16
|
autocoder/run_context.py,sha256=IUfSO6_gp2Wt1blFWAmOpN0b0nDrTTk4LmtCYUBIoro,1643
|
|
17
|
-
autocoder/version.py,sha256=
|
|
17
|
+
autocoder/version.py,sha256=AJWkVCVcNxcQiTT91kp7mLT7D-FHFLU1zfRiNjiCFIE,23
|
|
18
18
|
autocoder/agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
19
|
autocoder/agent/agentic_edit.py,sha256=XsfePZ-t6M-uBSdG1VLZXk1goqXk2HPeJ_A8IYyBuWQ,58896
|
|
20
20
|
autocoder/agent/agentic_edit_types.py,sha256=oFcDd_cxJ2yH9Ed1uTpD3BipudgoIEWDMPb5pAkq4gI,3288
|
|
@@ -24,7 +24,7 @@ autocoder/agent/auto_filegroup.py,sha256=pBsAkBcpFTff-9L5OwI8xhf2xPKpl-aZwz-skF2
|
|
|
24
24
|
autocoder/agent/auto_guess_query.py,sha256=rDSdhpPHcOGE5MuDXvIrhCXAPR4ARS1LqpyoLsx2Jhw,11374
|
|
25
25
|
autocoder/agent/auto_learn.py,sha256=uNpfvE_EYGi79KUJEe7g-j5VanbmYOYYnxWf32DaWGE,22359
|
|
26
26
|
autocoder/agent/auto_learn_from_commit.py,sha256=edD4GQJyO2qvVnTKyldeswWoNeKe1Aaua6ieJzlGlFI,10662
|
|
27
|
-
autocoder/agent/auto_review_commit.py,sha256=
|
|
27
|
+
autocoder/agent/auto_review_commit.py,sha256=1z9FOUDPZTWnrPmJ-TwUlXLOTDPmRcAlYqXbUj1pi08,11015
|
|
28
28
|
autocoder/agent/auto_tool.py,sha256=DBzip-P_T6ZtT2eHexPcusmKYD0h7ufzp7TLwXAY10E,11554
|
|
29
29
|
autocoder/agent/coder.py,sha256=x6bdJwDuETGg9ebQnYlUWCxCtQcDGg73LtI6McpWslQ,72034
|
|
30
30
|
autocoder/agent/designer.py,sha256=EpRbzO58Xym3GrnppIT1Z8ZFAlnNfgzHbIzZ3PX-Yv8,27037
|
|
@@ -64,10 +64,10 @@ autocoder/common/buildin_tokenizer.py,sha256=L7d5t39ZFvUd6EoMPXUhYK1toD0FHlRH1jt
|
|
|
64
64
|
autocoder/common/chunk_validation.py,sha256=BrR_ZWavW8IANuueEE7hS8NFAwEvm8TX34WnPx_1hs8,3030
|
|
65
65
|
autocoder/common/cleaner.py,sha256=NU72i8C6o9m0vXExab7nao5bstBUsfJFcj11cXa9l4U,1089
|
|
66
66
|
autocoder/common/code_auto_execute.py,sha256=4KXGmiGObr_B1d6tzV9dwS6MifCSc3Gm4j2d6ildBXQ,6867
|
|
67
|
-
autocoder/common/code_auto_generate.py,sha256=
|
|
68
|
-
autocoder/common/code_auto_generate_diff.py,sha256=
|
|
69
|
-
autocoder/common/code_auto_generate_editblock.py,sha256=
|
|
70
|
-
autocoder/common/code_auto_generate_strict_diff.py,sha256=
|
|
67
|
+
autocoder/common/code_auto_generate.py,sha256=gZ2TUcmXQkNZS_bpQb7Qi9uoYcSFXUjtLvZtrhp0CzI,12053
|
|
68
|
+
autocoder/common/code_auto_generate_diff.py,sha256=WJEXW44USo1NA-EyH2lEbQBbt6FiXdJ_GQ8xXWjy_sc,19097
|
|
69
|
+
autocoder/common/code_auto_generate_editblock.py,sha256=MQwkDUZlC8FBPEfmIn0wYZyOgD3b2OXQfysUsltnNXk,21809
|
|
70
|
+
autocoder/common/code_auto_generate_strict_diff.py,sha256=oYRl43-mzXdPkM4SSsiari8xqfhJTm0Z3PLfa24e9as,17969
|
|
71
71
|
autocoder/common/code_auto_merge.py,sha256=WaU-T-ZVn3QDaA_SrdkHciUPKDcTfVa-IbhHKBYEv5w,9961
|
|
72
72
|
autocoder/common/code_auto_merge_diff.py,sha256=DcljWrtlejq2cb9Gj-jBjvUQzRbCE2uMNGg8SBOhEnk,19271
|
|
73
73
|
autocoder/common/code_auto_merge_editblock.py,sha256=5PLH8Ey4GYsPNMGn36pSy_IgwgZ8-j1QF5FEv-THC-0,18397
|
|
@@ -121,8 +121,11 @@ autocoder/common/conversations/__init__.py,sha256=xGZeOFrDsgg2fkPK1zmvYBDhAyX66F
|
|
|
121
121
|
autocoder/common/conversations/compatibility.py,sha256=WuBXB4-dw5X9LUMsB16VWbihvRZQ1tT99m6zuBwDfqE,9606
|
|
122
122
|
autocoder/common/conversations/conversation_manager.py,sha256=ZhuhfSdOTncqgy3nHPoEU7Cg0dCsSl-VPcvLbUlL2Tk,18295
|
|
123
123
|
autocoder/common/conversations/example.py,sha256=Pz_EhO6qneUFMfHZiDmGAClZ6b0V4T1sbC8tIMxX2RM,5437
|
|
124
|
+
autocoder/common/directory_cache/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
125
|
+
autocoder/common/directory_cache/cache.py,sha256=Jknygb_U6DkF04_SX04IwsOcQdd-2QQ4d6k9Ucc9OZ0,9358
|
|
126
|
+
autocoder/common/directory_cache/test_cache.py,sha256=0iQkHaZQPhZBwSS6dwK_je93QMLbYGY0BYrTSt45Cao,6610
|
|
124
127
|
autocoder/common/file_monitor/__init__.py,sha256=9reL3IEnyLWU77WjPzeprM8-4lCetlSMZ94Nuxk5KNg,85
|
|
125
|
-
autocoder/common/file_monitor/monitor.py,sha256=
|
|
128
|
+
autocoder/common/file_monitor/monitor.py,sha256=biS9TJyNKga2dE-CeYAi9xfXvA9aMeMl0tBf68G-SBE,16609
|
|
126
129
|
autocoder/common/ignorefiles/__init__.py,sha256=P0hq7Avu1IeXBYEkPBZLsJhFzhzyktUWTqaRIXiAFLY,75
|
|
127
130
|
autocoder/common/ignorefiles/ignore_file_utils.py,sha256=H1gUjjYLHZ_4GZel9bN5lEgpTlOyfPdIhyNRqd7no4c,3484
|
|
128
131
|
autocoder/common/ignorefiles/test_ignore_file_utils.py,sha256=EydHG6E2iPsnbt-Jt8Go-WvRgFtBW6QkHUQ9nI4cF-w,3111
|
|
@@ -133,10 +136,10 @@ autocoder/common/rulefiles/__init__.py,sha256=babSbPdFaXk1NvdHtH2zrJLb_tWd7d2ELI
|
|
|
133
136
|
autocoder/common/rulefiles/autocoderrules_utils.py,sha256=JsB0BkdyygbTOEBfaAB2WA4RFU6Q3VamaFncmbbiLRE,10011
|
|
134
137
|
autocoder/common/v2/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
135
138
|
autocoder/common/v2/code_agentic_editblock_manager.py,sha256=pAh918YMgmLenXmNKXlmDPgKyVJjOuk69901VEEQHm4,34980
|
|
136
|
-
autocoder/common/v2/code_auto_generate.py,sha256=
|
|
137
|
-
autocoder/common/v2/code_auto_generate_diff.py,sha256=
|
|
138
|
-
autocoder/common/v2/code_auto_generate_editblock.py,sha256=
|
|
139
|
-
autocoder/common/v2/code_auto_generate_strict_diff.py,sha256=
|
|
139
|
+
autocoder/common/v2/code_auto_generate.py,sha256=g6hbBCFfqZnBUvswv_eO9r8b7HwnpIVh9f1M9ICivYQ,11547
|
|
140
|
+
autocoder/common/v2/code_auto_generate_diff.py,sha256=QJQk_D3l5yk3kczRF9-HwQgOLQYDlDhkGH72UtI-4Ks,15114
|
|
141
|
+
autocoder/common/v2/code_auto_generate_editblock.py,sha256=JKeAul5S5xKWtB9Ma4XlJw9ImxnxXetfUSESME3StUk,15621
|
|
142
|
+
autocoder/common/v2/code_auto_generate_strict_diff.py,sha256=k0aW21Gb40b8Z4-i5aHFgMtfoqK0ilFwQtv9C823X34,17968
|
|
140
143
|
autocoder/common/v2/code_auto_merge.py,sha256=FZHrIZyFUkFmv4EbproXfIIUfGx_L3EPfvjldsYlplI,9345
|
|
141
144
|
autocoder/common/v2/code_auto_merge_diff.py,sha256=DcljWrtlejq2cb9Gj-jBjvUQzRbCE2uMNGg8SBOhEnk,19271
|
|
142
145
|
autocoder/common/v2/code_auto_merge_editblock.py,sha256=rVohrpjTzgvWEX09GNRAQ0fbjjNjcxeP1bqczhFT8F8,20741
|
|
@@ -234,7 +237,7 @@ autocoder/privacy/__init__.py,sha256=LnIVvGu_K66zCE-yhN_-dPO8R80pQyedCsXJ7wRqQaI
|
|
|
234
237
|
autocoder/privacy/model_filter.py,sha256=-N9ZvxxDKpxU7hkn-tKv-QHyXjvkCopUaKgvJwTOGQs,3369
|
|
235
238
|
autocoder/pyproject/__init__.py,sha256=qn0_-6O_LP-ZH91nneYrn3yaIMYCYYRD1Z3MSNhXUXI,13754
|
|
236
239
|
autocoder/rag/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
237
|
-
autocoder/rag/api_server.py,sha256=
|
|
240
|
+
autocoder/rag/api_server.py,sha256=TNN5CmR1nlMgeuZVYZ1U3a48XBp647Io9P-VvCkdI9o,13936
|
|
238
241
|
autocoder/rag/conversation_to_queries.py,sha256=xwmErn4WbdADnhK1me-h_6fV3KYrl_y1qPNQl1aoI6o,4810
|
|
239
242
|
autocoder/rag/doc_filter.py,sha256=UduVO2mlrngwJICrefjDJTYfdmQ4GcRXrfWDQ7xXksk,14206
|
|
240
243
|
autocoder/rag/document_retriever.py,sha256=rFwbAuHTvEFJq16HQNlmRLyJp2ddn2RNFslw_ncU7NI,8847
|
|
@@ -307,9 +310,9 @@ autocoder/utils/types.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
307
310
|
autocoder/utils/auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
308
311
|
autocoder/utils/auto_coder_utils/chat_stream_out.py,sha256=t902pKxQ5xM7zgIHiAOsTPLwxhE6VuvXAqPy751S7fg,14096
|
|
309
312
|
autocoder/utils/chat_auto_coder_utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
310
|
-
auto_coder-0.1.
|
|
311
|
-
auto_coder-0.1.
|
|
312
|
-
auto_coder-0.1.
|
|
313
|
-
auto_coder-0.1.
|
|
314
|
-
auto_coder-0.1.
|
|
315
|
-
auto_coder-0.1.
|
|
313
|
+
auto_coder-0.1.360.dist-info/LICENSE,sha256=HrhfyXIkWY2tGFK11kg7vPCqhgh5DcxleloqdhrpyMY,11558
|
|
314
|
+
auto_coder-0.1.360.dist-info/METADATA,sha256=X4RcgNPLlNFVBEANGjlIbrqSgeJcWK0v8ACJWkPMGAY,2751
|
|
315
|
+
auto_coder-0.1.360.dist-info/WHEEL,sha256=GV9aMThwP_4oNCtvEC2ec3qUYutgWeAzklro_0m4WJQ,91
|
|
316
|
+
auto_coder-0.1.360.dist-info/entry_points.txt,sha256=0nzHtHH4pNcM7xq4EBA2toS28Qelrvcbrr59GqD_0Ak,350
|
|
317
|
+
auto_coder-0.1.360.dist-info/top_level.txt,sha256=Jqc0_uJSw2GwoFQAa9iJxYns-2mWla-9ok_Y3Gcznjk,10
|
|
318
|
+
auto_coder-0.1.360.dist-info/RECORD,,
|
|
@@ -47,7 +47,7 @@ class AutoReviewCommit:
|
|
|
47
47
|
@byzerllm.prompt()
|
|
48
48
|
def review(self, querie_with_urls_and_changes: List[Tuple[str, List[str], Dict[str, Tuple[str, str]]]], query: str) -> Generator[str,None,None]:
|
|
49
49
|
"""
|
|
50
|
-
|
|
50
|
+
如果前面我们对话提供了文档,请参考上面的文档对提交的代码变更进行审查,提供改进建议,你所有的输出都要以markdown语法输出。
|
|
51
51
|
|
|
52
52
|
下面包含最新一次提交的信息:
|
|
53
53
|
<commit>
|
|
@@ -96,7 +96,7 @@ class AutoReviewCommit:
|
|
|
96
96
|
- 依赖关系:组件耦合是否合理
|
|
97
97
|
- 复用性:是否有重复代码
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
评审结果包含以下内容
|
|
100
100
|
1. issues: 发现的具体问题列表
|
|
101
101
|
3. severity: 问题的严重程度(low/medium/high),从高到底进行描述。
|
|
102
102
|
4. suggestions: 对应的改进建议列表
|
|
@@ -115,7 +115,9 @@ class AutoReviewCommit:
|
|
|
115
115
|
1. 评审意见应该具体且可操作,而不是泛泛而谈
|
|
116
116
|
2. 对于每个问题都应该提供明确的改进建议
|
|
117
117
|
3. 严重程度的判断要考虑问题对系统的潜在影响
|
|
118
|
-
4. 建议应该符合项目的技术栈和开发规范
|
|
118
|
+
4. 建议应该符合项目的技术栈和开发规范
|
|
119
|
+
|
|
120
|
+
注意,请以「纯 Markdown」输出,不要出现 <markdown>、</markdown> 之类标签。
|
|
119
121
|
"""
|
|
120
122
|
return {}
|
|
121
123
|
|
|
@@ -59,16 +59,19 @@ class CodeAutoGenerate:
|
|
|
59
59
|
) -> str:
|
|
60
60
|
"""
|
|
61
61
|
{%- if structure %}
|
|
62
|
+
====
|
|
62
63
|
{{ structure }}
|
|
63
64
|
{%- endif %}
|
|
64
65
|
|
|
65
66
|
{%- if content %}
|
|
67
|
+
====
|
|
66
68
|
下面是一些文件路径以及每个文件对应的源码:
|
|
67
69
|
|
|
68
70
|
{{ content }}
|
|
69
71
|
{%- endif %}
|
|
70
72
|
|
|
71
73
|
{%- if package_context %}
|
|
74
|
+
====
|
|
72
75
|
下面是上面文件的一些信息(包括最近的变更情况):
|
|
73
76
|
<package_context>
|
|
74
77
|
{{ package_context }}
|
|
@@ -76,9 +79,27 @@ class CodeAutoGenerate:
|
|
|
76
79
|
{%- endif %}
|
|
77
80
|
|
|
78
81
|
{%- if context %}
|
|
82
|
+
====
|
|
79
83
|
{{ context }}
|
|
80
84
|
{%- endif %}
|
|
81
85
|
|
|
86
|
+
{%- if extra_docs %}
|
|
87
|
+
====
|
|
88
|
+
|
|
89
|
+
RULES PROVIDED BY USER
|
|
90
|
+
|
|
91
|
+
The following rules are provided by the user, and you must follow them strictly.
|
|
92
|
+
|
|
93
|
+
{% for key, value in extra_docs.items() %}
|
|
94
|
+
<user_rule>
|
|
95
|
+
##File: {{ key }}
|
|
96
|
+
{{ value }}
|
|
97
|
+
</user_rule>
|
|
98
|
+
{% endfor %}
|
|
99
|
+
{% endif %}
|
|
100
|
+
|
|
101
|
+
====
|
|
102
|
+
|
|
82
103
|
下面是用户的需求:
|
|
83
104
|
|
|
84
105
|
{{ instruction }}
|
|
@@ -16,6 +16,7 @@ from autocoder.rag.token_counter import count_tokens
|
|
|
16
16
|
from autocoder.utils import llms as llm_utils
|
|
17
17
|
from autocoder.common import SourceCodeList
|
|
18
18
|
from autocoder.memory.active_context_manager import ActiveContextManager
|
|
19
|
+
from autocoder.common.rulefiles.autocoderrules_utils import get_rules
|
|
19
20
|
|
|
20
21
|
class CodeAutoGenerateDiff:
|
|
21
22
|
def __init__(
|
|
@@ -135,10 +136,12 @@ class CodeAutoGenerateDiff:
|
|
|
135
136
|
现在让我们开始一个新的任务:
|
|
136
137
|
|
|
137
138
|
{%- if structure %}
|
|
139
|
+
====
|
|
138
140
|
{{ structure }}
|
|
139
141
|
{%- endif %}
|
|
140
142
|
|
|
141
143
|
{%- if content %}
|
|
144
|
+
====
|
|
142
145
|
下面是一些文件路径以及每个文件对应的源码:
|
|
143
146
|
<files>
|
|
144
147
|
{{ content }}
|
|
@@ -146,6 +149,7 @@ class CodeAutoGenerateDiff:
|
|
|
146
149
|
{%- endif %}
|
|
147
150
|
|
|
148
151
|
{%- if package_context %}
|
|
152
|
+
====
|
|
149
153
|
下面是上面文件的一些信息(包括最近的变更情况):
|
|
150
154
|
<package_context>
|
|
151
155
|
{{ package_context }}
|
|
@@ -153,11 +157,29 @@ class CodeAutoGenerateDiff:
|
|
|
153
157
|
{%- endif %}
|
|
154
158
|
|
|
155
159
|
{%- if context %}
|
|
160
|
+
====
|
|
156
161
|
<extra_context>
|
|
157
162
|
{{ context }}
|
|
158
163
|
</extra_context>
|
|
159
164
|
{%- endif %}
|
|
160
165
|
|
|
166
|
+
{%- if extra_docs %}
|
|
167
|
+
====
|
|
168
|
+
|
|
169
|
+
RULES PROVIDED BY USER
|
|
170
|
+
|
|
171
|
+
The following rules are provided by the user, and you must follow them strictly.
|
|
172
|
+
|
|
173
|
+
{% for key, value in extra_docs.items() %}
|
|
174
|
+
<user_rule>
|
|
175
|
+
##File: {{ key }}
|
|
176
|
+
{{ value }}
|
|
177
|
+
</user_rule>
|
|
178
|
+
{% endfor %}
|
|
179
|
+
{% endif %}
|
|
180
|
+
|
|
181
|
+
====
|
|
182
|
+
|
|
161
183
|
下面是用户的需求:
|
|
162
184
|
|
|
163
185
|
{{ instruction }}
|
|
@@ -169,13 +191,15 @@ class CodeAutoGenerateDiff:
|
|
|
169
191
|
return {
|
|
170
192
|
"structure": ""
|
|
171
193
|
}
|
|
172
|
-
|
|
194
|
+
|
|
195
|
+
extra_docs = get_rules()
|
|
173
196
|
return {
|
|
174
197
|
"structure": (
|
|
175
198
|
self.action.pp.get_tree_like_directory_structure()
|
|
176
199
|
if self.action
|
|
177
200
|
else ""
|
|
178
|
-
)
|
|
201
|
+
),
|
|
202
|
+
"extra_docs": extra_docs,
|
|
179
203
|
}
|
|
180
204
|
|
|
181
205
|
@byzerllm.prompt(llm=lambda self: self.llm)
|
|
@@ -20,6 +20,7 @@ from autocoder.rag.token_counter import count_tokens
|
|
|
20
20
|
from autocoder.utils import llms as llm_utils
|
|
21
21
|
from autocoder.common import SourceCodeList
|
|
22
22
|
from autocoder.memory.active_context_manager import ActiveContextManager
|
|
23
|
+
from autocoder.common.rulefiles.autocoderrules_utils import get_rules
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
|
|
@@ -188,10 +189,12 @@ class CodeAutoGenerateEditBlock:
|
|
|
188
189
|
现在让我们开始一个新的任务:
|
|
189
190
|
|
|
190
191
|
{%- if structure %}
|
|
192
|
+
====
|
|
191
193
|
{{ structure }}
|
|
192
194
|
{%- endif %}
|
|
193
195
|
|
|
194
196
|
{%- if content %}
|
|
197
|
+
====
|
|
195
198
|
下面是一些文件路径以及每个文件对应的源码:
|
|
196
199
|
<files>
|
|
197
200
|
{{ content }}
|
|
@@ -199,6 +202,7 @@ class CodeAutoGenerateEditBlock:
|
|
|
199
202
|
{%- endif %}
|
|
200
203
|
|
|
201
204
|
{%- if package_context %}
|
|
205
|
+
====
|
|
202
206
|
下面是上面文件的一些信息(包括最近的变更情况):
|
|
203
207
|
<package_context>
|
|
204
208
|
{{ package_context }}
|
|
@@ -206,11 +210,29 @@ class CodeAutoGenerateEditBlock:
|
|
|
206
210
|
{%- endif %}
|
|
207
211
|
|
|
208
212
|
{%- if context %}
|
|
213
|
+
====
|
|
209
214
|
<extra_context>
|
|
210
215
|
{{ context }}
|
|
211
216
|
</extra_context>
|
|
212
217
|
{%- endif %}
|
|
213
218
|
|
|
219
|
+
{%- if extra_docs %}
|
|
220
|
+
====
|
|
221
|
+
|
|
222
|
+
RULES PROVIDED BY USER
|
|
223
|
+
|
|
224
|
+
The following rules are provided by the user, and you must follow them strictly.
|
|
225
|
+
|
|
226
|
+
{% for key, value in extra_docs.items() %}
|
|
227
|
+
<user_rule>
|
|
228
|
+
##File: {{ key }}
|
|
229
|
+
{{ value }}
|
|
230
|
+
</user_rule>
|
|
231
|
+
{% endfor %}
|
|
232
|
+
{% endif %}
|
|
233
|
+
|
|
234
|
+
====
|
|
235
|
+
|
|
214
236
|
下面是用户的需求:
|
|
215
237
|
|
|
216
238
|
{{ instruction }}
|
|
@@ -224,6 +246,8 @@ class CodeAutoGenerateEditBlock:
|
|
|
224
246
|
"fence_0": self.fence_0,
|
|
225
247
|
"fence_1": self.fence_1,
|
|
226
248
|
}
|
|
249
|
+
|
|
250
|
+
extra_docs = get_rules()
|
|
227
251
|
|
|
228
252
|
return {
|
|
229
253
|
"structure": (
|
|
@@ -233,6 +257,7 @@ class CodeAutoGenerateEditBlock:
|
|
|
233
257
|
),
|
|
234
258
|
"fence_0": self.fence_0,
|
|
235
259
|
"fence_1": self.fence_1,
|
|
260
|
+
"extra_docs": extra_docs,
|
|
236
261
|
}
|
|
237
262
|
|
|
238
263
|
@byzerllm.prompt()
|
|
@@ -16,6 +16,8 @@ from autocoder.utils import llms as llm_utils
|
|
|
16
16
|
from autocoder.common import SourceCodeList
|
|
17
17
|
from autocoder.privacy.model_filter import ModelPathFilter
|
|
18
18
|
from autocoder.memory.active_context_manager import ActiveContextManager
|
|
19
|
+
from autocoder.common.rulefiles.autocoderrules_utils import get_rules
|
|
20
|
+
|
|
19
21
|
class CodeAutoGenerateStrictDiff:
|
|
20
22
|
def __init__(
|
|
21
23
|
self, llm: byzerllm.ByzerLLM, args: AutoCoderArgs, action=None
|
|
@@ -118,10 +120,12 @@ class CodeAutoGenerateStrictDiff:
|
|
|
118
120
|
现在让我们开始一个新的任务:
|
|
119
121
|
|
|
120
122
|
{%- if structure %}
|
|
123
|
+
====
|
|
121
124
|
{{ structure }}
|
|
122
125
|
{%- endif %}
|
|
123
126
|
|
|
124
127
|
{%- if content %}
|
|
128
|
+
====
|
|
125
129
|
下面是一些文件路径以及每个文件对应的源码:
|
|
126
130
|
<files>
|
|
127
131
|
{{ content }}
|
|
@@ -129,6 +133,7 @@ class CodeAutoGenerateStrictDiff:
|
|
|
129
133
|
{%- endif %}
|
|
130
134
|
|
|
131
135
|
{%- if package_context %}
|
|
136
|
+
====
|
|
132
137
|
下面是上面文件的一些信息(包括最近的变更情况):
|
|
133
138
|
<package_context>
|
|
134
139
|
{{ package_context }}
|
|
@@ -141,6 +146,23 @@ class CodeAutoGenerateStrictDiff:
|
|
|
141
146
|
</extra_context>
|
|
142
147
|
{%- endif %}
|
|
143
148
|
|
|
149
|
+
{%- if extra_docs %}
|
|
150
|
+
====
|
|
151
|
+
|
|
152
|
+
RULES PROVIDED BY USER
|
|
153
|
+
|
|
154
|
+
The following rules are provided by the user, and you must follow them strictly.
|
|
155
|
+
|
|
156
|
+
{% for key, value in extra_docs.items() %}
|
|
157
|
+
<user_rule>
|
|
158
|
+
##File: {{ key }}
|
|
159
|
+
{{ value }}
|
|
160
|
+
</user_rule>
|
|
161
|
+
{% endfor %}
|
|
162
|
+
{% endif %}
|
|
163
|
+
|
|
164
|
+
====
|
|
165
|
+
|
|
144
166
|
下面是用户的需求:
|
|
145
167
|
|
|
146
168
|
{{ instruction }}
|
|
@@ -152,13 +174,16 @@ class CodeAutoGenerateStrictDiff:
|
|
|
152
174
|
return {
|
|
153
175
|
"structure": "",
|
|
154
176
|
}
|
|
177
|
+
|
|
178
|
+
extra_docs = get_rules()
|
|
155
179
|
|
|
156
180
|
return {
|
|
157
181
|
"structure": (
|
|
158
182
|
self.action.pp.get_tree_like_directory_structure()
|
|
159
183
|
if self.action
|
|
160
184
|
else ""
|
|
161
|
-
)
|
|
185
|
+
),
|
|
186
|
+
"extra_docs": extra_docs,
|
|
162
187
|
}
|
|
163
188
|
|
|
164
189
|
@byzerllm.prompt(llm=lambda self: self.llm)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
import os, fnmatch, asyncio
|
|
3
|
+
from watchfiles import Change
|
|
4
|
+
from autocoder.common.ignorefiles.ignore_file_utils import should_ignore
|
|
5
|
+
from autocoder.common.file_monitor.monitor import get_file_monitor
|
|
6
|
+
import logging
|
|
7
|
+
import functools
|
|
8
|
+
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
class DirectoryCache:
|
|
12
|
+
_instance: "DirectoryCache|None" = None
|
|
13
|
+
|
|
14
|
+
def __init__(self, root: str):
|
|
15
|
+
self.root = os.path.abspath(root)
|
|
16
|
+
self.files_set: set[str] = set()
|
|
17
|
+
self.lock = asyncio.Lock()
|
|
18
|
+
self._main_loop = asyncio.get_event_loop() # 保存主事件循环引用
|
|
19
|
+
logger.info(f"Initializing DirectoryCache for root: {self.root}")
|
|
20
|
+
|
|
21
|
+
# ---------- 单例获取 ----------
|
|
22
|
+
@classmethod
|
|
23
|
+
def get_instance(cls, root: str | None = None) -> "DirectoryCache":
|
|
24
|
+
if cls._instance is None:
|
|
25
|
+
if root is None:
|
|
26
|
+
raise ValueError("root is required when initializing DirectoryCache for the first time")
|
|
27
|
+
logger.info("Creating new DirectoryCache instance.")
|
|
28
|
+
cls._instance = cls(root)
|
|
29
|
+
cls._instance._build() # 同步首扫
|
|
30
|
+
cls._instance._register_monitor()
|
|
31
|
+
elif root is not None and os.path.abspath(root) != cls._instance.root:
|
|
32
|
+
# 如果请求的 root 与已存在的实例 root 不同,可以选择抛出错误或重新初始化
|
|
33
|
+
logger.warning(f"Requested root {os.path.abspath(root)} differs from existing instance root {cls._instance.root}. Re-initializing cache.")
|
|
34
|
+
cls._instance = cls(root)
|
|
35
|
+
cls._instance._build()
|
|
36
|
+
cls._instance._register_monitor()
|
|
37
|
+
|
|
38
|
+
return cls._instance
|
|
39
|
+
|
|
40
|
+
# ---------- 构建 ----------
|
|
41
|
+
def _build(self) -> None:
|
|
42
|
+
logger.info(f"Building initial file cache for {self.root}...")
|
|
43
|
+
count = 0
|
|
44
|
+
for r, ds, fs in os.walk(self.root, followlinks=True):
|
|
45
|
+
# 过滤掉需要忽略的目录
|
|
46
|
+
ds[:] = [d for d in ds if not should_ignore(os.path.join(r, d))]
|
|
47
|
+
for f in fs:
|
|
48
|
+
fp = os.path.join(r, f)
|
|
49
|
+
abs_fp = os.path.abspath(fp)
|
|
50
|
+
if not should_ignore(abs_fp):
|
|
51
|
+
self.files_set.add(abs_fp)
|
|
52
|
+
count += 1
|
|
53
|
+
logger.info(f"Initial cache build complete. Found {count} files.")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# ---------- 监控回调 ----------
|
|
57
|
+
def _register_monitor(self) -> None:
|
|
58
|
+
try:
|
|
59
|
+
logger.info(f"Registering file monitor for {self.root}")
|
|
60
|
+
mon = get_file_monitor(self.root)
|
|
61
|
+
# 使用 functools.partial 包装异步回调
|
|
62
|
+
async_callback_wrapper = functools.partial(self._on_change_wrapper)
|
|
63
|
+
mon.register("**/*", async_callback_wrapper) # 监听所有文件变化
|
|
64
|
+
if not mon.is_running():
|
|
65
|
+
logger.info("Starting file monitor...")
|
|
66
|
+
mon.start()
|
|
67
|
+
logger.info("File monitor registered and running.")
|
|
68
|
+
except Exception as e:
|
|
69
|
+
logger.error(f"Failed to register or start file monitor: {e}", exc_info=True)
|
|
70
|
+
|
|
71
|
+
# Wrapper to run the async callback in the event loop
|
|
72
|
+
def _on_change_wrapper(self, change: Change, path: str):
|
|
73
|
+
try:
|
|
74
|
+
# 使用run_coroutine_threadsafe在主事件循环中运行协程
|
|
75
|
+
# 注意:主事件循环必须在其他地方运行,如主线程中
|
|
76
|
+
asyncio.run_coroutine_threadsafe(self._on_change(change, path), self._main_loop)
|
|
77
|
+
except Exception as e:
|
|
78
|
+
logger.error(f"Error executing _on_change_wrapper: {e}", exc_info=True)
|
|
79
|
+
# 如果run_coroutine_threadsafe失败,可以考虑一个同步的备用处理方法
|
|
80
|
+
try:
|
|
81
|
+
# 同步备份处理
|
|
82
|
+
ap = os.path.abspath(path)
|
|
83
|
+
if should_ignore(ap):
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
if change is Change.added:
|
|
87
|
+
self.files_set.add(ap)
|
|
88
|
+
elif change is Change.deleted:
|
|
89
|
+
self.files_set.discard(ap)
|
|
90
|
+
# Change.modified不需要更新集合
|
|
91
|
+
except Exception as backup_error:
|
|
92
|
+
logger.error(f"Backup handler also failed: {backup_error}", exc_info=True)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
async def _on_change(self, change: Change, path: str) -> None:
|
|
96
|
+
ap = os.path.abspath(path)
|
|
97
|
+
# Check ignore status again, as gitignore might change
|
|
98
|
+
if should_ignore(ap):
|
|
99
|
+
# If a previously tracked file becomes ignored, remove it
|
|
100
|
+
async with self.lock:
|
|
101
|
+
if ap in self.files_set:
|
|
102
|
+
logger.debug(f"File became ignored, removing from cache: {ap}")
|
|
103
|
+
self.files_set.discard(ap)
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
async with self.lock:
|
|
107
|
+
if change is Change.added:
|
|
108
|
+
logger.debug(f"File added, adding to cache: {ap}")
|
|
109
|
+
self.files_set.add(ap)
|
|
110
|
+
elif change is Change.deleted:
|
|
111
|
+
logger.debug(f"File deleted, removing from cache: {ap}")
|
|
112
|
+
self.files_set.discard(ap)
|
|
113
|
+
elif change is Change.modified:
|
|
114
|
+
# Modification doesn't change existence, but we might want to log or handle metadata updates if needed
|
|
115
|
+
logger.debug(f"File modified: {ap} (ignored in cache set)")
|
|
116
|
+
pass # No change needed for the set itself
|
|
117
|
+
|
|
118
|
+
# ---------- 查询 ----------
|
|
119
|
+
async def query(self, patterns: list[str]) -> list[str]:
|
|
120
|
+
logger.debug(f"Querying cache with patterns: {patterns}")
|
|
121
|
+
async with self.lock:
|
|
122
|
+
# Make a copy to avoid issues if the set is modified during iteration (though lock prevents this here)
|
|
123
|
+
current_files = list(self.files_set)
|
|
124
|
+
|
|
125
|
+
if not patterns or patterns == [""] or patterns == ["*"]:
|
|
126
|
+
logger.debug(f"Returning all {len(current_files)} cached files.")
|
|
127
|
+
return current_files
|
|
128
|
+
|
|
129
|
+
out: set[str] = set()
|
|
130
|
+
|
|
131
|
+
for p in patterns:
|
|
132
|
+
pattern_abs = os.path.abspath(os.path.join(self.root, p)) if not os.path.isabs(p) else p
|
|
133
|
+
is_glob = "*" in p or "?" in p or "[" in p # More robust glob check
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
if is_glob:
|
|
137
|
+
# fnmatch expects relative paths for matching within a root usually,
|
|
138
|
+
# but here we match against absolute paths in files_set.
|
|
139
|
+
# We need a pattern that works with absolute paths.
|
|
140
|
+
# If pattern 'p' is like '*.py', we need to match '/path/to/root/**/*.py'
|
|
141
|
+
# Let's adjust the pattern logic or filtering logic.
|
|
142
|
+
|
|
143
|
+
# Option 1: Match relative paths within the root
|
|
144
|
+
# Convert absolute paths in files_set to relative for matching
|
|
145
|
+
# relative_files = [os.path.relpath(f, self.root) for f in current_files]
|
|
146
|
+
# matched_relative = fnmatch.filter(relative_files, p)
|
|
147
|
+
# out.update(os.path.join(self.root, rel_f) for rel_f in matched_relative)
|
|
148
|
+
|
|
149
|
+
# Option 2: Match absolute paths directly (might need careful pattern construction)
|
|
150
|
+
# If p is relative, make it absolute based on root for matching
|
|
151
|
+
# Example: p = "src/*.py" -> effective_pattern = "/path/to/root/src/*.py"
|
|
152
|
+
# This requires fnmatch to handle absolute paths correctly or custom logic.
|
|
153
|
+
|
|
154
|
+
# Option 3: Simplified wildcard matching on absolute paths
|
|
155
|
+
# Treat '*' as a general wildcard anywhere in the path.
|
|
156
|
+
# fnmatch.filter might work if the pattern is constructed like `*pattern*`
|
|
157
|
+
# Let's stick to the user's original logic first, assuming it worked for their case
|
|
158
|
+
# The original `*{p}*` suggests substring matching with wildcards? Let's refine.
|
|
159
|
+
|
|
160
|
+
# Refined Glob Matching:
|
|
161
|
+
# If p contains wildcards, assume it's a glob pattern relative to root.
|
|
162
|
+
# Convert files_set paths to relative for matching.
|
|
163
|
+
for f_abs in current_files:
|
|
164
|
+
f_rel = os.path.relpath(f_abs, self.root)
|
|
165
|
+
if fnmatch.fnmatch(f_rel, p) or fnmatch.fnmatch(os.path.basename(f_abs), p):
|
|
166
|
+
out.add(f_abs)
|
|
167
|
+
|
|
168
|
+
else:
|
|
169
|
+
# Exact or substring matching for non-glob patterns
|
|
170
|
+
# Match against filename or full path segment
|
|
171
|
+
p_lower = p.lower()
|
|
172
|
+
for f_abs in current_files:
|
|
173
|
+
if p_lower in os.path.basename(f_abs).lower() or p_lower in f_abs.lower():
|
|
174
|
+
out.add(f_abs)
|
|
175
|
+
|
|
176
|
+
except Exception as e:
|
|
177
|
+
logger.error(f"Error during pattern matching for '{p}': {e}", exc_info=True)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
result = sorted(list(out)) # Sort for consistent output
|
|
181
|
+
logger.debug(f"Query returned {len(result)} files.")
|
|
182
|
+
return result
|
|
183
|
+
|
|
184
|
+
# Helper function (optional, could be integrated into get_instance)
|
|
185
|
+
def initialize_cache(root_path: str):
|
|
186
|
+
"""Initializes the DirectoryCache singleton."""
|
|
187
|
+
try:
|
|
188
|
+
DirectoryCache.get_instance(root_path)
|
|
189
|
+
logger.info("DirectoryCache initialized successfully.")
|
|
190
|
+
except Exception as e:
|
|
191
|
+
logger.error(f"Failed to initialize DirectoryCache: {e}", exc_info=True)
|
|
192
|
+
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import pytest
|
|
2
|
+
import os
|
|
3
|
+
import tempfile
|
|
4
|
+
import shutil
|
|
5
|
+
import asyncio
|
|
6
|
+
from unittest.mock import patch, MagicMock
|
|
7
|
+
|
|
8
|
+
# 导入被测模块
|
|
9
|
+
from autocoder.common.directory_cache.cache import DirectoryCache
|
|
10
|
+
|
|
11
|
+
# 测试环境辅助函数
|
|
12
|
+
def create_test_environment(base_dir, structure):
|
|
13
|
+
"""创建测试所需的文件/目录结构"""
|
|
14
|
+
for path, content in structure.items():
|
|
15
|
+
full_path = os.path.join(base_dir, path)
|
|
16
|
+
os.makedirs(os.path.dirname(full_path), exist_ok=True)
|
|
17
|
+
with open(full_path, 'w', encoding='utf-8') as f:
|
|
18
|
+
f.write(content)
|
|
19
|
+
|
|
20
|
+
# Pytest Fixture: 临时目录
|
|
21
|
+
@pytest.fixture(scope="function")
|
|
22
|
+
def temp_test_dir():
|
|
23
|
+
"""提供一个临时的、测试后自动清理的目录"""
|
|
24
|
+
temp_dir = tempfile.mkdtemp()
|
|
25
|
+
print(f"创建测试临时目录: {temp_dir}")
|
|
26
|
+
yield temp_dir
|
|
27
|
+
print(f"清理测试临时目录: {temp_dir}")
|
|
28
|
+
shutil.rmtree(temp_dir)
|
|
29
|
+
|
|
30
|
+
# Pytest Fixture: 重置单例
|
|
31
|
+
@pytest.fixture(autouse=True)
|
|
32
|
+
def reset_singleton():
|
|
33
|
+
"""每次测试前重置DirectoryCache单例"""
|
|
34
|
+
DirectoryCache._instance = None
|
|
35
|
+
yield
|
|
36
|
+
DirectoryCache._instance = None
|
|
37
|
+
|
|
38
|
+
# Pytest Fixture: 文件监视器模拟
|
|
39
|
+
@pytest.fixture
|
|
40
|
+
def mock_file_monitor():
|
|
41
|
+
"""模拟文件监视器"""
|
|
42
|
+
mock_monitor = MagicMock()
|
|
43
|
+
mock_monitor.register = MagicMock()
|
|
44
|
+
mock_monitor.is_running = MagicMock(return_value=False)
|
|
45
|
+
mock_monitor.start = MagicMock()
|
|
46
|
+
|
|
47
|
+
with patch('autocoder.common.directory_cache.cache.get_file_monitor', return_value=mock_monitor):
|
|
48
|
+
yield mock_monitor
|
|
49
|
+
|
|
50
|
+
# --- 测试用例 ---
|
|
51
|
+
|
|
52
|
+
@pytest.mark.asyncio
|
|
53
|
+
async def test_initialization(temp_test_dir, mock_file_monitor):
|
|
54
|
+
"""测试DirectoryCache的初始化和单例模式"""
|
|
55
|
+
# 创建测试文件结构
|
|
56
|
+
create_test_environment(temp_test_dir, {
|
|
57
|
+
"file1.txt": "测试内容1",
|
|
58
|
+
"subdir/file2.txt": "测试内容2",
|
|
59
|
+
".gitignore": "*.ignored"
|
|
60
|
+
})
|
|
61
|
+
create_test_environment(temp_test_dir, {
|
|
62
|
+
"file3.ignored": "这个文件应该被忽略"
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
# 获取实例
|
|
66
|
+
cache = DirectoryCache.get_instance(temp_test_dir)
|
|
67
|
+
|
|
68
|
+
# 验证单例模式
|
|
69
|
+
assert DirectoryCache.get_instance() is cache
|
|
70
|
+
assert cache.root == os.path.abspath(temp_test_dir)
|
|
71
|
+
|
|
72
|
+
# 验证文件缓存构建
|
|
73
|
+
assert len(cache.files_set) == 3 # file1.txt, subdir/file2.txt, .gitignore
|
|
74
|
+
assert os.path.join(temp_test_dir, "file1.txt") in [os.path.normpath(f) for f in cache.files_set]
|
|
75
|
+
assert os.path.join(temp_test_dir, "subdir/file2.txt") in [os.path.normpath(f) for f in cache.files_set]
|
|
76
|
+
|
|
77
|
+
# 验证监视器注册
|
|
78
|
+
mock_file_monitor.register.assert_called_once()
|
|
79
|
+
mock_file_monitor.start.assert_called_once()
|
|
80
|
+
|
|
81
|
+
@pytest.mark.asyncio
|
|
82
|
+
async def test_query_all_files(temp_test_dir, mock_file_monitor):
|
|
83
|
+
"""测试查询所有文件"""
|
|
84
|
+
# 创建测试文件结构
|
|
85
|
+
create_test_environment(temp_test_dir, {
|
|
86
|
+
"file1.py": "def test(): pass",
|
|
87
|
+
"file2.txt": "text content",
|
|
88
|
+
"subdir/file3.py": "class Test: pass"
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
# 获取实例并查询所有文件
|
|
92
|
+
cache = DirectoryCache.get_instance(temp_test_dir)
|
|
93
|
+
result = await cache.query(["*"])
|
|
94
|
+
|
|
95
|
+
# 验证结果
|
|
96
|
+
assert len(result) == 3
|
|
97
|
+
assert sorted([os.path.basename(f) for f in result]) == ["file1.py", "file2.txt", "file3.py"]
|
|
98
|
+
|
|
99
|
+
@pytest.mark.asyncio
|
|
100
|
+
async def test_query_with_pattern(temp_test_dir, mock_file_monitor):
|
|
101
|
+
"""测试使用模式查询文件"""
|
|
102
|
+
# 创建测试文件结构
|
|
103
|
+
create_test_environment(temp_test_dir, {
|
|
104
|
+
"file1.py": "def test(): pass",
|
|
105
|
+
"file2.txt": "text content",
|
|
106
|
+
"subdir/file3.py": "class Test: pass",
|
|
107
|
+
"another.doc": "document"
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
# 获取实例
|
|
111
|
+
cache = DirectoryCache.get_instance(temp_test_dir)
|
|
112
|
+
|
|
113
|
+
# 测试不同的查询模式
|
|
114
|
+
py_files = await cache.query(["*.py"])
|
|
115
|
+
assert len(py_files) == 2
|
|
116
|
+
assert all(f.endswith('.py') for f in py_files)
|
|
117
|
+
|
|
118
|
+
txt_files = await cache.query(["*.txt"])
|
|
119
|
+
assert len(txt_files) == 1
|
|
120
|
+
assert txt_files[0].endswith('file2.txt')
|
|
121
|
+
|
|
122
|
+
# 测试多模式查询
|
|
123
|
+
mixed_files = await cache.query(["*.py", "*.txt"])
|
|
124
|
+
assert len(mixed_files) == 3
|
|
125
|
+
assert len([f for f in mixed_files if f.endswith('.py')]) == 2
|
|
126
|
+
assert len([f for f in mixed_files if f.endswith('.txt')]) == 1
|
|
127
|
+
|
|
128
|
+
@pytest.mark.asyncio
|
|
129
|
+
async def test_file_change_events(temp_test_dir, mock_file_monitor):
|
|
130
|
+
"""测试文件变更事件处理"""
|
|
131
|
+
from watchfiles import Change
|
|
132
|
+
|
|
133
|
+
# 创建测试文件结构
|
|
134
|
+
create_test_environment(temp_test_dir, {
|
|
135
|
+
"file1.txt": "初始内容"
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
# 获取实例
|
|
139
|
+
cache = DirectoryCache.get_instance(temp_test_dir)
|
|
140
|
+
|
|
141
|
+
# 初始状态
|
|
142
|
+
file_path = os.path.join(temp_test_dir, "file1.txt")
|
|
143
|
+
abs_file_path = os.path.abspath(file_path)
|
|
144
|
+
assert abs_file_path in cache.files_set
|
|
145
|
+
|
|
146
|
+
# 测试删除事件
|
|
147
|
+
await cache._on_change(Change.deleted, abs_file_path)
|
|
148
|
+
assert abs_file_path not in cache.files_set
|
|
149
|
+
|
|
150
|
+
# 测试添加事件
|
|
151
|
+
await cache._on_change(Change.added, abs_file_path)
|
|
152
|
+
assert abs_file_path in cache.files_set
|
|
153
|
+
|
|
154
|
+
# 测试修改事件 (不应改变集合)
|
|
155
|
+
initial_set_size = len(cache.files_set)
|
|
156
|
+
await cache._on_change(Change.modified, abs_file_path)
|
|
157
|
+
assert len(cache.files_set) == initial_set_size
|
|
158
|
+
assert abs_file_path in cache.files_set
|
|
159
|
+
|
|
160
|
+
# 测试新文件
|
|
161
|
+
new_file_path = os.path.join(temp_test_dir, "newfile.txt")
|
|
162
|
+
abs_new_file_path = os.path.abspath(new_file_path)
|
|
163
|
+
await cache._on_change(Change.added, abs_new_file_path)
|
|
164
|
+
assert abs_new_file_path in cache.files_set
|
|
165
|
+
|
|
166
|
+
@pytest.mark.asyncio
|
|
167
|
+
async def test_reinitialization_with_different_root(temp_test_dir, mock_file_monitor):
|
|
168
|
+
"""测试使用不同根目录重新初始化缓存"""
|
|
169
|
+
# 创建第一个测试目录
|
|
170
|
+
first_dir = os.path.join(temp_test_dir, "first")
|
|
171
|
+
os.makedirs(first_dir)
|
|
172
|
+
create_test_environment(first_dir, {"test.txt": "first directory"})
|
|
173
|
+
|
|
174
|
+
# 初始化缓存
|
|
175
|
+
cache1 = DirectoryCache.get_instance(first_dir)
|
|
176
|
+
assert cache1.root == os.path.abspath(first_dir)
|
|
177
|
+
|
|
178
|
+
# 创建第二个测试目录
|
|
179
|
+
second_dir = os.path.join(temp_test_dir, "second")
|
|
180
|
+
os.makedirs(second_dir)
|
|
181
|
+
create_test_environment(second_dir, {"other.txt": "second directory"})
|
|
182
|
+
|
|
183
|
+
# 使用新目录重新初始化
|
|
184
|
+
cache2 = DirectoryCache.get_instance(second_dir)
|
|
185
|
+
assert cache2.root == os.path.abspath(second_dir)
|
|
186
|
+
|
|
187
|
+
# 验证文件集合已更新
|
|
188
|
+
all_files = await cache2.query(["*"])
|
|
189
|
+
assert len(all_files) == 1
|
|
190
|
+
assert all_files[0].endswith('other.txt')
|
|
@@ -153,7 +153,7 @@ class FileMonitor:
|
|
|
153
153
|
"""
|
|
154
154
|
# 将单个模式转换为pathspec格式
|
|
155
155
|
# 使用GitWildMatchPattern,它支持.gitignore样式的通配符,功能最全面
|
|
156
|
-
return pathspec.PathSpec
|
|
156
|
+
return pathspec.PathSpec([pathspec.patterns.GitWildMatchPattern(pattern)])
|
|
157
157
|
|
|
158
158
|
def register(self, path: Union[str, Path], callback: Callable[[Change, str], None]):
|
|
159
159
|
"""
|
|
@@ -16,6 +16,7 @@ from autocoder.utils import llms as llm_utils
|
|
|
16
16
|
from autocoder.common import SourceCodeList
|
|
17
17
|
from autocoder.memory.active_context_manager import ActiveContextManager
|
|
18
18
|
from loguru import logger
|
|
19
|
+
from autocoder.common.rulefiles.autocoderrules_utils import get_rules
|
|
19
20
|
|
|
20
21
|
class CodeAutoGenerate:
|
|
21
22
|
def __init__(
|
|
@@ -36,6 +37,88 @@ class CodeAutoGenerate:
|
|
|
36
37
|
if not isinstance(self.llms, list):
|
|
37
38
|
self.llms = [self.llms]
|
|
38
39
|
|
|
40
|
+
@byzerllm.prompt(llm=lambda self: self.llm)
|
|
41
|
+
def single_round_instruction(
|
|
42
|
+
self, instruction: str, content: str, context: str = "", package_context: str = ""
|
|
43
|
+
) -> str:
|
|
44
|
+
"""
|
|
45
|
+
{%- if structure %}
|
|
46
|
+
====
|
|
47
|
+
{{ structure }}
|
|
48
|
+
{%- endif %}
|
|
49
|
+
|
|
50
|
+
{%- if content %}
|
|
51
|
+
====
|
|
52
|
+
下面是一些文件路径以及每个文件对应的源码:
|
|
53
|
+
|
|
54
|
+
{{ content }}
|
|
55
|
+
{%- endif %}
|
|
56
|
+
|
|
57
|
+
{%- if package_context %}
|
|
58
|
+
====
|
|
59
|
+
下面是上面文件的一些信息(包括最近的变更情况):
|
|
60
|
+
<package_context>
|
|
61
|
+
{{ package_context }}
|
|
62
|
+
</package_context>
|
|
63
|
+
{%- endif %}
|
|
64
|
+
|
|
65
|
+
{%- if context %}
|
|
66
|
+
====
|
|
67
|
+
{{ context }}
|
|
68
|
+
{%- endif %}
|
|
69
|
+
|
|
70
|
+
{%- if extra_docs %}
|
|
71
|
+
====
|
|
72
|
+
|
|
73
|
+
RULES PROVIDED BY USER
|
|
74
|
+
|
|
75
|
+
The following rules are provided by the user, and you must follow them strictly.
|
|
76
|
+
|
|
77
|
+
{% for key, value in extra_docs.items() %}
|
|
78
|
+
<user_rule>
|
|
79
|
+
##File: {{ key }}
|
|
80
|
+
{{ value }}
|
|
81
|
+
</user_rule>
|
|
82
|
+
{% endfor %}
|
|
83
|
+
{% endif %}
|
|
84
|
+
|
|
85
|
+
下面是用户的需求:
|
|
86
|
+
|
|
87
|
+
{{ instruction }}
|
|
88
|
+
|
|
89
|
+
如果你需要生成代码,你生成的代码要符合这个格式:
|
|
90
|
+
|
|
91
|
+
```{lang}
|
|
92
|
+
##File: {FILE_PATH}
|
|
93
|
+
{CODE}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
```{lang}
|
|
97
|
+
##File: {FILE_PATH}
|
|
98
|
+
{CODE}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
其中,{lang}是代码的语言,{CODE}是代码的内容, {FILE_PATH} 是文件的路径(请尽量使用绝对路径),他们都在代码块中,请严格按上面的格式进行内容生成。
|
|
102
|
+
|
|
103
|
+
请确保每份代码的完整性,而不要只生成修改部分。
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
if not self.args.include_project_structure:
|
|
107
|
+
return {
|
|
108
|
+
"structure": "",
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
extra_docs = get_rules()
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
"structure": (
|
|
115
|
+
self.action.pp.get_tree_like_directory_structure()
|
|
116
|
+
if self.action
|
|
117
|
+
else ""
|
|
118
|
+
),
|
|
119
|
+
"extra_docs": extra_docs,
|
|
120
|
+
}
|
|
121
|
+
|
|
39
122
|
def single_round_run(
|
|
40
123
|
self, query: str, source_code_list: SourceCodeList
|
|
41
124
|
) -> CodeGenerateResult:
|
|
@@ -15,7 +15,7 @@ from autocoder.rag.token_counter import count_tokens
|
|
|
15
15
|
from autocoder.utils import llms as llm_utils
|
|
16
16
|
from autocoder.common import SourceCodeList
|
|
17
17
|
from autocoder.memory.active_context_manager import ActiveContextManager
|
|
18
|
-
|
|
18
|
+
from autocoder.common.rulefiles.autocoderrules_utils import get_rules
|
|
19
19
|
|
|
20
20
|
class CodeAutoGenerateDiff:
|
|
21
21
|
def __init__(
|
|
@@ -155,19 +155,23 @@ class CodeAutoGenerateDiff:
|
|
|
155
155
|
```
|
|
156
156
|
|
|
157
157
|
现在让我们开始一个新的任务:
|
|
158
|
-
|
|
158
|
+
|
|
159
159
|
{%- if structure %}
|
|
160
|
+
====
|
|
160
161
|
{{ structure }}
|
|
161
162
|
{%- endif %}
|
|
162
163
|
|
|
164
|
+
|
|
163
165
|
{%- if content %}
|
|
166
|
+
====
|
|
164
167
|
下面是一些文件路径以及每个文件对应的源码:
|
|
165
168
|
<files>
|
|
166
169
|
{{ content }}
|
|
167
170
|
</files>
|
|
168
171
|
{%- endif %}
|
|
169
|
-
|
|
172
|
+
|
|
170
173
|
{%- if package_context %}
|
|
174
|
+
====
|
|
171
175
|
下面是上面文件的一些信息(包括最近的变更情况):
|
|
172
176
|
<package_context>
|
|
173
177
|
{{ package_context }}
|
|
@@ -180,6 +184,22 @@ class CodeAutoGenerateDiff:
|
|
|
180
184
|
</extra_context>
|
|
181
185
|
{%- endif %}
|
|
182
186
|
|
|
187
|
+
{%- if extra_docs %}
|
|
188
|
+
====
|
|
189
|
+
|
|
190
|
+
RULES PROVIDED BY USER
|
|
191
|
+
|
|
192
|
+
The following rules are provided by the user, and you must follow them strictly.
|
|
193
|
+
|
|
194
|
+
{% for key, value in extra_docs.items() %}
|
|
195
|
+
<user_rule>
|
|
196
|
+
##File: {{ key }}
|
|
197
|
+
{{ value }}
|
|
198
|
+
</user_rule>
|
|
199
|
+
{% endfor %}
|
|
200
|
+
{% endif %}
|
|
201
|
+
|
|
202
|
+
====
|
|
183
203
|
下面是用户的需求:
|
|
184
204
|
|
|
185
205
|
{{ instruction }}
|
|
@@ -190,12 +210,15 @@ class CodeAutoGenerateDiff:
|
|
|
190
210
|
"structure": "",
|
|
191
211
|
}
|
|
192
212
|
|
|
213
|
+
extra_docs = get_rules()
|
|
214
|
+
|
|
193
215
|
return {
|
|
194
216
|
"structure": (
|
|
195
217
|
self.action.pp.get_tree_like_directory_structure()
|
|
196
218
|
if self.action
|
|
197
219
|
else ""
|
|
198
220
|
),
|
|
221
|
+
"extra_docs": extra_docs,
|
|
199
222
|
}
|
|
200
223
|
|
|
201
224
|
def single_round_run(
|
|
@@ -15,6 +15,7 @@ from autocoder.rag.token_counter import count_tokens
|
|
|
15
15
|
from autocoder.utils import llms as llm_utils
|
|
16
16
|
from autocoder.common import SourceCodeList
|
|
17
17
|
from autocoder.memory.active_context_manager import ActiveContextManager
|
|
18
|
+
from autocoder.common.rulefiles.autocoderrules_utils import get_rules
|
|
18
19
|
from loguru import logger
|
|
19
20
|
|
|
20
21
|
|
|
@@ -175,13 +176,15 @@ class CodeAutoGenerateEditBlock:
|
|
|
175
176
|
{%- endif %}
|
|
176
177
|
|
|
177
178
|
{%- if content %}
|
|
179
|
+
====
|
|
178
180
|
下面是一些文件路径以及每个文件对应的源码:
|
|
179
181
|
<files>
|
|
180
182
|
{{ content }}
|
|
181
183
|
</files>
|
|
182
184
|
{%- endif %}
|
|
183
|
-
|
|
185
|
+
|
|
184
186
|
{%- if package_context %}
|
|
187
|
+
====
|
|
185
188
|
下面是上面文件的一些信息(包括最近的变更情况):
|
|
186
189
|
<package_context>
|
|
187
190
|
{{ package_context }}
|
|
@@ -194,6 +197,22 @@ class CodeAutoGenerateEditBlock:
|
|
|
194
197
|
</extra_context>
|
|
195
198
|
{%- endif %}
|
|
196
199
|
|
|
200
|
+
{%- if extra_docs %}
|
|
201
|
+
====
|
|
202
|
+
|
|
203
|
+
RULES PROVIDED BY USER
|
|
204
|
+
|
|
205
|
+
The following rules are provided by the user, and you must follow them strictly.
|
|
206
|
+
|
|
207
|
+
{% for key, value in extra_docs.items() %}
|
|
208
|
+
<user_rule>
|
|
209
|
+
##File: {{ key }}
|
|
210
|
+
{{ value }}
|
|
211
|
+
</user_rule>
|
|
212
|
+
{% endfor %}
|
|
213
|
+
{% endif %}
|
|
214
|
+
|
|
215
|
+
====
|
|
197
216
|
下面是用户的需求:
|
|
198
217
|
|
|
199
218
|
{{ instruction }}
|
|
@@ -204,7 +223,9 @@ class CodeAutoGenerateEditBlock:
|
|
|
204
223
|
"structure": "",
|
|
205
224
|
"fence_0": self.fence_0,
|
|
206
225
|
"fence_1": self.fence_1,
|
|
207
|
-
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
extra_docs = get_rules()
|
|
208
229
|
|
|
209
230
|
return {
|
|
210
231
|
"structure": (
|
|
@@ -214,6 +235,7 @@ class CodeAutoGenerateEditBlock:
|
|
|
214
235
|
),
|
|
215
236
|
"fence_0": self.fence_0,
|
|
216
237
|
"fence_1": self.fence_1,
|
|
238
|
+
"extra_docs": extra_docs,
|
|
217
239
|
}
|
|
218
240
|
|
|
219
241
|
def single_round_run(
|
|
@@ -16,6 +16,7 @@ from autocoder.utils import llms as llm_utils
|
|
|
16
16
|
from autocoder.common import SourceCodeList
|
|
17
17
|
from autocoder.privacy.model_filter import ModelPathFilter
|
|
18
18
|
from autocoder.memory.active_context_manager import ActiveContextManager
|
|
19
|
+
from autocoder.common.rulefiles.autocoderrules_utils import get_rules
|
|
19
20
|
class CodeAutoGenerateStrictDiff:
|
|
20
21
|
def __init__(
|
|
21
22
|
self, llm: byzerllm.ByzerLLM, args: AutoCoderArgs, action=None
|
|
@@ -118,10 +119,12 @@ class CodeAutoGenerateStrictDiff:
|
|
|
118
119
|
现在让我们开始一个新的任务:
|
|
119
120
|
|
|
120
121
|
{%- if structure %}
|
|
122
|
+
====
|
|
121
123
|
{{ structure }}
|
|
122
124
|
{%- endif %}
|
|
123
125
|
|
|
124
126
|
{%- if content %}
|
|
127
|
+
====
|
|
125
128
|
下面是一些文件路径以及每个文件对应的源码:
|
|
126
129
|
<files>
|
|
127
130
|
{{ content }}
|
|
@@ -129,6 +132,7 @@ class CodeAutoGenerateStrictDiff:
|
|
|
129
132
|
{%- endif %}
|
|
130
133
|
|
|
131
134
|
{%- if package_context %}
|
|
135
|
+
====
|
|
132
136
|
下面是上面文件的一些信息(包括最近的变更情况):
|
|
133
137
|
<package_context>
|
|
134
138
|
{{ package_context }}
|
|
@@ -136,14 +140,29 @@ class CodeAutoGenerateStrictDiff:
|
|
|
136
140
|
{%- endif %}
|
|
137
141
|
|
|
138
142
|
{%- if context %}
|
|
143
|
+
====
|
|
139
144
|
<extra_context>
|
|
140
145
|
{{ context }}
|
|
141
146
|
</extra_context>
|
|
142
147
|
{%- endif %}
|
|
143
148
|
|
|
144
|
-
|
|
149
|
+
{%- if extra_docs %}
|
|
150
|
+
====
|
|
145
151
|
|
|
146
|
-
|
|
152
|
+
RULES PROVIDED BY USER
|
|
153
|
+
|
|
154
|
+
The following rules are provided by the user, and you must follow them strictly.
|
|
155
|
+
|
|
156
|
+
{% for key, value in extra_docs.items() %}
|
|
157
|
+
<user_rule>
|
|
158
|
+
##File: {{ key }}
|
|
159
|
+
{{ value }}
|
|
160
|
+
</user_rule>
|
|
161
|
+
{% endfor %}
|
|
162
|
+
{% endif %}
|
|
163
|
+
|
|
164
|
+
====
|
|
165
|
+
下面是用户的需求:
|
|
147
166
|
|
|
148
167
|
每次生成一个文件的diff,然后询问我是否继续,当我回复继续,继续生成下一个文件的diff。当没有后续任务时,请回复 "__完成__" 或者 "__EOF__"。
|
|
149
168
|
"""
|
|
@@ -152,13 +171,16 @@ class CodeAutoGenerateStrictDiff:
|
|
|
152
171
|
return {
|
|
153
172
|
"structure": "",
|
|
154
173
|
}
|
|
174
|
+
|
|
175
|
+
extra_docs = get_rules()
|
|
155
176
|
|
|
156
177
|
return {
|
|
157
178
|
"structure": (
|
|
158
179
|
self.action.pp.get_tree_like_directory_structure()
|
|
159
180
|
if self.action
|
|
160
181
|
else ""
|
|
161
|
-
)
|
|
182
|
+
),
|
|
183
|
+
"extra_docs": extra_docs,
|
|
162
184
|
}
|
|
163
185
|
|
|
164
186
|
@byzerllm.prompt(llm=lambda self: self.llm)
|
autocoder/rag/api_server.py
CHANGED
|
@@ -365,6 +365,13 @@ def serve(llm:ByzerLLM, args: ServerArgs):
|
|
|
365
365
|
server_model_name=args.served_model_name,
|
|
366
366
|
prompt_template=args.prompt_template
|
|
367
367
|
)
|
|
368
|
+
|
|
369
|
+
# Patch _check_model方法,使其永远返回None
|
|
370
|
+
async def always_return_none(*args, **kwargs):
|
|
371
|
+
return None
|
|
372
|
+
|
|
373
|
+
openai_serving_chat._check_model = always_return_none
|
|
374
|
+
openai_serving_completion._check_model = always_return_none
|
|
368
375
|
|
|
369
376
|
# 如果使用workers>1或reload=True,必须使用导入字符串而不是应用实例
|
|
370
377
|
uvicorn.run(
|
autocoder/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.1.
|
|
1
|
+
__version__ = "0.1.360"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|