auto-coder 0.1.316__py3-none-any.whl → 0.1.318__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 (41) hide show
  1. {auto_coder-0.1.316.dist-info → auto_coder-0.1.318.dist-info}/METADATA +2 -2
  2. {auto_coder-0.1.316.dist-info → auto_coder-0.1.318.dist-info}/RECORD +41 -20
  3. autocoder/auto_coder_runner.py +1 -2
  4. autocoder/common/__init__.py +3 -0
  5. autocoder/common/auto_coder_lang.py +24 -0
  6. autocoder/common/code_auto_merge_editblock.py +2 -42
  7. autocoder/common/git_utils.py +2 -2
  8. autocoder/common/token_cost_caculate.py +103 -42
  9. autocoder/common/v2/__init__.py +0 -0
  10. autocoder/common/v2/code_auto_generate.py +199 -0
  11. autocoder/common/v2/code_auto_generate_diff.py +361 -0
  12. autocoder/common/v2/code_auto_generate_editblock.py +380 -0
  13. autocoder/common/v2/code_auto_generate_strict_diff.py +269 -0
  14. autocoder/common/v2/code_auto_merge.py +211 -0
  15. autocoder/common/v2/code_auto_merge_diff.py +354 -0
  16. autocoder/common/v2/code_auto_merge_editblock.py +523 -0
  17. autocoder/common/v2/code_auto_merge_strict_diff.py +259 -0
  18. autocoder/common/v2/code_diff_manager.py +266 -0
  19. autocoder/common/v2/code_editblock_manager.py +282 -0
  20. autocoder/common/v2/code_manager.py +238 -0
  21. autocoder/common/v2/code_strict_diff_manager.py +241 -0
  22. autocoder/dispacher/actions/action.py +16 -0
  23. autocoder/dispacher/actions/plugins/action_regex_project.py +6 -0
  24. autocoder/events/event_manager_singleton.py +2 -2
  25. autocoder/helper/__init__.py +0 -0
  26. autocoder/helper/project_creator.py +570 -0
  27. autocoder/linters/linter_factory.py +44 -25
  28. autocoder/linters/models.py +220 -0
  29. autocoder/linters/python_linter.py +1 -7
  30. autocoder/linters/reactjs_linter.py +580 -0
  31. autocoder/linters/shadow_linter.py +390 -0
  32. autocoder/linters/vue_linter.py +576 -0
  33. autocoder/memory/active_context_manager.py +0 -4
  34. autocoder/memory/active_package.py +12 -12
  35. autocoder/shadows/__init__.py +0 -0
  36. autocoder/shadows/shadow_manager.py +235 -0
  37. autocoder/version.py +1 -1
  38. {auto_coder-0.1.316.dist-info → auto_coder-0.1.318.dist-info}/LICENSE +0 -0
  39. {auto_coder-0.1.316.dist-info → auto_coder-0.1.318.dist-info}/WHEEL +0 -0
  40. {auto_coder-0.1.316.dist-info → auto_coder-0.1.318.dist-info}/entry_points.txt +0 -0
  41. {auto_coder-0.1.316.dist-info → auto_coder-0.1.318.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,199 @@
1
+ from typing import List, Dict, Tuple
2
+ from autocoder.common.types import Mode, CodeGenerateResult
3
+ from autocoder.common import AutoCoderArgs
4
+ import byzerllm
5
+ from autocoder.common import sys_prompt
6
+ from autocoder.privacy.model_filter import ModelPathFilter
7
+ import json
8
+ from concurrent.futures import ThreadPoolExecutor
9
+ from autocoder.common.utils_code_auto_generate import chat_with_continue,stream_chat_with_continue,ChatWithContinueResult
10
+ from autocoder.utils.auto_coder_utils.chat_stream_out import stream_out
11
+ from autocoder.common.stream_out_type import CodeGenerateStreamOutType
12
+ from autocoder.common.auto_coder_lang import get_message_with_format
13
+ from autocoder.common.printer import Printer
14
+ from autocoder.rag.token_counter import count_tokens
15
+ from autocoder.utils import llms as llm_utils
16
+ from autocoder.common import SourceCodeList
17
+ from autocoder.memory.active_context_manager import ActiveContextManager
18
+
19
+
20
+ class CodeAutoGenerate:
21
+ def __init__(
22
+ self,
23
+ llm: byzerllm.ByzerLLM,
24
+ args: AutoCoderArgs,
25
+ action=None,
26
+ ) -> None:
27
+ self.llm = llm
28
+ self.args = args
29
+ self.action = action
30
+ self.generate_times_same_model = args.generate_times_same_model
31
+ if not self.llm:
32
+ raise ValueError(
33
+ "Please provide a valid model instance to use for code generation."
34
+ )
35
+ self.llms = self.llm.get_sub_client("code_model") or [self.llm]
36
+ if not isinstance(self.llms, list):
37
+ self.llms = [self.llms]
38
+
39
+ def single_round_run(
40
+ self, query: str, source_code_list: SourceCodeList
41
+ ) -> CodeGenerateResult:
42
+
43
+ # Apply model filter for code_llm
44
+ printer = Printer()
45
+ for llm in self.llms:
46
+ model_filter = ModelPathFilter.from_model_object(llm, self.args)
47
+ filtered_sources = []
48
+ for source in source_code_list.sources:
49
+ if model_filter.is_accessible(source.module_name):
50
+ filtered_sources.append(source)
51
+ else:
52
+ printer.print_in_terminal("index_file_filtered",
53
+ style="yellow",
54
+ file_path=source.module_name,
55
+ model_name=",".join(llm_utils.get_llm_names(llm)))
56
+
57
+ source_code_list = SourceCodeList(filtered_sources)
58
+
59
+ llm_config = {"human_as_model": self.args.human_as_model}
60
+
61
+ source_content = source_code_list.to_str()
62
+
63
+ active_context_manager = ActiveContextManager(self.llm, self.args.source_dir)
64
+
65
+ # 获取包上下文信息
66
+ package_context = ""
67
+
68
+ if self.args.enable_active_context:
69
+ # 获取活动上下文信息
70
+ result = active_context_manager.load_active_contexts_for_files(
71
+ [source.module_name for source in source_code_list.sources]
72
+ )
73
+ # 将活动上下文信息格式化为文本
74
+ if result.contexts:
75
+ package_context_parts = []
76
+ for dir_path, context in result.contexts.items():
77
+ package_context_parts.append(f"<package_info>{context.content}</package_info>")
78
+
79
+ package_context = "\n".join(package_context_parts)
80
+
81
+ init_prompt = self.single_round_instruction.prompt(
82
+ instruction=query, content=source_content, context=self.args.context,
83
+ package_context=package_context
84
+ )
85
+
86
+ with open(self.args.target_file, "w",encoding="utf-8") as file:
87
+ file.write(init_prompt)
88
+
89
+ conversations = []
90
+
91
+ if self.args.system_prompt and self.args.system_prompt.strip() == "claude":
92
+ conversations.append(
93
+ {"role": "system", "content": sys_prompt.claude_sys_prompt.prompt()})
94
+ elif self.args.system_prompt:
95
+ conversations.append(
96
+ {"role": "system", "content": self.args.system_prompt})
97
+
98
+ conversations.append({"role": "user", "content": init_prompt})
99
+
100
+ conversations_list = []
101
+ results = []
102
+ input_tokens_count = 0
103
+ generated_tokens_count = 0
104
+
105
+ input_tokens_cost = 0
106
+ generated_tokens_cost = 0
107
+
108
+ model_names = []
109
+
110
+ printer = Printer()
111
+ estimated_input_tokens = count_tokens(
112
+ json.dumps(conversations, ensure_ascii=False))
113
+ printer.print_in_terminal("estimated_input_tokens_in_generate",
114
+ style="yellow",
115
+ estimated_input_tokens_in_generate=estimated_input_tokens,
116
+ generate_mode="diff"
117
+ )
118
+
119
+ if not self.args.human_as_model:
120
+ with ThreadPoolExecutor(max_workers=len(self.llms) * self.generate_times_same_model) as executor:
121
+ futures = []
122
+ for llm in self.llms:
123
+
124
+ model_names_list = llm_utils.get_llm_names(llm)
125
+ model_name = None
126
+ if model_names_list:
127
+ model_name = model_names_list[0]
128
+
129
+ for i in range(self.generate_times_same_model):
130
+ model_names.append(model_name)
131
+ if i==0:
132
+ def job():
133
+ stream_generator = stream_chat_with_continue(
134
+ llm=llm,
135
+ conversations=conversations,
136
+ llm_config=llm_config,
137
+ args=self.args
138
+ )
139
+ full_response, last_meta = stream_out(
140
+ stream_generator,
141
+ model_name=model_name,
142
+ title=get_message_with_format(
143
+ "code_generate_title", model_name=model_name),
144
+ args=self.args,
145
+ extra_meta={
146
+ "stream_out_type": CodeGenerateStreamOutType.CODE_GENERATE.value
147
+ })
148
+ return ChatWithContinueResult(
149
+ content=full_response,
150
+ input_tokens_count=last_meta.input_tokens_count,
151
+ generated_tokens_count=last_meta.generated_tokens_count
152
+ )
153
+ futures.append(executor.submit(job))
154
+ else:
155
+ futures.append(executor.submit(
156
+ chat_with_continue,
157
+ llm=llm,
158
+ conversations=conversations,
159
+ llm_config=llm_config,
160
+ args=self.args
161
+ ))
162
+
163
+ temp_results = [future.result() for future in futures]
164
+
165
+ for result,model_name in zip(temp_results,model_names):
166
+ results.append(result.content)
167
+ input_tokens_count += result.input_tokens_count
168
+ generated_tokens_count += result.generated_tokens_count
169
+ model_info = llm_utils.get_model_info(model_name,self.args.product_mode)
170
+ input_cost = model_info.get("input_price", 0) if model_info else 0
171
+ output_cost = model_info.get("output_price", 0) if model_info else 0
172
+ input_tokens_cost += input_cost * result.input_tokens_count / 1000000
173
+ generated_tokens_cost += output_cost * result.generated_tokens_count / 1000000
174
+
175
+ for result in results:
176
+ conversations_list.append(
177
+ conversations + [{"role": "assistant", "content": result}])
178
+ else:
179
+ for _ in range(self.args.human_model_num):
180
+ single_result = chat_with_continue(
181
+ llm=self.llms[0],
182
+ conversations=conversations,
183
+ llm_config=llm_config,
184
+ args=self.args
185
+ )
186
+ results.append(single_result.content)
187
+ input_tokens_count += single_result.input_tokens_count
188
+ generated_tokens_count += single_result.generated_tokens_count
189
+ conversations_list.append(
190
+ conversations + [{"role": "assistant", "content": single_result.content}])
191
+
192
+ statistics = {
193
+ "input_tokens_count": input_tokens_count,
194
+ "generated_tokens_count": generated_tokens_count,
195
+ "input_tokens_cost": input_tokens_cost,
196
+ "generated_tokens_cost": generated_tokens_cost
197
+ }
198
+
199
+ return CodeGenerateResult(contents=results, conversations=conversations_list, metadata=statistics)
@@ -0,0 +1,361 @@
1
+ from typing import List, Dict, Tuple
2
+ from autocoder.common.types import Mode, CodeGenerateResult
3
+ from autocoder.common import AutoCoderArgs
4
+ import byzerllm
5
+ from autocoder.common import sys_prompt
6
+ from autocoder.privacy.model_filter import ModelPathFilter
7
+ import json
8
+ from concurrent.futures import ThreadPoolExecutor
9
+ from autocoder.common.utils_code_auto_generate import chat_with_continue,stream_chat_with_continue,ChatWithContinueResult
10
+ from autocoder.utils.auto_coder_utils.chat_stream_out import stream_out
11
+ from autocoder.common.stream_out_type import CodeGenerateStreamOutType
12
+ from autocoder.common.auto_coder_lang import get_message_with_format
13
+ from autocoder.common.printer import Printer
14
+ from autocoder.rag.token_counter import count_tokens
15
+ from autocoder.utils import llms as llm_utils
16
+ from autocoder.common import SourceCodeList
17
+ from autocoder.memory.active_context_manager import ActiveContextManager
18
+
19
+
20
+ class CodeAutoGenerateDiff:
21
+ def __init__(
22
+ self,
23
+ llm: byzerllm.ByzerLLM,
24
+ args: AutoCoderArgs,
25
+ action=None,
26
+ ) -> None:
27
+ self.llm = llm
28
+ self.args = args
29
+ self.action = action
30
+ self.generate_times_same_model = args.generate_times_same_model
31
+ if not self.llm:
32
+ raise ValueError(
33
+ "Please provide a valid model instance to use for code generation."
34
+ )
35
+ self.llms = self.llm.get_sub_client("code_model") or [self.llm]
36
+ if not isinstance(self.llms, list):
37
+ self.llms = [self.llms]
38
+
39
+
40
+ @byzerllm.prompt()
41
+ def single_round_instruction(self, instruction: str,
42
+ content: str,
43
+ context: str = "",
44
+ package_context: str = ""
45
+ ) -> str:
46
+ """
47
+ 如果你需要生成代码,对于每个需要更改的文件,你需要按 unified diff 的格式进行生成。
48
+
49
+ # Unified Diff Rules:
50
+
51
+ Every unified diff must use this format:
52
+ 1. The file path line starting with "--- " for the old file
53
+ 2. The file path line starting with "+++ " for the new file
54
+ 3. The hunk header line starting with "@@ " (without line numbers)
55
+ 4. Lines starting with " " for context lines
56
+ 5. Lines starting with "-" for lines to remove
57
+ 6. Lines starting with "+" for lines to add
58
+
59
+ The user's patch tool needs CORRECT patches that apply cleanly against the current contents of the file!
60
+ Think carefully and make sure you include and mark all lines that need to be removed or changed as `-` lines.
61
+ Make sure you mark all new or modified lines with `+`.
62
+ Don't leave out any lines or the diff patch won't apply correctly.
63
+
64
+ Indentation matters in the diffs!
65
+
66
+ Start a new hunk for each section of the file that needs changes.
67
+
68
+ Only output hunks that specify changes with `+` or `-` lines.
69
+ Skip any hunks that are entirely unchanging ` ` lines.
70
+
71
+ Output hunks in whatever order makes the most sense.
72
+ Hunks don't need to be in any particular order.
73
+
74
+ When editing a function, method, loop, etc use a hunk to replace the *entire* code block.
75
+ Delete the entire existing version with `-` lines and then add a new, updated version with `+` lines.
76
+ This will help you generate correct code and correct diffs.
77
+
78
+ To move code within a file, use 2 hunks: 1 to delete it from its current location, 1 to insert it in the new location.
79
+
80
+ To make a new file, show a diff from `--- /dev/null` to `+++ path/to/new/file.ext`.
81
+
82
+ The path start with `---` or `+++` should be the absolute path of the file or relative path from the project root.
83
+
84
+ 下面我们来看一个例子:
85
+
86
+ 当前项目目录结构:
87
+ 1. 项目根目录: /tmp/projects/mathweb
88
+ 2. 项目子目录/文件列表(类似tree 命令输出)
89
+ flask/
90
+ app.py
91
+ templates/
92
+ index.html
93
+ static/
94
+ style.css
95
+
96
+ 用户需求: Change get_factorial() to use math.factorial
97
+
98
+ 回答: To make this change we need to modify `/tmp/projects/mathweb/flask/app.py` to:
99
+
100
+ 1. Import the math package.
101
+ 2. Remove the existing factorial() function.
102
+ 3. Update get_factorial() to call math.factorial instead.
103
+
104
+ Here are the unified diffs:
105
+
106
+ ```diff
107
+ --- /tmp/projects/mathweb/flask/app.py
108
+ +++ /tmp/projects/mathweb/flask/app.py
109
+ @@ ... @@
110
+ -from flask import Flask
111
+ +import math
112
+ +from flask import Flask
113
+ @@ ... @@
114
+ -def factorial(n):
115
+ - "compute factorial"
116
+ -
117
+ - if n == 0:
118
+ - return 1
119
+ - else:
120
+ - return n * factorial(n-1)
121
+ -
122
+ @@ ... @@
123
+ - return str(factorial(n))
124
+ + return str(math.factorial(n))
125
+ ```
126
+
127
+ 用户需求: Refactor hello() into its own file.
128
+
129
+ 回答:To make this change we need to modify `main.py` and make a new file `hello.py`:
130
+
131
+ 1. Make a new hello.py file with hello() in it.
132
+ 2. Remove hello() from main.py and replace it with an import.
133
+
134
+ Here are the unified diffs:
135
+
136
+ ```diff
137
+ --- /dev/null
138
+ +++ /tmp/projects/mathweb/hello.py
139
+ @@ ... @@
140
+ +def hello():
141
+ + "print a greeting"
142
+ +
143
+ + print("hello")
144
+ ```
145
+
146
+ ```diff
147
+ --- /tmp/projects/mathweb/main.py
148
+ +++ /tmp/projects/mathweb/main.py
149
+ @@ ... @@
150
+ -def hello():
151
+ - "print a greeting"
152
+ -
153
+ - print("hello")
154
+ +from hello import hello
155
+ ```
156
+
157
+ 现在让我们开始一个新的任务:
158
+
159
+ {%- if structure %}
160
+ {{ structure }}
161
+ {%- endif %}
162
+
163
+ {%- if content %}
164
+ 下面是一些文件路径以及每个文件对应的源码:
165
+ <files>
166
+ {{ content }}
167
+ </files>
168
+ {%- endif %}
169
+
170
+ {%- if package_context %}
171
+ 下面是上面文件的一些信息(包括最近的变更情况):
172
+ <package_context>
173
+ {{ package_context }}
174
+ </package_context>
175
+ {%- endif %}
176
+
177
+ {%- if context %}
178
+ <extra_context>
179
+ {{ context }}
180
+ </extra_context>
181
+ {%- endif %}
182
+
183
+ 下面是用户的需求:
184
+
185
+ {{ instruction }}
186
+
187
+ """
188
+ if not self.args.include_project_structure:
189
+ return {
190
+ "structure": "",
191
+ }
192
+
193
+ return {
194
+ "structure": (
195
+ self.action.pp.get_tree_like_directory_structure()
196
+ if self.action
197
+ else ""
198
+ ),
199
+ }
200
+
201
+ def single_round_run(
202
+ self, query: str, source_code_list: SourceCodeList
203
+ ) -> CodeGenerateResult:
204
+
205
+ # Apply model filter for code_llm
206
+ printer = Printer()
207
+ for llm in self.llms:
208
+ model_filter = ModelPathFilter.from_model_object(llm, self.args)
209
+ filtered_sources = []
210
+ for source in source_code_list.sources:
211
+ if model_filter.is_accessible(source.module_name):
212
+ filtered_sources.append(source)
213
+ else:
214
+ printer.print_in_terminal("index_file_filtered",
215
+ style="yellow",
216
+ file_path=source.module_name,
217
+ model_name=",".join(llm_utils.get_llm_names(llm)))
218
+
219
+ source_code_list = SourceCodeList(filtered_sources)
220
+
221
+ llm_config = {"human_as_model": self.args.human_as_model}
222
+
223
+ source_content = source_code_list.to_str()
224
+
225
+ active_context_manager = ActiveContextManager(self.llm, self.args.source_dir)
226
+
227
+ # 获取包上下文信息
228
+ package_context = ""
229
+
230
+ if self.args.enable_active_context:
231
+ # 获取活动上下文信息
232
+ result = active_context_manager.load_active_contexts_for_files(
233
+ [source.module_name for source in source_code_list.sources]
234
+ )
235
+ # 将活动上下文信息格式化为文本
236
+ if result.contexts:
237
+ package_context_parts = []
238
+ for dir_path, context in result.contexts.items():
239
+ package_context_parts.append(f"<package_info>{context.content}</package_info>")
240
+
241
+ package_context = "\n".join(package_context_parts)
242
+
243
+ init_prompt = self.single_round_instruction.prompt(
244
+ instruction=query, content=source_content, context=self.args.context,
245
+ package_context=package_context
246
+ )
247
+
248
+ with open(self.args.target_file, "w",encoding="utf-8") as file:
249
+ file.write(init_prompt)
250
+
251
+ conversations = []
252
+
253
+ if self.args.system_prompt and self.args.system_prompt.strip() == "claude":
254
+ conversations.append(
255
+ {"role": "system", "content": sys_prompt.claude_sys_prompt.prompt()})
256
+ elif self.args.system_prompt:
257
+ conversations.append(
258
+ {"role": "system", "content": self.args.system_prompt})
259
+
260
+ conversations.append({"role": "user", "content": init_prompt})
261
+
262
+ conversations_list = []
263
+ results = []
264
+ input_tokens_count = 0
265
+ generated_tokens_count = 0
266
+
267
+ input_tokens_cost = 0
268
+ generated_tokens_cost = 0
269
+
270
+ model_names = []
271
+
272
+ printer = Printer()
273
+ estimated_input_tokens = count_tokens(
274
+ json.dumps(conversations, ensure_ascii=False))
275
+ printer.print_in_terminal("estimated_input_tokens_in_generate",
276
+ style="yellow",
277
+ estimated_input_tokens_in_generate=estimated_input_tokens,
278
+ generate_mode="diff"
279
+ )
280
+
281
+ if not self.args.human_as_model:
282
+ with ThreadPoolExecutor(max_workers=len(self.llms) * self.generate_times_same_model) as executor:
283
+ futures = []
284
+ for llm in self.llms:
285
+
286
+ model_names_list = llm_utils.get_llm_names(llm)
287
+ model_name = None
288
+ if model_names_list:
289
+ model_name = model_names_list[0]
290
+
291
+ for i in range(self.generate_times_same_model):
292
+ model_names.append(model_name)
293
+ if i==0:
294
+ def job():
295
+ stream_generator = stream_chat_with_continue(
296
+ llm=llm,
297
+ conversations=conversations,
298
+ llm_config=llm_config,
299
+ args=self.args
300
+ )
301
+ full_response, last_meta = stream_out(
302
+ stream_generator,
303
+ model_name=model_name,
304
+ title=get_message_with_format(
305
+ "code_generate_title", model_name=model_name),
306
+ args=self.args,
307
+ extra_meta={
308
+ "stream_out_type": CodeGenerateStreamOutType.CODE_GENERATE.value
309
+ })
310
+ return ChatWithContinueResult(
311
+ content=full_response,
312
+ input_tokens_count=last_meta.input_tokens_count,
313
+ generated_tokens_count=last_meta.generated_tokens_count
314
+ )
315
+ futures.append(executor.submit(job))
316
+ else:
317
+ futures.append(executor.submit(
318
+ chat_with_continue,
319
+ llm=llm,
320
+ conversations=conversations,
321
+ llm_config=llm_config,
322
+ args=self.args
323
+ ))
324
+
325
+ temp_results = [future.result() for future in futures]
326
+
327
+ for result,model_name in zip(temp_results,model_names):
328
+ results.append(result.content)
329
+ input_tokens_count += result.input_tokens_count
330
+ generated_tokens_count += result.generated_tokens_count
331
+ model_info = llm_utils.get_model_info(model_name,self.args.product_mode)
332
+ input_cost = model_info.get("input_price", 0) if model_info else 0
333
+ output_cost = model_info.get("output_price", 0) if model_info else 0
334
+ input_tokens_cost += input_cost * result.input_tokens_count / 1000000
335
+ generated_tokens_cost += output_cost * result.generated_tokens_count / 1000000
336
+
337
+ for result in results:
338
+ conversations_list.append(
339
+ conversations + [{"role": "assistant", "content": result}])
340
+ else:
341
+ for _ in range(self.args.human_model_num):
342
+ single_result = chat_with_continue(
343
+ llm=self.llms[0],
344
+ conversations=conversations,
345
+ llm_config=llm_config,
346
+ args=self.args
347
+ )
348
+ results.append(single_result.content)
349
+ input_tokens_count += single_result.input_tokens_count
350
+ generated_tokens_count += single_result.generated_tokens_count
351
+ conversations_list.append(
352
+ conversations + [{"role": "assistant", "content": single_result.content}])
353
+
354
+ statistics = {
355
+ "input_tokens_count": input_tokens_count,
356
+ "generated_tokens_count": generated_tokens_count,
357
+ "input_tokens_cost": input_tokens_cost,
358
+ "generated_tokens_cost": generated_tokens_cost
359
+ }
360
+
361
+ return CodeGenerateResult(contents=results, conversations=conversations_list, metadata=statistics)