auto-coder 0.1.251__py3-none-any.whl → 0.1.252__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.251.dist-info → auto_coder-0.1.252.dist-info}/METADATA +2 -2
- {auto_coder-0.1.251.dist-info → auto_coder-0.1.252.dist-info}/RECORD +26 -24
- autocoder/auto_coder.py +28 -4
- autocoder/auto_coder_rag.py +198 -35
- autocoder/chat_auto_coder.py +56 -3
- autocoder/chat_auto_coder_lang.py +21 -3
- autocoder/common/__init__.py +1 -0
- autocoder/common/auto_coder_lang.py +6 -4
- autocoder/common/code_modification_ranker.py +3 -3
- autocoder/common/global_cancel.py +21 -0
- autocoder/dispacher/actions/action.py +29 -8
- autocoder/dispacher/actions/plugins/action_regex_project.py +17 -5
- autocoder/index/filter/quick_filter.py +4 -6
- autocoder/index/index.py +13 -6
- autocoder/models.py +87 -6
- autocoder/rag/doc_filter.py +1 -3
- autocoder/rag/long_context_rag.py +7 -5
- autocoder/rag/token_limiter.py +1 -3
- autocoder/utils/auto_coder_utils/chat_stream_out.py +13 -2
- autocoder/utils/llms.py +15 -1
- autocoder/utils/thread_utils.py +201 -0
- autocoder/version.py +1 -1
- {auto_coder-0.1.251.dist-info → auto_coder-0.1.252.dist-info}/LICENSE +0 -0
- {auto_coder-0.1.251.dist-info → auto_coder-0.1.252.dist-info}/WHEEL +0 -0
- {auto_coder-0.1.251.dist-info → auto_coder-0.1.252.dist-info}/entry_points.txt +0 -0
- {auto_coder-0.1.251.dist-info → auto_coder-0.1.252.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
from concurrent.futures import ThreadPoolExecutor, TimeoutError, CancelledError
|
|
2
|
+
from threading import Event
|
|
3
|
+
from inspect import signature
|
|
4
|
+
from functools import wraps
|
|
5
|
+
from typing import Any, Optional
|
|
6
|
+
import threading
|
|
7
|
+
import logging
|
|
8
|
+
import time
|
|
9
|
+
from autocoder.common.global_cancel import global_cancel
|
|
10
|
+
|
|
11
|
+
class CancellationRequested(Exception):
|
|
12
|
+
"""Raised when a task is requested to be cancelled."""
|
|
13
|
+
pass
|
|
14
|
+
|
|
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():
|
|
155
|
+
"""A decorator that runs a function in a separate thread and handles exceptions.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
func: The function to run in a thread
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
A wrapper function that executes the decorated function in a thread
|
|
162
|
+
|
|
163
|
+
The decorator will:
|
|
164
|
+
1. Run the function in a separate thread
|
|
165
|
+
2. Handle KeyboardInterrupt properly
|
|
166
|
+
3. Propagate exceptions from the thread
|
|
167
|
+
4. Support function arguments
|
|
168
|
+
5. Preserve function metadata
|
|
169
|
+
"""
|
|
170
|
+
def decorator(func):
|
|
171
|
+
|
|
172
|
+
@wraps(func)
|
|
173
|
+
def wrapper(*args, **kwargs):
|
|
174
|
+
# Store thread results
|
|
175
|
+
result = []
|
|
176
|
+
exception = []
|
|
177
|
+
def worker():
|
|
178
|
+
try:
|
|
179
|
+
ret = func(*args, **kwargs)
|
|
180
|
+
result.append(ret)
|
|
181
|
+
global_cancel.reset()
|
|
182
|
+
except Exception as e:
|
|
183
|
+
global_cancel.reset()
|
|
184
|
+
raise
|
|
185
|
+
|
|
186
|
+
# Create and start thread with a meaningful name
|
|
187
|
+
thread = threading.Thread(target=worker, name=f"{func.__name__}_thread")
|
|
188
|
+
thread.daemon = True # Make thread daemon so it doesn't prevent program exit
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
thread.start()
|
|
192
|
+
while thread.is_alive():
|
|
193
|
+
thread.join(0.1)
|
|
194
|
+
|
|
195
|
+
return result[0] if result else None
|
|
196
|
+
except KeyboardInterrupt:
|
|
197
|
+
global_cancel.set()
|
|
198
|
+
raise KeyboardInterrupt("Task was cancelled by user")
|
|
199
|
+
|
|
200
|
+
return wrapper
|
|
201
|
+
return decorator
|
autocoder/version.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.1.
|
|
1
|
+
__version__ = "0.1.252"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|