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

@@ -41,6 +41,7 @@ COMMAND_HIERARCHY = {
41
41
  "/clear": {},
42
42
  "/cls": {},
43
43
  "/debug": {},
44
+ "/rules": {"/list", "/get", "/remove", "/analyze", "/commit", "/help"},
44
45
  }
45
46
 
46
47
  class CommandCompleterV2(Completer):
@@ -86,6 +87,7 @@ class CommandCompleterV2(Completer):
86
87
  "/ask": self._handle_text_with_symbols, # Treat like chat for @/@@
87
88
  "/summon": self._handle_text_with_symbols,
88
89
  "/design": self._handle_design,
90
+ "/rules": self._handle_rules,
89
91
  # Add handlers for other commands if they need specific logic beyond @/@@
90
92
  # Default handler for plain text or commands not explicitly handled
91
93
  "default": self._handle_text_with_symbols,
@@ -438,6 +440,43 @@ class CommandCompleterV2(Completer):
438
440
  elif word.startswith("<"): # Potential tag completion
439
441
  yield from self._handle_img_tag(document, complete_event, word, text)
440
442
 
443
+ def _handle_rules(self, document: Document, complete_event: CompleteEvent, word: str, text: str) -> Iterable[Completion]:
444
+ """处理 /rules 命令的补全,支持子命令和规则文件路径。同时支持 @ 和 @@ 符号。"""
445
+ args_text = text[len("/rules"):].lstrip()
446
+ parts = args_text.split()
447
+ last_part = parts[-1] if parts and not text.endswith(" ") else ""
448
+
449
+ # 补全子命令
450
+ if not args_text or (len(parts) == 1 and not text.endswith(" ") and parts[0].startswith("/")):
451
+ for sub_cmd in COMMAND_HIERARCHY["/rules"]:
452
+ if sub_cmd.startswith(last_part):
453
+ yield Completion(sub_cmd, start_position=-len(last_part))
454
+ return
455
+
456
+ # 根据子命令补全参数
457
+ if parts and parts[0] == "/list" or parts[0] == "/get" or parts[0] == "/remove":
458
+ # 获取规则文件或目录补全,可以是通配符
459
+ # 这里可以简单地提供文件路径补全
460
+ yield from self._complete_file_paths(last_part, text)
461
+ # 也可以添加常用通配符补全
462
+ common_patterns = ["*.md", "*.rules", "*.txt"]
463
+ for pattern in common_patterns:
464
+ if pattern.startswith(last_part):
465
+ yield Completion(pattern, start_position=-len(last_part))
466
+ return
467
+
468
+ # 对于 /commit 子命令,补全 /query
469
+ if parts and parts[0] == "/commit":
470
+ if "/query".startswith(last_part):
471
+ yield Completion("/query", start_position=-len(last_part))
472
+ return
473
+
474
+ # 支持 @ 和 @@ 符号的补全,不管当前命令是什么
475
+ if word.startswith("@") and not word.startswith("@@"):
476
+ yield from self._handle_at_completion(document, complete_event, word, text)
477
+ elif word.startswith("@@"):
478
+ yield from self._handle_double_at_completion(document, complete_event, word, text)
479
+
441
480
 
442
481
  # --- Symbol/Tag Handlers ---
443
482
  def _handle_at_completion(self, document: Document, complete_event: CompleteEvent, word: str, text: str) -> Iterable[Completion]:
@@ -4,6 +4,9 @@ from threading import Lock
4
4
  import threading
5
5
  from typing import Dict, List, Optional
6
6
  from loguru import logger
7
+ import re
8
+ import yaml
9
+ from pydantic import BaseModel, Field
7
10
 
8
11
  # 尝试导入 FileMonitor
9
12
  try:
@@ -15,6 +18,15 @@ except ImportError:
15
18
  Change = None
16
19
 
17
20
 
21
+ class RuleFile(BaseModel):
22
+ """规则文件的Pydantic模型"""
23
+ description: str = Field(default="", description="规则的描述")
24
+ globs: List[str] = Field(default_factory=list, description="文件匹配模式列表")
25
+ always_apply: bool = Field(default=False, alias="alwaysApply", description="是否总是应用规则")
26
+ content: str = Field(default="", description="规则文件的正文内容")
27
+ file_path: str = Field(default="", description="规则文件的路径")
28
+
29
+
18
30
  class AutocoderRulesManager:
