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.

Files changed (45) hide show
  1. {auto_coder-0.1.304.dist-info → auto_coder-0.1.306.dist-info}/METADATA +1 -1
  2. {auto_coder-0.1.304.dist-info → auto_coder-0.1.306.dist-info}/RECORD +45 -40
  3. autocoder/agent/auto_learn_from_commit.py +3 -1
  4. autocoder/agent/auto_review_commit.py +3 -1
  5. autocoder/auto_coder.py +3 -2
  6. autocoder/auto_coder_runner.py +116 -3
  7. autocoder/chat_auto_coder.py +9 -1
  8. autocoder/chat_auto_coder_lang.py +552 -278
  9. autocoder/commands/auto_command.py +1 -4
  10. autocoder/commands/auto_web.py +1 -9
  11. autocoder/common/__init__.py +4 -0
  12. autocoder/common/auto_coder_lang.py +737 -392
  13. autocoder/common/code_auto_generate.py +104 -16
  14. autocoder/common/code_auto_generate_diff.py +101 -10
  15. autocoder/common/code_auto_generate_editblock.py +103 -9
  16. autocoder/common/code_auto_generate_strict_diff.py +99 -9
  17. autocoder/common/code_auto_merge.py +8 -0
  18. autocoder/common/code_auto_merge_diff.py +8 -0
  19. autocoder/common/code_auto_merge_editblock.py +7 -0
  20. autocoder/common/code_auto_merge_strict_diff.py +5 -0
  21. autocoder/common/code_modification_ranker.py +4 -2
  22. autocoder/common/command_completer.py +12 -0
  23. autocoder/common/command_generator.py +5 -4
  24. autocoder/common/git_utils.py +13 -7
  25. autocoder/common/global_cancel.py +68 -7
  26. autocoder/common/stream_out_type.py +5 -1
  27. autocoder/common/utils_code_auto_generate.py +29 -3
  28. autocoder/dispacher/__init__.py +18 -19
  29. autocoder/dispacher/actions/action.py +6 -162
  30. autocoder/dispacher/actions/plugins/action_regex_project.py +2 -6
  31. autocoder/index/filter/quick_filter.py +6 -3
  32. autocoder/index/index.py +2 -4
  33. autocoder/memory/__init__.py +7 -0
  34. autocoder/memory/active_context_manager.py +649 -0
  35. autocoder/memory/active_package.py +469 -0
  36. autocoder/memory/async_processor.py +161 -0
  37. autocoder/memory/directory_mapper.py +67 -0
  38. autocoder/utils/auto_coder_utils/chat_stream_out.py +61 -11
  39. autocoder/utils/project_structure.py +35 -1
  40. autocoder/utils/thread_utils.py +78 -169
  41. autocoder/version.py +1 -1
  42. {auto_coder-0.1.304.dist-info → auto_coder-0.1.306.dist-info}/LICENSE +0 -0
  43. {auto_coder-0.1.304.dist-info → auto_coder-0.1.306.dist-info}/WHEEL +0 -0
  44. {auto_coder-0.1.304.dist-info → auto_coder-0.1.306.dist-info}/entry_points.txt +0 -0
  45. {auto_coder-0.1.304.dist-info → auto_coder-0.1.306.dist-info}/top_level.txt +0 -0
@@ -13,7 +13,7 @@ from autocoder.utils.request_queue import request_queue
13
13
  import time
14
14
  from byzerllm.utils.types import SingleOutputMeta
15
15
  from autocoder.common import AutoCoderArgs
16
- from autocoder.common.global_cancel import global_cancel
16
+ from autocoder.common.global_cancel import global_cancel, CancelRequestedException
17
17
  from autocoder.events.event_manager_singleton import get_event_manager
18
18
  from autocoder.events import event_content as EventContentCreator
19
19
  from autocoder.events.event_types import EventMetadata
@@ -68,23 +68,35 @@ class MultiStreamRenderer:
68
68
 
