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.
- {auto_coder-0.1.304.dist-info → auto_coder-0.1.306.dist-info}/METADATA +1 -1
- {auto_coder-0.1.304.dist-info → auto_coder-0.1.306.dist-info}/RECORD +45 -40
- autocoder/agent/auto_learn_from_commit.py +3 -1
- autocoder/agent/auto_review_commit.py +3 -1
- autocoder/auto_coder.py +3 -2
- autocoder/auto_coder_runner.py +116 -3
- autocoder/chat_auto_coder.py +9 -1
- autocoder/chat_auto_coder_lang.py +552 -278
- autocoder/commands/auto_command.py +1 -4
- autocoder/commands/auto_web.py +1 -9
- autocoder/common/__init__.py +4 -0
- autocoder/common/auto_coder_lang.py +737 -392
- autocoder/common/code_auto_generate.py +104 -16
- autocoder/common/code_auto_generate_diff.py +101 -10
- autocoder/common/code_auto_generate_editblock.py +103 -9
- autocoder/common/code_auto_generate_strict_diff.py +99 -9
- autocoder/common/code_auto_merge.py +8 -0
- autocoder/common/code_auto_merge_diff.py +8 -0
- autocoder/common/code_auto_merge_editblock.py +7 -0
- autocoder/common/code_auto_merge_strict_diff.py +5 -0
- autocoder/common/code_modification_ranker.py +4 -2
- autocoder/common/command_completer.py +12 -0
- autocoder/common/command_generator.py +5 -4
- autocoder/common/git_utils.py +13 -7
- autocoder/common/global_cancel.py +68 -7
- autocoder/common/stream_out_type.py +5 -1
- autocoder/common/utils_code_auto_generate.py +29 -3
- autocoder/dispacher/__init__.py +18 -19
- autocoder/dispacher/actions/action.py +6 -162
- autocoder/dispacher/actions/plugins/action_regex_project.py +2 -6
- autocoder/index/filter/quick_filter.py +6 -3
- autocoder/index/index.py +2 -4
- autocoder/memory/__init__.py +7 -0
- autocoder/memory/active_context_manager.py +649 -0
- autocoder/memory/active_package.py +469 -0
- autocoder/memory/async_processor.py +161 -0
- autocoder/memory/directory_mapper.py +67 -0
- autocoder/utils/auto_coder_utils/chat_stream_out.py +61 -11
- autocoder/utils/project_structure.py +35 -1
- autocoder/utils/thread_utils.py +78 -169
- autocoder/version.py +1 -1
- {auto_coder-0.1.304.dist-info → auto_coder-0.1.306.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.304.dist-info → auto_coder-0.1.306.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.304.dist-info → auto_coder-0.1.306.dist-info}/entry_points.txt +0 -0
- {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]]
|
|
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
|
-
|
|
201
|
-
|
|
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 = {
|
|
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 {
|
autocoder/utils/thread_utils.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
|
|
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
|
-
|
|
190
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
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.
|
|
1
|
+
__version__ = "0.1.306"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|