beswarm 0.2.88__py3-none-any.whl → 0.2.89__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.
- beswarm/agents/planact.py +4 -4
- beswarm/taskmanager.py +11 -4
- beswarm/tools/subtasks.py +56 -28
- {beswarm-0.2.88.dist-info → beswarm-0.2.89.dist-info}/METADATA +1 -1
- {beswarm-0.2.88.dist-info → beswarm-0.2.89.dist-info}/RECORD +7 -7
- {beswarm-0.2.88.dist-info → beswarm-0.2.89.dist-info}/WHEEL +0 -0
- {beswarm-0.2.88.dist-info → beswarm-0.2.89.dist-info}/top_level.txt +0 -0
beswarm/agents/planact.py
CHANGED
@@ -226,12 +226,10 @@ class BrokerWorker:
|
|
226
226
|
self.WORKER_RESPONSE_TOPIC = self.channel + ".worker_responses"
|
227
227
|
self.TASK_STATUS_TOPIC =self.channel + ".task_status"
|
228
228
|
|
229
|
-
|
230
|
-
|
231
|
-
def setup(self):
|
229
|
+
async def setup(self):
|
232
230
|
cache_dir = self.work_dir / ".beswarm"
|
233
231
|
cache_dir.mkdir(parents=True, exist_ok=True)
|
234
|
-
self.task_manager.set_root_path(self.work_dir)
|
232
|
+
await self.task_manager.set_root_path(self.work_dir)
|
235
233
|
self.kgm.set_root_path(self.work_dir)
|
236
234
|
self.cache_file = cache_dir / "work_agent_conversation_history.json"
|
237
235
|
if not self.cache_file.exists():
|
@@ -308,6 +306,7 @@ class BrokerWorker:
|
|
308
306
|
|
309
307
|
async def run(self):
|
310
308
|
"""Sets up subscriptions and starts the workflow."""
|
309
|
+
await self.setup()
|
311
310
|
os.chdir(self.work_dir.absolute())
|
312
311
|
await self._configure_tools()
|
313
312
|
|
@@ -326,6 +325,7 @@ class BrokerWorker:
|
|
326
325
|
|
327
326
|
async def stream_run(self):
|
328
327
|
"""Runs the workflow and yields status messages."""
|
328
|
+
await self.setup()
|
329
329
|
os.chdir(self.work_dir.absolute())
|
330
330
|
await self._configure_tools()
|
331
331
|
|
beswarm/taskmanager.py
CHANGED
@@ -30,6 +30,7 @@ class TaskManager:
|
|
30
30
|
raise ValueError("并发限制必须大于0")
|
31
31
|
|
32
32
|
self.tasks_cache = {} # 存储所有任务的状态和元数据, key: task_id
|
33
|
+
self.task_events = {} # 为每个任务存储一个asyncio.Event,用于等待特定任务完成
|
33
34
|
|
34
35
|
self._pending_queue = asyncio.Queue() # 内部待办任务队列
|
35
36
|
self._results_queue = asyncio.Queue() # 内部已完成任务结果队列
|
@@ -39,7 +40,7 @@ class TaskManager:
|
|
39
40
|
self.cache_dir = None
|
40
41
|
self.task_cache_file = None
|
41
42
|
|
42
|
-
def set_root_path(self, root_path):
|
43
|
+
async def set_root_path(self, root_path):
|
43
44
|
"""设置工作根目录并加载持久化的任务状态。"""
|
44
45
|
if self.root_path is not None:
|
45
46
|
return
|
@@ -57,7 +58,7 @@ class TaskManager:
|
|
57
58
|
# 启动工作者池
|
58
59
|
self.start()
|
59
60
|
# 恢复中断的任务
|
60
|
-
self.resume_interrupted_tasks()
|
61
|
+
await self.resume_interrupted_tasks()
|
61
62
|
|
62
63
|
def start(self):
|
63
64
|
"""启动并发工作者池。"""
|
@@ -118,6 +119,8 @@ class TaskManager:
|
|
118
119
|
|
119
120
|
self._update_task_status(task_id, status, result=str(result))
|
120
121
|
self._results_queue.put_nowait((task_id, status, result))
|
122
|
+
if task_id in self.task_events:
|
123
|
+
self.task_events[task_id].set()
|
121
124
|
|
122
125
|
def set_task_cache(self, *keys_and_value):
|
123
126
|
"""设置可嵌套的任务缓存。"""
|
@@ -167,6 +170,7 @@ class TaskManager:
|
|
167
170
|
task_ids = []
|
168
171
|
for params in tasks_params_list:
|
169
172
|
task_id = str(uuid.uuid4())
|
173
|
+
self.task_events[task_id] = asyncio.Event()
|
170
174
|
coro = task_coro_func(**params)
|
171
175
|
|
172
176
|
# 初始化任务状态为 PENDING
|
@@ -187,6 +191,7 @@ class TaskManager:
|
|
187
191
|
task_ids = []
|
188
192
|
for params in tasks_params_list:
|
189
193
|
task_id = str(uuid.uuid4())
|
194
|
+
self.task_events[task_id] = asyncio.Event()
|
190
195
|
coro = task_coro_func(**params)
|
191
196
|
|
192
197
|
self._update_task_status(task_id, TaskStatus.PENDING, args=params)
|
@@ -196,7 +201,7 @@ class TaskManager:
|
|
196
201
|
print(f"已将 {len(task_ids)} 个新任务加入待处理队列。队列当前大小: {self._pending_queue.qsize()}")
|
197
202
|
return task_ids
|
198
203
|
|
199
|
-
def resume_interrupted_tasks(self):
|
204
|
+
async def resume_interrupted_tasks(self):
|
200
205
|
"""在启动时,恢复所有处于 PENDING 或 RUNNING 状态的旧任务。"""
|
201
206
|
interrupted_tasks = [
|
202
207
|
(tid, info) for tid, info in self.tasks_cache.items()
|
@@ -210,6 +215,7 @@ class TaskManager:
|
|
210
215
|
worker_fun = registry.tools["worker"]
|
211
216
|
|
212
217
|
for task_id, task_info in interrupted_tasks:
|
218
|
+
self.task_events[task_id] = asyncio.Event()
|
213
219
|
args = task_info.get("args")
|
214
220
|
if not args:
|
215
221
|
print(f"警告:任务 <{task_id[:8]}> 缺少参数,无法恢复。")
|
@@ -218,7 +224,7 @@ class TaskManager:
|
|
218
224
|
|
219
225
|
coro = worker_fun(**args)
|
220
226
|
self._update_task_status(task_id, TaskStatus.PENDING)
|
221
|
-
self._pending_queue.
|
227
|
+
await self._pending_queue.put((task_id, coro))
|
222
228
|
|
223
229
|
print(f"{len(interrupted_tasks)} 个中断的任务已重新加入队列。")
|
224
230
|
|
@@ -238,6 +244,7 @@ class TaskManager:
|
|
238
244
|
worker_fun = registry.tools["worker"]
|
239
245
|
coro = worker_fun(**tasks_params)
|
240
246
|
|
247
|
+
self.task_events[task_id] = asyncio.Event()
|
241
248
|
self._update_task_status(task_id, TaskStatus.PENDING, args=tasks_params)
|
242
249
|
self._pending_queue.put_nowait((task_id, coro))
|
243
250
|
|
beswarm/tools/subtasks.py
CHANGED
@@ -71,74 +71,102 @@ def resume_task(task_id, goal):
|
|
71
71
|
@register_tool()
|
72
72
|
def get_all_tasks_status():
|
73
73
|
"""
|
74
|
-
|
75
|
-
|
74
|
+
立即获取并返回所有任务的当前状态快照。
|
75
|
+
此函数不会等待,它会立刻返回一个包含所有已知任务(包括已完成、正在运行和待处理的)信息的字典。
|
76
|
+
**警告:** 此工具会返回所有任务的完整信息,可能导致大量的token消耗。
|
77
|
+
如果需要等待任务完成,请使用 `get_task_result()`。仅在需要对所有任务进行全面概览或调试时才应使用此工具。
|
76
78
|
|
77
79
|
Returns:
|
78
|
-
|
80
|
+
dict: 包含所有任务当前状态的字典。
|
79
81
|
"""
|
80
82
|
task_manager = current_task_manager.get()
|
81
83
|
return task_manager.tasks_cache
|
82
84
|
|
83
85
|
@register_tool()
|
84
|
-
async def get_task_result(reduce: bool = False):
|
86
|
+
async def get_task_result(task_id: str = None, reduce: bool = False):
|
85
87
|
"""
|
86
|
-
|
88
|
+
等待并获取一个或多个子任务的执行结果。
|
87
89
|
|
88
|
-
|
89
|
-
一旦有任务完成,它会立即获取并返回该任务的结果。如果调用时没有任务完成,它会阻塞并等待。
|
90
|
+
此工具有多种操作模式,**推荐使用默认模式**。
|
90
91
|
|
91
|
-
|
92
|
-
|
93
|
-
|
92
|
+
1. **等待并获取下一个完成的任务(推荐的标准方法)**:
|
93
|
+
在不提供 `task_id` 且 `reduce=False` 的情况下,此工具会异步等待,直到队列中有任何一个子任务完成,然后返回该任务的结果。
|
94
|
+
这是处理子任务最常用的模式,它允许你以事件驱动的方式、按完成顺序处理结果。
|
94
95
|
|
95
|
-
|
96
|
+
2. **获取特定任务的结果(特殊需求)**:
|
97
|
+
仅当你需要等待一个 *特定* 子任务时,才提供 `task_id` 参数。工具将专门等待该ID的任务完成并返回其结果。
|
98
|
+
如果任务已经完成,结果会立即返回。
|
99
|
+
|
100
|
+
3. **规约模式(等待所有任务)**:
|
101
|
+
设置 `reduce=True` 会使工具等待 **所有** 正在运行或待处理的任务都完成后,才一次性返回所有结果的摘要。
|
102
|
+
**警告:** 仅在子任务间完全独立、可以并行执行时使用此模式。当使用此模式时,不应提供 `task_id`。
|
96
103
|
|
97
104
|
Args:
|
98
|
-
|
105
|
+
task_id (str, optional): 要获取结果的特定任务的ID。仅在需要等待特定任务时使用。
|
106
|
+
reduce (bool, optional): 是否等待所有任务完成后再汇总返回。默认为 `False`。
|
99
107
|
|
100
108
|
Returns:
|
101
|
-
str:
|
102
|
-
- 当 `reduce=False` 时,返回单个子任务的执行结果。
|
103
|
-
- 当 `reduce=True` 时,返回一个包含所有任务最终状态和结果的汇总字符串。
|
109
|
+
str: 任务的执行结果或结果摘要。
|
104
110
|
"""
|
105
111
|
task_manager = current_task_manager.get()
|
112
|
+
|
113
|
+
if task_id and reduce:
|
114
|
+
return "<tool_error>无效的参数组合:不能同时提供 `task_id` 和设置 `reduce=True`。</tool_error>"
|
115
|
+
|
106
116
|
def get_running_tasks_count():
|
107
117
|
return len([
|
108
|
-
|
109
|
-
if
|
118
|
+
tid for tid, task in task_manager.tasks_cache.items()
|
119
|
+
if tid != "root_path" and task.get("status") in ["PENDING", "RUNNING"]
|
110
120
|
])
|
111
121
|
|
122
|
+
if task_id:
|
123
|
+
# 模式1:等待并获取指定任务的结果
|
124
|
+
if task_id not in task_manager.tasks_cache:
|
125
|
+
return f"<tool_error>任务ID '{task_id}' 不存在。</tool_error>"
|
126
|
+
|
127
|
+
# 如果任务已经完成,直接返回结果
|
128
|
+
task_info = task_manager.tasks_cache[task_id]
|
129
|
+
if task_info.get("status") in ["DONE", "ERROR"]:
|
130
|
+
return f"Task ID: {task_id}\nStatus: {task_info['status']}\nResult: {task_info.get('result', 'N/A')}"
|
131
|
+
|
132
|
+
# 如果任务未完成,等待事件
|
133
|
+
event = task_manager.task_events.get(task_id)
|
134
|
+
if event:
|
135
|
+
await event.wait()
|
136
|
+
# 事件触发后,再次获取最新状态
|
137
|
+
task_info = task_manager.tasks_cache[task_id]
|
138
|
+
return f"Task ID: {task_id}\nStatus: {task_info['status']}\nResult: {task_info.get('result', 'N/A')}"
|
139
|
+
else:
|
140
|
+
return f"<tool_error>任务 '{task_id}' 的事件不存在,可能是一个旧任务或状态已损坏。</tool_error>"
|
141
|
+
|
142
|
+
# 检查是否还有正在运行的任务
|
112
143
|
if get_running_tasks_count() == 0:
|
113
144
|
return "All tasks are finished."
|
114
145
|
|
115
146
|
if not reduce:
|
116
|
-
#
|
117
|
-
|
118
|
-
|
147
|
+
# 模式2:获取下一个完成的任务结果
|
148
|
+
next_task_id, status, result = await task_manager.get_next_result()
|
119
149
|
unfinished_tasks = [tid for tid, task in task_manager.tasks_cache.items() if tid != "root_path" and task.get("status") not in ["DONE", "ERROR"]]
|
120
150
|
text = "".join([
|
121
|
-
f"Task ID: {
|
151
|
+
f"Task ID: {next_task_id}\n",
|
122
152
|
f"Status: {status.value}\n",
|
123
153
|
f"Result: {result}\n\n",
|
124
|
-
f"There are {len(unfinished_tasks)} unfinished tasks, unfinished task ids: {unfinished_tasks[
|
154
|
+
f"There are {len(unfinished_tasks)} unfinished tasks, unfinished task ids: {unfinished_tasks[0]}" if unfinished_tasks else "All tasks are finished.",
|
125
155
|
])
|
126
156
|
return text
|
127
157
|
else:
|
128
|
-
#
|
158
|
+
# 模式3:规约模式 - 等待所有任务完成
|
129
159
|
while get_running_tasks_count() > 0:
|
130
160
|
await task_manager.get_next_result()
|
131
161
|
|
132
|
-
# 收集所有任务的结果
|
133
162
|
all_results = []
|
134
|
-
for
|
135
|
-
if
|
163
|
+
for tid, task in task_manager.tasks_cache.items():
|
164
|
+
if tid == "root_path":
|
136
165
|
continue
|
137
|
-
|
138
166
|
status = task.get('status', 'UNKNOWN')
|
139
167
|
result = task.get('result', 'No result available')
|
140
168
|
all_results.append(
|
141
|
-
f"Task ID: {
|
169
|
+
f"Task ID: {tid}\nStatus: {status}\nResult: {result}"
|
142
170
|
)
|
143
171
|
|
144
172
|
summary = f"All {len(all_results)} subtasks have been completed.\n\n"
|
@@ -3,10 +3,10 @@ beswarm/broker.py,sha256=64Y-djrKYaZfBQ8obwHOmr921QgZeu9BtScZWaYLfDo,9887
|
|
3
3
|
beswarm/core.py,sha256=jKStpTTOu6Ojond_i-okTZLrvSAJ4yUoTZwtDfFTiRs,553
|
4
4
|
beswarm/knowledge_graph.py,sha256=oiOMknAJzGrOHc2AyQgvrCcZAkGLhFnsnvSBdfFBWMw,14831
|
5
5
|
beswarm/prompt.py,sha256=yuIDzFnU4nFmaZ4st_tS14W7RI_bvDDpaoTJYEuZTo4,34285
|
6
|
-
beswarm/taskmanager.py,sha256=
|
6
|
+
beswarm/taskmanager.py,sha256=2Uz_cthW9rWkQMJdzgsXAMlfN8Ni2Qj_DOq_L-p6XZc,12662
|
7
7
|
beswarm/utils.py,sha256=0J-b38P5QGT-A_38co7FjzaUNJykaskI7mbbcQ4w_68,8215
|
8
8
|
beswarm/agents/chatgroup.py,sha256=PzrmRcDKAbB7cxL16nMod_CzPosDV6bfTmXxQVuv-AQ,12012
|
9
|
-
beswarm/agents/planact.py,sha256=
|
9
|
+
beswarm/agents/planact.py,sha256=g4103GbyMSq3yT9dMwBYrZjZaPX6oQulvnSOvrospmU,18249
|
10
10
|
beswarm/aient/aient/__init__.py,sha256=SRfF7oDVlOOAi6nGKiJIUK6B_arqYLO9iSMp-2IZZps,21
|
11
11
|
beswarm/aient/aient/architext/architext/__init__.py,sha256=79Ih1151rfcqZdr7F8HSZSTs_iT2SKd1xCkehMsXeXs,19
|
12
12
|
beswarm/aient/aient/architext/architext/core.py,sha256=IHN1d8Pe1oqaFSS1EQ3OpxCJydiLB3goL5n2uohr5Q8,29737
|
@@ -118,10 +118,10 @@ beswarm/tools/request_input.py,sha256=3n2UW8m8Q7dxGhd7L7hzSJ1kk4ekMbtdtNZZT3dJf2
|
|
118
118
|
beswarm/tools/screenshot.py,sha256=hyL6F8_k9Y03Nb_X18cY-klCpWWdkqyC-iGXfKX-7jc,1007
|
119
119
|
beswarm/tools/search_arxiv.py,sha256=NLiJV1B7Um6EuZXLxnL950d837_he2LGG7qaGACSgwg,10750
|
120
120
|
beswarm/tools/search_web.py,sha256=0fTeczXiOX_LJQGaLEGbuJtIPzofeuquGWEt3yDMtVw,17498
|
121
|
-
beswarm/tools/subtasks.py,sha256=
|
121
|
+
beswarm/tools/subtasks.py,sha256=anKNMx3vpAhFSSn9FThTfET5w70c0iMr9RI3WbEaIJ4,12660
|
122
122
|
beswarm/tools/worker.py,sha256=mQ1qdrQ8MgL99byAbTvxfEByFFGN9mty3UHqHjARMQ8,2331
|
123
123
|
beswarm/tools/write_csv.py,sha256=u0Hq18Ksfheb52MVtyLNCnSDHibITpsYBPs2ub7USYA,1466
|
124
|
-
beswarm-0.2.
|
125
|
-
beswarm-0.2.
|
126
|
-
beswarm-0.2.
|
127
|
-
beswarm-0.2.
|
124
|
+
beswarm-0.2.89.dist-info/METADATA,sha256=39srjSp9Xc4PreZUnljiYmrmyIsqITMG3IHHCtnhBwo,3878
|
125
|
+
beswarm-0.2.89.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
126
|
+
beswarm-0.2.89.dist-info/top_level.txt,sha256=pJw4O87wvt5882smuSO6DfByJz7FJ8SxxT8h9fHCmpo,8
|
127
|
+
beswarm-0.2.89.dist-info/RECORD,,
|
File without changes
|
File without changes
|