Skip to content

Commit dcacf57

Browse files
committed
[bugf][planner-worker-swarm][run accepts img and never passes it on]
PlannerWorkerSwarm.run takes img, documents it, and lists it in the trace decorator's input_params, but the parameter appears nowhere else in the class. The planner, every worker and the judge are all called with task only, so a vision request runs as a text-only workflow and returns a confident answer about an image no agent ever saw. img now reaches the planner (and its sub-planners), the worker pool, and the judge. WorkerPool carries it because workers claim tasks from a queue rather than being called directly.
1 parent 6ba36f4 commit dcacf57

2 files changed

Lines changed: 44 additions & 7 deletions

File tree

swarms/structs/planner_worker_swarm.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -361,10 +361,12 @@ def __init__(
361361
max_workers: Optional[int] = None,
362362
poll_interval: float = 0.1,
363363
task_timeout: Optional[float] = None,
364+
img: Optional[str] = None,
364365
):
365366
self.agents = agents
366367
self.task_queue = task_queue
367368
self.conversation = conversation
369+
self.img = img
368370
self.max_workers = max_workers or min(
369371
len(agents), os.cpu_count() or 4
370372
)
@@ -462,7 +464,7 @@ def _worker_loop(
462464
max_workers=1
463465
) as task_executor:
464466
future = task_executor.submit(
465-
agent.run, task=context
467+
agent.run, task=context, img=self.img
466468
)
467469
try:
468470
result = future.result(
@@ -473,7 +475,7 @@ def _worker_loop(
473475
f"Task execution exceeded {self.task_timeout}s timeout"
474476
)
475477
else:
476-
result = agent.run(task=context)
478+
result = agent.run(task=context, img=self.img)
477479

478480
current = self.task_queue.get_task(task.id)
479481
if current and self.task_queue.complete(
@@ -691,6 +693,7 @@ def _run_planner(
691693
task: str,
692694
depth: int = 0,
693695
parent_task_id: Optional[str] = None,
696+
img: Optional[str] = None,
694697
) -> List[PlannerTask]:
695698
"""Run a planner and add produced tasks to the queue.
696699
@@ -711,7 +714,7 @@ def _run_planner(
711714
f"[PlannerWorkerSwarm] Running {planner_name} (depth={depth})"
712715
)
713716

714-
raw_output = planner.run(task=task)
717+
raw_output = planner.run(task=task, img=img)
715718

716719
spec = self._parse_structured_output(
717720
raw_output, PlannerTaskSpec
@@ -759,12 +762,13 @@ def _run_planner(
759762
task=f"Decompose this task into smaller subtasks:\n\n{ptask.description}",
760763
depth=depth + 1,
761764
parent_task_id=ptask.id,
765+
img=img,
762766
)
763767
added_tasks.extend(sub_tasks)
764768

765769
return added_tasks
766770

767-
def _run_judge(self) -> CycleVerdict:
771+
def _run_judge(self, img: Optional[str] = None) -> CycleVerdict:
768772
"""Run the judge agent to evaluate cycle results."""
769773
schema = BaseTool().base_model_to_dict(CycleVerdict)
770774

@@ -795,7 +799,7 @@ def _run_judge(self) -> CycleVerdict:
795799
"If not, identify specific gaps and provide instructions for the next planning cycle."
796800
)
797801

798-
raw_output = judge.run(task=eval_task)
802+
raw_output = judge.run(task=eval_task, img=img)
799803

800804
try:
801805
verdict = self._parse_structured_output(
@@ -889,7 +893,7 @@ def run(
889893
"Create new tasks to address these gaps."
890894
)
891895

892-
self._run_planner(planner_task)
896+
self._run_planner(planner_task, img=img)
893897

894898
# Phase 2: Worker execution
895899
worker_pool = WorkerPool(
@@ -898,6 +902,7 @@ def run(
898902
conversation=self.conversation,
899903
max_workers=self.max_workers,
900904
task_timeout=self.task_timeout,
905+
img=img,
901906
)
902907
worker_pool.run(timeout=self.worker_timeout)
903908

@@ -910,7 +915,7 @@ def run(
910915
)
911916

912917
# Phase 3: Judge evaluation
913-
verdict = self._run_judge()
918+
verdict = self._run_judge(img=img)
914919

915920
logger.info(
916921
f"[PlannerWorkerSwarm] Cycle {cycle + 1} done. "

tests/structs/test_planner_worker_swarm.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,38 @@ def test_drains_queue(self):
492492
).run(timeout=10)
493493
assert q.is_all_done() and a.run.call_count == 3
494494

495+
def test_img_reaches_every_worker(self):
496+
q = TaskQueue()
497+
q.add_tasks(
498+
[
499+
PlannerTask(title=f"T{i}", description=f"D{i}")
500+
for i in range(2)
501+
]
502+
)
503+
a = _mock_agent("W1")
504+
WorkerPool(
505+
agents=[a],
506+
task_queue=q,
507+
conversation=Conversation(time_enabled=False),
508+
max_workers=1,
509+
img="chart.png",
510+
).run(timeout=10)
511+
assert a.run.call_count == 2
512+
for call in a.run.call_args_list:
513+
assert call.kwargs["img"] == "chart.png"
514+
515+
def test_no_img_still_passes_none(self):
516+
q = TaskQueue()
517+
q.add_task(PlannerTask(title="T", description="D"))
518+
a = _mock_agent("W1")
519+
WorkerPool(
520+
agents=[a],
521+
task_queue=q,
522+
conversation=Conversation(time_enabled=False),
523+
max_workers=1,
524+
).run(timeout=10)
525+
assert a.run.call_args.kwargs["img"] is None
526+
495527
def test_worker_prompt_injected(self):
496528
q = TaskQueue()
497529
q.add_task(PlannerTask(title="MyTask", description="Do it"))

0 commit comments

Comments
 (0)