auto-coder 0.1.289__py3-none-any.whl → 0.1.290__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of auto-coder might be problematic. Click here for more details.

@@ -0,0 +1,265 @@
1
+ from typing import List, Dict, Any, Optional, Set, Tuple
2
+ import logging
3
+ from enum import Enum
4
+ from collections import defaultdict, Counter
5
+ from loguru import logger
6
+ class MergeStrategy(str, Enum):
7
+ """合并策略枚举类"""
8
+ SIMPLE_EXTEND = "simple_extend" # 简单扩展(当前实现)
9
+ FREQUENCY_RANK = "frequency_rank" # 按频率排序
10
+ WEIGHTED_RANK = "weighted_rank" # 加权排序
11
+ INTERLEAVE = "interleave" # 交错合并
12
+ DEDUPLICATE = "deduplicate" # 去重合并
13
+ QUERY_WEIGHTED = "query_weighted" # 按查询加权
14
+
15
+
16
+ class CacheResultMerger:
17
+ """
18
+ 缓存结果合并策略模块
19
+
20
+ 本模块提供了多种合并搜索结果的策略,用于处理多查询场景下的结果整合。
21
+
22
+ 主要包括:
23
+ 1. 简单扩展 (SIMPLE_EXTEND): 直接合并所有结果列表
24
+ 2. 频率排序 (FREQUENCY_RANK): 根据文件路径出现频率排序
25
+ 3. 加权排序 (WEIGHTED_RANK): 考虑结果排名位置的加权排序
26
+ 4. 交错合并 (INTERLEAVE): 交错合并多个查询结果
27
+ 5. 去重合并 (DEDUPLICATE): 合并结果并去除重复文件
28
+ 6. 查询加权 (QUERY_WEIGHTED): 考虑查询重要性的加权排序
29
+
30
+ 使用示例:
31
+ ```python
32
+ from cache_result_merge import CacheResultMerger, MergeStrategy
33
+
34
+ # 创建合并器
35
+ merger = CacheResultMerger(max_results=100)
36
+
37
+ # 假设有多个查询结果
38
+ query_results = [
39
+ ("query1", [result1, result2, ...]),
40
+ ("query2", [result3, result4, ...])
41
+ ]
42
+
43
+ # 使用特定策略合并
44
+ merged_results = merger.merge(
45
+ query_results,
46
+ strategy=MergeStrategy.WEIGHTED_RANK
47
+ )
48
+ ```
49
+ """
50
+
51
+ def __init__(self, max_results: int = None):
52
+ """
53
+ 初始化结果合并器
54
+
55
+ Args:
56
+ max_results: 最大结果数,如果为None则不限制
57
+ """
58
+ self.max_results = max_results
59
+
60
+ def merge(self, query_results: List[Tuple[str, List[Dict[str, Any]]]],
61
+ strategy: MergeStrategy = MergeStrategy.WEIGHTED_RANK) -> List[Dict[str, Any]]:
62
+ """
63
+ 根据指定策略合并查询结果
64
+
65
+ Args:
66
+ query_results: 查询结果列表,每项为(查询, 结果列表)的元组
67
+ strategy: 合并策略
68
+
69
+ Returns:
70
+ 合并后的结果列表
71
+ """
72
+ if strategy == MergeStrategy.SIMPLE_EXTEND:
73
+ return self._simple_extend_merge(query_results)
74
+ elif strategy == MergeStrategy.FREQUENCY_RANK:
75
+ return self._frequency_rank_merge(query_results)
76
+ elif strategy == MergeStrategy.WEIGHTED_RANK:
77
+ return self._weighted_rank_merge(query_results)
78
+ elif strategy == MergeStrategy.INTERLEAVE:
79
+ return self._interleave_merge(query_results)
80
+ elif strategy == MergeStrategy.DEDUPLICATE:
81
+ return self._deduplicate_merge(query_results)
82
+ elif strategy == MergeStrategy.QUERY_WEIGHTED:
83
+ return self._query_weighted_merge(query_results)
84
+ else:
85
+ logger.warning(f"未知的合并策略: {strategy},使用简单扩展策略")
86
+ return self._simple_extend_merge(query_results)
87
+
88
+ def _simple_extend_merge(self, query_results: List[Tuple[str, List[Dict[str, Any]]]]) -> List[Dict[str, Any]]:
89
+ """
90
+ 简单扩展策略:将所有结果简单合并
91
+
92
+ Args:
93
+ query_results: 查询结果列表
94
+
95
+ Returns:
96
+ 合并后的结果列表
97
+ """
98
+ all_results = []
99
+ for query, results in query_results:
100
+ all_results.extend(results)
101
+
102
+ logger.info(f"简单扩展策略合并结果: 从 {sum(len(r) for _, r in query_results)} 条到 {len(all_results)} 条")
103
+ return all_results[:self.max_results] if self.max_results else all_results
104
+
105
+ def _frequency_rank_merge(self, query_results: List[Tuple[str, List[Dict[str, Any]]]]) -> List[Dict[str, Any]]:
106
+ """
107
+ 频率排序策略:按文件路径出现频率排序
108
+
109
+ Args:
110
+ query_results: 查询结果列表
111
+
112
+ Returns:
113
+ 合并后的结果列表
114
+ """
115
+ # 合并所有结果
116
+ all_results = []
117
+ for _, results in query_results:
118
+ all_results.extend(results)
119
+
120
+ # 按文件路径计数
121
+ file_path_counts = Counter(result["file_path"] for result in all_results)
122
+
123
+ # 建立文件路径到结果的映射,保留每个文件路径的第一个结果
124
+ file_to_result = {}
125
+ for result in all_results:
126
+ file_path = result["file_path"]
127
+ if file_path not in file_to_result:
128
+ file_to_result[file_path] = result
129
+
130
+ # 按频率排序
131
+ sorted_results = [file_to_result[file_path] for file_path, _ in file_path_counts.most_common()]
132
+
133
+ logger.info(f"频率排序策略合并结果: 从 {len(all_results)} 条到 {len(sorted_results)} 条,按文件出现频率排序")
134
+ return sorted_results[:self.max_results] if self.max_results else sorted_results
135
+
136
+ def _weighted_rank_merge(self, query_results: List[Tuple[str, List[Dict[str, Any]]]]) -> List[Dict[str, Any]]:
137
+ """
138
+ 加权排序策略:考虑结果位置和频率的加权排序
139
+
140
+ Args:
141
+ query_results: 查询结果列表
142
+
143
+ Returns:
144
+ 合并后的结果列表
145
+ """
146
+ # 按文件路径评分
147
+ file_path_scores = defaultdict(float)
148
+ file_to_result = {}
149
+
150
+ for _, results in query_results:
151
+ for rank, result in enumerate(results):
152
+ file_path = result["file_path"]
153
+ # 排名越高,得分越高(排名从0开始,所以用1/(rank+1))
154
+ rank_score = 1.0 / (rank + 1)
155
+ file_path_scores[file_path] += rank_score
156
+
157
+ # 保存每个文件路径的第一个结果
158
+ if file_path not in file_to_result:
159
+ file_to_result[file_path] = result
160
+
161
+ # 按分数排序
162
+ sorted_results = [file_to_result[file_path]
163
+ for file_path, _ in sorted(file_path_scores.items(), key=lambda x: x[1], reverse=True)]
164
+
165
+ logger.info(f"加权排序策略合并结果: 得到 {len(sorted_results)} 条结果,按位置加权排序")
166
+ return sorted_results[:self.max_results] if self.max_results else sorted_results
167
+
168
+ def _interleave_merge(self, query_results: List[Tuple[str, List[Dict[str, Any]]]]) -> List[Dict[str, Any]]:
169
+ """
170
+ 交错合并策略:交错合并各查询的结果
171
+
172
+ Args:
173
+ query_results: 查询结果列表
174
+
175
+ Returns:
176
+ 合并后的结果列表
177
+ """
178
+ # 获取每个查询的结果列表
179
+ result_lists = [results for _, results in query_results]
180
+ if not result_lists:
181
+ return []
182
+
183
+ # 交错合并结果
184
+ interleaved = []
185
+ seen_files = set()
186
+
187
+ # 找出最长列表长度
188
+ max_len = max(len(results) for results in result_lists)
189
+
190
+ # 交错合并
191
+ for i in range(max_len):
192
+ for results in result_lists:
193
+ if i < len(results):
194
+ result = results[i]
195
+ file_path = result["file_path"]
196
+ if file_path not in seen_files:
197
+ seen_files.add(file_path)
198
+ interleaved.append(result)
199
+
200
+ logger.info(f"交错合并策略合并结果: 得到 {len(interleaved)} 条唯一结果")
201
+ return interleaved[:self.max_results] if self.max_results else interleaved
202
+
203
+ def _deduplicate_merge(self, query_results: List[Tuple[str, List[Dict[str, Any]]]]) -> List[Dict[str, Any]]:
204
+ """
205
+ 去重合并策略:合并并去除重复的文件路径
206
+
207
+ Args:
208
+ query_results: 查询结果列表
209
+
210
+ Returns:
211
+ 合并后的结果列表
212
+ """
213
+ all_results = []
214
+ seen_files = set()
215
+
216
+ for _, results in query_results:
217
+ for result in results:
218
+ file_path = result["file_path"]
219
+ if file_path not in seen_files:
220
+ seen_files.add(file_path)
221
+ all_results.append(result)
222
+
223
+ logger.info(f"去重合并策略合并结果: 从 {sum(len(r) for _, r in query_results)} 条到 {len(all_results)} 条唯一结果")
224
+ return all_results[:self.max_results] if self.max_results else all_results
225
+
226
+ def _query_weighted_merge(self, query_results: List[Tuple[str, List[Dict[str, Any]]]]) -> List[Dict[str, Any]]:
227
+ """
228
+ 查询加权策略:根据查询的重要性加权排序结果
229
+
230
+ Args:
231
+ query_results: 查询结果列表,包含(查询, 结果)对
232
+
233
+ Returns:
234
+ 合并后的结果列表
235
+ """
236
+ # 按照查询词加权
237
+ file_path_scores = defaultdict(float)
238
+ file_to_result = {}
239
+ query_weights = {}
240
+
241
+ # 计算每个查询的权重 (可以根据查询的长度、特殊性等调整)
242
+ total_queries = len(query_results)
243
+ for i, (query, _) in enumerate(query_results):
244
+ # 默认权重,可以根据查询特性调整
245
+ query_weights[query] = 1.0
246
+
247
+ # 计算文件得分
248
+ for query, results in query_results:
249
+ query_weight = query_weights[query]
250
+ for rank, result in enumerate(results):
251
+ file_path = result["file_path"]
252
+ # 排名越高,得分越高
253
+ rank_score = 1.0 / (rank + 1)
254
+ file_path_scores[file_path] += rank_score * query_weight
255
+
256
+ # 保存每个文件路径的第一个结果
257
+ if file_path not in file_to_result:
258
+ file_to_result[file_path] = result
259
+
260
+ # 按分数排序
261
+ sorted_results = [file_to_result[file_path]
262
+ for file_path, _ in sorted(file_path_scores.items(), key=lambda x: x[1], reverse=True)]
263
+
264
+ logger.info(f"查询加权策略合并结果: 得到 {len(sorted_results)} 条结果,按查询加权排序")
265
+ return sorted_results[:self.max_results] if self.max_results else sorted_results
@@ -11,6 +11,17 @@ from watchfiles import Change, DefaultFilter, awatch, watch
11
11
 
