auto-coder 0.1.304__py3-none-any.whl → 0.1.306__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.304.dist-info → auto_coder-0.1.306.dist-info}/METADATA +1 -1
- {auto_coder-0.1.304.dist-info → auto_coder-0.1.306.dist-info}/RECORD +45 -40
- autocoder/agent/auto_learn_from_commit.py +3 -1
- autocoder/agent/auto_review_commit.py +3 -1
- autocoder/auto_coder.py +3 -2
- autocoder/auto_coder_runner.py +116 -3
- autocoder/chat_auto_coder.py +9 -1
- autocoder/chat_auto_coder_lang.py +552 -278
- autocoder/commands/auto_command.py +1 -4
- autocoder/commands/auto_web.py +1 -9
- autocoder/common/__init__.py +4 -0
- autocoder/common/auto_coder_lang.py +737 -392
- autocoder/common/code_auto_generate.py +104 -16
- autocoder/common/code_auto_generate_diff.py +101 -10
- autocoder/common/code_auto_generate_editblock.py +103 -9
- autocoder/common/code_auto_generate_strict_diff.py +99 -9
- autocoder/common/code_auto_merge.py +8 -0
- autocoder/common/code_auto_merge_diff.py +8 -0
- autocoder/common/code_auto_merge_editblock.py +7 -0
- autocoder/common/code_auto_merge_strict_diff.py +5 -0
- autocoder/common/code_modification_ranker.py +4 -2
- autocoder/common/command_completer.py +12 -0
- autocoder/common/command_generator.py +5 -4
- autocoder/common/git_utils.py +13 -7
- autocoder/common/global_cancel.py +68 -7
- autocoder/common/stream_out_type.py +5 -1
- autocoder/common/utils_code_auto_generate.py +29 -3
- autocoder/dispacher/__init__.py +18 -19
- autocoder/dispacher/actions/action.py +6 -162
- autocoder/dispacher/actions/plugins/action_regex_project.py +2 -6
- autocoder/index/filter/quick_filter.py +6 -3
- autocoder/index/index.py +2 -4
- autocoder/memory/__init__.py +7 -0
- autocoder/memory/active_context_manager.py +649 -0
- autocoder/memory/active_package.py +469 -0
- autocoder/memory/async_processor.py +161 -0
- autocoder/memory/directory_mapper.py +67 -0
- autocoder/utils/auto_coder_utils/chat_stream_out.py +61 -11
- autocoder/utils/project_structure.py +35 -1
- autocoder/utils/thread_utils.py +78 -169
- autocoder/version.py +1 -1
- {auto_coder-0.1.304.dist-info → auto_coder-0.1.306.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.304.dist-info → auto_coder-0.1.306.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.304.dist-info → auto_coder-0.1.306.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.304.dist-info → auto_coder-0.1.306.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,469 @@
|
|
|
1
|
+
"""
|
|
2
|
+
活动包 - 生成目录的活动上下文文档
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from typing import Dict, Any, Optional, Tuple, List
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
import re
|
|
9
|
+
import byzerllm
|
|
10
|
+
from loguru import logger as global_logger
|
|
11
|
+
|
|
12
|
+
class ActivePackage:
|
|
13
|
+
"""
|
|
14
|
+
ActivePackage负责生成每个目录的活动上下文文档,
|
|
15
|
+
包括当前变更信息和相关文件的详细文档。
|
|
16
|
+
|
|
17
|
+
如果目录中已存在active.md文件,会先读取现有内容作为参考,
|
|
18
|
+
然后基于现有信息和新信息一起生成更新后的文档。
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, llm: byzerllm.ByzerLLM):
|
|
22
|
+
"""
|
|
23
|
+
初始化活动包生成器
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
llm: ByzerLLM实例,用于生成文档内容
|
|
27
|
+
"""
|
|
28
|
+
self.llm = llm
|
|
29
|
+
# 创建专用的 logger 实例
|
|
30
|
+
self.logger = global_logger.bind(name="ActivePackage")
|
|
31
|
+
|
|
32
|
+
def generate_active_file(self, context: Dict[str, Any], query: str,
|
|
33
|
+
existing_file_path: Optional[str] = None,
|
|
34
|
+
file_changes: Optional[Dict[str, Tuple[str, str]]] = None) -> str:
|
|
35
|
+
"""
|
|
36
|
+
生成完整的活动文件内容
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
context: 目录上下文字典
|
|
40
|
+
query: 用户查询/需求
|
|
41
|
+
existing_file_path: 可选的现有文件路径,如果提供,将读取并参考现有内容
|
|
42
|
+
file_changes: 文件变更字典,键为文件路径,值为(变更前内容, 变更后内容)的元组
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
str: 生成的活动文件内容
|
|
46
|
+
"""
|
|
47
|
+
try:
|
|
48
|
+
# 检查是否有现有文件
|
|
49
|
+
existing_content = None
|
|
50
|
+
if existing_file_path and os.path.exists(existing_file_path):
|
|
51
|
+
try:
|
|
52
|
+
with open(existing_file_path, 'r', encoding='utf-8') as f:
|
|
53
|
+
existing_content = f.read()
|
|
54
|
+
self.logger.info(f"Found existing active.md file: {existing_file_path}")
|
|
55
|
+
except Exception as e:
|
|
56
|
+
self.logger.error(f"Error reading existing file {existing_file_path}: {e}")
|
|
57
|
+
|
|
58
|
+
# 增强上下文信息,添加文件变更信息
|
|
59
|
+
enhanced_context = self._enhance_context_with_changes(context, file_changes)
|
|
60
|
+
|
|
61
|
+
# 根据是否有现有内容选择不同的生成方式
|
|
62
|
+
if existing_content:
|
|
63
|
+
# 有现有内容,使用更新模式
|
|
64
|
+
file_content = self.generate_updated_active_file(enhanced_context, query, existing_content)
|
|
65
|
+
else:
|
|
66
|
+
# 无现有内容,使用创建模式
|
|
67
|
+
file_content = self.generate_new_active_file(enhanced_context, query)
|
|
68
|
+
|
|
69
|
+
return file_content
|
|
70
|
+
except Exception as e:
|
|
71
|
+
self.logger.error(f"Error generating active file: {e}")
|
|
72
|
+
return f"# 生成文档时出错\n\n错误: {str(e)}"
|
|
73
|
+
|
|
74
|
+
def _enhance_context_with_changes(self, context: Dict[str, Any],
|
|
75
|
+
file_changes: Optional[Dict[str, Tuple[str, str]]]) -> Dict[str, Any]:
|
|
76
|
+
"""
|
|
77
|
+
使用文件变更信息增强上下文
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
context: 原始上下文字典
|
|
81
|
+
file_changes: 文件变更字典
|
|
82
|
+
|
|
83
|
+
Returns:
|
|
84
|
+
Dict[str, Any]: 增强后的上下文字典
|
|
85
|
+
"""
|
|
86
|
+
if not file_changes:
|
|
87
|
+
return context
|
|
88
|
+
|
|
89
|
+
# 创建上下文的深拷贝,避免修改原始内容
|
|
90
|
+
enhanced_context = context.copy()
|
|
91
|
+
|
|
92
|
+
# 添加文件变更信息到changed_files
|
|
93
|
+
if 'changed_files' in enhanced_context:
|
|
94
|
+
changed_files_with_diffs = []
|
|
95
|
+
for file_info in enhanced_context['changed_files']:
|
|
96
|
+
file_path = file_info['path']
|
|
97
|
+
# 创建文件信息的副本
|
|
98
|
+
new_file_info = file_info.copy()
|
|
99
|
+
|
|
100
|
+
# 添加变更内容(如果有)
|
|
101
|
+
if file_path in file_changes:
|
|
102
|
+
before_content, after_content = file_changes[file_path]
|
|
103
|
+
new_file_info['before_content'] = before_content
|
|
104
|
+
new_file_info['after_content'] = after_content
|
|
105
|
+
new_file_info['has_diff'] = True
|
|
106
|
+
|
|
107
|
+
changed_files_with_diffs.append(new_file_info)
|
|
108
|
+
|
|
109
|
+
enhanced_context['changed_files'] = changed_files_with_diffs
|
|
110
|
+
|
|
111
|
+
# 在上下文中添加文件变更摘要信息
|
|
112
|
+
file_diffs = []
|
|
113
|
+
for file_path, (before, after) in file_changes.items():
|
|
114
|
+
if before and after:
|
|
115
|
+
# 简单计算差异 - 实际应用中可能需要更复杂的差异计算
|
|
116
|
+
diff_info = {
|
|
117
|
+
'path': file_path,
|
|
118
|
+
'type': 'modified',
|
|
119
|
+
'before_lines': len(before.split('\n')) if before else 0,
|
|
120
|
+
'after_lines': len(after.split('\n')) if after else 0
|
|
121
|
+
}
|
|
122
|
+
elif not before and after:
|
|
123
|
+
diff_info = {'path': file_path, 'type': 'added'}
|
|
124
|
+
elif before and not after:
|
|
125
|
+
diff_info = {'path': file_path, 'type': 'deleted'}
|
|
126
|
+
else:
|
|
127
|
+
continue
|
|
128
|
+
|
|
129
|
+
file_diffs.append(diff_info)
|
|
130
|
+
|
|
131
|
+
enhanced_context['file_diffs'] = file_diffs
|
|
132
|
+
|
|
133
|
+
return enhanced_context
|
|
134
|
+
|
|
135
|
+
def generate_new_active_file(self, context: Dict[str, Any], query: str) -> str:
|
|
136
|
+
"""
|
|
137
|
+
生成全新的活动文件内容
|
|
138
|
+
|
|
139
|
+
Args:
|
|
140
|
+
context: 目录上下文字典
|
|
141
|
+
query: 用户查询/需求
|
|
142
|
+
|
|
143
|
+
Returns:
|
|
144
|
+
str: 新生成的活动文件内容
|
|
145
|
+
"""
|
|
146
|
+
try:
|
|
147
|
+
# 1. 生成current change部分
|
|
148
|
+
current_change = self.generate_current_change.with_llm(self.llm).run(context, query)
|
|
149
|
+
|
|
150
|
+
# 2. 生成document部分
|
|
151
|
+
document = self.generate_document.with_llm(self.llm).run(context, query)
|
|
152
|
+
|
|
153
|
+
# 3. 组合成完整的活动文件内容
|
|
154
|
+
file_content = f"# 活动上下文 - {os.path.basename(context['directory_path'])}\n\n"
|
|
155
|
+
file_content += f"## 当前变更\n\n{current_change}\n\n"
|
|
156
|
+
file_content += f"## 文档\n\n{document}\n"
|
|
157
|
+
|
|
158
|
+
return file_content
|
|
159
|
+
except Exception as e:
|
|
160
|
+
self.logger.error(f"Error generating new active file: {e}")
|
|
161
|
+
raise
|
|
162
|
+
|
|
163
|
+
def extract_sections(self, content: str) -> Tuple[str, str, str]:
|
|
164
|
+
"""
|
|
165
|
+
从现有内容中提取标题、当前变更和文档部分
|
|
166
|
+
|
|
167
|
+
Args:
|
|
168
|
+
content: 现有文件内容
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
Tuple[str, str, str]: 标题部分、当前变更部分、文档部分
|
|
172
|
+
"""
|
|
173
|
+
# 默认值
|
|
174
|
+
header = "# 活动上下文\n\n"
|
|
175
|
+
current_change_section = ""
|
|
176
|
+
document_section = ""
|
|
177
|
+
|
|
178
|
+
try:
|
|
179
|
+
# 提取标题部分(到第一个二级标题之前)
|
|
180
|
+
header_match = re.search(r'^(.*?)(?=\n## )', content, re.DOTALL)
|
|
181
|
+
if header_match:
|
|
182
|
+
header = header_match.group(1).strip() + "\n\n"
|
|
183
|
+
|
|
184
|
+
# 提取当前变更部分
|
|
185
|
+
current_change_match = re.search(r'## 当前变更\s*\n(.*?)(?=\n## |$)', content, re.DOTALL)
|
|
186
|
+
if current_change_match:
|
|
187
|
+
current_change_section = current_change_match.group(1).strip()
|
|
188
|
+
|
|
189
|
+
# 提取文档部分
|
|
190
|
+
document_match = re.search(r'## 文档\s*\n(.*?)(?=\n## |$)', content, re.DOTALL)
|
|
191
|
+
if document_match:
|
|
192
|
+
document_section = document_match.group(1).strip()
|
|
193
|
+
|
|
194
|
+
return header, current_change_section, document_section
|
|
195
|
+
except Exception as e:
|
|
196
|
+
self.logger.error(f"Error extracting sections: {e}")
|
|
197
|
+
return header, current_change_section, document_section
|
|
198
|
+
|
|
199
|
+
def generate_updated_active_file(self, context: Dict[str, Any], query: str, existing_content: str) -> str:
|
|
200
|
+
"""
|
|
201
|
+
基于现有内容生成更新后的活动文件内容
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
context: 目录上下文字典
|
|
205
|
+
query: 用户查询/需求
|
|
206
|
+
existing_content: 现有文件内容
|
|
207
|
+
|
|
208
|
+
Returns:
|
|
209
|
+
str: 更新后的活动文件内容
|
|
210
|
+
"""
|
|
211
|
+
try:
|
|
212
|
+
# 1. 从现有内容中提取各个部分
|
|
213
|
+
header, existing_current_change, existing_document = self.extract_sections(existing_content)
|
|
214
|
+
|
|
215
|
+
# 2. 分别更新每个部分
|
|
216
|
+
updated_current_change = self.update_current_change.with_llm(self.llm).run(
|
|
217
|
+
context=context,
|
|
218
|
+
query=query,
|
|
219
|
+
existing_current_change=existing_current_change
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
updated_document = self.update_document.with_llm(self.llm).run(
|
|
223
|
+
context=context,
|
|
224
|
+
query=query,
|
|
225
|
+
existing_document=existing_document
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
# 3. 组合成更新后的活动文件内容
|
|
229
|
+
file_content = f"{header}"
|
|
230
|
+
file_content += f"## 当前变更\n\n{updated_current_change}\n\n"
|
|
231
|
+
file_content += f"## 文档\n\n{updated_document}\n"
|
|
232
|
+
|
|
233
|
+
return file_content
|
|
234
|
+
except Exception as e:
|
|
235
|
+
self.logger.error(f"Error updating active file: {e}")
|
|
236
|
+
# 如果更新失败,回退到生成新文档
|
|
237
|
+
self.logger.info("Falling back to generating new active file")
|
|
238
|
+
return self.generate_new_active_file(context, query)
|
|
239
|
+
|
|
240
|
+
@byzerllm.prompt()
|
|
241
|
+
def update_current_change(self, context: Dict[str, Any], query: str, existing_current_change: str) -> str:
|
|
242
|
+
"""
|
|
243
|
+
请基于现有的"当前变更"文档和新的变更信息,生成一个更新后的"当前变更"部分。
|
|
244
|
+
|
|
245
|
+
现有的"当前变更"内容:
|
|
246
|
+
```
|
|
247
|
+
{{ existing_current_change }}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
当前需求:
|
|
251
|
+
{{ query }}
|
|
252
|
+
|
|
253
|
+
目录:{{ context.directory_path }}
|
|
254
|
+
|
|
255
|
+
最新变更的文件:
|
|
256
|
+
{% for file in context.changed_files %}
|
|
257
|
+
- {{ file.path }}
|
|
258
|
+
{% endfor %}
|
|
259
|
+
|
|
260
|
+
{% if context.file_diffs %}
|
|
261
|
+
文件变更摘要:
|
|
262
|
+
{% for diff in context.file_diffs %}
|
|
263
|
+
- {{ diff.path }}: {% if diff.type == 'modified' %}修改 (从{{ diff.before_lines }}行到{{ diff.after_lines }}行){% elif diff.type == 'added' %}新增{% elif diff.type == 'deleted' %}删除{% endif %}
|
|
264
|
+
{% endfor %}
|
|
265
|
+
{% endif %}
|
|
266
|
+
|
|
267
|
+
{% if context.changed_files and context.changed_files[0].has_diff %}
|
|
268
|
+
变更前后的代码对比:
|
|
269
|
+
{% for file in context.changed_files %}
|
|
270
|
+
{% if file.has_diff %}
|
|
271
|
+
文件: {{ file.path }}
|
|
272
|
+
变更前:
|
|
273
|
+
```
|
|
274
|
+
{{ file.before_content }}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
变更后:
|
|
278
|
+
```
|
|
279
|
+
{{ file.after_content }}
|
|
280
|
+
```
|
|
281
|
+
{% endif %}
|
|
282
|
+
{% endfor %}
|
|
283
|
+
{% endif %}
|
|
284
|
+
|
|
285
|
+
请执行以下任务:
|
|
286
|
+
1. 保留现有文档中的有用历史信息
|
|
287
|
+
2. 添加最新的变更信息,重点描述当前需求相关的变更
|
|
288
|
+
3. 明确指出新的变更与之前变更的关系(如继续完善、修复问题、新增功能等)
|
|
289
|
+
4. 确保变更描述清晰、具体,并表明每个文件的变更内容和目的
|
|
290
|
+
5. 如果有冲突的信息,优先保留最新的信息
|
|
291
|
+
|
|
292
|
+
你的回答应该是一个完整的"当前变更"部分内容,不需要包含标题。
|
|
293
|
+
"""
|
|
294
|
+
|
|
295
|
+
@byzerllm.prompt()
|
|
296
|
+
def update_document(self, context: Dict[str, Any], query: str, existing_document: str) -> str:
|
|
297
|
+
"""
|
|
298
|
+
请基于现有的"文档"部分和新的变更信息,生成一个更新后的"文档"部分。
|
|
299
|
+
|
|
300
|
+
现有的"文档"内容:
|
|
301
|
+
```
|
|
302
|
+
{{ existing_document }}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
当前需求:
|
|
306
|
+
{{ query }}
|
|
307
|
+
|
|
308
|
+
目录:{{ context.directory_path }}
|
|
309
|
+
|
|
310
|
+
相关文件:
|
|
311
|
+
{% for file in context.changed_files %}
|
|
312
|
+
- {{ file.path }}
|
|
313
|
+
{% endfor %}
|
|
314
|
+
|
|
315
|
+
{% if context.current_files %}
|
|
316
|
+
当前目录中的其他相关文件:
|
|
317
|
+
{% for file in context.current_files %}
|
|
318
|
+
- {{ file.path }}
|
|
319
|
+
{% endfor %}
|
|
320
|
+
{% endif %}
|
|
321
|
+
|
|
322
|
+
{% if context.file_diffs %}
|
|
323
|
+
文件变更摘要:
|
|
324
|
+
{% for diff in context.file_diffs %}
|
|
325
|
+
- {{ diff.path }}: {% if diff.type == 'modified' %}修改 (从{{ diff.before_lines }}行到{{ diff.after_lines }}行){% elif diff.type == 'added' %}新增{% elif diff.type == 'deleted' %}删除{% endif %}
|
|
326
|
+
{% endfor %}
|
|
327
|
+
{% endif %}
|
|
328
|
+
|
|
329
|
+
{% if context.changed_files and context.changed_files[0].has_diff %}
|
|
330
|
+
变更前后的代码对比:
|
|
331
|
+
{% for file in context.changed_files %}
|
|
332
|
+
{% if file.has_diff %}
|
|
333
|
+
文件: {{ file.path }}
|
|
334
|
+
变更前:
|
|
335
|
+
```
|
|
336
|
+
{{ file.before_content }}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
变更后:
|
|
340
|
+
```
|
|
341
|
+
{{ file.after_content }}
|
|
342
|
+
```
|
|
343
|
+
{% endif %}
|
|
344
|
+
{% endfor %}
|
|
345
|
+
{% endif %}
|
|
346
|
+
|
|
347
|
+
请执行以下任务:
|
|
348
|
+
1. 保留现有文档中的准确信息
|
|
349
|
+
2. 更新每个文件的文档,反映最新的变更
|
|
350
|
+
3. 如果有新文件,为其创建完整的文档
|
|
351
|
+
4. 确保文档格式一致性,每个文件的文档包含:功能、关键组件、变更影响、与其他文件的关系
|
|
352
|
+
5. 如有冲突信息,优先保留最新信息,但保留历史上下文
|
|
353
|
+
|
|
354
|
+
格式应为:
|
|
355
|
+
|
|
356
|
+
### [文件名]
|
|
357
|
+
- **功能**:
|
|
358
|
+
- **关键组件**:
|
|
359
|
+
- **变更影响**:
|
|
360
|
+
- **关系**:
|
|
361
|
+
|
|
362
|
+
你的回答应该是一个完整的"文档"部分内容,不需要包含标题。
|
|
363
|
+
"""
|
|
364
|
+
|
|
365
|
+
@byzerllm.prompt()
|
|
366
|
+
def generate_current_change(self, context: Dict[str, Any], query: str) -> str:
|
|
367
|
+
"""
|
|
368
|
+
请分析下面的代码变更,并描述它们与当前需求的关系。
|
|
369
|
+
|
|
370
|
+
需求:
|
|
371
|
+
{{ query }}
|
|
372
|
+
|
|
373
|
+
目录:{{ context.directory_path }}
|
|
374
|
+
|
|
375
|
+
变更的文件:
|
|
376
|
+
{% for file in context.changed_files %}
|
|
377
|
+
- {{ file.path }}
|
|
378
|
+
{% endfor %}
|
|
379
|
+
|
|
380
|
+
{% if context.file_diffs %}
|
|
381
|
+
文件变更摘要:
|
|
382
|
+
{% for diff in context.file_diffs %}
|
|
383
|
+
- {{ diff.path }}: {% if diff.type == 'modified' %}修改 (从{{ diff.before_lines }}行到{{ diff.after_lines }}行){% elif diff.type == 'added' %}新增{% elif diff.type == 'deleted' %}删除{% endif %}
|
|
384
|
+
{% endfor %}
|
|
385
|
+
{% endif %}
|
|
386
|
+
|
|
387
|
+
{% if context.changed_files and context.changed_files[0].has_diff %}
|
|
388
|
+
变更前后的代码对比:
|
|
389
|
+
{% for file in context.changed_files %}
|
|
390
|
+
{% if file.has_diff %}
|
|
391
|
+
文件: {{ file.path }}
|
|
392
|
+
变更前:
|
|
393
|
+
```
|
|
394
|
+
{{ file.before_content }}
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
变更后:
|
|
398
|
+
```
|
|
399
|
+
{{ file.after_content }}
|
|
400
|
+
```
|
|
401
|
+
{% endif %}
|
|
402
|
+
{% endfor %}
|
|
403
|
+
{% endif %}
|
|
404
|
+
|
|
405
|
+
分析并描述这些变更如何满足需求,以及这个目录中的文件在整体变更中起到什么作用。
|
|
406
|
+
描述应该清晰、具体,并表明每个文件的变更内容和目的。
|
|
407
|
+
"""
|
|
408
|
+
|
|
409
|
+
@byzerllm.prompt()
|
|
410
|
+
def generate_document(self, context: Dict[str, Any], query: str) -> str:
|
|
411
|
+
"""
|
|
412
|
+
请为下面列出的每个文件生成详细的文档说明。
|
|
413
|
+
|
|
414
|
+
需求:
|
|
415
|
+
{{ query }}
|
|
416
|
+
|
|
417
|
+
目录:{{ context.directory_path }}
|
|
418
|
+
|
|
419
|
+
文件列表:
|
|
420
|
+
{% for file in context.changed_files %}
|
|
421
|
+
- {{ file.path }}
|
|
422
|
+
{% endfor %}
|
|
423
|
+
|
|
424
|
+
{% if context.current_files %}
|
|
425
|
+
当前目录中的其他相关文件:
|
|
426
|
+
{% for file in context.current_files %}
|
|
427
|
+
- {{ file.path }}
|
|
428
|
+
{% endfor %}
|
|
429
|
+
{% endif %}
|
|
430
|
+
|
|
431
|
+
{% if context.file_diffs %}
|
|
432
|
+
文件变更摘要:
|
|
433
|
+
{% for diff in context.file_diffs %}
|
|
434
|
+
- {{ diff.path }}: {% if diff.type == 'modified' %}修改 (从{{ diff.before_lines }}行到{{ diff.after_lines }}行){% elif diff.type == 'added' %}新增{% elif diff.type == 'deleted' %}删除{% endif %}
|
|
435
|
+
{% endfor %}
|
|
436
|
+
{% endif %}
|
|
437
|
+
|
|
438
|
+
{% if context.changed_files and context.changed_files[0].has_diff %}
|
|
439
|
+
变更前后的代码对比:
|
|
440
|
+
{% for file in context.changed_files %}
|
|
441
|
+
{% if file.has_diff %}
|
|
442
|
+
文件: {{ file.path }}
|
|
443
|
+
变更前:
|
|
444
|
+
```
|
|
445
|
+
{{ file.before_content }}
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
变更后:
|
|
449
|
+
```
|
|
450
|
+
{{ file.after_content }}
|
|
451
|
+
```
|
|
452
|
+
{% endif %}
|
|
453
|
+
{% endfor %}
|
|
454
|
+
{% endif %}
|
|
455
|
+
|
|
456
|
+
对于每个文件,请提供:
|
|
457
|
+
1. 文件的主要功能
|
|
458
|
+
2. 文件中的关键组件(类、函数等)
|
|
459
|
+
3. 此次变更对文件的影响(如果适用)
|
|
460
|
+
4. 文件与其他文件的关系
|
|
461
|
+
|
|
462
|
+
格式应为:
|
|
463
|
+
|
|
464
|
+
### [文件名]
|
|
465
|
+
- **功能**:
|
|
466
|
+
- **关键组件**:
|
|
467
|
+
- **变更影响**:
|
|
468
|
+
- **关系**:
|
|
469
|
+
"""
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""
|
|
2
|
+
异步处理器 - 处理异步任务执行
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from concurrent.futures import ThreadPoolExecutor, wait
|
|
6
|
+
from typing import Dict, List, Optional, Any, Callable
|
|
7
|
+
from loguru import logger as global_logger
|
|
8
|
+
import sys
|
|
9
|
+
import time
|
|
10
|
+
|
|
11
|
+
class AsyncProcessor:
|
|
12
|
+
"""
|
|
13
|
+
AsyncProcessor负责异步执行任务,并提供任务状态管理。
|
|
14
|
+
使用线程池实现并发处理。
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, max_workers: int = 3):
|
|
18
|
+
"""
|
|
19
|
+
初始化异步处理器
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
max_workers: 最大工作线程数
|
|
23
|
+
"""
|
|
24
|
+
self.executor = ThreadPoolExecutor(max_workers=max_workers)
|
|
25
|
+
self.futures = {} # 使用字典存储futures,以便跟踪
|
|
26
|
+
self.results = {} # 存储任务结果
|
|
27
|
+
# 创建专用的 logger 实例
|
|
28
|
+
self.logger = global_logger.bind(name="AsyncProcessor")
|
|
29
|
+
|
|
30
|
+
def schedule(self, func: Callable, task_id: str, *args, **kwargs) -> str:
|
|
31
|
+
"""
|
|
32
|
+
调度异步任务
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
func: 要执行的函数
|
|
36
|
+
task_id: 任务ID
|
|
37
|
+
*args, **kwargs: 传递给函数的参数
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
str: 任务ID
|
|
41
|
+
"""
|
|
42
|
+
try:
|
|
43
|
+
future = self.executor.submit(func, task_id, *args, **kwargs)
|
|
44
|
+
self.futures[task_id] = future
|
|
45
|
+
return task_id
|
|
46
|
+
except Exception as e:
|
|
47
|
+
self.logger.error(f"Error scheduling task {task_id}: {e}")
|
|
48
|
+
raise
|
|
49
|
+
|
|
50
|
+
def cancel_task(self, task_id: str) -> bool:
|
|
51
|
+
"""
|
|
52
|
+
取消任务,如果任务正在运行则尝试取消
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
task_id: 任务ID
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
bool: 是否成功取消
|
|
59
|
+
"""
|
|
60
|
+
if task_id in self.futures:
|
|
61
|
+
return self.futures[task_id].cancel()
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
def get_task_status(self, task_id: str) -> str:
|
|
65
|
+
"""
|
|
66
|
+
获取任务状态
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
task_id: 任务ID
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
str: 任务状态
|
|
73
|
+
"""
|
|
74
|
+
if task_id not in self.futures:
|
|
75
|
+
return "unknown"
|
|
76
|
+
|
|
77
|
+
future = self.futures[task_id]
|
|
78
|
+
if future.running():
|
|
79
|
+
return "running"
|
|
80
|
+
if future.cancelled():
|
|
81
|
+
return "cancelled"
|
|
82
|
+
if future.done():
|
|
83
|
+
if future.exception():
|
|
84
|
+
return "failed"
|
|
85
|
+
return "completed"
|
|
86
|
+
return "pending"
|
|
87
|
+
|
|
88
|
+
def wait_for_task(self, task_id: str, timeout: Optional[float] = None) -> str:
|
|
89
|
+
"""
|
|
90
|
+
等待特定任务完成
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
task_id: 任务ID
|
|
94
|
+
timeout: 超时时间(秒)
|
|
95
|
+
|
|
96
|
+
Returns:
|
|
97
|
+
str: 任务状态
|
|
98
|
+
"""
|
|
99
|
+
if task_id not in self.futures:
|
|
100
|
+
return "unknown"
|
|
101
|
+
|
|
102
|
+
try:
|
|
103
|
+
future = self.futures[task_id]
|
|
104
|
+
future.result(timeout=timeout)
|
|
105
|
+
return "completed"
|
|
106
|
+
except TimeoutError:
|
|
107
|
+
return "timeout"
|
|
108
|
+
except Exception as e:
|
|
109
|
+
self.logger.error(f"Error in task {task_id}: {e}")
|
|
110
|
+
return "failed"
|
|
111
|
+
|
|
112
|
+
def wait_all(self, timeout: Optional[float] = None):
|
|
113
|
+
"""
|
|
114
|
+
等待所有任务完成
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
timeout: 超时时间(秒)
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
tuple: (已完成任务, 未完成任务)
|
|
121
|
+
"""
|
|
122
|
+
return wait(list(self.futures.values()), timeout=timeout)
|
|
123
|
+
|
|
124
|
+
def shutdown(self, wait: bool = True):
|
|
125
|
+
"""
|
|
126
|
+
关闭执行器
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
wait: 是否等待所有任务完成
|
|
130
|
+
"""
|
|
131
|
+
self.executor.shutdown(wait=wait)
|
|
132
|
+
|
|
133
|
+
def get_task_info(self, task_id: str) -> Dict[str, Any]:
|
|
134
|
+
"""
|
|
135
|
+
获取任务详细信息
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
task_id: 任务ID
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Dict: 任务信息
|
|
142
|
+
"""
|
|
143
|
+
status = self.get_task_status(task_id)
|
|
144
|
+
info = {
|
|
145
|
+
"task_id": task_id,
|
|
146
|
+
"status": status,
|
|
147
|
+
"start_time": time.time(), # 这里应该是实际的启动时间,但我们没有保存
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if status == "completed" and task_id in self.results:
|
|
151
|
+
info["result"] = self.results[task_id]
|
|
152
|
+
elif status == "failed" and task_id in self.futures:
|
|
153
|
+
try:
|
|
154
|
+
# 获取异常信息
|
|
155
|
+
future = self.futures[task_id]
|
|
156
|
+
if future.done():
|
|
157
|
+
info["error"] = str(future.exception())
|
|
158
|
+
except Exception:
|
|
159
|
+
pass
|
|
160
|
+
|
|
161
|
+
return info
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""
|
|
2
|
+
目录映射器 - 处理URL到目录上下文的映射
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import sys
|
|
7
|
+
from typing import List, Dict, Any
|
|
8
|
+
from loguru import logger as global_logger
|
|
9
|
+
|
|
10
|
+
class DirectoryMapper:
|
|
11
|
+
"""
|
|
12
|
+
DirectoryMapper负责将文件URL映射到对应的目录结构,
|
|
13
|
+
用于确定需要生成active.md文件的目录。
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self):
|
|
17
|
+
"""
|
|
18
|
+
初始化目录映射器
|
|
19
|
+
"""
|
|
20
|
+
# 创建专用的 logger 实例
|
|
21
|
+
self.logger = global_logger.bind(name="DirectoryMapper")
|
|
22
|
+
|
|
23
|
+
def map_directories(self, project_path: str, changed_urls: List[str],
|
|
24
|
+
current_urls: List[str] = None) -> List[Dict[str, Any]]:
|
|
25
|
+
"""
|
|
26
|
+
映射URLs到目录上下文
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
project_path: 项目根目录
|
|
30
|
+
changed_urls: 变更的文件路径列表
|
|
31
|
+
current_urls: 当前相关的文件路径列表
|
|
32
|
+
|
|
33
|
+
Returns:
|
|
34
|
+
List[Dict]: 目录上下文列表,每个上下文包含目录路径和相关文件信息
|
|
35
|
+
"""
|
|
36
|
+
# 1. 提取所有相关的目录
|
|
37
|
+
directories = set()
|
|
38
|
+
for url in changed_urls:
|
|
39
|
+
try:
|
|
40
|
+
directories.add(os.path.dirname(url))
|
|
41
|
+
except Exception as e:
|
|
42
|
+
self.logger.error(f"Error extracting directory from {url}: {e}")
|
|
43
|
+
|
|
44
|
+
# 2. 创建目录上下文字典
|
|
45
|
+
directory_contexts = []
|
|
46
|
+
for directory in directories:
|
|
47
|
+
try:
|
|
48
|
+
# 收集该目录下的所有变更文件
|
|
49
|
+
dir_changed_files = [url for url in changed_urls if os.path.dirname(url) == directory]
|
|
50
|
+
|
|
51
|
+
# 如果有current_urls,也收集
|
|
52
|
+
dir_current_files = []
|
|
53
|
+
if current_urls:
|
|
54
|
+
dir_current_files = [url for url in current_urls if os.path.dirname(url) == directory]
|
|
55
|
+
|
|
56
|
+
# 只有当目录中有变更文件时才添加上下文
|
|
57
|
+
if dir_changed_files:
|
|
58
|
+
context = {
|
|
59
|
+
'directory_path': directory,
|
|
60
|
+
'changed_files': [{'path': url} for url in dir_changed_files],
|
|
61
|
+
'current_files': [{'path': url} for url in dir_current_files]
|
|
62
|
+
}
|
|
63
|
+
directory_contexts.append(context)
|
|
64
|
+
except Exception as e:
|
|
65
|
+
self.logger.error(f"Error processing directory {directory}: {e}")
|
|
66
|
+
|
|
67
|
+
return directory_contexts
|