#2321·swarms

[BUG][AuctionSwarm/_extract_bid][Six copies of the tool-call unwrap accept different provider shapes, so every bid silently ties]

Author: ayaangazaliCreated Sep 21, 2026Updated Sep 21, 2026

Summary

On a provider that returns tool-call objects rather than dicts, AuctionSwarm gives every agent the no-confidence sentinel (0.0, 1.0). All bids tie, the winner is whichever agent the tie-break happens to land on, and the auction silently stops auctioning. Nothing raises and nothing is logged.

The cause is that unwrapping a forced tool call into its arguments dict is hand-rolled in six places, and no two copies accept the same set of inputs. GroupChat handles object-shaped tool calls; AuctionSwarm, ten files away and parsing the identical structure, does not.

Reproduction

python
from pydantic import BaseModel
from swarms.structs.groupchat import _extract_args
from swarms.structs.auction_swarm import _extract_bid

class Fn(BaseModel):
    name: str
    arguments: str

class ToolCall(BaseModel):
    function: Fn

gc = [ToolCall(function=Fn(name="respond", arguments='{"score": 0.9, "message": "hi"}'))]
au = [ToolCall(function=Fn(name="bid",     arguments='{"confidence": 0.9, "estimated_cost": 5.0}'))]

print(_extract_args(gc))   # -> (0.9, 'hi')     correct
print(_extract_bid(au))    # -> (0.0, 1.0)      silently dropped
pydantic tool call:
  groupchat._extract_args   -> (0.9, 'hi')    (expected (0.9, 'hi'))
  auction._extract_bid      -> (0.0, 1.0)     (expected (0.9, 5.0))
plain dict tool call:
  groupchat._extract_args   -> (0.9, 'hi')
  auction._extract_bid      -> (0.9, 5.0)

Same input shape, two sibling parsers, one works.

The shape matrix

Six copies of the same "string → literal_eval, list → first element, pydantic → model_dump(), .get("function"), .get("arguments"), json.loads if str" chain, each supporting a different subset:

site pydantic object plain dict str(list) repr
groupchat.py:110 _extract_args yes yes yes
auction_swarm.py:118 _extract_bid no yes yes
auto_agent_builder.py:90 _extract_agents yes yes no
heavy_swarm.py:1237 _parse_tool_calls yes no no
planner_worker_swarm.py:621 _parse_structured_output no yes no
hierarchical_order_parser.py:68 _from_function no yes no

Every gap fails silently into that copy's own "nothing" sentinel, except heavy_swarm, which is the mirror image of auction_swarm: it reaches straight through tool_call.function.arguments and catches only json.JSONDecodeError, so a dict-returning provider raises an uncaught AttributeError out of question generation.

groupchat._extract_args is the only copy that accepts all three shapes, and its own inline note records why each branch exists:

Providers return either a plain dict or a pydantic object whose function/arguments are attributes.

That knowledge never reached the other five.

Expected fix

One shared helper that turns raw tool-call output into an arguments dict, covering all three shapes. Each site keeps only its own key reads and its own defaults. Removes roughly 140 duplicated lines and, more to the point, makes every structure accept the same provider output.

swarms/utils/str_to_dict.py is the natural home: it is 27 lines and already owns the "provider output to dict" job for the string half.

Related

This is a concrete instance of the copy-paste families in #1850. The helper is the tool_call_arguments half; the mixin is separate.