19
31
  """
20
32
  管理和监控 autocoderrules 目录中的规则文件。
@@ -157,9 +169,66 @@ class AutocoderRulesManager:
157
169
  self._load_rules()
158
170
  logger.info("已重新加载规则")
159
171
 
172
+ def parse_rule_file(self, file_path: str) -> RuleFile:
173
+ """
174
+ 解析规则文件并返回结构化的Pydantic模型对象
175
+
176
+ Args:
177
+ file_path: 规则文件的路径
178
+
179
+ Returns:
180
+ RuleFile: 包含规则文件结构化内容的Pydantic模型
181
+ """
182
+ if not os.path.exists(file_path) or not file_path.endswith('.md'):
183
+ logger.warning(f"无效的规则文件路径: {file_path}")
184
+ return RuleFile(file_path=file_path)
185
+
186
+ try:
187
+ with open(file_path, 'r', encoding='utf-8') as f:
188
+ content = f.read()
189
+
190
+ # 解析YAML头部和Markdown内容
191
+ yaml_pattern = re.compile(r'^---\s*\n(.*?)\n---\s*\n', re.DOTALL)
192
+ yaml_match = yaml_pattern.search(content)
193
+
194
+ metadata = {}
195
+ markdown_content = content
196
+
197
+ if yaml_match:
198
+ yaml_content = yaml_match.group(1)
199
+ try:
200
+ metadata = yaml.safe_load(yaml_content)
201
+ # 移除YAML部分,仅保留Markdown内容
202
+ markdown_content = content[yaml_match.end():]
203
+ except Exception as e:
204
+ logger.warning(f"解析规则文件YAML头部时出错: {e}")
205
+
206
+ # 创建并返回Pydantic模型
207
+ rule = RuleFile(
208
+ description=metadata.get('description', ''),
209
+ globs=metadata.get('globs', []),
210
+ always_apply=metadata.get('alwaysApply', False),
211
+ content=markdown_content.strip(),
212
+ file_path=file_path
213
+ )
214
+
215
+ return rule
216
+
217
+ except Exception as e:
218
+ logger.warning(f"解析规则文件时出错: {file_path}, 错误: {e}")
219
+ return RuleFile(file_path=file_path)
220
+
160
221
  def get_rules(self) -> Dict[str, str]:
161
222
  """获取所有规则文件内容"""
162
223
  return self._rules.copy()
224
+
225
+ def get_parsed_rules(self) -> List[RuleFile]:
226
+ """获取所有解析后的规则文件"""
227
+ parsed_rules = []
228
+ for file_path in self._rules:
229
+ parsed_rule = self.parse_rule_file(file_path)
230
+ parsed_rules.append(parsed_rule)
231
+ return parsed_rules
163
232
 
164
233
 
165
234
  # 对外提供单例
@@ -171,3 +240,17 @@ def get_rules(project_root: Optional[str] = None) -> Dict[str, str]:
171
240
  if _rules_manager is None:
172
241
  _rules_manager = AutocoderRulesManager(project_root=project_root)
173
242
  return _rules_manager.get_rules()
243
+
244
+ def get_parsed_rules(project_root: Optional[str] = None) -> List[RuleFile]:
245
+ """获取所有解析后的规则文件,可指定项目根目录"""
246
+ global _rules_manager
247
+ if _rules_manager is None:
248
+ _rules_manager = AutocoderRulesManager(project_root=project_root)
249
+ return _rules_manager.get_parsed_rules()
250
+
251
+ def parse_rule_file(file_path: str, project_root: Optional[str] = None) -> RuleFile:
252
+ """解析指定的规则文件,可指定项目根目录"""
253
+ global _rules_manager
254
+ if _rules_manager is None:
255
+ _rules_manager = AutocoderRulesManager(project_root=project_root)
256
+ return _rules_manager.parse_rule_file(file_path)
autocoder/index/index.py CHANGED
@@ -487,6 +487,7 @@ class IndexManager:
487
487
  keys_to_remove.append(file_path)
488
488
 
489
489
  # 删除被排除的文件
490
+ exclude_patterns = []
490
491
  try:
491
492
  exclude_patterns = self.parse_exclude_files(
492
493
  self.args.exclude_files)
@@ -498,7 +499,7 @@ class IndexManager:
498
499
  "index_exclude_files_error",
499
500
  style="red",
500
501
  error=str(e)
501
- )
502
+ )
502
503
 
503
504
  # 删除无效条目并记录日志
504
505
  for key in set(keys_to_remove):
@@ -528,6 +529,9 @@ class IndexManager:
528
529
  if self.should_skip(file_path):
529
530
  continue
530
531
 
532
+ if self.filter_exclude_files(file_path, exclude_patterns):
533
+ continue
534
+
531
535
  source_code = source.source_code
532
536
  if self.args.auto_merge == "strict_diff":
533
537
  v = source.source_code.splitlines()
@@ -33,6 +33,9 @@ class ImageLoader:
33
33
  and converting the content to markdown format.
34
34
  """
35
35
 
36
+ # 存储不同参数组合的PaddleOCR实例
37
+ _ocr_instances = {}
38
+
36
39
  @staticmethod
