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

Files changed (51) hide show
  1. {auto_coder-0.1.375.dist-info → auto_coder-0.1.376.dist-info}/METADATA +1 -1
  2. {auto_coder-0.1.375.dist-info → auto_coder-0.1.376.dist-info}/RECORD +17 -51
  3. autocoder/agent/base_agentic/base_agent.py +9 -8
  4. autocoder/auto_coder_rag.py +12 -0
  5. autocoder/models.py +2 -2
  6. autocoder/rag/cache/local_duckdb_storage_cache.py +63 -33
  7. autocoder/rag/conversation_to_queries.py +37 -5
  8. autocoder/rag/long_context_rag.py +161 -41
  9. autocoder/rag/tools/recall_tool.py +2 -1
  10. autocoder/rag/tools/search_tool.py +2 -1
  11. autocoder/rag/types.py +36 -0
  12. autocoder/utils/_markitdown.py +59 -13
  13. autocoder/version.py +1 -1
  14. autocoder/agent/agentic_edit.py +0 -833
  15. autocoder/agent/agentic_edit_tools/__init__.py +0 -28
  16. autocoder/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py +0 -32
  17. autocoder/agent/agentic_edit_tools/attempt_completion_tool_resolver.py +0 -29
  18. autocoder/agent/agentic_edit_tools/base_tool_resolver.py +0 -29
  19. autocoder/agent/agentic_edit_tools/execute_command_tool_resolver.py +0 -84
  20. autocoder/agent/agentic_edit_tools/list_code_definition_names_tool_resolver.py +0 -75
  21. autocoder/agent/agentic_edit_tools/list_files_tool_resolver.py +0 -62
  22. autocoder/agent/agentic_edit_tools/plan_mode_respond_tool_resolver.py +0 -30
  23. autocoder/agent/agentic_edit_tools/read_file_tool_resolver.py +0 -36
  24. autocoder/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +0 -95
  25. autocoder/agent/agentic_edit_tools/search_files_tool_resolver.py +0 -70
  26. autocoder/agent/agentic_edit_tools/use_mcp_tool_resolver.py +0 -55
  27. autocoder/agent/agentic_edit_tools/write_to_file_tool_resolver.py +0 -98
  28. autocoder/agent/agentic_edit_types.py +0 -124
  29. autocoder/auto_coder_lang.py +0 -60
  30. autocoder/auto_coder_rag_client_mcp.py +0 -170
  31. autocoder/auto_coder_rag_mcp.py +0 -193
  32. autocoder/common/llm_rerank.py +0 -84
  33. autocoder/common/model_speed_test.py +0 -392
  34. autocoder/common/v2/agent/agentic_edit_conversation.py +0 -188
  35. autocoder/common/v2/agent/ignore_utils.py +0 -50
  36. autocoder/dispacher/actions/plugins/action_translate.py +0 -214
  37. autocoder/ignorefiles/__init__.py +0 -4
  38. autocoder/ignorefiles/ignore_file_utils.py +0 -63
  39. autocoder/ignorefiles/test_ignore_file_utils.py +0 -91
  40. autocoder/linters/code_linter.py +0 -588
  41. autocoder/rag/loaders/test_image_loader.py +0 -209
  42. autocoder/rag/raw_rag.py +0 -96
  43. autocoder/rag/simple_directory_reader.py +0 -646
  44. autocoder/rag/simple_rag.py +0 -404
  45. autocoder/regex_project/__init__.py +0 -162
  46. autocoder/utils/coder.py +0 -125
  47. autocoder/utils/tests.py +0 -37
  48. {auto_coder-0.1.375.dist-info → auto_coder-0.1.376.dist-info}/LICENSE +0 -0
  49. {auto_coder-0.1.375.dist-info → auto_coder-0.1.376.dist-info}/WHEEL +0 -0
  50. {auto_coder-0.1.375.dist-info → auto_coder-0.1.376.dist-info}/entry_points.txt +0 -0
  51. {auto_coder-0.1.375.dist-info → auto_coder-0.1.376.dist-info}/top_level.txt +0 -0