69
69
  def _process_stream(self,
70
70
  stream_idx: int,
71
- stream_generator: Generator[Tuple[str, Dict[str, Any]], None, None]):
71
+ stream_generator: Generator[Tuple[str, Dict[str, Any]], None, None],
72
+ cancel_token: Optional[str] = None):
72
73
  """Process a single stream in a separate thread"""
73
74
  stream = self.streams[stream_idx]
74
75
  try:
75
76
  for content, meta in stream_generator:
77
+ try:
78
+ # 使用新的异常机制检查取消请求
79
+ global_cancel.check_and_raise(cancel_token)
80
+ except CancelRequestedException:
81
+ break
82
+
76
83
  if content:
77
84
  stream.update(content)
85
+ except CancelRequestedException:
86
+ # 处理取消异常
87
+ stream.update("\n\n**Operation was cancelled**")
78
88
  finally:
79
89
  stream.complete()
80
90
 
81
91
  def render_streams(self,
82
- stream_generators: List[Generator[Tuple[str, Dict[str, Any]], None, None]]) -> List[str]:
92
+ stream_generators: List[Generator[Tuple[str, Dict[str, Any]], None, None]],
93
+ cancel_token: Optional[str] = None) -> List[str]:
83
94
  """
84
95
  Render multiple streams simultaneously
85
96
 
86
97
  Args:
87
98
  stream_generators: List of stream generators to render
99
+ cancel_token: Optional cancellation token
88
100
 
89
101
  Returns:
90
102
  List of final content from each stream
@@ -94,7 +106,7 @@ class MultiStreamRenderer:
94
106
  # Start processing threads
95
107
  threads = []
96
108
  for i, generator in enumerate(stream_generators):
97
- thread = Thread(target=self._process_stream, args=(i, generator))
109
+ thread = Thread(target=self._process_stream, args=(i, generator, cancel_token))
98
110
  thread.daemon = True
99
111
  thread.start()
100
112
  threads.append(thread)
@@ -102,6 +114,13 @@ class MultiStreamRenderer:
102
114
  try:
103
115
  with Live(self.layout, console=self.console, refresh_per_second=10) as live:
104
116
  while any(not stream.is_complete for stream in self.streams):
117
+ try:
118
+ # 使用新的异常机制检查取消请求
119
+ global_cancel.check_and_raise(cancel_token)
120
+ except CancelRequestedException:
121
+ print("\nCancelling streams...")
122
+ break
123
+
105
124
  # Update all panels
106
125
  for i, stream in enumerate(self.streams):
107
126
  panel = Panel(
@@ -116,7 +135,11 @@ class MultiStreamRenderer:
116
135
  time.sleep(0.1) # Prevent excessive CPU usage
117
136
 
118
137
  except KeyboardInterrupt:
138
+ # 键盘中断时设置取消标志
139
+ global_cancel.set(cancel_token, {"message": "Keyboard interrupt"})
119
140
  print("\nStopping streams...")
141
+ except CancelRequestedException:
142
+ print("\nCancelling streams...")
120
143
 
121
144
  # Wait for all threads to complete
122
145
  for thread in threads:
@@ -128,7 +151,8 @@ def multi_stream_out(
128
151
  stream_generators: List[Generator[Tuple[str, Dict[str, Any]], None, None]],
129
152
  titles: List[str],
130
153
  layout: str = "horizontal",
131
- console: Optional[Console] = None
154
+ console: Optional[Console] = None,
155
+ cancel_token: Optional[str] = None
132
156
  ) -> List[str]:
133
157
  """
134
158
  Render multiple streams with Rich