12
12
 
13
13
  class AutoCoderRAGDocListener(BaseCacheManager):
14
+ """
15
+ 基于文件系统实时监控的代码缓存管理器。
16
+
17
+ 此类实现了对代码库的实时监控,当文件发生变化时(新增、修改、删除)自动更新缓存。
18
+ 与其他缓存管理器不同,它使用 watchfiles 库进行文件变更监控,无需定期扫描文件系统。
19
+
20
+ 类属性:
21
+ cache: 缓存字典,存储处理后的文件内容
22
+ ignore_dirs: 需要忽略的目录列表
23
+ ignore_entity_patterns: 需要忽略的文件模式列表
24
+ """
14
25
  cache: Dict[str, Dict] = {}
15
26
  ignore_dirs = [
16
27
  "__pycache__",
@@ -38,6 +49,43 @@ class AutoCoderRAGDocListener(BaseCacheManager):
38
49
  ]
39
50
 
40
51
  def __init__(self, path: str, ignore_spec, required_exts: List) -> None:
52
+ """
53
+ 初始化文件监控缓存管理器。
54
+
55
+ 参数:
56
+ path: 需要监控的代码库根目录
57
+ ignore_spec: 指定哪些文件/目录应被忽略的规则
58
+ required_exts: 需要处理的文件扩展名列表
59
+
60
+ 缓存结构 (self.cache):
61
+ self.cache 是一个字典,其结构比其他缓存管理器更简单:
62
+ {
63
+ "file_path1": { # 键为文件的绝对路径
64
+ "file_path": str, # 文件的绝对路径
65
+ "content": List[Dict], # 文件内容的结构化表示,每个元素是 SourceCode 对象的序列化
66
+ },
67
+ "file_path2": { ... },
68
+ ...
69
+ }
70
+
71
+ 与其他缓存管理器的主要区别:
72
+ 1. 不需要存储 MD5 哈希或修改时间,因为文件变更通过监控系统直接获取
73
+ 2. 没有本地持久化机制,所有缓存在内存中维护
74
+ 3. 缓存更新基于事件驱动,而非定期扫描
75
+
76
+ 文件监控机制:
77
+ - 使用 watchfiles 库监控文件系统变更
78
+ - 支持三种事件类型: 添加(added)、修改(modified)、删除(deleted)
79
+ - 使用单独线程进行监控,不阻塞主线程
80
+ - 监控遵循配置的忽略规则和所需扩展名过滤
81
+ - 初始化时会先加载所有符合条件的文件
82
+
83
+ 源代码处理:
84
+ 使用 process_file_local 函数处理单个文件:
85
+ - 参数: file_path (文件路径)
86
+ - 返回值: List[SourceCode]
87
+ - 文件处理后,直接更新内存中的缓存
88
+ """
41
89
  self.path = path