37
40
  def parse_diff(diff_content: str) -> List[Tuple[str, str]]:
38
41
  """
@@ -106,19 +109,28 @@ class ImageLoader:
106
109
  print("paddleocr not installed")
107
110
  return ""
108
111
 
109
- # 初始化 OCR
110
- try:
111
- ocr = PaddleOCR(
112
- use_angle_cls=use_angle_cls,
113
- lang=lang,
114
- page_num=page_num,
115
- det_model_dir=det_model_dir,
116
- rec_model_dir=rec_model_dir,
117
- **kwargs
118
- )
119
- except Exception:
120
- traceback.print_exc()
121
- return ""
112
+ # 创建一个参数的哈希键,用于在缓存中存储OCR实例
113
+ param_key = f"{lang}_{use_angle_cls}_{page_num}_{det_model_dir}_{rec_model_dir}_{hash(frozenset(kwargs.items()) if kwargs else 0)}"
114
+
115
+ # 检查是否已经有对应参数的OCR实例
116
+ if param_key not in ImageLoader._ocr_instances:
117
+ try:
118
+ # 初始化OCR并缓存
119
+ ImageLoader._ocr_instances[param_key] = PaddleOCR(
120
+ use_angle_cls=use_angle_cls,
121
+ lang=lang,
122
+ page_num=page_num,
123
+ det_model_dir=det_model_dir,
124
+ rec_model_dir=rec_model_dir,
125
+ **kwargs
126
+ )
127
+ logger.info(f"初始化新的PaddleOCR实例,参数:{param_key}")
128
+ except Exception:
129
+ traceback.print_exc()
130
+ return ""
131
+
132
+ # 使用缓存的OCR实例
133
+ ocr = ImageLoader._ocr_instances[param_key]
122
134
 
123
135
  try:
124
136
  ext = os.path.splitext(file_path)[1].lower()
@@ -27,7 +27,7 @@ from autocoder.rag.relevant_utils import (
27
27
  DocFilterResult
28
28
  )
29
29
  from autocoder.rag.token_checker import check_token_limit
30
- from autocoder.rag.token_counter import RemoteTokenCounter, TokenCounter
30
+ from autocoder.rag.token_counter import RemoteTokenCounter, TokenCounter,count_tokens
31
31
  from autocoder.rag.token_limiter import TokenLimiter
32
32
  from tokenizers import Tokenizer
33
33
  from autocoder.rag.variable_holder import VariableHolder
@@ -782,7 +782,7 @@ class LongContextRAG:
782
782
  )
783
783
 
784
784
  # 记录令牌统计
785
- request_tokens = sum([doc.tokens for doc in relevant_docs])
785
+ request_tokens = sum([count_tokens(doc.source_code) for doc in relevant_docs])
786
786
  target_model = target_llm.default_model_name
787
787
  logger.info(
788
788
  f"=== LLM Request ===\n"
@@ -26,7 +26,7 @@ PROVIDER_INFO_LIST = [
26
26
  name="volcano",
27
27
  endpoint="https://ark.cn-beijing.volces.com/api/v3",
28
28
  r1_model="deepseek-r1-250120",
29
- v3_model="deepseek-v3-241226",
29
+ v3_model="deepseek-v3-250324",
30
30
  api_key="",
31
31
  r1_input_price=2.0,
32
32
  r1_output_price=8.0,
@@ -55,6 +55,17 @@ PROVIDER_INFO_LIST = [
55
55
  v3_input_price=2.0,
56
56
  v3_output_price=8.0,
57
57
  ),
58
+ ProviderInfo(
59
+ name="openrouter",
60
+ endpoint="https://openrouter.ai/api/v1",
61
+ r1_model="deepseek/deepseek-r1",
62
+ v3_model="deepseek/deepseek-chat-v3-0324",
63
+ api_key="",
64
+ r1_input_price=0.0,
65
+ r1_output_price=0.0,
66
+ v3_input_price=0.0,
67
+ v3_output_price=0.0,
68
+ )
58
69
  ]
59
70
 
60
71
  dialog_style = Style.from_dict({
@@ -147,7 +158,8 @@ class ModelProviderSelector:
147
158
  values=[
148
159
  ("volcano", self.printer.get_message_from_key("model_provider_volcano")),
149
160
  ("siliconflow", self.printer.get_message_from_key("model_provider_siliconflow")),
150
- ("deepseek", self.printer.get_message_from_key("model_provider_deepseek"))
161
+ ("deepseek", self.printer.get_message_from_key("model_provider_deepseek")),
162
+ ("openrouter", self.printer.get_message_from_key("model_provider_openrouter"))
151
163
  ],
152
164
  style=dialog_style
153
165
  ).run()
autocoder/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.355"
1
+ __version__ = "0.1.357"