@@ -1,50 +0,0 @@
1
-
2
- import os
3
- from typing import Optional, List
4
- import pathspec
5
-
6
- DEFAULT_IGNORED_DIRS = ['.git', '.auto-coder', 'node_modules', '.mvn', '.idea', '__pycache__', '.venv', 'venv', 'dist', 'build', '.gradle']
7
-
8
-
9
- def load_ignore_spec(source_dir: str) -> Optional[pathspec.PathSpec]:
10
- """
11
- Loads .autocoderignore file from the source_dir if it exists.
12
- Returns a PathSpec object or None if no ignore file.
13
- """
14
- ignore_file_path = os.path.join(source_dir, ".autocoderignore")
15
- if not os.path.isfile(ignore_file_path):
16
- return None
17
- try:
18
- with open(ignore_file_path, "r") as f:
19
- ignore_patterns = f.read().splitlines()
20
- spec = pathspec.PathSpec.from_lines("gitwildmatch", ignore_patterns)
21
- return spec
22
- except Exception:
23
- return None
24
-
25
-
26
- def should_ignore(path: str, ignore_spec: Optional[pathspec.PathSpec], ignored_dirs: List[str], source_dir: str) -> bool:
27
- """
28
- Determine if a given path should be ignored based on ignore_spec and ignored_dirs.
29
- - path: absolute path
30
- - ignore_spec: PathSpec object or None
31
- - ignored_dirs: list of directory names to ignore
32
- - source_dir: root source directory absolute path
33
- """
34
- rel_path = os.path.relpath(path, source_dir)
35
- parts = rel_path.split(os.sep)
36
-
37
- # Always ignore if any part matches ignored_dirs
38
- for part in parts:
39
- if part in ignored_dirs:
40
- return True
41
-
42
- # If ignore_spec exists, use it to check
43
- if ignore_spec:
44
- # pathspec expects posix style paths
45
- rel_path_posix = rel_path.replace(os.sep, "/")
46
- # Check both file and dir ignoring
47
- if ignore_spec.match_file(rel_path_posix):
48
- return True
49
-
50
- return False
@@ -1,214 +0,0 @@
1
- from autocoder.common import (
2
- AutoCoderArgs,
3
- TranslateArgs,
4
- TranslateReadme,
5
- split_code_into_segments,
6
- )
7
- from autocoder.suffixproject import SuffixProject
8
- from typing import Optional
9
- import byzerllm
10
- import os
11
- import time
12
- from loguru import logger
13
- from prompt_toolkit import prompt
14
- from prompt_toolkit.shortcuts import confirm, radiolist_dialog
15
- from rich import print
16
- from rich.table import Table
17
-
18
-
19
- @byzerllm.prompt()
20
- def translate_readme(content: str, lang: str, instruction: Optional[str] = None) -> str:
21
- """
22
- 你做翻译时,需要遵循如下要求:
23
-
24
- {%- if instruction %}
25
- {{ instruction }}
26
- {%- endif %}
27
-
28
- 请将下面的内容翻译成{{ lang }}:
29
-
30
- {{ content }}
31
- """
32
-
33
-
34
- def get_translate_part(content: str) -> str:
35
- return content
36
-
37
-
38
- def confirm_translation_parameters(translate_args: TranslateArgs) -> TranslateArgs:
39
- while True:
40
- table = Table(title="Translation Parameters")
41
- table.add_column("Parameter", style="cyan")
42
- table.add_column("Value", style="magenta")
43
- table.add_row("Target Language", translate_args.target_lang)
44
- table.add_row("File Suffixes", translate_args.file_suffix)
45
- table.add_row("New File Mark", translate_args.new_file_mark)
46
- table.add_row(
47
- "Translate File Name", str(translate_args.should_translate_file_name)
48
- )
49
- table.add_row("File List", ", ".join(translate_args.file_list))
50
- table.add_row("Output Directory", translate_args.output_dir)
51
- print(table)
52
-
53
- if confirm("Are the above parameters correct?"):
54
- break
55
-
56
- param_options = [
57
- ("1", "Target Language"),
58
- ("2", "File Suffixes"),
59
- ("3", "New File Mark"),
60
- ("4", "Translate File Name"),
61
- ("5", "File List"),
62
- ("6", "Output Directory"),
63
- ]
64
- selected_param = radiolist_dialog(
65
- title="Select parameter to modify",
66
- text="Choose the parameter you want to change:",
67
- values=param_options,
68
- ).run()
69
-
70
- if selected_param == "1":
71
- translate_args.target_lang = prompt("Enter the new target language: ")
72
- elif selected_param == "2":
73
- translate_args.file_suffix = prompt(
74
- "Enter the new file suffixes (comma-separated): "
75
- )
76
- elif selected_param == "3":
77
- translate_args.new_file_mark = prompt("Enter the new file mark: ")
78
- elif selected_param == "4":
79
- translate_args.should_translate_file_name = confirm("Translate file names?")
80
- elif selected_param == "5":
81
- translate_args.file_list = prompt(
82
- "Enter the new file list (comma-separated): "
83
- ).split(",")
84
- elif selected_param == "6":
85
- translate_args.output_dir = prompt("Enter the new output directory: ")
86
-
87
- return translate_args
88
-
89
-
90
- class ActionTranslate:
91
- def __init__(
92
- self, args: AutoCoderArgs, llm: Optional[byzerllm.ByzerLLM] = None
93
- ) -> None:
94
- self.args = args
95
- self.llm = llm
96
- self.pp = None
97
-
98
- def run(self):
99
- args = self.args
100
- if not args.project_type.startswith("translate"):
101
- return False
102
-
103
- if (
104
- args.project_type == "translate"
105
- and args.query is not None
106
- and self.llm is not None
107
- ):
108
- t = self.llm.chat_oai(
109
- conversations=[{"role": "user", "content": args.query}],
110
- response_class=TranslateArgs,
111
- )
112
- tranlate_args: TranslateArgs = t[0].value
113
- if tranlate_args:
114
- lang = tranlate_args.target_lang
115
- suffixes = tranlate_args.file_suffix
116
- new_file_mark = tranlate_args.new_file_mark
117
- file_list = tranlate_args.file_list
118
- output_dir = tranlate_args.output_dir
119
- should_translate_file_name = tranlate_args.should_translate_file_name
120
- else:
121
- [
122
- _,
123
- lang,
124
- suffixes,
125
- new_file_mark,
126
- file_list_str,
127
- output_dir,
128
- should_translate_file_name,
129
- ] = args.project_type.split("/")
130
- file_list = file_list_str.split(",")
131
-
132
- translate_args = TranslateArgs(
133
- target_lang=lang,
134
- file_suffix=suffixes,
135
- new_file_mark=new_file_mark,
136
- file_list=file_list,
137
- output_dir=output_dir,
138
- should_translate_file_name=should_translate_file_name,
139
- )
140
-
141
- translate_args = confirm_translation_parameters(translate_args)
142
-
143
- def file_filter(file_path, suffixes):
144
- for suffix in suffixes:
145
- if suffix.startswith("."):
146
- if file_path.endswith(f"-{translate_args.new_file_mark}{suffix}"):
147
- return False
148
- else:
149
- if file_path.endswith(f"-{translate_args.new_file_mark}.{suffix}"):
150
- return False
151
- return True
152
-
153
- args.project_type = translate_args.file_suffix
154
- pp = SuffixProject(args=args, llm=self.llm, file_filter=file_filter)
155
- self.pp = pp
156
- pp.run()
157
- for source in pp.sources:
158
- if (
159
- translate_args.file_list
160
- and source.module_name not in translate_args.file_list
161
- ):
162
- continue
163
- logger.info(f"Translating {source.module_name}...")
164
- max_tokens = self.args.model_max_length or 2000
165
- segments = split_code_into_segments(
166
- source_code=source.source_code, max_tokens=max_tokens
167
- )
168
- temp_result = []
169
- segment_count = 0
170
- for segment in segments:
171
- content = translate_readme(
172
- content=segment,
173
- lang=translate_args.target_lang,
174
- instruction=args.query,
175
- )
176
- t = self.llm.chat_oai(
177
- conversations=[{"role": "user", "content": content}]
178
- )
179
- temp_result.append(get_translate_part(t[0].output))
180
- time.sleep(args.anti_quota_limit)
181
- segment_count += 1
182
- print(
183
- f"Translated {segment_count}({len(content)}) of {len(segments)} segments from {source.module_name}",
184
- flush=True,
185
- )
186
- readme = TranslateReadme(
187
- filename=source.module_name, content="".join(temp_result)
188
- )
189
- filename, extension = os.path.splitext(readme.filename)
190
- file_short_name, _ = os.path.splitext(os.path.basename(filename))
191
-
192
- if translate_args.new_file_mark:
193
- new_file_mark = f"-{translate_args.new_file_mark}"
194
-
195
- if translate_args.should_translate_file_name:
196
- file_short_name = translate_readme.with_llm(self.llm).run(
197
- content=file_short_name,
198
- lang=translate_args.target_lang,
199
- instruction=args.query,
200
- )
201
- file_short_name = file_short_name.replace(" ", "_")
202
-
203
- if translate_args.output_dir:
204
- new_filename = os.path.join(
205
- translate_args.output_dir,
206
- f"{file_short_name}{new_file_mark}{extension}",
207
- )
208
- else:
209
- new_filename = f"{filename}{new_file_mark}{extension}"
210
-
211
- logger.info(f"Writing to {new_filename}...")
212
- with open(new_filename, "w",encoding="utf-8") as file:
213
- file.write(readme.content)
214
- return True
@@ -1,4 +0,0 @@
1
-
2
- from .ignore_file_utils import should_ignore
3
-
4
- __all__ = ["should_ignore"]
@@ -1,63 +0,0 @@
1
-
2
- import os
3
- from pathlib import Path
4
- from threading import Lock
5
- import pathspec
6
-
7
- DEFAULT_EXCLUDES = [
8
- '.git', '.auto-coder', 'node_modules', '.mvn', '.idea',
9
- '__pycache__', '.venv', 'venv', 'dist', 'build', '.gradle',".next"
10
- ]
11
-
12
-
13
- class IgnoreFileManager:
14
- _instance = None
15
- _lock = Lock()
16
-
17
- def __new__(cls):
18
- if not cls._instance:
19
- with cls._lock:
20
- if not cls._instance:
21
- cls._instance = super(IgnoreFileManager, cls).__new__(cls)
22
- cls._instance._initialized = False
23
- return cls._instance
24
-
25
- def __init__(self):
26
- if self._initialized:
27
- return
28
- self._initialized = True
29
- self._spec = None
30
- self._load_ignore_spec()
31
-
32
- def _load_ignore_spec(self):
33
- ignore_patterns = []
34
- project_root = Path(os.getcwd())
35
-
36
- ignore_file_paths = [
37
- project_root / '.autocoderignore',
38
- project_root / '.auto-coder' / '.autocoderignore'
39
- ]
40
-
41
- for ignore_file in ignore_file_paths:
42
- if ignore_file.is_file():
43
- with open(ignore_file, 'r', encoding='utf-8') as f:
44
- ignore_patterns = f.read().splitlines()
45
- break
46
-
47
- # 添加默认排除目录
48
- ignore_patterns.extend(DEFAULT_EXCLUDES)
49
-
50
- self._spec = pathspec.PathSpec.from_lines('gitwildmatch', ignore_patterns)
51
-
52
- def should_ignore(self, path: str) -> bool:
53
- rel_path = os.path.relpath(path, os.getcwd())
54
- # 标准化分隔符
55
- rel_path = rel_path.replace(os.sep, '/')
56
- return self._spec.match_file(rel_path)
57
-
58
-
59
- # 对外提供单例
60
- _ignore_manager = IgnoreFileManager()
61
-
62
- def should_ignore(path: str) -> bool:
63
- return _ignore_manager.should_ignore(path)
@@ -1,91 +0,0 @@
1
-
2
- import os
3
- import shutil
4
- from pathlib import Path
5
-
6
- import pytest
7
-
8
- from src.autocoder.ignorefiles import ignore_file_utils
9
-
10
- @pytest.fixture(autouse=True)
11
- def cleanup_ignore_manager(monkeypatch):
12
- """
13
- 在每个测试前后清理 IgnoreFileManager 的单例状态,保证测试隔离
14
- """
15
- # 备份原始实例
16
- original_instance = ignore_file_utils._ignore_manager
17
- # 强制重新加载忽略规则
18
- def reset_ignore_manager():
19
- ignore_file_utils.IgnoreFileManager._instance = None
20
- return ignore_file_utils.IgnoreFileManager()
21
-
22
- monkeypatch.setattr(ignore_file_utils, "_ignore_manager", reset_ignore_manager())
23
- yield
24
- # 恢复原始实例
25
- ignore_file_utils._ignore_manager = original_instance
26
-
27
-
28
- def test_default_excludes(tmp_path, monkeypatch):
29
- # 切换当前工作目录
30
- monkeypatch.chdir(tmp_path)
31
-
32
- # 不创建任何 .autocoderignore 文件,使用默认排除规则
33
- # 创建默认排除目录
34
- for dirname in ignore_file_utils.DEFAULT_EXCLUDES:
35
- (tmp_path / dirname).mkdir(parents=True, exist_ok=True)
36
- # 应该被忽略
37
- assert ignore_file_utils.should_ignore(str(tmp_path / dirname)) is True
38
-
39
- # 创建不会被忽略的文件
40
- normal_file = tmp_path / "myfile.txt"
41
- normal_file.write_text("hello")
42
- assert ignore_file_utils.should_ignore(str(normal_file)) is False
43
-
44
-
45
- def test_custom_ignore_file(tmp_path, monkeypatch):
46
- monkeypatch.chdir(tmp_path)
47
-
48
- # 创建自定义忽略文件
49
- ignore_file = tmp_path / ".autocoderignore"
50
- ignore_file.write_text("data/**\nsecret.txt")
51
-
52
- # 重新初始化忽略管理器以加载新规则
53
- ignore_file_utils.IgnoreFileManager._instance = None
54
- ignore_file_utils._ignore_manager = ignore_file_utils.IgnoreFileManager()
55
-
56
- # 符合忽略规则的路径
57
- ignored_dir = tmp_path / "data" / "subdir"
58
- ignored_dir.mkdir(parents=True)
59
- ignored_file = tmp_path / "secret.txt"
60
- ignored_file.write_text("secret")
61
-
62
- assert ignore_file_utils.should_ignore(str(ignored_dir)) is True
63
- assert ignore_file_utils.should_ignore(str(ignored_file)) is True
64
-
65
- # 不应被忽略的文件
66
- normal_file = tmp_path / "keepme.txt"
67
- normal_file.write_text("keep me")
68
- assert ignore_file_utils.should_ignore(str(normal_file)) is False
69
-
70
-
71
- def test_nested_ignore_file(tmp_path, monkeypatch):
72
- monkeypatch.chdir(tmp_path)
73
-
74
- # 没有根目录的.ignore,创建.auto-coder/.autocoderignore
75
- nested_dir = tmp_path / ".auto-coder"
76
- nested_dir.mkdir()
77
-
78
- ignore_file = nested_dir / ".autocoderignore"
79
- ignore_file.write_text("logs/**")
80
-
81
- # 重新初始化忽略管理器以加载新规则
82
- ignore_file_utils.IgnoreFileManager._instance = None
83
- ignore_file_utils._ignore_manager = ignore_file_utils.IgnoreFileManager()
84
-
85
- ignored_dir = tmp_path / "logs" / "2024"
86
- ignored_dir.mkdir(parents=True)
87
- assert ignore_file_utils.should_ignore(str(ignored_dir)) is True
88
-
89
- normal_file = tmp_path / "main.py"
90
- normal_file.write_text("# main")
91
- assert ignore_file_utils.should_ignore(str(normal_file)) is False