42
90
  self.ignore_spec = ignore_spec
43
91
  self.required_exts = required_exts
@@ -59,13 +107,27 @@ class AutoCoderRAGDocListener(BaseCacheManager):
59
107
  self.watch_thread.start()
60
108
 
61
109
  def stop(self):
110
+ """
111
+ 停止文件监控线程。
112
+
113
+ 设置停止事件并等待监控线程结束,用于在对象销毁前优雅地关闭监控。
114
+ """
62
115
  self.stop_event.set()
63
116
  self.watch_thread.join()
64
117
 
65
118
  def __del__(self):
119
+ """
120
+ 析构函数,确保在对象被销毁时停止监控线程。
121
+ """
66
122
  self.stop()
67
123
 
68
124
  def load_first(self):
125
+ """
126
+ 初始化时加载所有符合条件的文件。
127
+
128
+ 获取所有符合过滤条件的文件,并将它们添加到缓存中。
129
+ 这确保了缓存在开始监控前已经包含所有现有文件。
130
+ """
69
131
  files_to_process = self.get_all_files()
70
132
  if not files_to_process:
71
133
  return
@@ -73,6 +135,17 @@ class AutoCoderRAGDocListener(BaseCacheManager):
73
135
  self.update_cache(item)
74
136
 
75
137
  def update_cache(self, file_path):
