From 1b005f3d1c5e0c28280eb13c0e8fd44148b2cfa9 Mon Sep 17 00:00:00 2001 From: ChethanUK Date: Sat, 4 Oct 2025 23:43:23 +0200 Subject: [PATCH] fix Attempting to unpack Pydantic BaseModel directly with ** operator --- swarms/structs/auto_swarm_builder.py | 22 +- tests/structs/test_auto_swarm_builder_fix.py | 294 +++++++++++++++++++ 2 files changed, 315 insertions(+), 1 deletion(-) create mode 100644 tests/structs/test_auto_swarm_builder_fix.py diff --git a/swarms/structs/auto_swarm_builder.py b/swarms/structs/auto_swarm_builder.py index 54a36e9d..0a9bd689 100644 --- a/swarms/structs/auto_swarm_builder.py +++ b/swarms/structs/auto_swarm_builder.py @@ -489,6 +489,10 @@ class AutoSwarmBuilder: Returns: List[Agent]: List of created agents + + Notes: + - Handles both dict and Pydantic AgentSpec inputs + - Maps 'description' field to 'agent_description' for Agent compatibility """ # Create agents from config agents = [] @@ -504,7 +508,23 @@ class AutoSwarmBuilder: if isinstance(agent_config, dict): agent_config = AgentSpec(**agent_config) - agent = Agent(**agent_config) + # Convert Pydantic model to dict for Agent initialization + if isinstance(agent_config, BaseModel): + agent_data = agent_config.model_dump() + else: + agent_data = agent_config + + # Handle parameter name mapping: description -> agent_description + if ( + "description" in agent_data + and "agent_description" not in agent_data + ): + agent_data["agent_description"] = agent_data.pop( + "description" + ) + + # Create agent from processed data + agent = Agent(**agent_data) agents.append(agent) return agents diff --git a/tests/structs/test_auto_swarm_builder_fix.py b/tests/structs/test_auto_swarm_builder_fix.py new file mode 100644 index 00000000..4167117d --- /dev/null +++ b/tests/structs/test_auto_swarm_builder_fix.py @@ -0,0 +1,294 @@ +""" +Tests for bug #1115 fix in AutoSwarmBuilder. + +This test module verifies the fix for AttributeError when creating agents +from AgentSpec Pydantic models in AutoSwarmBuilder. + +Bug: https://github.com/kyegomez/swarms/issues/1115 +""" + +import pytest +from pydantic import BaseModel + +from swarms.structs.agent import Agent +from swarms.structs.auto_swarm_builder import ( + AgentSpec, + AutoSwarmBuilder, +) +from swarms.structs.ma_utils import set_random_models_for_agents + + +class TestAutoSwarmBuilderFix: + """Tests for bug #1115 fix in AutoSwarmBuilder.""" + + def test_create_agents_from_specs_with_dict(self): + """Test that create_agents_from_specs handles dict input correctly.""" + builder = AutoSwarmBuilder() + + # Create specs as a dictionary + specs = { + "agents": [ + { + "agent_name": "test_agent_1", + "description": "Test agent 1 description", + "system_prompt": "You are a helpful assistant", + "model_name": "gpt-4o-mini", + "max_loops": 1, + } + ] + } + + agents = builder.create_agents_from_specs(specs) + + # Verify agents were created correctly + assert len(agents) == 1 + assert isinstance(agents[0], Agent) + assert agents[0].agent_name == "test_agent_1" + + # Verify description was mapped to agent_description + assert hasattr(agents[0], "agent_description") + assert ( + agents[0].agent_description == "Test agent 1 description" + ) + + def test_create_agents_from_specs_with_pydantic(self): + """Test that create_agents_from_specs handles Pydantic model input correctly. + + This is the main test for bug #1115 - it verifies that AgentSpec + Pydantic models can be unpacked correctly. + """ + builder = AutoSwarmBuilder() + + # Create specs as Pydantic AgentSpec objects + agent_spec = AgentSpec( + agent_name="test_agent_pydantic", + description="Pydantic test agent", + system_prompt="You are a helpful assistant", + model_name="gpt-4o-mini", + max_loops=1, + ) + + specs = {"agents": [agent_spec]} + + agents = builder.create_agents_from_specs(specs) + + # Verify agents were created correctly + assert len(agents) == 1 + assert isinstance(agents[0], Agent) + assert agents[0].agent_name == "test_agent_pydantic" + + # Verify description was mapped to agent_description + assert hasattr(agents[0], "agent_description") + assert agents[0].agent_description == "Pydantic test agent" + + def test_parameter_name_mapping(self): + """Test that 'description' field maps to 'agent_description' correctly.""" + builder = AutoSwarmBuilder() + + # Test with dict that has 'description' + specs = { + "agents": [ + { + "agent_name": "mapping_test", + "description": "This should map to agent_description", + "system_prompt": "You are helpful", + } + ] + } + + agents = builder.create_agents_from_specs(specs) + + assert len(agents) == 1 + agent = agents[0] + + # Verify description was mapped + assert hasattr(agent, "agent_description") + assert ( + agent.agent_description + == "This should map to agent_description" + ) + + def test_create_agents_from_specs_mixed_input(self): + """Test that create_agents_from_specs handles mixed dict and Pydantic input.""" + builder = AutoSwarmBuilder() + + # Mix of dict and Pydantic objects + dict_spec = { + "agent_name": "dict_agent", + "description": "Dict agent description", + "system_prompt": "You are helpful", + } + + pydantic_spec = AgentSpec( + agent_name="pydantic_agent", + description="Pydantic agent description", + system_prompt="You are smart", + ) + + specs = {"agents": [dict_spec, pydantic_spec]} + + agents = builder.create_agents_from_specs(specs) + + # Verify both agents were created + assert len(agents) == 2 + assert all(isinstance(agent, Agent) for agent in agents) + + # Verify both have correct descriptions + dict_agent = next( + a for a in agents if a.agent_name == "dict_agent" + ) + pydantic_agent = next( + a for a in agents if a.agent_name == "pydantic_agent" + ) + + assert ( + dict_agent.agent_description == "Dict agent description" + ) + assert ( + pydantic_agent.agent_description + == "Pydantic agent description" + ) + + def test_set_random_models_for_agents_with_valid_agents( + self, + ): + """Test set_random_models_for_agents with proper Agent objects.""" + # Create proper Agent objects + agents = [ + Agent( + agent_name="agent1", + system_prompt="You are agent 1", + max_loops=1, + ), + Agent( + agent_name="agent2", + system_prompt="You are agent 2", + max_loops=1, + ), + ] + + # Set random models + model_names = ["gpt-4o-mini", "gpt-4o", "claude-3-5-sonnet"] + result = set_random_models_for_agents( + agents=agents, model_names=model_names + ) + + # Verify results + assert len(result) == 2 + assert all(isinstance(agent, Agent) for agent in result) + assert all(hasattr(agent, "model_name") for agent in result) + assert all( + agent.model_name in model_names for agent in result + ) + + def test_set_random_models_for_agents_with_single_agent( + self, + ): + """Test set_random_models_for_agents with a single agent.""" + agent = Agent( + agent_name="single_agent", + system_prompt="You are helpful", + max_loops=1, + ) + + model_names = ["gpt-4o-mini", "gpt-4o"] + result = set_random_models_for_agents( + agents=agent, model_names=model_names + ) + + assert isinstance(result, Agent) + assert hasattr(result, "model_name") + assert result.model_name in model_names + + def test_set_random_models_for_agents_with_none(self): + """Test set_random_models_for_agents with None returns random model name.""" + model_names = ["gpt-4o-mini", "gpt-4o", "claude-3-5-sonnet"] + result = set_random_models_for_agents( + agents=None, model_names=model_names + ) + + assert isinstance(result, str) + assert result in model_names + + @pytest.mark.skip( + reason="This test requires API key and makes LLM calls" + ) + def test_auto_swarm_builder_return_agents_objects_integration( + self, + ): + """Integration test for AutoSwarmBuilder with execution_type='return-agents-objects'. + + This test requires OPENAI_API_KEY and makes actual LLM calls. + Run manually with: pytest -k test_auto_swarm_builder_return_agents_objects_integration -v + """ + builder = AutoSwarmBuilder( + execution_type="return-agents-objects", + model_name="gpt-4o-mini", + max_loops=1, + verbose=False, + ) + + agents = builder.run( + "Create a team of 2 data analysis agents with specific roles" + ) + + # Verify agents were created + assert isinstance(agents, list) + assert len(agents) >= 1 + assert all(isinstance(agent, Agent) for agent in agents) + assert all(hasattr(agent, "agent_name") for agent in agents) + assert all( + hasattr(agent, "agent_description") for agent in agents + ) + + def test_agent_spec_to_agent_all_fields(self): + """Test that all AgentSpec fields are properly passed to Agent.""" + builder = AutoSwarmBuilder() + + agent_spec = AgentSpec( + agent_name="full_test_agent", + description="Full test description", + system_prompt="You are a comprehensive test agent", + model_name="gpt-4o-mini", + auto_generate_prompt=False, + max_tokens=4096, + temperature=0.7, + role="worker", + max_loops=3, + goal="Test all parameters", + ) + + agents = builder.create_agents_from_specs( + {"agents": [agent_spec]} + ) + + assert len(agents) == 1 + agent = agents[0] + + # Verify all fields were set + assert agent.agent_name == "full_test_agent" + assert agent.agent_description == "Full test description" + # Agent may modify system_prompt by adding additional instructions + assert ( + "You are a comprehensive test agent" + in agent.system_prompt + ) + assert agent.max_loops == 3 + assert agent.max_tokens == 4096 + assert agent.temperature == 0.7 + + def test_create_agents_from_specs_empty_list(self): + """Test that create_agents_from_specs handles empty agent list.""" + builder = AutoSwarmBuilder() + + specs = {"agents": []} + + agents = builder.create_agents_from_specs(specs) + + assert isinstance(agents, list) + assert len(agents) == 0 + + +if __name__ == "__main__": + # Run tests with pytest + pytest.main([__file__, "-v", "--tb=short"])