@@ -138,12 +162,13 @@ def multi_stream_out(
138
162
  titles: List of titles for each stream
139
163
  layout: "horizontal" or "vertical"
140
164
  console: Optional Rich console instance
165
+ cancel_token: Optional cancellation token
141
166
 
142
167
  Returns:
143
168
  List of final content from each stream
144
169
  """
145
170
  renderer = MultiStreamRenderer(titles, layout, console)
146
- return renderer.render_streams(stream_generators)
171
+ return renderer.render_streams(stream_generators, cancel_token)
147
172
 
148
173
 
149
174
  def stream_out(
@@ -155,7 +180,8 @@ def stream_out(
155
180
  final_title: Optional[str] = None,
156
181
  args: Optional[AutoCoderArgs] = None,
157
182
  display_func: Optional[Callable] = None,
158
- extra_meta: Dict[str, Any] = {}
183
+ extra_meta: Dict[str, Any] = {},
184
+ cancel_token: Optional[str] = None
159
185
  ) -> Tuple[str, Optional[SingleOutputMeta]]:
160
186
  """
161
187
  处理流式输出事件并在终端中展示
@@ -167,9 +193,17 @@ def stream_out(
167
193
  model_name: 模型名称
168
194
  title: 面板标题,如果没有提供则使用默认值
169
195
  args: AutoCoderArgs对象
196
+ display_func: 可选的显示函数
197
+ extra_meta: 额外的元数据
198
+ cancel_token: 可选的取消令牌
170
199
  Returns:
171
200
  Tuple[str, Dict[SingleOutputMeta]]: 返回完整的响应内容和最后的元数据
172
201
  """
202
+ if args is None:
203
+ import traceback
204
+ traceback.print_stack()
205
+ print("=="*100)
206
+
173
207
  if console is None:
174
208
  console = Console(force_terminal=True, color_system="auto", height=None)
175
209
 
@@ -197,10 +231,8 @@ def stream_out(
197
231
  console=console
198
232
  ) as live:
199
233
  for res in stream_generator:
200
- if global_cancel.requested:
201
- printer = Printer(console)
202
- printer.print_in_terminal("generation_cancelled")
203
- break
234
+ global_cancel.check_and_raise(cancel_token)
235
+
204
236
  last_meta = res[1]
205
237
  content = res[0]
206
238
 
@@ -312,6 +344,24 @@ def stream_out(
312
344
  )
313
345
  )
314
346
 
347
+ except CancelRequestedException as cancel_exc:
348
+ # 捕获取消异常,显示取消信息
349
+ console.print(Panel(
350
+ "Generation was cancelled",
351
+ title=f"Cancelled[ {panel_title} ]",
352
+ border_style="yellow"
353
+ ))
354
+
355
+ if request_id and request_queue:
356
+ request_queue.add_request(
357
+ request_id,
358
+ RequestValue(
359
+ value=StreamValue(value=["Operation was cancelled"]),
360
+ status=RequestOption.FAILED
361
+ ),
362
+ )
363
+ raise cancel_exc
364
+
315
365
  except Exception as e:
316
366
  console.print(Panel(
317
367
  f"Error: {str(e)}",
@@ -169,7 +169,41 @@ class EnhancedFileAnalyzer:
169
169
 
170
170
  def _basic_analysis(self, extensions: Set[str]) -> Dict:
171
171
  """基于规则的基础分析"""
172
- CODE_EXTS = {'.py', '.js', '.ts', '.java', '.c', '.cpp'}
172
+ CODE_EXTS = {
173
+ # 通用脚本语言
174
+ '.py', # Python
175
+ '.js', '.jsx', '.ts', '.tsx', # JavaScript/TypeScript
176
+ '.rb', '.erb', # Ruby
177
+ '.php', # PHP
178
+ '.pl', '.pm', # Perl
179
+
180
+ # 编译型语言
181
+ '.java', '.kt', '.groovy', # JVM系
182
+ '.c', '.cpp', '.cc', '.cxx', '.h', '.hpp', # C/C++
183
+ '.cs', # C#
184
+ '.go', # Go
185
+ '.rs', # Rust
186
+ '.swift', # Swift
187
+
188
+ # Web开发
189
+ '.vue', '.svelte', # 前端框架
190
+ '.html', '.htm', # HTML
191
+ '.css', '.scss', '.sass', '.less', # 样式表
192
+
193
+ # 其他语言
194
+ '.scala', # Scala
195
+ '.clj', # Clojure
196
+ '.coffee', # CoffeeScript
197
+ '.lua', # Lua
198
+ '.r', # R
199
+ '.sh', '.bash', # Shell脚本
200
+ '.sql', # SQL
201
+ '.dart', # Dart
202
+ '.ex', '.exs', # Elixir
203
+ '.fs', '.fsx', # F#
204
+ '.hs', # Haskell
205
+ '.ml', '.mli' # OCaml
206
+ }
173
207
  CONFIG_EXTS = {'.yml', '.yaml', '.json', '.toml', '.ini'}
174
208
 
175
209
  return {
@@ -1,161 +1,22 @@
1
- from concurrent.futures import ThreadPoolExecutor, TimeoutError, CancelledError
2
- from threading import Event
3
- from inspect import signature
4
1
  from functools import wraps
5
- from typing import Any, Optional
2
+ from typing import Any, Optional, Dict, Callable
6
3
  import threading
7
- import logging
8
4
  import time
9
- from autocoder.common.global_cancel import global_cancel
5
+ from autocoder.common.global_cancel import global_cancel, CancelRequestedException
6
+ from autocoder.common.printer import Printer
7
+ from autocoder.common.auto_coder_lang import get_message, get_message_with_format
8
+ from autocoder.events.event_manager_singleton import get_event_manager
9
+ from autocoder.events import event_content as EventContentCreator
10
+ from autocoder.events.event_types import EventMetadata
10
11
 
11
- class CancellationRequested(Exception):
12
- """Raised when a task is requested to be cancelled."""
13
- pass
12
+ printer = Printer()
14
13
 
15
-
16
- def run_in_thread(timeout: Optional[float] = None):
17
- """Decorator that runs a function in a thread with signal handling.
18
-
19
- Args:
20
- timeout (float, optional): Maximum time to wait for thread completion in seconds.
21
- If None, will wait indefinitely.
22
-
23
- The decorated function will run in a separate thread and can be interrupted by
24
- signals like Ctrl+C (KeyboardInterrupt). When interrupted, it will log the event
25
- and clean up gracefully.
26
- """
27
- def decorator(func):
28
- @wraps(func)
29
- def wrapper(*args, **kwargs):
30
- with ThreadPoolExecutor(max_workers=1) as executor:
31
- future = executor.submit(func, *args, **kwargs)
32
- start_time = time.time()
33
-
34
- while True:
35
- try:
36
- # 使用较短的超时时间进行轮询,确保能够响应中断信号
37
- poll_timeout = 0.1
38
- if timeout is not None:
39
- remaining = timeout - (time.time() - start_time)
40
- if remaining <= 0:
41
- future.cancel()
42
- raise TimeoutError(f"Timeout after {timeout}s in {func.__name__}")
43
- poll_timeout = min(poll_timeout, remaining)
44
-
45
- try:
46
- return future.result(timeout=poll_timeout)
47
- except TimeoutError:
48
- continue # 继续轮询
49
-
50
- except KeyboardInterrupt:
51
- logging.warning("KeyboardInterrupt received, attempting to cancel task...")
52
- future.cancel()
53
- raise
54
- except Exception as e:
55
- logging.error(f"Error occurred in thread: {str(e)}")
56
- raise
57
- return wrapper
58
- return decorator
59
-
60
- def run_in_thread_with_cancel(timeout: Optional[float] = None):
61
- """Decorator that runs a function in a thread with explicit cancellation support.
62
-
63
- Args:
64
- timeout (float, optional): Maximum time to wait for thread completion in seconds.
65
- If None, will wait indefinitely.
66
-
67
- The decorated function MUST accept 'cancel_event' as its first parameter.
68
- This cancel_event is a threading.Event object that can be used to check if
69
- cancellation has been requested.
70
-
71
- The decorated function can be called with an external cancel_event passed as a keyword argument.
72
- If not provided, a new Event will be created.
73
-
74
- Example:
75
- @run_in_thread_with_cancel(timeout=10)
76
- def long_task(cancel_event, arg1, arg2):
77
- while not cancel_event.is_set():
78
- # do work
79
- if cancel_event.is_set():
80
- raise CancellationRequested()
81
-
82
- # 使用外部传入的cancel_event
83
- external_cancel = Event()
84
- try:
85
- result = long_task(arg1, arg2, cancel_event=external_cancel)
86
- except CancelledError:
87
- print("Task was cancelled")
88
-
89
- # 在其他地方取消任务
90
- external_cancel.set()
91
- """
92
- def decorator(func):
93
- # 检查函数签名
94
- sig = signature(func)
95
- params = list(sig.parameters.keys())
96
- if not params or params[0] != 'cancel_event':
97
- raise ValueError(
98
- f"Function {func.__name__} must have 'cancel_event' as its first parameter. "
99
- f"Current parameters: {params}"
100
- )
101
-
102
- @wraps(func)
103
- def wrapper(*args, **kwargs):
104
- # 从kwargs中提取或创建cancel_event
105
- cancel_event = kwargs.pop('cancel_event', None) or Event()
106
-
107
- def cancellable_task():
108
- try:
109
- return func(cancel_event, *args, **kwargs)
110
- except CancellationRequested:
111
- logging.info(f"Task {func.__name__} was cancelled")
112
- raise
113
- except Exception as e:
114
- logging.error(f"Error in {func.__name__}: {str(e)}")
115
- raise
116
-
117
- with ThreadPoolExecutor(max_workers=1) as executor:
118
- future = executor.submit(cancellable_task)
119
- start_time = time.time()
120
-
121
- while True:
122
- try:
123
- # 使用较短的超时时间进行轮询,确保能够响应中断信号
124
- poll_timeout = 0.1
125
- if timeout is not None:
126
- remaining = timeout - (time.time() - start_time)
127
- if remaining <= 0:
128
- cancel_event.set()
129
- future.cancel()
130
- raise TimeoutError(f"Timeout after {timeout}s in {func.__name__}")
131
- poll_timeout = min(poll_timeout, remaining)
132
-
133
- try:
134
- return future.result(timeout=poll_timeout)
135
- except TimeoutError:
136
- continue # 继续轮询
137
-
138
- except KeyboardInterrupt:
139
- logging.warning(f"KeyboardInterrupt received, cancelling {func.__name__}...")
140
- cancel_event.set()
141
- future.cancel()
142
- raise CancelledError("Task cancelled by user")
143
- except CancellationRequested:
144
- logging.info(f"Task {func.__name__} was cancelled")
145
- raise CancelledError("Task cancelled by request")
146
- except Exception as e:
147
- logging.error(f"Error occurred in thread: {str(e)}")
148
- raise
149
-
150
- return wrapper
151
- return decorator
152
-
153
-
154
- def run_in_raw_thread():
14
+ def run_in_raw_thread(token: Optional[str] = None, context: Optional[Dict[str, Any]] = None):
155
15
  """A decorator that runs a function in a separate thread and handles exceptions.
156
16
 
157
17
  Args:
158
- func: The function to run in a thread
18
+ token (Optional[str]): Optional cancellation token for this specific thread
19
+ context (Optional[Dict[str, Any]]): Optional context information for cancellation
159
20
 
160
21
  Returns:
161
22
  A wrapper function that executes the decorated function in a thread
@@ -166,28 +27,36 @@ def run_in_raw_thread():
166
27
  3. Propagate exceptions from the thread
167
28
  4. Support function arguments
168
29
  5. Preserve function metadata
30
+ 6. Support token-based cancellation
31
+ 7. Provide context information for cancellation
169
32
  """
170
- def decorator(func):
33
+ def decorator(func: Callable):
171
34
 
172
35
  @wraps(func)
173
36
  def wrapper(*args, **kwargs):
174
37
  # Store thread results
175
38
  result = []
176
- exception = []
177
- def worker():
178
- try:
179
- # 如果刚开始就遇到了,可能是用户中断的还没有释放
180
- # 等待五秒后强行释放
181
- if global_cancel.requested:
182
- time.sleep(5)
183
- global_cancel.reset()
184
-
39
+ exception_raised = [None] # 存储工作线程中的异常
40
+ thread_token = token
41
+ thread_context = context or {}
42
+ thread_terminated = threading.Event() # 用于标记线程是否已终止
43
+
44
+ def worker():
45
+ try:
46
+ # 执行用户函数
185
47
  ret = func(*args, **kwargs)
186
48
  result.append(ret)
187
- global_cancel.reset()
49
+ except CancelRequestedException as e:
50
+ # 处理取消异常
51
+ printer.print_in_terminal("generation_cancelled")
52
+ exception_raised[0] = e
188
53
  except Exception as e:
189
- global_cancel.reset()
190
- raise
54
+ # 存储其他异常
55
+ exception_raised[0] = e
56
+ finally:
57
+ # 无论如何执行完毕后,重置取消标志并标记线程已终止
58
+ global_cancel.reset(thread_token)
59
+ thread_terminated.set()
191
60
 
192
61
  # Create and start thread with a meaningful name
193
62
  thread = threading.Thread(target=worker, name=f"{func.__name__}_thread")
@@ -195,16 +64,56 @@ def run_in_raw_thread():
195
64
 
196
65
  try:
197
66
  thread.start()
67
+
68
+ # Poll thread status with timeout to allow for interruption
69
+ cancelled_by_keyboard = False
70
+ max_wait_time = 30 # 最大等待时间(秒)
71
+ wait_start_time = time.time()
72
+
198
73
  while thread.is_alive():
74
+ # 每次等待较短时间,以便能够及时响应中断
199
75
  thread.join(0.1)
76
+
77
+ # 检查是否已经超过最大等待时间(仅适用于已取消的情况)
78
+ elapsed_time = time.time() - wait_start_time
79
+ if cancelled_by_keyboard and elapsed_time > max_wait_time:
80
+ printer.print_in_terminal("force_terminating_thread")
81
+ break
82
+
83
+ # 检查线程间的取消请求
84
+ if global_cancel.is_requested(thread_token):
85
+ # 传播取消请求到工作线程
86
+ raise CancelRequestedException(thread_token)
200
87
 
88
+ # 如果工作线程出现了异常,在主线程中重新抛出
89
+ if exception_raised[0] is not None:
90
+ raise exception_raised[0]
91
+
92
+ # 返回结果
201
93
  return result[0] if result else None
202
- except KeyboardInterrupt:
203
- global_cancel.set()
204
- raise KeyboardInterrupt("Task was cancelled by user")
205
- except Exception as e:
206
- global_cancel.reset()
207
- raise
94
+
95
+ except KeyboardInterrupt:
96
+ # 设置取消标志
97
+ cancel_context = {"message": get_message("task_cancelled_by_user"), "source": "keyboard_interrupt"}
98
+ cancel_context.update(thread_context)
99
+ global_cancel.set(thread_token, cancel_context)
100
+ printer.print_in_terminal("cancellation_requested")
101
+
102
+ # 标记为键盘中断取消
103
+ cancelled_by_keyboard = True
104
+ wait_start_time = time.time()
105
+
106
+ # 等待线程终止或检测到取消
107
+ while thread.is_alive() and not thread_terminated.is_set():
108
+ thread.join(0.5)
109
+ elapsed_time = time.time() - wait_start_time
110
+ if elapsed_time > max_wait_time:
111
+ printer.print_in_terminal("force_raising_keyboard_interrupt")
112
+ break
113
+
114
+ # 如果线程已终止且有异常,优先抛出该异常
115
+ if exception_raised[0] is not None:
116
+ raise exception_raised[0]
208
117
 
209
118
  return wrapper
210
119
  return decorator
autocoder/version.py CHANGED
@@ -1 +1 @@
1
- __version__ = "0.1.304"
1
+ __version__ = "0.1.306"