138
+ """
139
+ 处理单个文件并更新缓存。
140
+
141
+ 参数:
142
+ file_path: 文件的绝对路径
143
+
144
+ 处理流程:
145
+ 1. 使用 process_file_local 函数解析文件内容
146
+ 2. 将解析结果序列化并存储在缓存中
147
+ 3. 日志记录更新的文件及当前缓存状态
148
+ """
76
149
  source_code = process_file_local(file_path)
77
150
  self.cache[file_path] = {
78
151
  "file_path": file_path,
@@ -82,11 +155,25 @@ class AutoCoderRAGDocListener(BaseCacheManager):
82
155
  logger.info(f"current cache: {self.cache.keys()}")
83
156
 
84
157
  def remove_cache(self, file_path):
158
+ """
159
+ 从缓存中移除指定文件。
160
+
161
+ 参数:
162
+ file_path: 要移除的文件的绝对路径
163
+ """
85
164
  del self.cache[file_path]
86
165
  logger.info(f"remove cache: {file_path}")
87
166
  logger.info(f"current cache: {self.cache.keys()}")
88
167
 
89
168
  def open_watch(self):
169
+ """
170
+ 启动文件系统监控线程。
171
+
172
+ 此方法会持续监控文件系统变更,直到 stop_event 被设置。
173
+ 当检测到文件变更时,会根据变更类型执行相应的操作:
174
+ - 添加/修改文件: 调用 update_cache 更新缓存
175
+ - 删除文件: 调用 remove_cache 从缓存中移除
176
+ """
90
177
  logger.info(f"start monitor: {self.path}...")
91
178
  for changes in watch(
92
179
  self.path, watch_filter=self.file_filter, stop_event=self.stop_event
@@ -98,26 +185,52 @@ class AutoCoderRAGDocListener(BaseCacheManager):
98
185
  elif action == Change.deleted:
99
186
  self.remove_cache(path)
100
187
 
101
- def get_cache(self,options:Optional[Dict[str,Any]]=None):
188
+ def get_cache(self, options: Optional[Dict[str, Any]] = None):
189
+ """
190
+ 获取当前缓存。
191
+
192
+ 参数:
193
+ options: 可选的参数,指定获取缓存时的选项
194
+
195
+ 返回:
196
+ 当前内存中的缓存字典
197
+ """
102
198
  return self.cache
103
199
 
104
200
  def _load_ignore_file(self):
201
+ """
202
+ 加载忽略文件规则。
203
+
204
+ 首先尝试加载 .serveignore 文件,如果不存在,则尝试加载 .gitignore 文件。
205
+
206
+ 返回:
207
+ 包含忽略规则的字符串列表
208
+ """
105
209
  serveignore_path = os.path.join(self.path, ".serveignore")
106
210
  gitignore_path = os.path.join(self.path, ".gitignore")
107
211
 
108
212
  if os.path.exists(serveignore_path):
109
- with open(serveignore_path, "r",encoding="utf-8") as ignore_file:
213
+ with open(serveignore_path, "r", encoding="utf-8") as ignore_file:
110
214
  patterns = ignore_file.readlines()
111
215
  return [pattern.strip() for pattern in patterns]
112
216
  elif os.path.exists(gitignore_path):
113
- with open(gitignore_path, "r",encoding="utf-8") as ignore_file:
217
+ with open(gitignore_path, "r", encoding="utf-8") as ignore_file:
114
218
  patterns = ignore_file.readlines()
115
219
  return [pattern.strip() for pattern in patterns]
116
220
  return []
117
221
 
118
222
  def get_all_files(self) -> List[str]:
223
+ """
224
+ 获取所有符合条件的文件路径。
225
+
226
+ 遍历指定目录,应用忽略规则和扩展名过滤,
227
+ 返回所有符合条件的文件的绝对路径。
228
+
229
+ 返回:
230
+ 符合条件的文件路径列表
231
+ """
119
232
  all_files = []
120
- for root, dirs, files in os.walk(self.path,followlinks=True):
233
+ for root, dirs, files in os.walk(self.path, followlinks=True):
121
234
  dirs[:] = [d for d in dirs if not d.startswith(".")]
122
235
 
123
236
  if self.ignore_spec: