fix(swarm): unblock run_swarm retries and clean Ctrl+C shutdown (#42)
Two follow-ups to b769580 reported by @codewings:
1. "Blocked duplicate call: run_swarm" — loop.py dedups by tool name only,
and SwarmTool defaulted to repeatable=False. A single successful
run_swarm per session blocked every subsequent call (retry with a
different preset, different market, etc). Mark SwarmTool.repeatable=True.
2. threading._shutdown noise on Ctrl+C — `with ThreadPoolExecutor` calls
shutdown(wait=True) on exit, which blocks indefinitely while kimi
workers are mid-stream. Switch to manual executor + try/finally with
shutdown(wait=False, cancel_futures=True); catch KeyboardInterrupt at
layer boundary to set cancel_event. CLI now returns immediately instead
of deadlocking on exit.
This commit is contained in:
1 parent
a1040bbb71
commit
fc708d15c5
2 files changed
+15
-4
No files matched your search
@@ -343,10 +343,14 @@ class SwarmRuntime:
|
||||
def _event_callback(event: SwarmEvent) -> None:
|
||||
self._emit_event(run.id, event)
|
||||
|
||||
with ThreadPoolExecutor(max_workers=self._max_workers) as executor:
|
||||
futures: dict[Future[WorkerResult], str] = {}
|
||||
layer_budget = 0 # seconds — max per-task (retries × timeout) across layer
|
||||
|
||||
# Manual executor lifecycle (not `with`) so KeyboardInterrupt and
|
||||
# the layer deadline don't block main on `shutdown(wait=True)` —
|
||||
# `wait=False + cancel_futures=True` lets pending work drop and
|
||||
# the CLI return immediately. Running workers finish naturally.
|
||||
executor = ThreadPoolExecutor(max_workers=self._max_workers)
|
||||
futures: dict[Future[WorkerResult], str] = {}
|
||||
layer_budget = 0 # seconds — max per-task (retries × timeout) across layer
|
||||
try:
|
||||
for tid in layer_task_ids:
|
||||
task = task_store.load_task(tid)
|
||||
agent_spec = agent_map.get(task.agent_id)
|
||||
@@ -417,6 +421,12 @@ class SwarmRuntime:
|
||||
status="timeout", summary="",
|
||||
error=f"Worker exceeded layer deadline of {layer_deadline}s",
|
||||
)
|
||||
except KeyboardInterrupt:
|
||||
cancel_event.set()
|
||||
logger.warning("Swarm layer interrupted — cancelling pending workers")
|
||||
raise
|
||||
finally:
|
||||
executor.shutdown(wait=False, cancel_futures=True)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
@@ -583,6 +583,7 @@ class SwarmTool(BaseTool):
|
||||
"required": ["prompt"],
|
||||
}
|
||||
is_readonly = False
|
||||
repeatable = True # loop.py dedups by tool name; each prompt is a distinct run (#42)
|
||||
|
||||
def execute(self, **kwargs: Any) -> str:
|
||||
"""Start a swarm run: auto-match preset, extract variables, wait for completion.
|
||||
|
||||
Reference in new issue
Block a user