diff --git a/docs/examples/llm_council_examples.md b/docs/examples/llm_council_examples.md
new file mode 100644
index 00000000..ab607dbc
--- /dev/null
+++ b/docs/examples/llm_council_examples.md
@@ -0,0 +1,106 @@
+# LLM Council Examples
+
+This page provides examples demonstrating the LLM Council pattern, inspired by Andrej Karpathy's llm-council implementation. The LLM Council uses multiple specialized AI agents that:
+
+1. Each respond independently to queries
+2. Review and rank each other's anonymized responses
+3. Have a Chairman synthesize all responses into a final comprehensive answer
+
+## Example Files
+
+All LLM Council examples are located in the [`examples/multi_agent/llm_council_examples/`](https://github.com/kyegomez/swarms/tree/master/examples/multi_agent/llm_council_examples) directory.
+
+### Marketing & Business
+
+- **[marketing_strategy_council.py](https://github.com/kyegomez/swarms/blob/master/examples/multi_agent/llm_council_examples/marketing_strategy_council.py)** - Marketing strategy analysis and recommendations
+- **[business_strategy_council.py](https://github.com/kyegomez/swarms/blob/master/examples/multi_agent/llm_council_examples/business_strategy_council.py)** - Comprehensive business strategy development
+
+### Finance & Investment
+
+- **[finance_analysis_council.py](https://github.com/kyegomez/swarms/blob/master/examples/multi_agent/llm_council_examples/finance_analysis_council.py)** - Financial analysis and investment recommendations
+- **[etf_stock_analysis_council.py](https://github.com/kyegomez/swarms/blob/master/examples/multi_agent/llm_council_examples/etf_stock_analysis_council.py)** - ETF and stock analysis with portfolio recommendations
+
+### Medical & Healthcare
+
+- **[medical_treatment_council.py](https://github.com/kyegomez/swarms/blob/master/examples/multi_agent/llm_council_examples/medical_treatment_council.py)** - Medical treatment recommendations and care plans
+- **[medical_diagnosis_council.py](https://github.com/kyegomez/swarms/blob/master/examples/multi_agent/llm_council_examples/medical_diagnosis_council.py)** - Diagnostic analysis based on symptoms
+
+### Technology & Research
+
+- **[technology_assessment_council.py](https://github.com/kyegomez/swarms/blob/master/examples/multi_agent/llm_council_examples/technology_assessment_council.py)** - Technology evaluation and implementation strategy
+- **[research_analysis_council.py](https://github.com/kyegomez/swarms/blob/master/examples/multi_agent/llm_council_examples/research_analysis_council.py)** - Comprehensive research analysis on complex topics
+
+### Legal
+
+- **[legal_analysis_council.py](https://github.com/kyegomez/swarms/blob/master/examples/multi_agent/llm_council_examples/legal_analysis_council.py)** - Legal implications and compliance analysis
+
+## Basic Usage Pattern
+
+All examples follow the same pattern:
+
+```python
+from swarms.structs.llm_council import LLMCouncil
+
+# Create the council
+council = LLMCouncil(verbose=True)
+
+# Run a query
+result = council.run("Your query here")
+
+# Access results
+print(result["final_response"]) # Chairman's synthesized answer
+print(result["original_responses"]) # Individual member responses
+print(result["evaluations"]) # How members ranked each other
+```
+
+## Running Examples
+
+Run any example directly:
+
+```bash
+python examples/multi_agent/llm_council_examples/marketing_strategy_council.py
+python examples/multi_agent/llm_council_examples/finance_analysis_council.py
+python examples/multi_agent/llm_council_examples/medical_diagnosis_council.py
+```
+
+## Key Features
+
+- **Multiple Perspectives**: Each council member (GPT-5.1, Gemini, Claude, Grok) provides unique insights
+- **Peer Review**: Members evaluate and rank each other's responses anonymously
+- **Synthesis**: Chairman combines the best elements from all responses
+- **Transparency**: See both individual responses and evaluation rankings
+
+## Council Members
+
+The default council consists of:
+- **GPT-5.1-Councilor**: Analytical and comprehensive
+- **Gemini-3-Pro-Councilor**: Concise and well-processed
+- **Claude-Sonnet-4.5-Councilor**: Thoughtful and balanced
+- **Grok-4-Councilor**: Creative and innovative
+
+## Customization
+
+You can create custom council members:
+
+```python
+from swarms import Agent
+from swarms.structs.llm_council import LLMCouncil, get_gpt_councilor_prompt
+
+custom_agent = Agent(
+ agent_name="Custom-Councilor",
+ system_prompt=get_gpt_councilor_prompt(),
+ model_name="gpt-4.1",
+ max_loops=1,
+)
+
+council = LLMCouncil(
+ council_members=[custom_agent, ...],
+ chairman_model="gpt-5.1",
+ verbose=True
+)
+```
+
+## Documentation
+
+For complete API reference and detailed documentation, see the [LLM Council Reference Documentation](../swarms/structs/llm_council.md).
+
diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml
index 1619374f..53936b07 100644
--- a/docs/mkdocs.yml
+++ b/docs/mkdocs.yml
@@ -281,6 +281,7 @@ nav:
- MALT: "swarms/structs/malt.md"
- Multi-Agent Execution Utilities: "swarms/structs/various_execution_methods.md"
- Council of Judges: "swarms/structs/council_of_judges.md"
+ - LLM Council: "swarms/structs/llm_council.md"
- Heavy Swarm: "swarms/structs/heavy_swarm.md"
- Social Algorithms: "swarms/structs/social_algorithms.md"
@@ -399,7 +400,9 @@ nav:
- SwarmRouter Example: "swarms/examples/swarm_router.md"
- MultiAgentRouter Minimal Example: "swarms/examples/multi_agent_router_minimal.md"
- ConcurrentWorkflow Example: "swarms/examples/concurrent_workflow.md"
+ - Multi-Agentic Patterns with GraphWorkflow: "swarms/examples/graphworkflow_rustworkx_patterns.md"
- Mixture of Agents Example: "swarms/examples/moa_example.md"
+ - LLM Council Examples: "examples/llm_council_examples.md"
- Unique Swarms: "swarms/examples/unique_swarms.md"
- Agents as Tools: "swarms/examples/agents_as_tools.md"
- Aggregate Multi-Agent Responses: "swarms/examples/aggregate.md"
diff --git a/docs/swarms/examples/graphworkflow_rustworkx_patterns.md b/docs/swarms/examples/graphworkflow_rustworkx_patterns.md
new file mode 100644
index 00000000..5d392c49
--- /dev/null
+++ b/docs/swarms/examples/graphworkflow_rustworkx_patterns.md
@@ -0,0 +1,1479 @@
+# GraphWorkflow with Rustworkx: Complete Patterns Guide
+
+A comprehensive guide to implementing various agentic patterns using GraphWorkflow with the rustworkx backend for optimal performance.
+
+## Table of Contents
+
+1. [Introduction](#introduction)
+2. [Basic Patterns](#basic-patterns)
+3. [Hierarchical Patterns](#hierarchical-patterns)
+4. [Concurrent/Parallel Patterns](#concurrentparallel-patterns)
+5. [Majority Voting Patterns](#majority-voting-patterns)
+6. [Fan-Out/Fan-In Patterns](#fan-outfan-in-patterns)
+7. [Sequential Patterns](#sequential-patterns)
+8. [Advanced Patterns](#advanced-patterns)
+9. [Performance Optimization](#performance-optimization)
+
+## Introduction
+
+GraphWorkflow with rustworkx backend provides a high-performance framework for orchestrating complex multi-agent workflows. This guide demonstrates how to implement various agentic patterns that are commonly used in production systems.
+
+### Why Rustworkx?
+
+- **Performance**: 2-10x faster for large graphs (1000+ nodes)
+- **Memory Efficiency**: Optimized for large-scale workflows
+- **Scalability**: Better performance with complex graph operations
+- **API Compatibility**: Drop-in replacement for NetworkX backend
+
+### Installation
+
+```bash
+pip install rustworkx
+```
+
+## Basic Patterns
+
+### Simple Sequential Workflow
+
+The most basic pattern - agents execute one after another in sequence.
+
+**Architecture Diagram:**
+
+```mermaid
+graph LR
+ A[ResearchAgent] --> B[AnalysisAgent]
+ B --> C[SynthesisAgent]
+```
+
+```python
+from swarms import Agent, GraphWorkflow
+
+# Create agents
+research_agent = Agent(
+ agent_name="ResearchAgent",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+analysis_agent = Agent(
+ agent_name="AnalysisAgent",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+synthesis_agent = Agent(
+ agent_name="SynthesisAgent",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Build sequential workflow
+workflow = GraphWorkflow(
+ name="Sequential-Workflow",
+ backend="rustworkx",
+ verbose=True,
+)
+
+workflow.add_node(research_agent)
+workflow.add_node(analysis_agent)
+workflow.add_node(synthesis_agent)
+
+# Create sequential chain
+workflow.add_edge(research_agent, analysis_agent)
+workflow.add_edge(analysis_agent, synthesis_agent)
+
+# Execute
+results = workflow.run("Analyze the impact of AI on healthcare")
+```
+
+**Use Case**: When each agent needs the previous agent's output before proceeding.
+
+## Hierarchical Patterns
+
+### Multi-Level Hierarchy
+
+Hierarchical patterns organize agents into levels, where higher-level agents coordinate lower-level agents.
+
+**Architecture Diagram:**
+
+```mermaid
+graph TB
+ A[Executive] --> B[Research-Head]
+ A --> C[Analysis-Head]
+ B --> D[Researcher-1]
+ B --> E[Researcher-2]
+ C --> F[Analyst-1]
+ C --> G[Analyst-2]
+ D --> H[Synthesis-Agent]
+ E --> H
+ F --> H
+ G --> H
+```
+
+```python
+from swarms import Agent, GraphWorkflow
+
+# Level 1: Executive/Coordinator
+executive = Agent(
+ agent_name="Executive",
+ agent_description="Coordinates overall strategy",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Level 2: Department Heads
+research_head = Agent(
+ agent_name="Research-Head",
+ agent_description="Leads research department",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+analysis_head = Agent(
+ agent_name="Analysis-Head",
+ agent_description="Leads analysis department",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Level 3: Specialists
+researcher_1 = Agent(
+ agent_name="Researcher-1",
+ agent_description="Market research specialist",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+researcher_2 = Agent(
+ agent_name="Researcher-2",
+ agent_description="Technical research specialist",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+analyst_1 = Agent(
+ agent_name="Analyst-1",
+ agent_description="Data analyst",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+analyst_2 = Agent(
+ agent_name="Analyst-2",
+ agent_description="Financial analyst",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Level 4: Synthesis
+synthesis_agent = Agent(
+ agent_name="Synthesis-Agent",
+ agent_description="Synthesizes all outputs",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Build hierarchical workflow
+workflow = GraphWorkflow(
+ name="Hierarchical-Workflow",
+ backend="rustworkx",
+ verbose=True,
+)
+
+# Add all agents
+all_agents = [
+ executive,
+ research_head,
+ analysis_head,
+ researcher_1,
+ researcher_2,
+ analyst_1,
+ analyst_2,
+ synthesis_agent,
+]
+
+for agent in all_agents:
+ workflow.add_node(agent)
+
+# Level 1 -> Level 2
+workflow.add_edge(executive, research_head)
+workflow.add_edge(executive, analysis_head)
+
+# Level 2 -> Level 3
+workflow.add_edges_from_source(
+ research_head,
+ [researcher_1, researcher_2],
+)
+
+workflow.add_edges_from_source(
+ analysis_head,
+ [analyst_1, analyst_2],
+)
+
+# Level 3 -> Level 4 (convergence)
+workflow.add_edges_to_target(
+ [researcher_1, researcher_2, analyst_1, analyst_2],
+ synthesis_agent,
+)
+
+# Execute
+results = workflow.run("Conduct a comprehensive market analysis")
+```
+
+**Use Case**: Organizational structures, multi-level decision making, hierarchical data processing.
+
+### Tree Structure Hierarchy
+
+A tree-like hierarchy where one root agent branches into multiple specialized branches.
+
+**Architecture Diagram:**
+
+```mermaid
+graph TB
+ A[Root-Coordinator] --> B[Tech-Branch-Head]
+ A --> C[Business-Branch-Head]
+ B --> D[Tech-Specialist-1]
+ B --> E[Tech-Specialist-2]
+ C --> F[Business-Specialist-1]
+ C --> G[Business-Specialist-2]
+ D --> H[Final-Synthesis]
+ E --> H
+ F --> H
+ G --> H
+```
+
+```python
+from swarms import Agent, GraphWorkflow
+
+# Root agent
+root_coordinator = Agent(
+ agent_name="Root-Coordinator",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Branch 1: Technical Analysis
+tech_branch_head = Agent(
+ agent_name="Tech-Branch-Head",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+tech_specialist_1 = Agent(
+ agent_name="Tech-Specialist-1",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+tech_specialist_2 = Agent(
+ agent_name="Tech-Specialist-2",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Branch 2: Business Analysis
+business_branch_head = Agent(
+ agent_name="Business-Branch-Head",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+business_specialist_1 = Agent(
+ agent_name="Business-Specialist-1",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+business_specialist_2 = Agent(
+ agent_name="Business-Specialist-2",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Convergence point
+final_synthesis = Agent(
+ agent_name="Final-Synthesis",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+workflow = GraphWorkflow(
+ name="Tree-Hierarchy-Workflow",
+ backend="rustworkx",
+)
+
+all_agents = [
+ root_coordinator,
+ tech_branch_head,
+ tech_specialist_1,
+ tech_specialist_2,
+ business_branch_head,
+ business_specialist_1,
+ business_specialist_2,
+ final_synthesis,
+]
+
+for agent in all_agents:
+ workflow.add_node(agent)
+
+# Root -> Branch heads
+workflow.add_edge(root_coordinator, tech_branch_head)
+workflow.add_edge(root_coordinator, business_branch_head)
+
+# Branch heads -> Specialists
+workflow.add_edges_from_source(
+ tech_branch_head,
+ [tech_specialist_1, tech_specialist_2],
+)
+
+workflow.add_edges_from_source(
+ business_branch_head,
+ [business_specialist_1, business_specialist_2],
+)
+
+# All specialists -> Final synthesis
+workflow.add_edges_to_target(
+ [
+ tech_specialist_1,
+ tech_specialist_2,
+ business_specialist_1,
+ business_specialist_2,
+ ],
+ final_synthesis,
+)
+
+results = workflow.run("Analyze a technology startup from multiple perspectives")
+```
+
+## Concurrent/Parallel Patterns
+
+### Full Parallel Execution
+
+All agents execute simultaneously without dependencies.
+
+**Architecture Diagram:**
+
+```mermaid
+graph TB
+ A[Parallel-Agent-1] --> D[Collector]
+ B[Parallel-Agent-2] --> D
+ C[Parallel-Agent-3] --> D
+```
+
+```python
+from swarms import Agent, GraphWorkflow
+
+# Create independent parallel agents
+parallel_agent_1 = Agent(
+ agent_name="Parallel-Agent-1",
+ agent_description="Independent analysis 1",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+parallel_agent_2 = Agent(
+ agent_name="Parallel-Agent-2",
+ agent_description="Independent analysis 2",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+parallel_agent_3 = Agent(
+ agent_name="Parallel-Agent-3",
+ agent_description="Independent analysis 3",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Convergence agent
+collector = Agent(
+ agent_name="Collector",
+ agent_description="Collects all parallel results",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+workflow = GraphWorkflow(
+ name="Full-Parallel-Workflow",
+ backend="rustworkx",
+)
+
+for agent in [parallel_agent_1, parallel_agent_2, parallel_agent_3, collector]:
+ workflow.add_node(agent)
+
+# All parallel agents feed into collector
+workflow.add_edges_to_target(
+ [parallel_agent_1, parallel_agent_2, parallel_agent_3],
+ collector,
+)
+
+results = workflow.run("Analyze three different aspects of renewable energy")
+```
+
+**Use Case**: Independent analyses, parallel data collection, multi-perspective evaluation.
+
+### Layer-Based Parallel Execution
+
+Agents execute in layers, with all agents in a layer running in parallel.
+
+**Architecture Diagram:**
+
+```mermaid
+graph TB
+ subgraph Layer1["Layer 1: Data Collection"]
+ A1[Data-Collector-1]
+ A2[Data-Collector-2]
+ A3[Data-Collector-3]
+ end
+ subgraph Layer2["Layer 2: Analysis"]
+ B1[Analyst-1]
+ B2[Analyst-2]
+ B3[Analyst-3]
+ end
+ subgraph Layer3["Layer 3: Synthesis"]
+ C[Synthesis]
+ end
+ A1 --> B1
+ A1 --> B2
+ A1 --> B3
+ A2 --> B1
+ A2 --> B2
+ A2 --> B3
+ A3 --> B1
+ A3 --> B2
+ A3 --> B3
+ B1 --> C
+ B2 --> C
+ B3 --> C
+```
+
+```python
+from swarms import Agent, GraphWorkflow
+
+# Layer 1: Data Collection (parallel)
+data_collector_1 = Agent(
+ agent_name="Data-Collector-1",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+data_collector_2 = Agent(
+ agent_name="Data-Collector-2",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+data_collector_3 = Agent(
+ agent_name="Data-Collector-3",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Layer 2: Analysis (parallel, depends on Layer 1)
+analyst_1 = Agent(
+ agent_name="Analyst-1",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+analyst_2 = Agent(
+ agent_name="Analyst-2",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+analyst_3 = Agent(
+ agent_name="Analyst-3",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Layer 3: Synthesis (depends on Layer 2)
+synthesis = Agent(
+ agent_name="Synthesis",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+workflow = GraphWorkflow(
+ name="Layer-Based-Parallel-Workflow",
+ backend="rustworkx",
+)
+
+all_agents = [
+ data_collector_1,
+ data_collector_2,
+ data_collector_3,
+ analyst_1,
+ analyst_2,
+ analyst_3,
+ synthesis,
+]
+
+for agent in all_agents:
+ workflow.add_node(agent)
+
+# Layer 1 -> Layer 2: Full mesh connection
+workflow.add_parallel_chain(
+ [data_collector_1, data_collector_2, data_collector_3],
+ [analyst_1, analyst_2, analyst_3],
+)
+
+# Layer 2 -> Layer 3: Convergence
+workflow.add_edges_to_target(
+ [analyst_1, analyst_2, analyst_3],
+ synthesis,
+)
+
+results = workflow.run("Process and analyze data in parallel layers")
+```
+
+**Use Case**: Pipeline processing, multi-stage analysis, batch processing workflows.
+
+## Majority Voting Patterns
+
+### Simple Majority Vote
+
+Multiple agents vote on a decision, with a majority vote aggregator.
+
+**Architecture Diagram:**
+
+```mermaid
+graph TB
+ A[Voter-1] --> F[Vote-Aggregator]
+ B[Voter-2] --> F
+ C[Voter-3] --> F
+ D[Voter-4] --> F
+ E[Voter-5] --> F
+```
+
+```python
+from swarms import Agent, GraphWorkflow
+
+# Voting agents
+voter_1 = Agent(
+ agent_name="Voter-1",
+ agent_description="Provides vote/opinion 1",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+voter_2 = Agent(
+ agent_name="Voter-2",
+ agent_description="Provides vote/opinion 2",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+voter_3 = Agent(
+ agent_name="Voter-3",
+ agent_description="Provides vote/opinion 3",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+voter_4 = Agent(
+ agent_name="Voter-4",
+ agent_description="Provides vote/opinion 4",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+voter_5 = Agent(
+ agent_name="Voter-5",
+ agent_description="Provides vote/opinion 5",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Vote aggregator (implements majority voting logic)
+vote_aggregator = Agent(
+ agent_name="Vote-Aggregator",
+ agent_description="Aggregates votes and determines majority decision",
+ system_prompt="""You are a vote aggregator. Analyze all the votes/opinions provided
+ and determine the majority consensus. Provide a clear summary of the majority decision.""",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+workflow = GraphWorkflow(
+ name="Majority-Voting-Workflow",
+ backend="rustworkx",
+)
+
+all_agents = [voter_1, voter_2, voter_3, voter_4, voter_5, vote_aggregator]
+
+for agent in all_agents:
+ workflow.add_node(agent)
+
+# All voters -> Aggregator
+workflow.add_edges_to_target(
+ [voter_1, voter_2, voter_3, voter_4, voter_5],
+ vote_aggregator,
+)
+
+results = workflow.run(
+ "Should we invest in renewable energy stocks? Provide your vote and reasoning."
+)
+```
+
+**Use Case**: Decision making, consensus building, quality assurance, validation.
+
+### Weighted Majority Vote
+
+Similar to simple majority vote but with weighted voters.
+
+**Architecture Diagram:**
+
+```mermaid
+graph TB
+ A[Expert-Voter-1
Weight: 2x] --> F[Weighted-Aggregator]
+ B[Expert-Voter-2
Weight: 2x] --> F
+ C[Regular-Voter-1
Weight: 1x] --> F
+ D[Regular-Voter-2
Weight: 1x] --> F
+ E[Regular-Voter-3
Weight: 1x] --> F
+```
+
+```python
+from swarms import Agent, GraphWorkflow
+
+# Expert voters (higher weight)
+expert_voter_1 = Agent(
+ agent_name="Expert-Voter-1",
+ agent_description="Senior expert with high weight",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+expert_voter_2 = Agent(
+ agent_name="Expert-Voter-2",
+ agent_description="Senior expert with high weight",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Regular voters (standard weight)
+regular_voter_1 = Agent(
+ agent_name="Regular-Voter-1",
+ agent_description="Regular voter",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+regular_voter_2 = Agent(
+ agent_name="Regular-Voter-2",
+ agent_description="Regular voter",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+regular_voter_3 = Agent(
+ agent_name="Regular-Voter-3",
+ agent_description="Regular voter",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Weighted aggregator
+weighted_aggregator = Agent(
+ agent_name="Weighted-Aggregator",
+ agent_description="Aggregates votes with expert weighting",
+ system_prompt="""You are a weighted vote aggregator. Expert voters (Expert-Voter-1, Expert-Voter-2)
+ have 2x weight compared to regular voters. Analyze all votes and determine the weighted majority decision.""",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+workflow = GraphWorkflow(
+ name="Weighted-Majority-Voting-Workflow",
+ backend="rustworkx",
+)
+
+all_agents = [
+ expert_voter_1,
+ expert_voter_2,
+ regular_voter_1,
+ regular_voter_2,
+ regular_voter_3,
+ weighted_aggregator,
+]
+
+for agent in all_agents:
+ workflow.add_node(agent)
+
+# All voters -> Weighted aggregator
+workflow.add_edges_to_target(
+ [
+ expert_voter_1,
+ expert_voter_2,
+ regular_voter_1,
+ regular_voter_2,
+ regular_voter_3,
+ ],
+ weighted_aggregator,
+)
+
+results = workflow.run(
+ "Evaluate a business proposal. Experts should provide detailed analysis, regular voters provide standard evaluation."
+)
+```
+
+## Fan-Out/Fan-In Patterns
+
+### Simple Fan-Out
+
+One source agent distributes work to multiple target agents.
+
+**Architecture Diagram:**
+
+```mermaid
+graph TB
+ A[Coordinator] --> B[Specialist-1]
+ A --> C[Specialist-2]
+ A --> D[Specialist-3]
+```
+
+```python
+from swarms import Agent, GraphWorkflow
+
+# Source agent
+coordinator = Agent(
+ agent_name="Coordinator",
+ agent_description="Distributes tasks to specialists",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Target agents (specialists)
+specialist_1 = Agent(
+ agent_name="Specialist-1",
+ agent_description="Technical specialist",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+specialist_2 = Agent(
+ agent_name="Specialist-2",
+ agent_description="Business specialist",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+specialist_3 = Agent(
+ agent_name="Specialist-3",
+ agent_description="Financial specialist",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+workflow = GraphWorkflow(
+ name="Fan-Out-Workflow",
+ backend="rustworkx",
+)
+
+for agent in [coordinator, specialist_1, specialist_2, specialist_3]:
+ workflow.add_node(agent)
+
+# Fan-out: One source to multiple targets
+workflow.add_edges_from_source(
+ coordinator,
+ [specialist_1, specialist_2, specialist_3],
+)
+
+results = workflow.run("Analyze a startup from technical, business, and financial perspectives")
+```
+
+**Use Case**: Task distribution, parallel specialization, workload splitting.
+
+### Simple Fan-In
+
+Multiple source agents converge to a single target agent.
+
+**Architecture Diagram:**
+
+```mermaid
+graph TB
+ A[Analyst-1] --> D[Synthesis]
+ B[Analyst-2] --> D
+ C[Analyst-3] --> D
+```
+
+```python
+from swarms import Agent, GraphWorkflow
+
+# Source agents
+analyst_1 = Agent(
+ agent_name="Analyst-1",
+ agent_description="Technical analyst",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+analyst_2 = Agent(
+ agent_name="Analyst-2",
+ agent_description="Market analyst",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+analyst_3 = Agent(
+ agent_name="Analyst-3",
+ agent_description="Financial analyst",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Target agent (synthesis)
+synthesis = Agent(
+ agent_name="Synthesis",
+ agent_description="Synthesizes all analyses",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+workflow = GraphWorkflow(
+ name="Fan-In-Workflow",
+ backend="rustworkx",
+)
+
+for agent in [analyst_1, analyst_2, analyst_3, synthesis]:
+ workflow.add_node(agent)
+
+# Fan-in: Multiple sources to one target
+workflow.add_edges_to_target(
+ [analyst_1, analyst_2, analyst_3],
+ synthesis,
+)
+
+results = workflow.run("Provide comprehensive analysis from multiple perspectives")
+```
+
+**Use Case**: Result aggregation, synthesis, convergence of parallel work.
+
+### Fan-Out Followed by Fan-In
+
+A common pattern: distribute work, then aggregate results.
+
+**Architecture Diagram:**
+
+```mermaid
+graph TB
+ A[Coordinator] --> B[Worker-1]
+ A --> C[Worker-2]
+ A --> D[Worker-3]
+ A --> E[Worker-4]
+ B --> F[Aggregator]
+ C --> F
+ D --> F
+ E --> F
+```
+
+```python
+from swarms import Agent, GraphWorkflow
+
+# Initial coordinator
+coordinator = Agent(
+ agent_name="Coordinator",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Parallel workers
+worker_1 = Agent(
+ agent_name="Worker-1",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+worker_2 = Agent(
+ agent_name="Worker-2",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+worker_3 = Agent(
+ agent_name="Worker-3",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+worker_4 = Agent(
+ agent_name="Worker-4",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Final aggregator
+aggregator = Agent(
+ agent_name="Aggregator",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+workflow = GraphWorkflow(
+ name="Fan-Out-Fan-In-Workflow",
+ backend="rustworkx",
+)
+
+all_agents = [
+ coordinator,
+ worker_1,
+ worker_2,
+ worker_3,
+ worker_4,
+ aggregator,
+]
+
+for agent in all_agents:
+ workflow.add_node(agent)
+
+# Fan-out: Coordinator -> Workers
+workflow.add_edges_from_source(
+ coordinator,
+ [worker_1, worker_2, worker_3, worker_4],
+)
+
+# Fan-in: Workers -> Aggregator
+workflow.add_edges_to_target(
+ [worker_1, worker_2, worker_3, worker_4],
+ aggregator,
+)
+
+results = workflow.run("Distribute research tasks and synthesize results")
+```
+
+**Use Case**: Map-reduce patterns, parallel processing with aggregation, distributed analysis.
+
+## Sequential Patterns
+
+### Linear Chain
+
+Simple sequential execution where each agent depends on the previous one.
+
+**Architecture Diagram:**
+
+```mermaid
+graph LR
+ A[Agent-1] --> B[Agent-2]
+ B --> C[Agent-3]
+ C --> D[Agent-4]
+ D --> E[Agent-5]
+```
+
+```python
+from swarms import Agent, GraphWorkflow
+
+agents = [
+ Agent(
+ agent_name=f"Agent-{i+1}",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ )
+ for i in range(5)
+]
+
+workflow = GraphWorkflow(
+ name="Linear-Chain-Workflow",
+ backend="rustworkx",
+)
+
+for agent in agents:
+ workflow.add_node(agent)
+
+# Create linear chain
+for i in range(len(agents) - 1):
+ workflow.add_edge(agents[i], agents[i + 1])
+
+results = workflow.run("Process data through a linear pipeline")
+```
+
+### Sequential with Branching
+
+Sequential flow with conditional branching.
+
+**Architecture Diagram:**
+
+```mermaid
+graph TB
+ A[Initial] --> B[Branch-1-Agent]
+ A --> C[Branch-2-Agent]
+ B --> D[Branch-1-Continuation]
+ C --> E[Branch-2-Continuation]
+ D --> F[Final]
+ E --> F
+```
+
+```python
+from swarms import Agent, GraphWorkflow
+
+# Initial agent
+initial = Agent(
+ agent_name="Initial",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Branch 1
+branch_1_agent = Agent(
+ agent_name="Branch-1-Agent",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+branch_1_continuation = Agent(
+ agent_name="Branch-1-Continuation",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Branch 2
+branch_2_agent = Agent(
+ agent_name="Branch-2-Agent",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+branch_2_continuation = Agent(
+ agent_name="Branch-2-Continuation",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Convergence
+final = Agent(
+ agent_name="Final",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+workflow = GraphWorkflow(
+ name="Sequential-Branching-Workflow",
+ backend="rustworkx",
+)
+
+all_agents = [
+ initial,
+ branch_1_agent,
+ branch_1_continuation,
+ branch_2_agent,
+ branch_2_continuation,
+ final,
+]
+
+for agent in all_agents:
+ workflow.add_node(agent)
+
+# Initial -> Branches
+workflow.add_edge(initial, branch_1_agent)
+workflow.add_edge(initial, branch_2_agent)
+
+# Branch continuations
+workflow.add_edge(branch_1_agent, branch_1_continuation)
+workflow.add_edge(branch_2_agent, branch_2_continuation)
+
+# Convergence
+workflow.add_edge(branch_1_continuation, final)
+workflow.add_edge(branch_2_continuation, final)
+
+results = workflow.run("Process through branching paths")
+```
+
+## Advanced Patterns
+
+### Pipeline with Validation
+
+Sequential pipeline with validation checkpoints.
+
+**Architecture Diagram:**
+
+```mermaid
+graph LR
+ A[Data-Collector] --> B[Validator-1]
+ B --> C[Processor]
+ C --> D[Validator-2]
+ D --> E[Finalizer]
+```
+
+```python
+from swarms import Agent, GraphWorkflow
+
+# Pipeline stages
+data_collector = Agent(
+ agent_name="Data-Collector",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+validator_1 = Agent(
+ agent_name="Validator-1",
+ agent_description="Validates data quality",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+processor = Agent(
+ agent_name="Processor",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+validator_2 = Agent(
+ agent_name="Validator-2",
+ agent_description="Validates processing results",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+finalizer = Agent(
+ agent_name="Finalizer",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+workflow = GraphWorkflow(
+ name="Pipeline-With-Validation",
+ backend="rustworkx",
+)
+
+for agent in [data_collector, validator_1, processor, validator_2, finalizer]:
+ workflow.add_node(agent)
+
+# Sequential pipeline with validation checkpoints
+workflow.add_edge(data_collector, validator_1)
+workflow.add_edge(validator_1, processor)
+workflow.add_edge(processor, validator_2)
+workflow.add_edge(validator_2, finalizer)
+
+results = workflow.run("Process data with quality checkpoints")
+```
+
+### Multi-Stage Review Process
+
+Multiple review stages before final approval.
+
+**Architecture Diagram:**
+
+```mermaid
+graph TB
+ A[Submitter] --> B[Reviewer-1A]
+ A --> C[Reviewer-1B]
+ B --> D[Stage-1-Aggregator]
+ C --> D
+ D --> E[Reviewer-2A]
+ D --> F[Reviewer-2B]
+ E --> G[Stage-2-Aggregator]
+ F --> G
+ G --> H[Approver]
+```
+
+```python
+from swarms import Agent, GraphWorkflow
+
+# Initial submission
+submitter = Agent(
+ agent_name="Submitter",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Review stage 1 (parallel reviewers)
+reviewer_1a = Agent(
+ agent_name="Reviewer-1A",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+reviewer_1b = Agent(
+ agent_name="Reviewer-1B",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Stage 1 aggregator
+stage_1_aggregator = Agent(
+ agent_name="Stage-1-Aggregator",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Review stage 2
+reviewer_2a = Agent(
+ agent_name="Reviewer-2A",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+reviewer_2b = Agent(
+ agent_name="Reviewer-2B",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Stage 2 aggregator
+stage_2_aggregator = Agent(
+ agent_name="Stage-2-Aggregator",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Final approver
+approver = Agent(
+ agent_name="Approver",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+workflow = GraphWorkflow(
+ name="Multi-Stage-Review",
+ backend="rustworkx",
+)
+
+all_agents = [
+ submitter,
+ reviewer_1a,
+ reviewer_1b,
+ stage_1_aggregator,
+ reviewer_2a,
+ reviewer_2b,
+ stage_2_aggregator,
+ approver,
+]
+
+for agent in all_agents:
+ workflow.add_node(agent)
+
+# Stage 1: Parallel review
+workflow.add_edge(submitter, reviewer_1a)
+workflow.add_edge(submitter, reviewer_1b)
+workflow.add_edges_to_target([reviewer_1a, reviewer_1b], stage_1_aggregator)
+
+# Stage 2: Parallel review
+workflow.add_edge(stage_1_aggregator, reviewer_2a)
+workflow.add_edge(stage_1_aggregator, reviewer_2b)
+workflow.add_edges_to_target([reviewer_2a, reviewer_2b], stage_2_aggregator)
+
+# Final approval
+workflow.add_edge(stage_2_aggregator, approver)
+
+results = workflow.run("Review and approve a proposal through multiple stages")
+```
+
+### Circular/Iterative Pattern
+
+Agents form a cycle for iterative refinement.
+
+**Architecture Diagram:**
+
+```mermaid
+graph LR
+ A[Agent-1] --> B[Agent-2]
+ B --> C[Agent-3]
+ C --> D[Exit-Checker]
+ D -.->|Iterate| A
+```
+
+```python
+from swarms import Agent, GraphWorkflow
+
+# Create iterative refinement agents
+agent_1 = Agent(
+ agent_name="Agent-1",
+ agent_description="First refinement stage",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+agent_2 = Agent(
+ agent_name="Agent-2",
+ agent_description="Second refinement stage",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+agent_3 = Agent(
+ agent_name="Agent-3",
+ agent_description="Third refinement stage",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Exit condition checker
+exit_checker = Agent(
+ agent_name="Exit-Checker",
+ agent_description="Checks if refinement is complete",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+workflow = GraphWorkflow(
+ name="Iterative-Refinement",
+ backend="rustworkx",
+ max_loops=3, # Limit iterations
+)
+
+for agent in [agent_1, agent_2, agent_3, exit_checker]:
+ workflow.add_node(agent)
+
+# Circular refinement
+workflow.add_edge(agent_1, agent_2)
+workflow.add_edge(agent_2, agent_3)
+workflow.add_edge(agent_3, exit_checker)
+# Note: For true iteration, you'd need to add edge back to agent_1
+# This is a simplified example
+
+results = workflow.run("Iteratively refine a document")
+```
+
+### Star Pattern
+
+Central hub agent coordinates with multiple spoke agents.
+
+**Architecture Diagram:**
+
+```mermaid
+graph TB
+ A[Hub] --> B[Spoke-1]
+ A --> C[Spoke-2]
+ A --> D[Spoke-3]
+ A --> E[Spoke-4]
+ B --> A
+ C --> A
+ D --> A
+ E --> A
+```
+
+```python
+from swarms import Agent, GraphWorkflow
+
+# Central hub
+hub = Agent(
+ agent_name="Hub",
+ agent_description="Central coordinator",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+# Spoke agents
+spoke_1 = Agent(
+ agent_name="Spoke-1",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+spoke_2 = Agent(
+ agent_name="Spoke-2",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+spoke_3 = Agent(
+ agent_name="Spoke-3",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+spoke_4 = Agent(
+ agent_name="Spoke-4",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+)
+
+workflow = GraphWorkflow(
+ name="Star-Pattern-Workflow",
+ backend="rustworkx",
+)
+
+for agent in [hub, spoke_1, spoke_2, spoke_3, spoke_4]:
+ workflow.add_node(agent)
+
+# Hub -> Spokes (fan-out)
+workflow.add_edges_from_source(
+ hub,
+ [spoke_1, spoke_2, spoke_3, spoke_4],
+)
+
+# Spokes -> Hub (fan-in)
+workflow.add_edges_to_target(
+ [spoke_1, spoke_2, spoke_3, spoke_4],
+ hub,
+)
+
+results = workflow.run("Coordinate work through a central hub")
+```
+
+## Performance Optimization
+
+### Compilation Best Practices
+
+Always compile workflows before execution for optimal performance:
+
+```python
+workflow = GraphWorkflow(
+ name="Optimized-Workflow",
+ backend="rustworkx",
+ auto_compile=True, # Automatic compilation
+)
+
+# Or manually compile
+workflow.compile()
+
+# Check compilation status
+status = workflow.get_compilation_status()
+print(f"Compiled: {status['is_compiled']}")
+print(f"Layers: {status['cached_layers_count']}")
+```
+
+### Large-Scale Workflow Tips
+
+For workflows with 100+ agents:
+
+1. **Use rustworkx backend** for better performance
+2. **Compile before execution** to cache topological layers
+3. **Use parallel patterns** to maximize throughput
+4. **Monitor compilation status** to ensure optimization
+
+```python
+# Large-scale workflow example
+workflow = GraphWorkflow(
+ name="Large-Scale-Workflow",
+ backend="rustworkx", # Essential for large graphs
+ auto_compile=True,
+ verbose=True, # Monitor performance
+)
+
+# Add many agents...
+# Use parallel patterns for efficiency
+
+# Check performance
+status = workflow.get_compilation_status()
+print(f"Max workers: {status['max_workers']}")
+print(f"Layers: {status['cached_layers_count']}")
+```
+
+### Visualization for Debugging
+
+Visualize workflows to understand structure and optimize:
+
+```python
+# Generate visualization
+output_file = workflow.visualize(
+ format="png",
+ show_summary=True, # Shows parallel patterns
+ view=True,
+)
+
+# Or simple text visualization
+workflow.visualize_simple()
+```
+
+## Conclusion
+
+GraphWorkflow with rustworkx backend provides a powerful framework for implementing complex multi-agent patterns. Key takeaways:
+
+1. **Choose the right pattern** for your use case
+2. **Use rustworkx** for large-scale workflows (100+ nodes)
+3. **Leverage parallel patterns** for performance
+4. **Compile workflows** before execution
+5. **Visualize** to understand and debug workflows
+
+For more examples, see the [rustworkx examples directory](https://github.com/kyegomez/swarms/tree/master/examples/multi_agent/graphworkflow_examples/rustworkx_examples).
diff --git a/docs/swarms/structs/graph_workflow.md b/docs/swarms/structs/graph_workflow.md
index ef48d8d0..f0182be3 100644
--- a/docs/swarms/structs/graph_workflow.md
+++ b/docs/swarms/structs/graph_workflow.md
@@ -12,6 +12,7 @@ Key features:
|------------------------|-----------------------------------------------------------------------------------------------|
| **Agent-based nodes** | Each node represents an agent that can process tasks |
| **Directed graph structure** | Edges define the flow of data between agents |
+| **Dual backend support** | Choose between NetworkX (compatibility) or Rustworkx (performance) backends |
| **Parallel execution** | Multiple agents can run simultaneously within layers |
| **Automatic compilation** | Optimizes workflow structure for efficient execution |
| **Rich visualization** | Generate visual representations using Graphviz |
@@ -25,37 +26,40 @@ graph TB
subgraph "GraphWorkflow Architecture"
A[GraphWorkflow] --> B[Node Collection]
A --> C[Edge Collection]
- A --> D[NetworkX Graph]
+ A --> D[Graph Backend]
A --> E[Execution Engine]
B --> F[Agent Nodes]
C --> G[Directed Edges]
- D --> H[Topological Sort]
- E --> I[Parallel Execution]
- E --> J[Layer Processing]
+ D --> H[NetworkX Backend]
+ D --> I[Rustworkx Backend]
+ D --> J[Topological Sort]
+ E --> K[Parallel Execution]
+ E --> L[Layer Processing]
subgraph "Node Types"
- F --> K[Agent Node]
- K --> L[Agent Instance]
- K --> M[Node Metadata]
+ F --> M[Agent Node]
+ M --> N[Agent Instance]
+ M --> O[Node Metadata]
end
subgraph "Edge Types"
- G --> N[Simple Edge]
- G --> O[Fan-out Edge]
- G --> P[Fan-in Edge]
- G --> Q[Parallel Chain]
+ G --> P[Simple Edge]
+ G --> Q[Fan-out Edge]
+ G --> R[Fan-in Edge]
+ G --> S[Parallel Chain]
end
subgraph "Execution Patterns"
- I --> R[Thread Pool]
- I --> S[Concurrent Futures]
- J --> T[Layer-by-layer]
- J --> U[Dependency Resolution]
+ K --> T[Thread Pool]
+ K --> U[Concurrent Futures]
+ L --> V[Layer-by-layer]
+ L --> W[Dependency Resolution]
end
end
```
+
## Class Reference
| Parameter | Type | Description | Default |
@@ -71,6 +75,70 @@ graph TB
| `task` | `Optional[str]` | The task to be executed by the workflow | `None` |
| `auto_compile` | `bool` | Whether to automatically compile the workflow | `True` |
| `verbose` | `bool` | Whether to enable detailed logging | `False` |
+| `backend` | `str` | Graph backend to use ("networkx" or "rustworkx") | `"networkx"` |
+
+## Graph Backends
+
+GraphWorkflow supports two graph backend implementations, each with different performance characteristics:
+
+### NetworkX Backend (Default)
+
+The **NetworkX** backend is the default and most widely compatible option. It provides:
+
+| Feature | Description |
+|---------------------|---------------------------------------------------------|
+| ✅ Full compatibility | Works out of the box with no additional dependencies |
+| ✅ Mature ecosystem | Well-tested and stable |
+| ✅ Rich features | Comprehensive graph algorithms and operations |
+| ✅ Python-native | Pure Python implementation |
+
+**Use NetworkX when:**
+
+- You need maximum compatibility
+
+- Working with small to medium-sized graphs (< 1000 nodes)
+
+- You want zero additional dependencies
+
+### Rustworkx Backend (High Performance)
+
+The **Rustworkx** backend provides significant performance improvements for large graphs:
+
+| Feature | Description |
+|--------------------|-----------------------------------------------------------------|
+| ⚡ High performance| Rust-based implementation for faster operations |
+| ⚡ Memory efficient| Optimized for large-scale graphs |
+| ⚡ Scalable | Better performance with graphs containing 1000+ nodes |
+| ⚡ Same API | Drop-in replacement with identical interface |
+
+**Use Rustworkx when:**
+
+- Working with large graphs (1000+ nodes)
+
+- Performance is critical
+
+- You can install additional dependencies
+
+**Installation:**
+```bash
+pip install rustworkx
+```
+
+**Note:** If rustworkx is not installed and you specify `backend="rustworkx"`, GraphWorkflow will automatically fall back to NetworkX with a warning.
+
+### Backend Selection
+
+Both backends implement the same `GraphBackend` interface, ensuring complete API compatibility. You can switch between backends without changing your code:
+
+```python
+# Use NetworkX (default)
+workflow = GraphWorkflow(backend="networkx")
+
+# Use Rustworkx for better performance
+workflow = GraphWorkflow(backend="rustworkx")
+```
+
+The backend choice is transparent to the rest of the API - all methods work identically regardless of which backend is used.
### Core Methods
@@ -455,7 +523,7 @@ Constructs a workflow from a list of agents and connections.
| `entry_points` | `List[str]` | List of entry point node IDs | `None` |
| `end_points` | `List[str]` | List of end point node IDs | `None` |
| `task` | `str` | Task to be executed by the workflow | `None` |
-| `**kwargs` | `Any` | Additional keyword arguments | `{}` |
+| `**kwargs` | `Any` | Additional keyword arguments (e.g., `backend`, `verbose`, `auto_compile`) | `{}` |
**Returns:**
@@ -464,6 +532,7 @@ Constructs a workflow from a list of agents and connections.
**Example:**
```python
+# Using NetworkX backend (default)
workflow = GraphWorkflow.from_spec(
agents=[agent1, agent2, agent3],
edges=[
@@ -473,10 +542,56 @@ workflow = GraphWorkflow.from_spec(
],
task="Analyze market data"
)
+
+# Using Rustworkx backend for better performance
+workflow = GraphWorkflow.from_spec(
+ agents=[agent1, agent2, agent3],
+ edges=[
+ ("agent1", "agent2"),
+ ("agent2", "agent3"),
+ ],
+ task="Analyze market data",
+ backend="rustworkx" # Specify backend via kwargs
+)
```
## Examples
+### Using Rustworkx Backend for Performance
+
+```python
+from swarms import Agent, GraphWorkflow
+
+# Create agents
+research_agent = Agent(
+ agent_name="ResearchAgent",
+ model_name="gpt-4",
+ max_loops=1
+)
+
+analysis_agent = Agent(
+ agent_name="AnalysisAgent",
+ model_name="gpt-4",
+ max_loops=1
+)
+
+# Build workflow with rustworkx backend for better performance
+workflow = GraphWorkflow(
+ name="High-Performance-Workflow",
+ backend="rustworkx" # Use rustworkx backend
+)
+
+workflow.add_node(research_agent)
+workflow.add_node(analysis_agent)
+workflow.add_edge("ResearchAgent", "AnalysisAgent")
+
+# Execute - backend is transparent to the API
+results = workflow.run("What are the latest trends in AI?")
+print(results)
+```
+
+**Note:** Make sure to install rustworkx first: `pip install rustworkx`
+
### Basic Sequential Workflow
```python
@@ -667,6 +782,46 @@ loaded_workflow = GraphWorkflow.load_from_file(
new_results = loaded_workflow.run("Continue with quantum cryptography analysis")
```
+### Large-Scale Workflow with Rustworkx
+
+```python
+from swarms import Agent, GraphWorkflow
+
+# Create a large workflow with many agents
+# Rustworkx backend provides better performance for large graphs
+workflow = GraphWorkflow(
+ name="Large-Scale-Workflow",
+ backend="rustworkx", # Use rustworkx for better performance
+ verbose=True
+)
+
+# Create many agents (e.g., for parallel data processing)
+agents = []
+for i in range(50):
+ agent = Agent(
+ agent_name=f"Processor{i}",
+ model_name="gpt-4",
+ max_loops=1
+ )
+ agents.append(agent)
+ workflow.add_node(agent)
+
+# Create complex interconnections
+# Rustworkx handles this efficiently
+for i in range(0, 50, 10):
+ source_agents = [f"Processor{j}" for j in range(i, min(i+10, 50))]
+ target_agents = [f"Processor{j}" for j in range(i+10, min(i+20, 50))]
+ if target_agents:
+ workflow.add_parallel_chain(source_agents, target_agents)
+
+# Compile and execute
+workflow.compile()
+status = workflow.get_compilation_status()
+print(f"Compiled workflow with {status['cached_layers_count']} layers")
+
+results = workflow.run("Process large dataset in parallel")
+```
+
### Advanced Pattern Detection
```python
@@ -770,7 +925,8 @@ The `GraphWorkflow` class provides a powerful and flexible framework for orchest
|-----------------|--------------------------------------------------------------------------------------------------|
| **Scalability** | Supports workflows with hundreds of agents through efficient parallel execution |
| **Flexibility** | Multiple connection patterns (sequential, fan-out, fan-in, parallel chains) |
-| **Performance** | Automatic compilation and optimization for faster execution |
+| **Performance** | Automatic compilation and optimization for faster execution; rustworkx backend for large-scale graphs |
+| **Backend Choice** | Choose between NetworkX (compatibility) or Rustworkx (performance) based on your needs |
| **Visualization** | Rich visual representations for workflow understanding and debugging |
| **Persistence** | Complete serialization and deserialization capabilities |
| **Error Handling** | Comprehensive error handling and recovery mechanisms |
@@ -793,10 +949,28 @@ The `GraphWorkflow` class provides a powerful and flexible framework for orchest
|---------------------------------------|------------------------------------------------------------------|
| **Use meaningful agent names** | Helps with debugging and visualization |
| **Leverage parallel patterns** | Use fan-out and fan-in for better performance |
+| **Choose the right backend** | Use rustworkx for large graphs (1000+ nodes), networkx for smaller graphs |
| **Compile workflows** | Always compile before execution for optimal performance |
| **Monitor execution** | Use verbose mode and status reporting for debugging |
| **Save important workflows** | Use serialization for workflow persistence |
| **Handle errors gracefully** | Implement proper error handling and recovery |
| **Visualize complex workflows** | Use visualization to understand and debug workflows |
+### Backend Performance Considerations
+
+When choosing between NetworkX and Rustworkx backends:
+
+| Graph Size | Recommended Backend | Reason |
+|------------|-------------------|--------|
+| < 100 nodes | NetworkX | Minimal overhead, no extra dependencies |
+| 100-1000 nodes | NetworkX or Rustworkx | Both perform well, choose based on dependency preferences |
+| 1000+ nodes | Rustworkx | Significant performance benefits for large graphs |
+| Very large graphs (10k+ nodes) | Rustworkx | Essential for acceptable performance |
+
+**Performance Tips:**
+- Rustworkx provides 2-10x speedup for topological operations on large graphs
+- Both backends support the same features and API
+- You can switch backends without code changes
+- Rustworkx uses less memory for large graphs
+
The GraphWorkflow system represents a significant advancement in multi-agent orchestration, providing the tools needed to build complex, scalable, and maintainable AI workflows.
\ No newline at end of file
diff --git a/docs/swarms/structs/llm_council.md b/docs/swarms/structs/llm_council.md
new file mode 100644
index 00000000..e1092bb4
--- /dev/null
+++ b/docs/swarms/structs/llm_council.md
@@ -0,0 +1,534 @@
+# LLM Council Class Documentation
+
+```mermaid
+flowchart TD
+ A[User Query] --> B[Council Members]
+
+ subgraph "Council Members"
+ C1[GPT-5.1-Councilor]
+ C2[Gemini-3-Pro-Councilor]
+ C3[Claude-Sonnet-4.5-Councilor]
+ C4[Grok-4-Councilor]
+ end
+
+ B --> C1
+ B --> C2
+ B --> C3
+ B --> C4
+
+ C1 --> D[Responses]
+ C2 --> D
+ C3 --> D
+ C4 --> D
+
+ D --> E[Anonymize & Evaluate]
+ E --> F[Chairman Synthesis]
+ F --> G[Final Response]
+
+```
+
+The `LLMCouncil` class orchestrates multiple specialized LLM agents to collaboratively answer queries through a structured peer review and synthesis process. Inspired by Andrej Karpathy's llm-council implementation, this architecture demonstrates how different models evaluate and rank each other's work, often selecting responses from other models as superior to their own.
+
+The class automatically tracks all agent messages in a `Conversation` object and formats output using `history_output_formatter`, providing flexible output formats including dictionaries, lists, strings, JSON, YAML, and more.
+
+## Workflow Overview
+
+The LLM Council follows a four-step process:
+
+1. **Parallel Response Generation**: All council members independently respond to the user query
+2. **Anonymization**: Responses are anonymized with random IDs (A, B, C, D, etc.) to ensure objective evaluation
+3. **Peer Review**: Each member evaluates and ranks all responses (including potentially their own)
+4. **Synthesis**: The Chairman agent synthesizes all responses and evaluations into a final comprehensive answer
+
+## Class Definition
+
+### LLMCouncil
+
+```python
+class LLMCouncil:
+```
+
+### Attributes
+
+| Attribute | Type | Description | Default |
+|-----------|------|-------------|---------|
+| `council_members` | `List[Agent]` | List of Agent instances representing council members | `None` (creates default council) |
+| `chairman` | `Agent` | The Chairman agent responsible for synthesizing responses | Created during initialization |
+| `conversation` | `Conversation` | Conversation object tracking all messages throughout the workflow | Created during initialization |
+| `output_type` | `HistoryOutputType` | Format for the output (e.g., "dict", "list", "string", "json", "yaml") | `"dict"` |
+| `verbose` | `bool` | Whether to print progress and intermediate results | `True` |
+
+## Methods
+
+### `__init__`
+
+Initializes the LLM Council with council members and a Chairman agent.
+
+#### Parameters
+
+| Parameter | Type | Default | Description |
+|-----------|------|---------|-------------|
+| `id` | `str` | `swarm_id()` | Unique identifier for the council instance. |
+| `name` | `str` | `"LLM Council"` | Name of the council instance. |
+| `description` | `str` | `"A collaborative council..."` | Description of the council's purpose. |
+| `council_members` | `Optional[List[Agent]]` | `None` | List of Agent instances representing council members. If `None`, creates default council with GPT-5.1, Gemini 3 Pro, Claude Sonnet 4.5, and Grok-4. |
+| `chairman_model` | `str` | `"gpt-5.1"` | Model name for the Chairman agent that synthesizes responses. |
+| `verbose` | `bool` | `True` | Whether to print progress and intermediate results. |
+| `output_type` | `HistoryOutputType` | `"dict"` | Format for the output. Options: "list", "dict", "string", "final", "json", "yaml", "xml", "dict-all-except-first", "str-all-except-first", "dict-final", "list-final". |
+
+#### Returns
+
+| Type | Description |
+|------|-------------|
+| `LLMCouncil` | Initialized LLM Council instance. |
+
+#### Description
+
+Creates an LLM Council instance with specialized council members. If no members are provided, it creates a default council consisting of:
+
+| Council Member | Description |
+|---------------------------------|------------------------------------------|
+| **GPT-5.1-Councilor** | Analytical and comprehensive responses |
+| **Gemini-3-Pro-Councilor** | Concise and well-processed responses |
+| **Claude-Sonnet-4.5-Councilor** | Thoughtful and balanced responses |
+| **Grok-4-Councilor** | Creative and innovative responses |
+
+The Chairman agent is automatically created with a specialized prompt for synthesizing responses. A `Conversation` object is also initialized to track all messages throughout the workflow, including user queries, council member responses, evaluations, and the final synthesis.
+
+#### Example Usage
+
+```python
+from swarms.structs.llm_council import LLMCouncil
+
+# Create council with default members
+council = LLMCouncil(verbose=True)
+
+# Create council with custom members and output format
+from swarms import Agent
+custom_members = [
+ Agent(agent_name="Expert-1", model_name="gpt-4", max_loops=1),
+ Agent(agent_name="Expert-2", model_name="claude-3-opus", max_loops=1),
+]
+council = LLMCouncil(
+ council_members=custom_members,
+ chairman_model="gpt-4",
+ verbose=True,
+ output_type="json" # Output as JSON string
+)
+```
+
+---
+
+### `run`
+
+Executes the full LLM Council workflow: parallel responses, anonymization, peer review, and synthesis. All messages are tracked in the conversation object and formatted according to the `output_type` setting.
+
+#### Parameters
+
+| Parameter | Type | Default | Description |
+|-----------|------|---------|-------------|
+| `query` | `str` | Required | The user's query to process through the council. |
+
+#### Returns
+
+| Type | Description |
+|------|-------------|
+| `Union[List, Dict, str]` | Formatted output based on `output_type`. The output contains the conversation history with all messages tracked throughout the workflow. |
+
+#### Output Format
+
+The return value depends on the `output_type` parameter set during initialization:
+
+| `output_type` value | Description |
+|---------------------------------|---------------------------------------------------------------------|
+| **`"dict"`** (default) | Returns conversation as a dictionary/list of message dictionaries |
+| **`"list"`** | Returns conversation as a list of formatted strings (`"role: content"`) |
+| **`"string"`** or **`"str"`** | Returns conversation as a formatted string |
+| **`"final"`** or **`"last"`** | Returns only the content of the final message (Chairman's response) |
+| **`"json"`** | Returns conversation as a JSON string |
+| **`"yaml"`** | Returns conversation as a YAML string |
+| **`"xml"`** | Returns conversation as an XML string |
+| **`"dict-all-except-first"`** | Returns all messages except the first as a dictionary |
+| **`"str-all-except-first"`** | Returns all messages except the first as a string |
+| **`"dict-final"`** | Returns the final message as a dictionary |
+| **`"list-final"`** | Returns the final message as a list |
+
+#### Conversation Tracking
+
+All messages are automatically tracked in the conversation object with the following roles:
+
+- **`"User"`**: The original user query
+- **`"{member_name}"`**: Each council member's response (e.g., "GPT-5.1-Councilor")
+- **`"{member_name}-Evaluation"`**: Each council member's evaluation (e.g., "GPT-5.1-Councilor-Evaluation")
+- **`"Chairman"`**: The final synthesized response
+
+#### Description
+
+Executes the complete LLM Council workflow:
+
+1. **User Query Tracking**: Adds the user query to the conversation as "User" role
+2. **Dispatch Phase**: Sends the query to all council members in parallel using `run_agents_concurrently`
+3. **Collection Phase**: Collects all responses, maps them to member names, and adds each to the conversation with the member's name as the role
+4. **Anonymization Phase**: Creates anonymous IDs (A, B, C, D, etc.) and shuffles them to ensure anonymity
+5. **Evaluation Phase**: Each member evaluates and ranks all anonymized responses using `batched_grid_agent_execution`, then adds evaluations to the conversation with "{member_name}-Evaluation" as the role
+6. **Synthesis Phase**: The Chairman agent synthesizes all responses and evaluations into a final comprehensive answer, which is added to the conversation as "Chairman" role
+7. **Output Formatting**: Returns the conversation formatted according to the `output_type` setting using `history_output_formatter`
+
+The method provides verbose output by default, showing progress at each stage. All messages are tracked in the `conversation` attribute for later access or export.
+
+#### Example Usage
+
+```python
+from swarms.structs.llm_council import LLMCouncil
+
+# Create council with default output format (dict)
+council = LLMCouncil(verbose=True)
+
+query = "What are the top five best energy stocks across nuclear, solar, gas, and other energy sources?"
+
+# Run the council - returns formatted conversation based on output_type
+result = council.run(query)
+
+# With default "dict" output_type, result is a list of message dictionaries
+# Access conversation messages
+for message in result:
+ print(f"{message['role']}: {message['content'][:200]}...")
+
+# Access the conversation object directly for more control
+conversation = council.conversation
+print("\nFinal message:", conversation.get_final_message_content())
+
+# Get conversation as string
+print("\nFull conversation:")
+print(conversation.get_str())
+
+# Example with different output types
+council_json = LLMCouncil(output_type="json", verbose=False)
+result_json = council_json.run(query) # Returns JSON string
+
+council_final = LLMCouncil(output_type="final", verbose=False)
+result_final = council_final.run(query) # Returns only final response string
+```
+
+---
+
+### `_create_default_council`
+
+Creates default council members with specialized prompts and models.
+
+#### Parameters
+
+None (internal method).
+
+#### Returns
+
+| Type | Description |
+|------|-------------|
+| `List[Agent]` | List of Agent instances configured as council members. |
+
+#### Description
+
+Internal method that creates the default council configuration with four specialized agents:
+
+- **GPT-5.1-Councilor** (`model_name="gpt-5.1"`): Analytical and comprehensive, temperature=0.7
+- **Gemini-3-Pro-Councilor** (`model_name="gemini-2.5-flash"`): Concise and structured, temperature=0.7
+- **Claude-Sonnet-4.5-Councilor** (`model_name="anthropic/claude-sonnet-4-5"`): Thoughtful and balanced, temperature=0.0
+- **Grok-4-Councilor** (`model_name="x-ai/grok-4"`): Creative and innovative, temperature=0.8
+
+Each agent is configured with:
+
+- Specialized system prompts matching their role
+- `max_loops=1` for single-response generation
+- `verbose=False` to reduce noise during parallel execution
+- Appropriate temperature settings for their style
+
+---
+
+## Helper Functions
+
+### `get_gpt_councilor_prompt()`
+
+Returns the system prompt for GPT-5.1 councilor agent.
+
+#### Returns
+
+| Type | Description |
+|------|-------------|
+| `str` | System prompt string emphasizing analytical thinking and comprehensive coverage. |
+
+---
+
+### `get_gemini_councilor_prompt()`
+
+Returns the system prompt for Gemini 3 Pro councilor agent.
+
+#### Returns
+
+| Type | Description |
+|------|-------------|
+| `str` | System prompt string emphasizing concise, well-processed, and structured responses. |
+
+---
+
+### `get_claude_councilor_prompt()`
+
+Returns the system prompt for Claude Sonnet 4.5 councilor agent.
+
+#### Returns
+
+| Type | Description |
+|------|-------------|
+| `str` | System prompt string emphasizing thoughtful, balanced, and nuanced responses. |
+
+---
+
+### `get_grok_councilor_prompt()`
+
+Returns the system prompt for Grok-4 councilor agent.
+
+#### Returns
+
+| Type | Description |
+|------|-------------|
+| `str` | System prompt string emphasizing creative, innovative, and unique perspectives. |
+
+---
+
+### `get_chairman_prompt()`
+
+Returns the system prompt for the Chairman agent.
+
+#### Returns
+
+| Type | Description |
+|------|-------------|
+| `str` | System prompt string for synthesizing responses and evaluations into a final answer. |
+
+---
+
+### `get_evaluation_prompt(query, responses, evaluator_name)`
+
+Creates evaluation prompt for council members to review and rank responses.
+
+#### Parameters
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `query` | `str` | The original user query. |
+| `responses` | `Dict[str, str]` | Dictionary mapping anonymous IDs to response texts. |
+| `evaluator_name` | `str` | Name of the agent doing the evaluation. |
+
+#### Returns
+
+| Type | Description |
+|------|-------------|
+| `str` | Formatted evaluation prompt string with instructions for ranking responses. |
+
+---
+
+### `get_synthesis_prompt(query, original_responses, evaluations, id_to_member)`
+
+Creates synthesis prompt for the Chairman.
+
+#### Parameters
+
+| Parameter | Type | Description |
+|-----------|------|-------------|
+| `query` | `str` | Original user query. |
+| `original_responses` | `Dict[str, str]` | Dictionary mapping member names to their responses. |
+| `evaluations` | `Dict[str, str]` | Dictionary mapping evaluator names to their evaluation texts. |
+| `id_to_member` | `Dict[str, str]` | Mapping from anonymous IDs to member names. |
+
+#### Returns
+
+| Type | Description |
+|------|-------------|
+| `str` | Formatted synthesis prompt for the Chairman agent. |
+
+---
+
+## Use Cases
+
+The LLM Council is ideal for scenarios requiring:
+
+- **Multi-perspective Analysis**: When you need diverse viewpoints on complex topics
+- **Quality Assurance**: When peer review and ranking can improve response quality
+- **Transparent Decision Making**: When you want to see how different models evaluate each other
+- **Synthesis of Expertise**: When combining multiple specialized perspectives is valuable
+
+### Common Applications
+
+| Use Case | Description |
+|-----------------------|--------------------------------------------------------------------------------------------------|
+| **Medical Diagnosis** | Multiple medical AI agents provide diagnoses, evaluate each other, and synthesize recommendations |
+| **Financial Analysis**| Different financial experts analyze investments and rank each other's assessments |
+| **Legal Analysis** | Multiple legal perspectives evaluate compliance and risk |
+| **Business Strategy** | Diverse strategic viewpoints are synthesized into comprehensive plans |
+| **Research Analysis** | Multiple research perspectives are combined for thorough analysis |
+
+
+## Examples
+
+For comprehensive examples demonstrating various use cases, see the [LLM Council Examples](../../../examples/multi_agent/llm_council_examples/) directory:
+
+- **Medical**: `medical_diagnosis_council.py`, `medical_treatment_council.py`
+- **Finance**: `finance_analysis_council.py`, `etf_stock_analysis_council.py`
+- **Business**: `business_strategy_council.py`, `marketing_strategy_council.py`
+- **Technology**: `technology_assessment_council.py`, `research_analysis_council.py`
+- **Legal**: `legal_analysis_council.py`
+
+### Quick Start Example
+
+```python
+from swarms.structs.llm_council import LLMCouncil
+
+# Create the council with default output format
+council = LLMCouncil(verbose=True)
+
+# Example query
+query = "What are the top five best energy stocks across nuclear, solar, gas, and other energy sources?"
+
+# Run the council - returns formatted conversation
+result = council.run(query)
+
+# With default "dict" output_type, result is a list of message dictionaries
+# Print all messages
+for message in result:
+ role = message['role']
+ content = message['content']
+ print(f"\n{role}:")
+ print(content[:500] + "..." if len(content) > 500 else content)
+
+# Access conversation object directly for more options
+conversation = council.conversation
+
+# Get only the final response
+print("\n" + "="*80)
+print("FINAL RESPONSE")
+print("="*80)
+print(conversation.get_final_message_content())
+
+# Get conversation as formatted string
+print("\n" + "="*80)
+print("FULL CONVERSATION")
+print("="*80)
+print(conversation.get_str())
+
+# Export conversation to JSON
+conversation.export()
+```
+
+## Customization
+
+### Creating Custom Council Members
+
+You can create custom council members with specialized roles:
+
+```python
+from swarms import Agent
+from swarms.structs.llm_council import LLMCouncil, get_gpt_councilor_prompt
+
+# Create custom councilor
+custom_agent = Agent(
+ agent_name="Domain-Expert-Councilor",
+ agent_description="Specialized domain expert for specific analysis",
+ system_prompt=get_gpt_councilor_prompt(), # Or create custom prompt
+ model_name="gpt-4",
+ max_loops=1,
+ verbose=False,
+ temperature=0.7,
+)
+
+# Create council with custom members
+council = LLMCouncil(
+ council_members=[custom_agent, ...], # Add your custom agents
+ chairman_model="gpt-4",
+ verbose=True
+)
+```
+
+### Custom Chairman Model
+
+You can specify a different model for the Chairman:
+
+```python
+council = LLMCouncil(
+ chairman_model="claude-3-opus", # Use Claude as Chairman
+ verbose=True
+)
+```
+
+### Custom Output Format
+
+You can control the output format using the `output_type` parameter:
+
+```python
+# Get output as JSON string
+council = LLMCouncil(output_type="json")
+result = council.run(query) # Returns JSON string
+
+# Get only the final response
+council = LLMCouncil(output_type="final")
+result = council.run(query) # Returns only final response string
+
+# Get as YAML
+council = LLMCouncil(output_type="yaml")
+result = council.run(query) # Returns YAML string
+
+# Get as formatted string
+council = LLMCouncil(output_type="string")
+result = council.run(query) # Returns formatted conversation string
+```
+
+### Accessing Conversation History
+
+The conversation object is accessible for advanced usage:
+
+```python
+council = LLMCouncil()
+council.run(query)
+
+# Access conversation directly
+conversation = council.conversation
+
+# Get conversation history
+history = conversation.conversation_history
+
+# Export to file
+conversation.export() # Saves to default location
+
+# Get specific format
+json_output = conversation.to_json()
+yaml_output = conversation.return_messages_as_dictionary()
+```
+
+## Architecture Benefits
+
+1. **Diversity**: Multiple models provide varied perspectives and approaches
+2. **Quality Control**: Peer review ensures responses are evaluated objectively
+3. **Synthesis**: Chairman combines the best elements from all responses
+4. **Transparency**: Full visibility into individual responses and evaluation rankings
+5. **Scalability**: Easy to add or remove council members
+6. **Flexibility**: Supports custom agents and models
+7. **Conversation Tracking**: All messages are automatically tracked in a Conversation object for history and export
+8. **Flexible Output**: Multiple output formats supported via `history_output_formatter` (dict, list, string, JSON, YAML, XML, etc.)
+
+## Performance Considerations
+
+| Feature | Description |
+|---------------------------|----------------------------------------------------------------------------------------------------------------|
+| **Parallel Execution** | Both response generation and evaluation phases run in parallel for efficiency |
+| **Anonymization** | Responses are anonymized to prevent bias in evaluation |
+| **Model Selection** | Different models can be used for different roles based on their strengths |
+| **Verbose Mode** | Can be disabled for production use to reduce output |
+| **Conversation Management** | Conversation object efficiently tracks all messages in memory and supports export to JSON/YAML files |
+| **Output Formatting** | Choose lightweight output formats (e.g., "final") for production to reduce memory usage |
+
+## Related Documentation
+
+- [Multi-Agent Architectures Overview](overview.md)
+- [Council of Judges](council_of_judges.md) - Similar peer review pattern
+- [Agent Class Reference](agent.md) - Understanding individual agents
+- [Conversation Class Reference](conversation.md) - Understanding conversation tracking and management
+- [Multi-Agent Execution Utilities](various_execution_methods.md) - Underlying execution methods
+- [History Output Formatter](../../../swarms/utils/history_output_formatter.py) - Output formatting utilities
diff --git a/docs/swarms/structs/swarm_router.md b/docs/swarms/structs/swarm_router.md
index 8ccf1203..44bd1c8b 100644
--- a/docs/swarms/structs/swarm_router.md
+++ b/docs/swarms/structs/swarm_router.md
@@ -42,6 +42,7 @@ Main class for routing tasks to different swarm types.
| `verbose` | bool | Flag to enable/disable verbose logging (default: False) |
| `worker_tools` | List[Callable] | List of tools available to worker agents |
| `aggregation_strategy` | str | Aggregation strategy for HeavySwarm (default: "synthesis") |
+| `chairman_model` | str | Model name for the Chairman in LLMCouncil (default: "gpt-5.1") |
### Methods
@@ -123,6 +124,7 @@ The `SwarmRouter` supports many various multi-agent architectures for various ap
| `InteractiveGroupChat` | Interactive group chat with user participation |
| `HeavySwarm` | Heavy swarm architecture with question and worker agents |
| `BatchedGridWorkflow` | Batched grid workflow for parallel task processing |
+| `LLMCouncil` | Council of specialized LLM agents with peer review and synthesis |
| `auto` | Automatically selects best swarm type via embedding search |
## Basic Usage
@@ -456,6 +458,30 @@ result = batched_grid_router.run(tasks=["Task 1", "Task 2", "Task 3"])
BatchedGridWorkflow is designed for efficiently processing multiple tasks in parallel batches, optimizing resource utilization.
+### LLMCouncil
+
+Use Case: Collaborative analysis with multiple specialized LLM agents that evaluate each other's responses and synthesize a final answer.
+
+```python
+llm_council_router = SwarmRouter(
+ name="LLMCouncil",
+ description="Collaborative council of LLM agents with peer review",
+ swarm_type="LLMCouncil",
+ chairman_model="gpt-5.1", # Model for the Chairman agent
+ output_type="dict", # Output format: "dict", "list", "string", "json", "yaml", "final", etc.
+ verbose=True # Show progress and intermediate results
+)
+
+result = llm_council_router.run("What are the top five best energy stocks across nuclear, solar, gas, and other energy sources?")
+```
+
+LLMCouncil creates a council of specialized agents (GPT-5.1, Gemini, Claude, Grok by default) that:
+1. Each independently responds to the query
+2. Evaluates and ranks each other's anonymized responses
+3. A Chairman synthesizes all responses and evaluations into a final comprehensive answer
+
+The council automatically tracks all messages in a conversation object and supports flexible output formats. Note: LLMCouncil uses default council members and doesn't require the `agents` parameter.
+
## Advanced Features
### Processing Documents
diff --git a/example.py b/example.py
index d13636db..386b6597 100644
--- a/example.py
+++ b/example.py
@@ -10,7 +10,6 @@ agent = Agent(
dynamic_context_window=True,
streaming_on=False,
top_p=None,
- # stream=True,
)
out = agent.run(
@@ -18,5 +17,4 @@ out = agent.run(
n=1,
)
-for token in out:
- print(token, end="", flush=True)
+print(out)
diff --git a/examples/aop_examples/server.py b/examples/aop_examples/server.py
index adcaaa2c..b91bcbaa 100644
--- a/examples/aop_examples/server.py
+++ b/examples/aop_examples/server.py
@@ -92,7 +92,13 @@ financial_agent = Agent(
)
# Basic usage - individual agent addition
-deployer = AOP(server_name="MyAgentServer", verbose=True, port=5932, json_response=True, queue_enabled=False)
+deployer = AOP(
+ server_name="MyAgentServer",
+ verbose=True,
+ port=5932,
+ json_response=True,
+ queue_enabled=False,
+)
agents = [
research_agent,
diff --git a/examples/multi_agent/graphworkflow_examples/rustworkx_examples/01_basic_usage.py b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/01_basic_usage.py
new file mode 100644
index 00000000..a9d0a344
--- /dev/null
+++ b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/01_basic_usage.py
@@ -0,0 +1,46 @@
+from swarms.structs.graph_workflow import GraphWorkflow
+from swarms.structs.agent import Agent
+
+research_agent = Agent(
+ agent_name="Research-Analyst",
+ agent_description="Specialized in comprehensive research and data gathering",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+analysis_agent = Agent(
+ agent_name="Data-Analyst",
+ agent_description="Expert in data analysis and pattern recognition",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+strategy_agent = Agent(
+ agent_name="Strategy-Consultant",
+ agent_description="Specialized in strategic planning and recommendations",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+workflow = GraphWorkflow(
+ name="Rustworkx-Basic-Workflow",
+ description="Basic workflow using rustworkx backend for faster graph operations",
+ backend="rustworkx",
+ verbose=False,
+)
+
+workflow.add_node(research_agent)
+workflow.add_node(analysis_agent)
+workflow.add_node(strategy_agent)
+
+workflow.add_edge(research_agent, analysis_agent)
+workflow.add_edge(analysis_agent, strategy_agent)
+
+task = "Conduct a research analysis on water stocks and ETFs"
+results = workflow.run(task=task)
+
+for agent_name, output in results.items():
+ print(f"{agent_name}: {output}")
diff --git a/examples/multi_agent/graphworkflow_examples/rustworkx_examples/02_backend_comparison.py b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/02_backend_comparison.py
new file mode 100644
index 00000000..35cfe83e
--- /dev/null
+++ b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/02_backend_comparison.py
@@ -0,0 +1,56 @@
+import time
+from swarms.structs.graph_workflow import GraphWorkflow
+from swarms.structs.agent import Agent
+
+agents = [
+ Agent(
+ agent_name=f"Agent-{i}",
+ agent_description=f"Agent number {i}",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+ )
+ for i in range(5)
+]
+
+nx_workflow = GraphWorkflow(
+ name="NetworkX-Workflow",
+ backend="networkx",
+ verbose=False,
+)
+
+for agent in agents:
+ nx_workflow.add_node(agent)
+
+for i in range(len(agents) - 1):
+ nx_workflow.add_edge(agents[i], agents[i + 1])
+
+nx_start = time.time()
+nx_workflow.compile()
+nx_compile_time = time.time() - nx_start
+
+rx_workflow = GraphWorkflow(
+ name="Rustworkx-Workflow",
+ backend="rustworkx",
+ verbose=False,
+)
+
+for agent in agents:
+ rx_workflow.add_node(agent)
+
+for i in range(len(agents) - 1):
+ rx_workflow.add_edge(agents[i], agents[i + 1])
+
+rx_start = time.time()
+rx_workflow.compile()
+rx_compile_time = time.time() - rx_start
+
+speedup = (
+ nx_compile_time / rx_compile_time if rx_compile_time > 0 else 0
+)
+print(f"NetworkX compile time: {nx_compile_time:.4f}s")
+print(f"Rustworkx compile time: {rx_compile_time:.4f}s")
+print(f"Speedup: {speedup:.2f}x")
+print(
+ f"Identical layers: {nx_workflow._sorted_layers == rx_workflow._sorted_layers}"
+)
diff --git a/examples/multi_agent/graphworkflow_examples/rustworkx_examples/03_fan_out_fan_in_patterns.py b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/03_fan_out_fan_in_patterns.py
new file mode 100644
index 00000000..8be4fecf
--- /dev/null
+++ b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/03_fan_out_fan_in_patterns.py
@@ -0,0 +1,73 @@
+from swarms import Agent, GraphWorkflow
+
+coordinator = Agent(
+ agent_name="Coordinator",
+ agent_description="Coordinates and distributes tasks",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+tech_analyst = Agent(
+ agent_name="Tech-Analyst",
+ agent_description="Technical analysis specialist",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+fundamental_analyst = Agent(
+ agent_name="Fundamental-Analyst",
+ agent_description="Fundamental analysis specialist",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+sentiment_analyst = Agent(
+ agent_name="Sentiment-Analyst",
+ agent_description="Sentiment analysis specialist",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+synthesis_agent = Agent(
+ agent_name="Synthesis-Agent",
+ agent_description="Synthesizes multiple analyses into final report",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+workflow = GraphWorkflow(
+ name="Fan-Out-Fan-In-Workflow",
+ description="Demonstrates parallel processing patterns with rustworkx",
+ backend="rustworkx",
+ verbose=False,
+)
+
+workflow.add_node(coordinator)
+workflow.add_node(tech_analyst)
+workflow.add_node(fundamental_analyst)
+workflow.add_node(sentiment_analyst)
+workflow.add_node(synthesis_agent)
+
+workflow.add_edges_from_source(
+ coordinator,
+ [tech_analyst, fundamental_analyst, sentiment_analyst],
+)
+
+workflow.add_edges_to_target(
+ [tech_analyst, fundamental_analyst, sentiment_analyst],
+ synthesis_agent,
+)
+
+task = "Analyze Tesla stock from technical, fundamental, and sentiment perspectives"
+results = workflow.run(task=task)
+
+for agent_name, output in results.items():
+ print(f"{agent_name}: {output}")
+
+
+workflow.visualize(view=True)
diff --git a/examples/multi_agent/graphworkflow_examples/rustworkx_examples/04_complex_workflow.py b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/04_complex_workflow.py
new file mode 100644
index 00000000..4f025a71
--- /dev/null
+++ b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/04_complex_workflow.py
@@ -0,0 +1,101 @@
+from swarms.structs.graph_workflow import GraphWorkflow
+from swarms.structs.agent import Agent
+
+data_collector_1 = Agent(
+ agent_name="Data-Collector-1",
+ agent_description="Collects market data",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+data_collector_2 = Agent(
+ agent_name="Data-Collector-2",
+ agent_description="Collects financial data",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+technical_analyst = Agent(
+ agent_name="Technical-Analyst",
+ agent_description="Performs technical analysis",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+fundamental_analyst = Agent(
+ agent_name="Fundamental-Analyst",
+ agent_description="Performs fundamental analysis",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+risk_analyst = Agent(
+ agent_name="Risk-Analyst",
+ agent_description="Performs risk analysis",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+strategy_consultant = Agent(
+ agent_name="Strategy-Consultant",
+ agent_description="Develops strategic recommendations",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+report_writer = Agent(
+ agent_name="Report-Writer",
+ agent_description="Writes comprehensive reports",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+workflow = GraphWorkflow(
+ name="Complex-Multi-Layer-Workflow",
+ description="Complex workflow with multiple layers and parallel processing",
+ backend="rustworkx",
+ verbose=False,
+)
+
+all_agents = [
+ data_collector_1,
+ data_collector_2,
+ technical_analyst,
+ fundamental_analyst,
+ risk_analyst,
+ strategy_consultant,
+ report_writer,
+]
+
+for agent in all_agents:
+ workflow.add_node(agent)
+
+workflow.add_parallel_chain(
+ [data_collector_1, data_collector_2],
+ [technical_analyst, fundamental_analyst, risk_analyst],
+)
+
+workflow.add_edges_to_target(
+ [technical_analyst, fundamental_analyst, risk_analyst],
+ strategy_consultant,
+)
+
+workflow.add_edges_to_target(
+ [technical_analyst, fundamental_analyst, risk_analyst],
+ report_writer,
+)
+
+workflow.add_edge(strategy_consultant, report_writer)
+
+task = "Conduct a comprehensive analysis of the renewable energy sector including market trends, financial health, and risk assessment"
+results = workflow.run(task=task)
+
+for agent_name, output in results.items():
+ print(f"{agent_name}: {output}")
diff --git a/examples/multi_agent/graphworkflow_examples/rustworkx_examples/05_performance_benchmark.py b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/05_performance_benchmark.py
new file mode 100644
index 00000000..2b5251f7
--- /dev/null
+++ b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/05_performance_benchmark.py
@@ -0,0 +1,104 @@
+import time
+from swarms.structs.graph_workflow import GraphWorkflow
+from swarms.structs.agent import Agent
+
+agents_small = [
+ Agent(
+ agent_name=f"Agent-{i}",
+ agent_description=f"Agent number {i}",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+ )
+ for i in range(5)
+]
+
+agents_medium = [
+ Agent(
+ agent_name=f"Agent-{i}",
+ agent_description=f"Agent number {i}",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+ )
+ for i in range(20)
+]
+
+nx_workflow_small = GraphWorkflow(
+ name="NetworkX-Small",
+ backend="networkx",
+ verbose=False,
+ auto_compile=False,
+)
+
+for agent in agents_small:
+ nx_workflow_small.add_node(agent)
+
+for i in range(len(agents_small) - 1):
+ nx_workflow_small.add_edge(agents_small[i], agents_small[i + 1])
+
+nx_start = time.time()
+nx_workflow_small.compile()
+nx_small_time = time.time() - nx_start
+
+rx_workflow_small = GraphWorkflow(
+ name="Rustworkx-Small",
+ backend="rustworkx",
+ verbose=False,
+ auto_compile=False,
+)
+
+for agent in agents_small:
+ rx_workflow_small.add_node(agent)
+
+for i in range(len(agents_small) - 1):
+ rx_workflow_small.add_edge(agents_small[i], agents_small[i + 1])
+
+rx_start = time.time()
+rx_workflow_small.compile()
+rx_small_time = time.time() - rx_start
+
+nx_workflow_medium = GraphWorkflow(
+ name="NetworkX-Medium",
+ backend="networkx",
+ verbose=False,
+ auto_compile=False,
+)
+
+for agent in agents_medium:
+ nx_workflow_medium.add_node(agent)
+
+for i in range(len(agents_medium) - 1):
+ nx_workflow_medium.add_edge(
+ agents_medium[i], agents_medium[i + 1]
+ )
+
+nx_start = time.time()
+nx_workflow_medium.compile()
+nx_medium_time = time.time() - nx_start
+
+rx_workflow_medium = GraphWorkflow(
+ name="Rustworkx-Medium",
+ backend="rustworkx",
+ verbose=False,
+ auto_compile=False,
+)
+
+for agent in agents_medium:
+ rx_workflow_medium.add_node(agent)
+
+for i in range(len(agents_medium) - 1):
+ rx_workflow_medium.add_edge(
+ agents_medium[i], agents_medium[i + 1]
+ )
+
+rx_start = time.time()
+rx_workflow_medium.compile()
+rx_medium_time = time.time() - rx_start
+
+print(
+ f"Small (5 agents) - NetworkX: {nx_small_time:.4f}s, Rustworkx: {rx_small_time:.4f}s, Speedup: {nx_small_time/rx_small_time if rx_small_time > 0 else 0:.2f}x"
+)
+print(
+ f"Medium (20 agents) - NetworkX: {nx_medium_time:.4f}s, Rustworkx: {rx_medium_time:.4f}s, Speedup: {nx_medium_time/rx_medium_time if rx_medium_time > 0 else 0:.2f}x"
+)
diff --git a/examples/multi_agent/graphworkflow_examples/rustworkx_examples/06_error_handling.py b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/06_error_handling.py
new file mode 100644
index 00000000..3fd9f25c
--- /dev/null
+++ b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/06_error_handling.py
@@ -0,0 +1,55 @@
+from swarms.structs.graph_workflow import GraphWorkflow
+from swarms.structs.agent import Agent
+
+test_agent = Agent(
+ agent_name="Test-Agent",
+ agent_description="Test agent for error handling",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+workflow_rx = GraphWorkflow(
+ name="Rustworkx-Workflow",
+ backend="rustworkx",
+ verbose=False,
+)
+workflow_rx.add_node(test_agent)
+
+workflow_nx = GraphWorkflow(
+ name="NetworkX-Workflow",
+ backend="networkx",
+ verbose=False,
+)
+workflow_nx.add_node(test_agent)
+
+workflow_default = GraphWorkflow(
+ name="Default-Workflow",
+ verbose=False,
+)
+workflow_default.add_node(test_agent)
+
+workflow_invalid = GraphWorkflow(
+ name="Invalid-Workflow",
+ backend="invalid_backend",
+ verbose=False,
+)
+workflow_invalid.add_node(test_agent)
+
+print(
+ f"Rustworkx backend: {type(workflow_rx.graph_backend).__name__}"
+)
+print(f"NetworkX backend: {type(workflow_nx.graph_backend).__name__}")
+print(
+ f"Default backend: {type(workflow_default.graph_backend).__name__}"
+)
+print(
+ f"Invalid backend fallback: {type(workflow_invalid.graph_backend).__name__}"
+)
+
+try:
+ import rustworkx as rx
+
+ print("Rustworkx available: True")
+except ImportError:
+ print("Rustworkx available: False")
diff --git a/examples/multi_agent/graphworkflow_examples/rustworkx_examples/07_large_scale_workflow.py b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/07_large_scale_workflow.py
new file mode 100644
index 00000000..edaeef0c
--- /dev/null
+++ b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/07_large_scale_workflow.py
@@ -0,0 +1,61 @@
+import time
+from swarms.structs.graph_workflow import GraphWorkflow
+from swarms.structs.agent import Agent
+
+NUM_AGENTS = 30
+
+agents = [
+ Agent(
+ agent_name=f"Agent-{i:02d}",
+ agent_description=f"Agent number {i} in large-scale workflow",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+ )
+ for i in range(NUM_AGENTS)
+]
+
+workflow = GraphWorkflow(
+ name="Large-Scale-Workflow",
+ description=f"Large-scale workflow with {NUM_AGENTS} agents using rustworkx",
+ backend="rustworkx",
+ verbose=False,
+)
+
+start_time = time.time()
+for agent in agents:
+ workflow.add_node(agent)
+add_nodes_time = time.time() - start_time
+
+start_time = time.time()
+for i in range(9):
+ workflow.add_edge(agents[i], agents[i + 1])
+
+workflow.add_edges_from_source(
+ agents[5],
+ agents[10:20],
+)
+
+workflow.add_edges_to_target(
+ agents[10:20],
+ agents[20],
+)
+
+for i in range(20, 29):
+ workflow.add_edge(agents[i], agents[i + 1])
+
+add_edges_time = time.time() - start_time
+
+start_time = time.time()
+workflow.compile()
+compile_time = time.time() - start_time
+
+print(
+ f"Agents: {len(workflow.nodes)}, Edges: {len(workflow.edges)}, Layers: {len(workflow._sorted_layers)}"
+)
+print(
+ f"Node addition: {add_nodes_time:.4f}s, Edge addition: {add_edges_time:.4f}s, Compilation: {compile_time:.4f}s"
+)
+print(
+ f"Total setup: {add_nodes_time + add_edges_time + compile_time:.4f}s"
+)
diff --git a/examples/multi_agent/graphworkflow_examples/rustworkx_examples/08_parallel_chain_example.py b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/08_parallel_chain_example.py
new file mode 100644
index 00000000..21b18d23
--- /dev/null
+++ b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/08_parallel_chain_example.py
@@ -0,0 +1,73 @@
+from swarms.structs.graph_workflow import GraphWorkflow
+from swarms.structs.agent import Agent
+
+data_collector_1 = Agent(
+ agent_name="Data-Collector-1",
+ agent_description="Collects market data",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+data_collector_2 = Agent(
+ agent_name="Data-Collector-2",
+ agent_description="Collects financial data",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+data_collector_3 = Agent(
+ agent_name="Data-Collector-3",
+ agent_description="Collects news data",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+technical_analyst = Agent(
+ agent_name="Technical-Analyst",
+ agent_description="Performs technical analysis",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+fundamental_analyst = Agent(
+ agent_name="Fundamental-Analyst",
+ agent_description="Performs fundamental analysis",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+sentiment_analyst = Agent(
+ agent_name="Sentiment-Analyst",
+ agent_description="Performs sentiment analysis",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+workflow = GraphWorkflow(
+ name="Parallel-Chain-Workflow",
+ description="Demonstrates parallel chain pattern with rustworkx",
+ backend="rustworkx",
+ verbose=False,
+)
+
+sources = [data_collector_1, data_collector_2, data_collector_3]
+targets = [technical_analyst, fundamental_analyst, sentiment_analyst]
+
+for agent in sources + targets:
+ workflow.add_node(agent)
+
+workflow.add_parallel_chain(sources, targets)
+
+workflow.compile()
+
+task = "Analyze the technology sector using multiple data sources and analysis methods"
+results = workflow.run(task=task)
+
+for agent_name, output in results.items():
+ print(f"{agent_name}: {output}")
diff --git a/examples/multi_agent/graphworkflow_examples/rustworkx_examples/09_workflow_validation.py b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/09_workflow_validation.py
new file mode 100644
index 00000000..79c2de3d
--- /dev/null
+++ b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/09_workflow_validation.py
@@ -0,0 +1,79 @@
+from swarms.structs.graph_workflow import GraphWorkflow
+from swarms.structs.agent import Agent
+
+agent_a = Agent(
+ agent_name="Agent-A",
+ agent_description="Agent A",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+agent_b = Agent(
+ agent_name="Agent-B",
+ agent_description="Agent B",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+agent_c = Agent(
+ agent_name="Agent-C",
+ agent_description="Agent C",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+agent_isolated = Agent(
+ agent_name="Agent-Isolated",
+ agent_description="Isolated agent with no connections",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+workflow = GraphWorkflow(
+ name="Validation-Workflow",
+ description="Workflow for validation testing",
+ backend="rustworkx",
+ verbose=False,
+)
+
+workflow.add_node(agent_a)
+workflow.add_node(agent_b)
+workflow.add_node(agent_c)
+workflow.add_node(agent_isolated)
+
+workflow.add_edge(agent_a, agent_b)
+workflow.add_edge(agent_b, agent_c)
+
+validation_result = workflow.validate(auto_fix=False)
+print(f"Valid: {validation_result['is_valid']}")
+print(f"Warnings: {len(validation_result['warnings'])}")
+print(f"Errors: {len(validation_result['errors'])}")
+
+validation_result_fixed = workflow.validate(auto_fix=True)
+print(
+ f"After auto-fix - Valid: {validation_result_fixed['is_valid']}"
+)
+print(f"Fixed: {len(validation_result_fixed['fixed'])}")
+print(f"Entry points: {workflow.entry_points}")
+print(f"End points: {workflow.end_points}")
+
+workflow_cycle = GraphWorkflow(
+ name="Cycle-Test-Workflow",
+ backend="rustworkx",
+ verbose=False,
+)
+
+workflow_cycle.add_node(agent_a)
+workflow_cycle.add_node(agent_b)
+workflow_cycle.add_node(agent_c)
+
+workflow_cycle.add_edge(agent_a, agent_b)
+workflow_cycle.add_edge(agent_b, agent_c)
+workflow_cycle.add_edge(agent_c, agent_a)
+
+cycle_validation = workflow_cycle.validate(auto_fix=False)
+print(f"Cycles detected: {len(cycle_validation.get('cycles', []))}")
diff --git a/examples/multi_agent/graphworkflow_examples/rustworkx_examples/10_real_world_scenario.py b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/10_real_world_scenario.py
new file mode 100644
index 00000000..cc6e83ff
--- /dev/null
+++ b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/10_real_world_scenario.py
@@ -0,0 +1,122 @@
+from swarms.structs.graph_workflow import GraphWorkflow
+from swarms.structs.agent import Agent
+
+market_researcher = Agent(
+ agent_name="Market-Researcher",
+ agent_description="Conducts comprehensive market research and data collection",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+competitor_analyst = Agent(
+ agent_name="Competitor-Analyst",
+ agent_description="Analyzes competitor landscape and positioning",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+market_analyst = Agent(
+ agent_name="Market-Analyst",
+ agent_description="Analyzes market trends and opportunities",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+financial_analyst = Agent(
+ agent_name="Financial-Analyst",
+ agent_description="Analyzes financial metrics and projections",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+risk_analyst = Agent(
+ agent_name="Risk-Analyst",
+ agent_description="Assesses market risks and challenges",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+strategy_consultant = Agent(
+ agent_name="Strategy-Consultant",
+ agent_description="Develops strategic recommendations based on all analyses",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+report_writer = Agent(
+ agent_name="Report-Writer",
+ agent_description="Compiles comprehensive market research report",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+executive_summary_writer = Agent(
+ agent_name="Executive-Summary-Writer",
+ agent_description="Creates executive summary for leadership",
+ model_name="gpt-4o-mini",
+ max_loops=1,
+ verbose=False,
+)
+
+workflow = GraphWorkflow(
+ name="Market-Research-Workflow",
+ description="Real-world market research workflow using rustworkx backend",
+ backend="rustworkx",
+ verbose=False,
+)
+
+all_agents = [
+ market_researcher,
+ competitor_analyst,
+ market_analyst,
+ financial_analyst,
+ risk_analyst,
+ strategy_consultant,
+ report_writer,
+ executive_summary_writer,
+]
+
+for agent in all_agents:
+ workflow.add_node(agent)
+
+workflow.add_parallel_chain(
+ [market_researcher, competitor_analyst],
+ [market_analyst, financial_analyst, risk_analyst],
+)
+
+workflow.add_edges_to_target(
+ [market_analyst, financial_analyst, risk_analyst],
+ strategy_consultant,
+)
+
+workflow.add_edges_from_source(
+ strategy_consultant,
+ [report_writer, executive_summary_writer],
+)
+
+workflow.add_edges_to_target(
+ [market_analyst, financial_analyst, risk_analyst],
+ report_writer,
+)
+
+task = """
+Conduct a comprehensive market research analysis on the electric vehicle (EV) industry:
+1. Research current market size, growth trends, and key players
+2. Analyze competitor landscape and market positioning
+3. Assess financial opportunities and investment potential
+4. Evaluate risks and challenges in the EV market
+5. Develop strategic recommendations
+6. Create detailed report and executive summary
+"""
+
+results = workflow.run(task=task)
+
+for agent_name, output in results.items():
+ print(f"{agent_name}: {output}")
diff --git a/examples/multi_agent/graphworkflow_examples/rustworkx_examples/Fan-Out-Fan-In-Workflow_visualization_1329d9aa-4cba-4eb5-a42c-5e4ccd165e4d.png b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/Fan-Out-Fan-In-Workflow_visualization_1329d9aa-4cba-4eb5-a42c-5e4ccd165e4d.png
new file mode 100644
index 00000000..d45a9a2d
Binary files /dev/null and b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/Fan-Out-Fan-In-Workflow_visualization_1329d9aa-4cba-4eb5-a42c-5e4ccd165e4d.png differ
diff --git a/examples/multi_agent/graphworkflow_examples/rustworkx_examples/README.md b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/README.md
new file mode 100644
index 00000000..7292caad
--- /dev/null
+++ b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/README.md
@@ -0,0 +1,156 @@
+# Rustworkx Backend Examples
+
+This directory contains comprehensive examples demonstrating the use of the **rustworkx backend** in GraphWorkflow. Rustworkx provides faster graph operations compared to NetworkX, especially for large graphs and complex operations.
+
+## Installation
+
+Before running these examples, ensure rustworkx is installed:
+
+```bash
+pip install rustworkx
+```
+
+If rustworkx is not installed, GraphWorkflow will automatically fallback to NetworkX backend.
+
+## Examples Overview
+
+### 01_basic_usage.py
+Basic example showing how to use rustworkx backend with GraphWorkflow. Demonstrates simple linear workflow creation and execution.
+
+**Key Concepts:**
+- Initializing GraphWorkflow with rustworkx backend
+- Adding agents and creating edges
+- Running a workflow
+
+### 02_backend_comparison.py
+Compares NetworkX and Rustworkx backends side-by-side, showing performance differences and functional equivalence.
+
+**Key Concepts:**
+- Backend comparison
+- Performance metrics
+- Functional equivalence verification
+
+### 03_fan_out_fan_in_patterns.py
+Demonstrates parallel processing patterns: fan-out (one-to-many) and fan-in (many-to-one) connections.
+
+**Key Concepts:**
+- Fan-out pattern: `add_edges_from_source()`
+- Fan-in pattern: `add_edges_to_target()`
+- Parallel execution optimization
+
+### 04_complex_workflow.py
+Shows a complex multi-layer workflow with multiple parallel branches and convergence points.
+
+**Key Concepts:**
+- Multi-layer workflows
+- Parallel chains: `add_parallel_chain()`
+- Complex graph structures
+
+### 05_performance_benchmark.py
+Benchmarks performance differences between NetworkX and Rustworkx for various graph sizes and structures.
+
+**Key Concepts:**
+- Performance benchmarking
+- Scalability testing
+- Different graph topologies (chain, tree)
+
+### 06_error_handling.py
+Demonstrates error handling and graceful fallback behavior when rustworkx is unavailable.
+
+**Key Concepts:**
+- Error handling
+- Automatic fallback to NetworkX
+- Backend availability checking
+
+### 07_large_scale_workflow.py
+Demonstrates rustworkx's efficiency with large-scale workflows containing many agents.
+
+**Key Concepts:**
+- Large-scale workflows
+- Performance with many nodes/edges
+- Complex interconnections
+
+### 08_parallel_chain_example.py
+Detailed example of the parallel chain pattern creating a full mesh connection.
+
+**Key Concepts:**
+- Parallel chain pattern
+- Full mesh connections
+- Maximum parallelization
+
+### 09_workflow_validation.py
+Shows workflow validation features including cycle detection, isolated nodes, and auto-fixing.
+
+**Key Concepts:**
+- Workflow validation
+- Cycle detection
+- Auto-fixing capabilities
+
+### 10_real_world_scenario.py
+A realistic market research workflow demonstrating real-world agent coordination scenarios.
+
+**Key Concepts:**
+- Real-world use case
+- Complex multi-phase workflow
+- Practical application
+
+## Quick Start
+
+Run any example:
+
+```bash
+python 01_basic_usage.py
+```
+
+## Backend Selection
+
+To use rustworkx backend:
+
+```python
+workflow = GraphWorkflow(
+ backend="rustworkx", # Use rustworkx
+ # ... other parameters
+)
+```
+
+To use NetworkX backend (default):
+
+```python
+workflow = GraphWorkflow(
+ backend="networkx", # Or omit for default
+ # ... other parameters
+)
+```
+
+## Performance Benefits
+
+Rustworkx provides performance benefits especially for:
+- **Large graphs** (100+ nodes)
+- **Complex operations** (topological sorting, cycle detection)
+- **Frequent graph modifications** (adding/removing nodes/edges)
+
+## Key Differences
+
+While both backends are functionally equivalent, rustworkx:
+- Uses integer indices internally (abstracted away)
+- Provides faster graph operations
+- Better memory efficiency for large graphs
+- Maintains full compatibility with GraphWorkflow API
+
+## Notes
+
+- Both backends produce identical results
+- Rustworkx automatically falls back to NetworkX if not installed
+- All GraphWorkflow features work with both backends
+- Performance gains become more significant with larger graphs
+
+## Requirements
+
+- `swarms` package
+- `rustworkx` (optional, for rustworkx backend)
+- `networkx` (always available, default backend)
+
+## Contributing
+
+Feel free to add more examples demonstrating rustworkx capabilities or specific use cases!
+
diff --git a/examples/multi_agent/graphworkflow_examples/rustworkx_examples/test_graph_workflow_rustworkx.py b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/test_graph_workflow_rustworkx.py
new file mode 100644
index 00000000..65cc4776
--- /dev/null
+++ b/examples/multi_agent/graphworkflow_examples/rustworkx_examples/test_graph_workflow_rustworkx.py
@@ -0,0 +1,632 @@
+import pytest
+from swarms.structs.graph_workflow import (
+ GraphWorkflow,
+)
+from swarms.structs.agent import Agent
+
+try:
+ import rustworkx as rx
+
+ RUSTWORKX_AVAILABLE = True
+except ImportError:
+ RUSTWORKX_AVAILABLE = False
+
+
+def create_test_agent(name: str, description: str = None) -> Agent:
+ """Create a test agent"""
+ if description is None:
+ description = f"Test agent for {name} operations"
+
+ return Agent(
+ agent_name=name,
+ agent_description=description,
+ model_name="gpt-4o-mini",
+ verbose=False,
+ print_on=False,
+ max_loops=1,
+ )
+
+
+@pytest.mark.skipif(
+ not RUSTWORKX_AVAILABLE, reason="rustworkx not available"
+)
+class TestRustworkxBackend:
+ """Test suite for rustworkx backend"""
+
+ def test_rustworkx_backend_initialization(self):
+ """Test that rustworkx backend is properly initialized"""
+ workflow = GraphWorkflow(name="Test", backend="rustworkx")
+ assert (
+ workflow.graph_backend.__class__.__name__
+ == "RustworkxBackend"
+ )
+ assert hasattr(workflow.graph_backend, "_node_id_to_index")
+ assert hasattr(workflow.graph_backend, "_index_to_node_id")
+ assert hasattr(workflow.graph_backend, "graph")
+
+ def test_rustworkx_node_addition(self):
+ """Test adding nodes to rustworkx backend"""
+ workflow = GraphWorkflow(name="Test", backend="rustworkx")
+ agent = create_test_agent("TestAgent", "Test agent")
+
+ workflow.add_node(agent)
+
+ assert "TestAgent" in workflow.nodes
+ assert "TestAgent" in workflow.graph_backend._node_id_to_index
+ assert (
+ workflow.graph_backend._node_id_to_index["TestAgent"]
+ in workflow.graph_backend._index_to_node_id
+ )
+
+ def test_rustworkx_edge_addition(self):
+ """Test adding edges to rustworkx backend"""
+ workflow = GraphWorkflow(name="Test", backend="rustworkx")
+ agent1 = create_test_agent("Agent1", "First agent")
+ agent2 = create_test_agent("Agent2", "Second agent")
+
+ workflow.add_node(agent1)
+ workflow.add_node(agent2)
+ workflow.add_edge(agent1, agent2)
+
+ assert len(workflow.edges) == 1
+ assert workflow.edges[0].source == "Agent1"
+ assert workflow.edges[0].target == "Agent2"
+
+ def test_rustworkx_topological_generations_linear(self):
+ """Test topological generations with linear chain"""
+ workflow = GraphWorkflow(
+ name="Linear-Test", backend="rustworkx"
+ )
+ agents = [
+ create_test_agent(f"Agent{i}", f"Agent {i}")
+ for i in range(5)
+ ]
+
+ for agent in agents:
+ workflow.add_node(agent)
+
+ for i in range(len(agents) - 1):
+ workflow.add_edge(agents[i], agents[i + 1])
+
+ workflow.compile()
+
+ assert len(workflow._sorted_layers) == 5
+ assert workflow._sorted_layers[0] == ["Agent0"]
+ assert workflow._sorted_layers[1] == ["Agent1"]
+ assert workflow._sorted_layers[2] == ["Agent2"]
+ assert workflow._sorted_layers[3] == ["Agent3"]
+ assert workflow._sorted_layers[4] == ["Agent4"]
+
+ def test_rustworkx_topological_generations_fan_out(self):
+ """Test topological generations with fan-out pattern"""
+ workflow = GraphWorkflow(
+ name="FanOut-Test", backend="rustworkx"
+ )
+ coordinator = create_test_agent("Coordinator", "Coordinates")
+ analyst1 = create_test_agent("Analyst1", "First analyst")
+ analyst2 = create_test_agent("Analyst2", "Second analyst")
+ analyst3 = create_test_agent("Analyst3", "Third analyst")
+
+ workflow.add_node(coordinator)
+ workflow.add_node(analyst1)
+ workflow.add_node(analyst2)
+ workflow.add_node(analyst3)
+
+ workflow.add_edges_from_source(
+ coordinator, [analyst1, analyst2, analyst3]
+ )
+
+ workflow.compile()
+
+ assert len(workflow._sorted_layers) == 2
+ assert len(workflow._sorted_layers[0]) == 1
+ assert "Coordinator" in workflow._sorted_layers[0]
+ assert len(workflow._sorted_layers[1]) == 3
+ assert "Analyst1" in workflow._sorted_layers[1]
+ assert "Analyst2" in workflow._sorted_layers[1]
+ assert "Analyst3" in workflow._sorted_layers[1]
+
+ def test_rustworkx_topological_generations_fan_in(self):
+ """Test topological generations with fan-in pattern"""
+ workflow = GraphWorkflow(
+ name="FanIn-Test", backend="rustworkx"
+ )
+ analyst1 = create_test_agent("Analyst1", "First analyst")
+ analyst2 = create_test_agent("Analyst2", "Second analyst")
+ analyst3 = create_test_agent("Analyst3", "Third analyst")
+ synthesizer = create_test_agent("Synthesizer", "Synthesizes")
+
+ workflow.add_node(analyst1)
+ workflow.add_node(analyst2)
+ workflow.add_node(analyst3)
+ workflow.add_node(synthesizer)
+
+ workflow.add_edges_to_target(
+ [analyst1, analyst2, analyst3], synthesizer
+ )
+
+ workflow.compile()
+
+ assert len(workflow._sorted_layers) == 2
+ assert len(workflow._sorted_layers[0]) == 3
+ assert "Analyst1" in workflow._sorted_layers[0]
+ assert "Analyst2" in workflow._sorted_layers[0]
+ assert "Analyst3" in workflow._sorted_layers[0]
+ assert len(workflow._sorted_layers[1]) == 1
+ assert "Synthesizer" in workflow._sorted_layers[1]
+
+ def test_rustworkx_topological_generations_complex(self):
+ """Test topological generations with complex topology"""
+ workflow = GraphWorkflow(
+ name="Complex-Test", backend="rustworkx"
+ )
+ agents = [
+ create_test_agent(f"Agent{i}", f"Agent {i}")
+ for i in range(6)
+ ]
+
+ for agent in agents:
+ workflow.add_node(agent)
+
+ # Create: Agent0 -> Agent1, Agent2
+ # Agent1, Agent2 -> Agent3
+ # Agent3 -> Agent4, Agent5
+ workflow.add_edge(agents[0], agents[1])
+ workflow.add_edge(agents[0], agents[2])
+ workflow.add_edge(agents[1], agents[3])
+ workflow.add_edge(agents[2], agents[3])
+ workflow.add_edge(agents[3], agents[4])
+ workflow.add_edge(agents[3], agents[5])
+
+ workflow.compile()
+
+ assert len(workflow._sorted_layers) == 4
+ assert "Agent0" in workflow._sorted_layers[0]
+ assert (
+ "Agent1" in workflow._sorted_layers[1]
+ or "Agent2" in workflow._sorted_layers[1]
+ )
+ assert "Agent3" in workflow._sorted_layers[2]
+ assert (
+ "Agent4" in workflow._sorted_layers[3]
+ or "Agent5" in workflow._sorted_layers[3]
+ )
+
+ def test_rustworkx_predecessors(self):
+ """Test predecessor retrieval"""
+ workflow = GraphWorkflow(
+ name="Predecessors-Test", backend="rustworkx"
+ )
+ agent1 = create_test_agent("Agent1", "First agent")
+ agent2 = create_test_agent("Agent2", "Second agent")
+ agent3 = create_test_agent("Agent3", "Third agent")
+
+ workflow.add_node(agent1)
+ workflow.add_node(agent2)
+ workflow.add_node(agent3)
+
+ workflow.add_edge(agent1, agent2)
+ workflow.add_edge(agent2, agent3)
+
+ predecessors = list(
+ workflow.graph_backend.predecessors("Agent2")
+ )
+ assert "Agent1" in predecessors
+ assert len(predecessors) == 1
+
+ predecessors = list(
+ workflow.graph_backend.predecessors("Agent3")
+ )
+ assert "Agent2" in predecessors
+ assert len(predecessors) == 1
+
+ predecessors = list(
+ workflow.graph_backend.predecessors("Agent1")
+ )
+ assert len(predecessors) == 0
+
+ def test_rustworkx_descendants(self):
+ """Test descendant retrieval"""
+ workflow = GraphWorkflow(
+ name="Descendants-Test", backend="rustworkx"
+ )
+ agent1 = create_test_agent("Agent1", "First agent")
+ agent2 = create_test_agent("Agent2", "Second agent")
+ agent3 = create_test_agent("Agent3", "Third agent")
+
+ workflow.add_node(agent1)
+ workflow.add_node(agent2)
+ workflow.add_node(agent3)
+
+ workflow.add_edge(agent1, agent2)
+ workflow.add_edge(agent2, agent3)
+
+ descendants = workflow.graph_backend.descendants("Agent1")
+ assert "Agent2" in descendants
+ assert "Agent3" in descendants
+ assert len(descendants) == 2
+
+ descendants = workflow.graph_backend.descendants("Agent2")
+ assert "Agent3" in descendants
+ assert len(descendants) == 1
+
+ descendants = workflow.graph_backend.descendants("Agent3")
+ assert len(descendants) == 0
+
+ def test_rustworkx_in_degree(self):
+ """Test in-degree calculation"""
+ workflow = GraphWorkflow(
+ name="InDegree-Test", backend="rustworkx"
+ )
+ agent1 = create_test_agent("Agent1", "First agent")
+ agent2 = create_test_agent("Agent2", "Second agent")
+ agent3 = create_test_agent("Agent3", "Third agent")
+
+ workflow.add_node(agent1)
+ workflow.add_node(agent2)
+ workflow.add_node(agent3)
+
+ workflow.add_edge(agent1, agent2)
+ workflow.add_edge(agent3, agent2)
+
+ assert workflow.graph_backend.in_degree("Agent1") == 0
+ assert workflow.graph_backend.in_degree("Agent2") == 2
+ assert workflow.graph_backend.in_degree("Agent3") == 0
+
+ def test_rustworkx_out_degree(self):
+ """Test out-degree calculation"""
+ workflow = GraphWorkflow(
+ name="OutDegree-Test", backend="rustworkx"
+ )
+ agent1 = create_test_agent("Agent1", "First agent")
+ agent2 = create_test_agent("Agent2", "Second agent")
+ agent3 = create_test_agent("Agent3", "Third agent")
+
+ workflow.add_node(agent1)
+ workflow.add_node(agent2)
+ workflow.add_node(agent3)
+
+ workflow.add_edge(agent1, agent2)
+ workflow.add_edge(agent1, agent3)
+
+ assert workflow.graph_backend.out_degree("Agent1") == 2
+ assert workflow.graph_backend.out_degree("Agent2") == 0
+ assert workflow.graph_backend.out_degree("Agent3") == 0
+
+ def test_rustworkx_agent_objects_in_edges(self):
+ """Test using Agent objects directly in edge methods"""
+ workflow = GraphWorkflow(
+ name="AgentObjects-Test", backend="rustworkx"
+ )
+ agent1 = create_test_agent("Agent1", "First agent")
+ agent2 = create_test_agent("Agent2", "Second agent")
+ agent3 = create_test_agent("Agent3", "Third agent")
+
+ workflow.add_node(agent1)
+ workflow.add_node(agent2)
+ workflow.add_node(agent3)
+
+ # Use Agent objects directly
+ workflow.add_edges_from_source(agent1, [agent2, agent3])
+ workflow.add_edges_to_target([agent2, agent3], agent1)
+
+ workflow.compile()
+
+ assert len(workflow.edges) == 4
+ assert len(workflow._sorted_layers) >= 1
+
+ def test_rustworkx_parallel_chain(self):
+ """Test parallel chain pattern"""
+ workflow = GraphWorkflow(
+ name="ParallelChain-Test", backend="rustworkx"
+ )
+ sources = [
+ create_test_agent(f"Source{i}", f"Source {i}")
+ for i in range(3)
+ ]
+ targets = [
+ create_test_agent(f"Target{i}", f"Target {i}")
+ for i in range(3)
+ ]
+
+ for agent in sources + targets:
+ workflow.add_node(agent)
+
+ workflow.add_parallel_chain(sources, targets)
+
+ workflow.compile()
+
+ assert len(workflow.edges) == 9 # 3x3 = 9 edges
+ assert len(workflow._sorted_layers) == 2
+
+ def test_rustworkx_large_scale(self):
+ """Test rustworkx with large workflow"""
+ workflow = GraphWorkflow(
+ name="LargeScale-Test", backend="rustworkx"
+ )
+ agents = [
+ create_test_agent(f"Agent{i}", f"Agent {i}")
+ for i in range(20)
+ ]
+
+ for agent in agents:
+ workflow.add_node(agent)
+
+ # Create linear chain
+ for i in range(len(agents) - 1):
+ workflow.add_edge(agents[i], agents[i + 1])
+
+ workflow.compile()
+
+ assert len(workflow._sorted_layers) == 20
+ assert len(workflow.nodes) == 20
+ assert len(workflow.edges) == 19
+
+ def test_rustworkx_reverse(self):
+ """Test graph reversal"""
+ workflow = GraphWorkflow(
+ name="Reverse-Test", backend="rustworkx"
+ )
+ agent1 = create_test_agent("Agent1", "First agent")
+ agent2 = create_test_agent("Agent2", "Second agent")
+
+ workflow.add_node(agent1)
+ workflow.add_node(agent2)
+ workflow.add_edge(agent1, agent2)
+
+ reversed_backend = workflow.graph_backend.reverse()
+
+ # In reversed graph, Agent2 should have Agent1 as predecessor
+ preds = list(reversed_backend.predecessors("Agent1"))
+ assert "Agent2" in preds
+
+ # Agent2 should have no predecessors in reversed graph
+ preds = list(reversed_backend.predecessors("Agent2"))
+ assert len(preds) == 0
+
+ def test_rustworkx_entry_end_points(self):
+ """Test entry and end point detection"""
+ workflow = GraphWorkflow(
+ name="EntryEnd-Test", backend="rustworkx"
+ )
+ agent1 = create_test_agent("Agent1", "Entry agent")
+ agent2 = create_test_agent("Agent2", "Middle agent")
+ agent3 = create_test_agent("Agent3", "End agent")
+
+ workflow.add_node(agent1)
+ workflow.add_node(agent2)
+ workflow.add_node(agent3)
+
+ workflow.add_edge(agent1, agent2)
+ workflow.add_edge(agent2, agent3)
+
+ workflow.auto_set_entry_points()
+ workflow.auto_set_end_points()
+
+ assert "Agent1" in workflow.entry_points
+ assert "Agent3" in workflow.end_points
+ assert workflow.graph_backend.in_degree("Agent1") == 0
+ assert workflow.graph_backend.out_degree("Agent3") == 0
+
+ def test_rustworkx_isolated_nodes(self):
+ """Test handling of isolated nodes"""
+ workflow = GraphWorkflow(
+ name="Isolated-Test", backend="rustworkx"
+ )
+ agent1 = create_test_agent("Agent1", "Connected agent")
+ agent2 = create_test_agent("Agent2", "Isolated agent")
+
+ workflow.add_node(agent1)
+ workflow.add_node(agent2)
+ workflow.add_edge(agent1, agent1) # Self-loop
+
+ workflow.compile()
+
+ assert len(workflow.nodes) == 2
+ assert "Agent2" in workflow.nodes
+
+ def test_rustworkx_workflow_execution(self):
+ """Test full workflow execution with rustworkx"""
+ workflow = GraphWorkflow(
+ name="Execution-Test", backend="rustworkx"
+ )
+ agent1 = create_test_agent("Agent1", "First agent")
+ agent2 = create_test_agent("Agent2", "Second agent")
+
+ workflow.add_node(agent1)
+ workflow.add_node(agent2)
+ workflow.add_edge(agent1, agent2)
+
+ result = workflow.run("Test task")
+
+ assert result is not None
+ assert "Agent1" in result
+ assert "Agent2" in result
+
+ def test_rustworkx_compilation_caching(self):
+ """Test that compilation is cached correctly"""
+ workflow = GraphWorkflow(
+ name="Cache-Test", backend="rustworkx"
+ )
+ agent1 = create_test_agent("Agent1", "First agent")
+ agent2 = create_test_agent("Agent2", "Second agent")
+
+ workflow.add_node(agent1)
+ workflow.add_node(agent2)
+ workflow.add_edge(agent1, agent2)
+
+ # First compilation
+ workflow.compile()
+ layers1 = workflow._sorted_layers.copy()
+ compiled1 = workflow._compiled
+
+ # Second compilation should use cache
+ workflow.compile()
+ layers2 = workflow._sorted_layers.copy()
+ compiled2 = workflow._compiled
+
+ assert compiled1 == compiled2 == True
+ assert layers1 == layers2
+
+ def test_rustworkx_node_metadata(self):
+ """Test node metadata handling"""
+ workflow = GraphWorkflow(
+ name="Metadata-Test", backend="rustworkx"
+ )
+ agent = create_test_agent("Agent", "Test agent")
+
+ workflow.add_node(
+ agent, metadata={"priority": "high", "timeout": 60}
+ )
+
+ node_index = workflow.graph_backend._node_id_to_index["Agent"]
+ node_data = workflow.graph_backend.graph[node_index]
+
+ assert isinstance(node_data, dict)
+ assert node_data.get("node_id") == "Agent"
+ assert node_data.get("priority") == "high"
+ assert node_data.get("timeout") == 60
+
+ def test_rustworkx_edge_metadata(self):
+ """Test edge metadata handling"""
+ workflow = GraphWorkflow(
+ name="EdgeMetadata-Test", backend="rustworkx"
+ )
+ agent1 = create_test_agent("Agent1", "First agent")
+ agent2 = create_test_agent("Agent2", "Second agent")
+
+ workflow.add_node(agent1)
+ workflow.add_node(agent2)
+ workflow.add_edge(agent1, agent2, weight=5, label="test")
+
+ assert len(workflow.edges) == 1
+ assert workflow.edges[0].metadata.get("weight") == 5
+ assert workflow.edges[0].metadata.get("label") == "test"
+
+
+@pytest.mark.skipif(
+ not RUSTWORKX_AVAILABLE, reason="rustworkx not available"
+)
+class TestRustworkxPerformance:
+ """Performance tests for rustworkx backend"""
+
+ def test_rustworkx_large_graph_compilation(self):
+ """Test compilation performance with large graph"""
+ workflow = GraphWorkflow(
+ name="LargeGraph-Test", backend="rustworkx"
+ )
+ agents = [
+ create_test_agent(f"Agent{i}", f"Agent {i}")
+ for i in range(50)
+ ]
+
+ for agent in agents:
+ workflow.add_node(agent)
+
+ # Create a complex topology
+ for i in range(len(agents) - 1):
+ workflow.add_edge(agents[i], agents[i + 1])
+
+ import time
+
+ start = time.time()
+ workflow.compile()
+ compile_time = time.time() - start
+
+ assert compile_time < 1.0 # Should compile quickly
+ assert len(workflow._sorted_layers) == 50
+
+ def test_rustworkx_many_predecessors(self):
+ """Test performance with many predecessors"""
+ workflow = GraphWorkflow(
+ name="ManyPreds-Test", backend="rustworkx"
+ )
+ target = create_test_agent("Target", "Target agent")
+ sources = [
+ create_test_agent(f"Source{i}", f"Source {i}")
+ for i in range(100)
+ ]
+
+ workflow.add_node(target)
+ for source in sources:
+ workflow.add_node(source)
+
+ workflow.add_edges_to_target(sources, target)
+
+ workflow.compile()
+
+ predecessors = list(
+ workflow.graph_backend.predecessors("Target")
+ )
+ assert len(predecessors) == 100
+
+
+@pytest.mark.skipif(
+ not RUSTWORKX_AVAILABLE, reason="rustworkx not available"
+)
+class TestRustworkxEdgeCases:
+ """Edge case tests for rustworkx backend"""
+
+ def test_rustworkx_empty_graph(self):
+ """Test empty graph handling"""
+ workflow = GraphWorkflow(
+ name="Empty-Test", backend="rustworkx"
+ )
+ workflow.compile()
+
+ assert len(workflow._sorted_layers) == 0
+ assert len(workflow.nodes) == 0
+
+ def test_rustworkx_single_node(self):
+ """Test single node graph"""
+ workflow = GraphWorkflow(
+ name="Single-Test", backend="rustworkx"
+ )
+ agent = create_test_agent("Agent", "Single agent")
+
+ workflow.add_node(agent)
+ workflow.compile()
+
+ assert len(workflow._sorted_layers) == 1
+ assert workflow._sorted_layers[0] == ["Agent"]
+
+ def test_rustworkx_self_loop(self):
+ """Test self-loop handling"""
+ workflow = GraphWorkflow(
+ name="SelfLoop-Test", backend="rustworkx"
+ )
+ agent = create_test_agent("Agent", "Self-looping agent")
+
+ workflow.add_node(agent)
+ workflow.add_edge(agent, agent)
+
+ workflow.compile()
+
+ assert len(workflow.edges) == 1
+ assert workflow.graph_backend.in_degree("Agent") == 1
+ assert workflow.graph_backend.out_degree("Agent") == 1
+
+ def test_rustworkx_duplicate_edge(self):
+ """Test duplicate edge handling"""
+ workflow = GraphWorkflow(
+ name="Duplicate-Test", backend="rustworkx"
+ )
+ agent1 = create_test_agent("Agent1", "First agent")
+ agent2 = create_test_agent("Agent2", "Second agent")
+
+ workflow.add_node(agent1)
+ workflow.add_node(agent2)
+
+ # Add same edge twice
+ workflow.add_edge(agent1, agent2)
+ workflow.add_edge(agent1, agent2)
+
+ # rustworkx should handle duplicate edges
+ assert (
+ len(workflow.edges) == 2
+ ) # Both edges are stored in workflow
+ workflow.compile() # Should not crash
+
+
+if __name__ == "__main__":
+ pytest.main([__file__, "-v"])
diff --git a/examples/multi_agent/llm_council_examples/README.md b/examples/multi_agent/llm_council_examples/README.md
new file mode 100644
index 00000000..3dd62f16
--- /dev/null
+++ b/examples/multi_agent/llm_council_examples/README.md
@@ -0,0 +1,95 @@
+# LLM Council Examples
+
+This directory contains examples demonstrating the LLM Council pattern, inspired by Andrej Karpathy's llm-council implementation. The LLM Council uses multiple specialized AI agents that:
+
+1. Each respond independently to queries
+2. Review and rank each other's anonymized responses
+3. Have a Chairman synthesize all responses into a final comprehensive answer
+
+## Examples
+
+### Marketing & Business
+- **marketing_strategy_council.py** - Marketing strategy analysis and recommendations
+- **business_strategy_council.py** - Comprehensive business strategy development
+
+### Finance & Investment
+- **finance_analysis_council.py** - Financial analysis and investment recommendations
+- **etf_stock_analysis_council.py** - ETF and stock analysis with portfolio recommendations
+
+### Medical & Healthcare
+- **medical_treatment_council.py** - Medical treatment recommendations and care plans
+- **medical_diagnosis_council.py** - Diagnostic analysis based on symptoms
+
+### Technology & Research
+- **technology_assessment_council.py** - Technology evaluation and implementation strategy
+- **research_analysis_council.py** - Comprehensive research analysis on complex topics
+
+### Legal
+- **legal_analysis_council.py** - Legal implications and compliance analysis
+
+## Usage
+
+Each example follows the same pattern:
+
+```python
+from swarms.structs.llm_council import LLMCouncil
+
+# Create the council
+council = LLMCouncil(verbose=True)
+
+# Run a query
+result = council.run("Your query here")
+
+# Access results
+print(result["final_response"]) # Chairman's synthesized answer
+print(result["original_responses"]) # Individual member responses
+print(result["evaluations"]) # How members ranked each other
+```
+
+## Running Examples
+
+Run any example directly:
+
+```bash
+python examples/multi_agent/llm_council_examples/marketing_strategy_council.py
+python examples/multi_agent/llm_council_examples/finance_analysis_council.py
+python examples/multi_agent/llm_council_examples/medical_diagnosis_council.py
+```
+
+## Key Features
+
+- **Multiple Perspectives**: Each council member (GPT-5.1, Gemini, Claude, Grok) provides unique insights
+- **Peer Review**: Members evaluate and rank each other's responses anonymously
+- **Synthesis**: Chairman combines the best elements from all responses
+- **Transparency**: See both individual responses and evaluation rankings
+
+## Council Members
+
+The default council consists of:
+- **GPT-5.1-Councilor**: Analytical and comprehensive
+- **Gemini-3-Pro-Councilor**: Concise and well-processed
+- **Claude-Sonnet-4.5-Councilor**: Thoughtful and balanced
+- **Grok-4-Councilor**: Creative and innovative
+
+## Customization
+
+You can create custom council members:
+
+```python
+from swarms import Agent
+from swarms.structs.llm_council import LLMCouncil, get_gpt_councilor_prompt
+
+custom_agent = Agent(
+ agent_name="Custom-Councilor",
+ system_prompt=get_gpt_councilor_prompt(),
+ model_name="gpt-4.1",
+ max_loops=1,
+)
+
+council = LLMCouncil(
+ council_members=[custom_agent, ...],
+ chairman_model="gpt-5.1",
+ verbose=True
+)
+```
+
diff --git a/examples/multi_agent/llm_council_examples/business_strategy_council.py b/examples/multi_agent/llm_council_examples/business_strategy_council.py
new file mode 100644
index 00000000..10b5087b
--- /dev/null
+++ b/examples/multi_agent/llm_council_examples/business_strategy_council.py
@@ -0,0 +1,31 @@
+"""
+LLM Council Example: Business Strategy Development
+
+This example demonstrates using the LLM Council to develop comprehensive
+business strategies for new ventures.
+"""
+
+from swarms.structs.llm_council import LLMCouncil
+
+# Create the council
+council = LLMCouncil(verbose=True)
+
+# Business strategy query
+query = """
+A tech startup wants to launch an AI-powered personal finance app targeting
+millennials and Gen Z. Develop a comprehensive business strategy including:
+1. Market opportunity and competitive landscape analysis
+2. Product positioning and unique value proposition
+3. Go-to-market strategy and customer acquisition plan
+4. Revenue model and pricing strategy
+5. Key partnerships and distribution channels
+6. Resource requirements and funding needs
+7. Risk assessment and mitigation strategies
+8. Success metrics and KPIs for first 12 months
+"""
+
+# Run the council
+result = council.run(query)
+
+# Print final response
+print(result["final_response"])
diff --git a/examples/multi_agent/llm_council_examples/etf_stock_analysis_council.py b/examples/multi_agent/llm_council_examples/etf_stock_analysis_council.py
new file mode 100644
index 00000000..7e85d851
--- /dev/null
+++ b/examples/multi_agent/llm_council_examples/etf_stock_analysis_council.py
@@ -0,0 +1,29 @@
+"""
+LLM Council Example: ETF Stock Analysis
+
+This example demonstrates using the LLM Council to analyze ETF holdings
+and provide stock investment recommendations.
+"""
+
+from swarms.structs.llm_council import LLMCouncil
+
+# Create the council
+council = LLMCouncil(verbose=True)
+
+# ETF and stock analysis query
+query = """
+Analyze the top energy ETFs (including nuclear, solar, gas, and renewable energy)
+and provide:
+1. Top 5 best-performing energy stocks across all energy sectors
+2. ETF recommendations for diversified energy exposure
+3. Risk-return profiles for each recommendation
+4. Current market conditions affecting energy investments
+5. Allocation strategy for a $100,000 portfolio
+6. Key metrics to track for each investment
+"""
+
+# Run the council
+result = council.run(query)
+
+# Print final response
+print(result["final_response"])
diff --git a/examples/multi_agent/llm_council_examples/finance_analysis_council.py b/examples/multi_agent/llm_council_examples/finance_analysis_council.py
new file mode 100644
index 00000000..f014be47
--- /dev/null
+++ b/examples/multi_agent/llm_council_examples/finance_analysis_council.py
@@ -0,0 +1,29 @@
+"""
+LLM Council Example: Financial Analysis
+
+This example demonstrates using the LLM Council to provide comprehensive
+financial analysis and investment recommendations.
+"""
+
+from swarms.structs.llm_council import LLMCouncil
+
+# Create the council
+council = LLMCouncil(verbose=True)
+
+# Financial analysis query
+query = """
+Provide a comprehensive financial analysis for investing in emerging markets
+technology ETFs. Include:
+1. Risk assessment and volatility analysis
+2. Historical performance trends
+3. Sector composition and diversification benefits
+4. Comparison with developed market tech ETFs
+5. Recommended allocation percentage for a moderate risk portfolio
+6. Key factors to monitor going forward
+"""
+
+# Run the council
+result = council.run(query)
+
+# Print final response
+print(result["final_response"])
diff --git a/examples/multi_agent/llm_council_examples/legal_analysis_council.py b/examples/multi_agent/llm_council_examples/legal_analysis_council.py
new file mode 100644
index 00000000..5ea3481e
--- /dev/null
+++ b/examples/multi_agent/llm_council_examples/legal_analysis_council.py
@@ -0,0 +1,31 @@
+"""
+LLM Council Example: Legal Analysis
+
+This example demonstrates using the LLM Council to analyze legal scenarios
+and provide comprehensive legal insights.
+"""
+
+from swarms.structs.llm_council import LLMCouncil
+
+# Create the council
+council = LLMCouncil(verbose=True)
+
+# Legal analysis query
+query = """
+A startup is considering using AI-generated content for their marketing materials.
+Analyze the legal implications including:
+1. Intellectual property rights and ownership of AI-generated content
+2. Copyright and trademark considerations
+3. Liability for AI-generated content that may be inaccurate or misleading
+4. Compliance with advertising regulations (FTC, FDA, etc.)
+5. Data privacy implications if using customer data to train models
+6. Contractual considerations with AI service providers
+7. Risk mitigation strategies
+8. Best practices for legal compliance
+"""
+
+# Run the council
+result = council.run(query)
+
+# Print final response
+print(result["final_response"])
diff --git a/examples/multi_agent/llm_council_examples/marketing_strategy_council.py b/examples/multi_agent/llm_council_examples/marketing_strategy_council.py
new file mode 100644
index 00000000..a799c364
--- /dev/null
+++ b/examples/multi_agent/llm_council_examples/marketing_strategy_council.py
@@ -0,0 +1,28 @@
+"""
+LLM Council Example: Marketing Strategy Analysis
+
+This example demonstrates using the LLM Council to analyze and develop
+comprehensive marketing strategies by leveraging multiple AI perspectives.
+"""
+
+from swarms.structs.llm_council import LLMCouncil
+
+# Create the council
+council = LLMCouncil(verbose=True)
+
+# Marketing strategy query
+query = """
+Analyze the marketing strategy for a new sustainable energy startup launching
+a solar panel subscription service. Provide recommendations on:
+1. Target audience segmentation
+2. Key messaging and value propositions
+3. Marketing channels and budget allocation
+4. Competitive positioning
+5. Launch timeline and milestones
+"""
+
+# Run the council
+result = council.run(query)
+
+# Print final response
+print(result["final_response"])
diff --git a/examples/multi_agent/llm_council_examples/medical_diagnosis_council.py b/examples/multi_agent/llm_council_examples/medical_diagnosis_council.py
new file mode 100644
index 00000000..90532f38
--- /dev/null
+++ b/examples/multi_agent/llm_council_examples/medical_diagnosis_council.py
@@ -0,0 +1,36 @@
+"""
+LLM Council Example: Medical Diagnosis Analysis
+
+This example demonstrates using the LLM Council to analyze symptoms
+and provide diagnostic insights.
+"""
+
+from swarms.structs.llm_council import LLMCouncil
+
+# Create the council
+council = LLMCouncil(verbose=True)
+
+# Medical diagnosis query
+query = """
+A 35-year-old patient presents with:
+- Persistent fatigue for 3 months
+- Unexplained weight loss (15 lbs)
+- Night sweats
+- Intermittent low-grade fever
+- Swollen lymph nodes in neck and armpits
+- Recent blood work shows elevated ESR and CRP
+
+Provide:
+1. Differential diagnosis with most likely conditions ranked
+2. Additional diagnostic tests needed to confirm
+3. Red flag symptoms requiring immediate attention
+4. Possible causes and risk factors
+5. Recommended next steps for the patient
+6. When to seek emergency care
+"""
+
+# Run the council
+result = council.run(query)
+
+# Print final response
+print(result["final_response"])
diff --git a/examples/multi_agent/llm_council_examples/medical_treatment_council.py b/examples/multi_agent/llm_council_examples/medical_treatment_council.py
new file mode 100644
index 00000000..6084db4c
--- /dev/null
+++ b/examples/multi_agent/llm_council_examples/medical_treatment_council.py
@@ -0,0 +1,30 @@
+"""
+LLM Council Example: Medical Treatment Analysis
+
+This example demonstrates using the LLM Council to analyze medical treatments
+and provide comprehensive treatment recommendations.
+"""
+
+from swarms.structs.llm_council import LLMCouncil
+
+# Create the council
+council = LLMCouncil(verbose=True)
+
+# Medical treatment query
+query = """
+A 45-year-old patient with Type 2 diabetes, hypertension, and early-stage
+kidney disease needs treatment recommendations. Provide:
+1. Comprehensive treatment plan addressing all conditions
+2. Medication options with pros/cons for each condition
+3. Lifestyle modifications and their expected impact
+4. Monitoring schedule and key metrics to track
+5. Potential drug interactions and contraindications
+6. Expected outcomes and timeline for improvement
+7. When to consider specialist referrals
+"""
+
+# Run the council
+result = council.run(query)
+
+# Print final response
+print(result["final_response"])
diff --git a/examples/multi_agent/llm_council_examples/research_analysis_council.py b/examples/multi_agent/llm_council_examples/research_analysis_council.py
new file mode 100644
index 00000000..74a8585a
--- /dev/null
+++ b/examples/multi_agent/llm_council_examples/research_analysis_council.py
@@ -0,0 +1,31 @@
+"""
+LLM Council Example: Research Analysis
+
+This example demonstrates using the LLM Council to conduct comprehensive
+research analysis on complex topics.
+"""
+
+from swarms.structs.llm_council import LLMCouncil
+
+# Create the council
+council = LLMCouncil(verbose=True)
+
+# Research analysis query
+query = """
+Conduct a comprehensive analysis of the potential impact of climate change
+on global food security over the next 20 years. Include:
+1. Key climate factors affecting agriculture (temperature, precipitation, extreme weather)
+2. Regional vulnerabilities and impacts on major food-producing regions
+3. Crop yield projections and food availability scenarios
+4. Economic implications and food price volatility
+5. Adaptation strategies and technological solutions
+6. Policy recommendations for governments and international organizations
+7. Role of innovation in agriculture (precision farming, GMOs, vertical farming)
+8. Social and geopolitical implications of food insecurity
+"""
+
+# Run the council
+result = council.run(query)
+
+# Print final response
+print(result["final_response"])
diff --git a/examples/multi_agent/llm_council_examples/technology_assessment_council.py b/examples/multi_agent/llm_council_examples/technology_assessment_council.py
new file mode 100644
index 00000000..4db4dd95
--- /dev/null
+++ b/examples/multi_agent/llm_council_examples/technology_assessment_council.py
@@ -0,0 +1,31 @@
+"""
+LLM Council Example: Technology Assessment
+
+This example demonstrates using the LLM Council to assess emerging technologies
+and their business implications.
+"""
+
+from swarms.structs.llm_council import LLMCouncil
+
+# Create the council
+council = LLMCouncil(verbose=True)
+
+# Technology assessment query
+query = """
+Evaluate the business potential and implementation strategy for integrating
+quantum computing capabilities into a financial services company. Consider:
+1. Current state of quantum computing technology
+2. Specific use cases in financial services (risk modeling, portfolio optimization, fraud detection)
+3. Competitive advantages and potential ROI
+4. Implementation timeline and resource requirements
+5. Technical challenges and limitations
+6. Risk factors and mitigation strategies
+7. Partnership opportunities with quantum computing providers
+8. Expected timeline for practical business value
+"""
+
+# Run the council
+result = council.run(query)
+
+# Print final response
+print(result["final_response"])
diff --git a/examples/multi_agent/swarm_router/swarm_router.py b/examples/multi_agent/swarm_router/swarm_router.py
index 1801c25a..b8f73365 100644
--- a/examples/multi_agent/swarm_router/swarm_router.py
+++ b/examples/multi_agent/swarm_router/swarm_router.py
@@ -26,7 +26,6 @@ router = SwarmRouter(
agents=agents,
swarm_type="SequentialWorkflow",
output_type="dict",
- return_entire_history=False,
)
output = router.run("How are you doing?")
diff --git a/hiearchical_swarm_example.py b/hiearchical_swarm_example.py
index 753ebf0f..a5ed0633 100644
--- a/hiearchical_swarm_example.py
+++ b/hiearchical_swarm_example.py
@@ -1,5 +1,4 @@
-from swarms.structs.hiearchical_swarm import HierarchicalSwarm
-from swarms.structs.agent import Agent
+from swarms import Agent, HierarchicalSwarm
# Create specialized agents
research_agent = Agent(
diff --git a/llm_council_example.py b/llm_council_example.py
new file mode 100644
index 00000000..1cc415d0
--- /dev/null
+++ b/llm_council_example.py
@@ -0,0 +1,22 @@
+from swarms.structs.llm_council import LLMCouncil
+
+# Create the council
+council = LLMCouncil(verbose=True)
+
+# Example query
+query = "What are the top five best energy stocks across nuclear, solar, gas, and other energy sources?"
+
+# Run the council
+result = council.run(query)
+
+# Print final response
+print(result["final_response"])
+
+# Optionally print evaluations
+for name, evaluation in result["evaluations"].items():
+ print(f"\n{name}:")
+ print(
+ evaluation[:500] + "..."
+ if len(evaluation) > 500
+ else evaluation
+ )
diff --git a/pyproject.toml b/pyproject.toml
index 10ad1565..0336f41f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api"
[tool.poetry]
name = "swarms"
-version = "8.6.3"
+version = "8.6.5"
description = "Swarms - TGSC"
license = "MIT"
authors = ["Kye Gomez "]
diff --git a/swarms/agents/reasoning_agents.py b/swarms/agents/reasoning_agents.py
index 122ccb01..e64ab828 100644
--- a/swarms/agents/reasoning_agents.py
+++ b/swarms/agents/reasoning_agents.py
@@ -88,9 +88,7 @@ class ReasoningAgentRouter:
eval: bool = False,
random_models_on: bool = False,
majority_voting_prompt: Optional[str] = None,
- reasoning_model_name: Optional[
- str
- ] = "claude-3-5-sonnet-20240620",
+ reasoning_model_name: Optional[str] = "gpt-4o",
):
"""
Initialize the ReasoningAgentRouter with the specified configuration.
diff --git a/swarms/agents/reasoning_duo.py b/swarms/agents/reasoning_duo.py
index c0ddc156..581a69e7 100644
--- a/swarms/agents/reasoning_duo.py
+++ b/swarms/agents/reasoning_duo.py
@@ -35,9 +35,7 @@ class ReasoningDuo:
model_names: list[str] = ["gpt-4o-mini", "gpt-4.1"],
system_prompt: str = "You are a helpful assistant that can answer questions and help with tasks.",
output_type: OutputType = "dict-all-except-first",
- reasoning_model_name: Optional[
- str
- ] = "claude-3-5-sonnet-20240620",
+ reasoning_model_name: Optional[str] = "gpt-4o",
max_loops: int = 1,
*args,
**kwargs,
diff --git a/swarms/structs/__init__.py b/swarms/structs/__init__.py
index e0d3430a..6952d2b0 100644
--- a/swarms/structs/__init__.py
+++ b/swarms/structs/__init__.py
@@ -11,6 +11,7 @@ from swarms.structs.concurrent_workflow import ConcurrentWorkflow
from swarms.structs.conversation import Conversation
from swarms.structs.council_as_judge import CouncilAsAJudge
from swarms.structs.cron_job import CronJob
+from swarms.structs.llm_council import LLMCouncil
from swarms.structs.debate_with_judge import DebateWithJudge
from swarms.structs.graph_workflow import (
Edge,
@@ -161,6 +162,7 @@ __all__ = [
"get_swarms_info",
"AutoSwarmBuilder",
"CouncilAsAJudge",
+ "LLMCouncil",
"batch_agent_execution",
"aggregate",
"find_agent_by_name",
diff --git a/swarms/structs/agent_rearrange.py b/swarms/structs/agent_rearrange.py
index d3016de4..a0155ef6 100644
--- a/swarms/structs/agent_rearrange.py
+++ b/swarms/structs/agent_rearrange.py
@@ -1,7 +1,7 @@
import json
from concurrent.futures import ThreadPoolExecutor
from typing import Any, Callable, Dict, List, Optional, Union
-
+import asyncio
from swarms.structs.agent import Agent
from swarms.structs.conversation import Conversation
from swarms.structs.multi_agent_exec import run_agents_concurrently
@@ -908,6 +908,45 @@ class AgentRearrange:
except Exception as e:
self._catch_error(e)
+ async def run_async(
+ self,
+ task: str,
+ img: Optional[str] = None,
+ *args,
+ **kwargs,
+ ) -> Any:
+ """
+ Asynchronously executes a task through the agent workflow.
+
+ This method enables asynchronous execution of tasks by running the
+ synchronous run method in a separate thread using asyncio.to_thread.
+ This is ideal for integrating the agent workflow into async applications
+ or when you want non-blocking execution.
+
+ Args:
+ task (str): The task to be executed through the agent workflow.
+ img (Optional[str]): Optional image input for the task. Defaults to None.
+ *args: Additional positional arguments passed to the run method.
+ **kwargs: Additional keyword arguments passed to the run method.
+
+ Returns:
+ Any: The result of the task execution, format depends on output_type setting.
+
+ Raises:
+ Exception: If an error occurs during task execution.
+
+ Note:
+ This method uses asyncio.to_thread to run the synchronous run method
+ asynchronously, allowing integration with async/await patterns.
+ """
+
+ try:
+ return await asyncio.to_thread(
+ self.run, task=task, img=img, *args, **kwargs
+ )
+ except Exception as e:
+ self._catch_error(e)
+
def _serialize_callable(
self, attr_value: Callable
) -> Dict[str, Any]:
diff --git a/swarms/structs/aop.py b/swarms/structs/aop.py
index a8f7bea4..141dfe62 100644
--- a/swarms/structs/aop.py
+++ b/swarms/structs/aop.py
@@ -12,8 +12,10 @@ from typing import Any, Callable, Dict, List, Literal, Optional
from uuid import uuid4
from loguru import logger
+from mcp.server.auth.settings import AuthSettings
from mcp.server.fastmcp import FastMCP
from mcp.server.lowlevel.server import LifespanResultT
+from mcp.server.transport_security import TransportSecuritySettings
from swarms.structs.agent import Agent
from swarms.structs.omni_agent_types import AgentType
@@ -21,7 +23,6 @@ from swarms.tools.mcp_client_tools import (
get_tools_for_multiple_mcp_servers,
)
-from mcp.server.fastmcp import AuthSettings, TransportSecuritySettings
class TaskStatus(Enum):
"""Status of a task in the queue."""
@@ -603,7 +604,13 @@ class AOP:
log_level: Literal[
"DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"
] = "INFO",
- lifespan: Callable[[FastMCP[LifespanResultT]], AbstractAsyncContextManager[LifespanResultT]] | None = None,
+ lifespan: (
+ Callable[
+ [FastMCP[LifespanResultT]],
+ AbstractAsyncContextManager[LifespanResultT],
+ ]
+ | None
+ ) = None,
auth: AuthSettings | None = None,
transport_security: TransportSecuritySettings | None = None,
*args,
@@ -672,6 +679,7 @@ class AOP:
self.tool_configs: Dict[str, AgentToolConfig] = {}
self.task_queues: Dict[str, TaskQueue] = {}
self.transport = transport
+
self.mcp_server = FastMCP(
name=server_name,
port=port,
diff --git a/swarms/structs/graph_workflow.py b/swarms/structs/graph_workflow.py
index 4a2b0c90..d1a23594 100644
--- a/swarms/structs/graph_workflow.py
+++ b/swarms/structs/graph_workflow.py
@@ -1,10 +1,10 @@
-import json
import asyncio
import concurrent.futures
+import json
import time
-from enum import Enum
-from typing import Any, Dict, List, Optional
import uuid
+from enum import Enum
+from typing import Any, Dict, Iterator, List, Optional, Set
import networkx as nx
@@ -16,6 +16,14 @@ except ImportError:
GRAPHVIZ_AVAILABLE = False
graphviz = None
+try:
+ import rustworkx as rx
+
+ RUSTWORKX_AVAILABLE = True
+except ImportError:
+ RUSTWORKX_AVAILABLE = False
+ rx = None
+
from swarms.structs.agent import Agent # noqa: F401
from swarms.structs.conversation import Conversation
from swarms.utils.get_cpu_cores import get_cpu_cores
@@ -24,6 +32,525 @@ from swarms.utils.loguru_logger import initialize_logger
logger = initialize_logger(log_folder="graph_workflow")
+class GraphBackend:
+ """
+ Abstract base class for graph backends.
+ Provides a unified interface for different graph libraries.
+ """
+
+ def add_node(self, node_id: str, **attrs) -> None:
+ """
+ Add a node to the graph.
+
+ Args:
+ node_id (str): The unique identifier of the node.
+ **attrs: Additional attributes for the node.
+ """
+ raise NotImplementedError
+
+ def add_edge(self, source: str, target: str, **attrs) -> None:
+ """
+ Add an edge to the graph.
+
+ Args:
+ source (str): The source node ID.
+ target (str): The target node ID.
+ **attrs: Additional attributes for the edge.
+ """
+ raise NotImplementedError
+
+ def in_degree(self, node_id: str) -> int:
+ """
+ Get the in-degree of a node.
+
+ Args:
+ node_id (str): The node ID.
+
+ Returns:
+ int: The in-degree of the node.
+ """
+ raise NotImplementedError
+
+ def out_degree(self, node_id: str) -> int:
+ """
+ Get the out-degree of a node.
+
+ Args:
+ node_id (str): The node ID.
+
+ Returns:
+ int: The out-degree of the node.
+ """
+ raise NotImplementedError
+
+ def predecessors(self, node_id: str) -> Iterator[str]:
+ """
+ Get the predecessors of a node.
+
+ Args:
+ node_id (str): The node ID.
+
+ Returns:
+ Iterator[str]: Iterator of predecessor node IDs.
+ """
+ raise NotImplementedError
+
+ def reverse(self) -> "GraphBackend":
+ """
+ Return a reversed copy of the graph.
+
+ Returns:
+ GraphBackend: A new backend instance with reversed edges.
+ """
+ raise NotImplementedError
+
+ def topological_generations(self) -> List[List[str]]:
+ """
+ Get topological generations (layers) of the graph.
+
+ Returns:
+ List[List[str]]: List of layers, where each layer is a list of node IDs.
+ """
+ raise NotImplementedError
+
+ def simple_cycles(self) -> List[List[str]]:
+ """
+ Find simple cycles in the graph.
+
+ Returns:
+ List[List[str]]: List of cycles, where each cycle is a list of node IDs.
+ """
+ raise NotImplementedError
+
+ def descendants(self, node_id: str) -> Set[str]:
+ """
+ Get all descendants of a node.
+
+ Args:
+ node_id (str): The node ID.
+
+ Returns:
+ Set[str]: Set of descendant node IDs.
+ """
+ raise NotImplementedError
+
+
+class NetworkXBackend(GraphBackend):
+ """
+ NetworkX backend implementation.
+ """
+
+ def __init__(self):
+ """
+ Initialize the NetworkX backend.
+ """
+ self.graph = nx.DiGraph()
+
+ def add_node(self, node_id: str, **attrs) -> None:
+ """
+ Add a node to the NetworkX graph.
+
+ Args:
+ node_id (str): The unique identifier of the node.
+ **attrs: Additional attributes for the node.
+ """
+ self.graph.add_node(node_id, **attrs)
+
+ def add_edge(self, source: str, target: str, **attrs) -> None:
+ """
+ Add an edge to the NetworkX graph.
+
+ Args:
+ source (str): The source node ID.
+ target (str): The target node ID.
+ **attrs: Additional attributes for the edge.
+ """
+ self.graph.add_edge(source, target, **attrs)
+
+ def in_degree(self, node_id: str) -> int:
+ """
+ Get the in-degree of a node.
+
+ Args:
+ node_id (str): The node ID.
+
+ Returns:
+ int: The in-degree of the node.
+ """
+ return self.graph.in_degree(node_id)
+
+ def out_degree(self, node_id: str) -> int:
+ """
+ Get the out-degree of a node.
+
+ Args:
+ node_id (str): The node ID.
+
+ Returns:
+ int: The out-degree of the node.
+ """
+ return self.graph.out_degree(node_id)
+
+ def predecessors(self, node_id: str) -> Iterator[str]:
+ """
+ Get the predecessors of a node.
+
+ Args:
+ node_id (str): The node ID.
+
+ Returns:
+ Iterator[str]: Iterator of predecessor node IDs.
+ """
+ return self.graph.predecessors(node_id)
+
+ def reverse(self) -> "NetworkXBackend":
+ """
+ Return a reversed copy of the graph.
+
+ Returns:
+ NetworkXBackend: A new backend instance with reversed edges.
+ """
+ reversed_backend = NetworkXBackend()
+ reversed_backend.graph = self.graph.reverse()
+ return reversed_backend
+
+ def topological_generations(self) -> List[List[str]]:
+ """
+ Get topological generations (layers) of the graph.
+
+ Returns:
+ List[List[str]]: List of layers, where each layer is a list of node IDs.
+ """
+ return list(nx.topological_generations(self.graph))
+
+ def simple_cycles(self) -> List[List[str]]:
+ """
+ Find simple cycles in the graph.
+
+ Returns:
+ List[List[str]]: List of cycles, where each cycle is a list of node IDs.
+ """
+ return list(nx.simple_cycles(self.graph))
+
+ def descendants(self, node_id: str) -> Set[str]:
+ """
+ Get all descendants of a node.
+
+ Args:
+ node_id (str): The node ID.
+
+ Returns:
+ Set[str]: Set of descendant node IDs.
+ """
+ return nx.descendants(self.graph, node_id)
+
+
+class RustworkxBackend(GraphBackend):
+ """
+ Rustworkx backend implementation.
+ Uses integer indices internally but exposes string node IDs.
+ """
+
+ def __init__(self):
+ """
+ Initialize the Rustworkx backend.
+ """
+ if not RUSTWORKX_AVAILABLE:
+ raise ImportError(
+ "rustworkx is not installed. Install it with: pip install rustworkx"
+ )
+ self.graph = rx.PyDiGraph()
+ # Mapping from node ID (string) to node index (int)
+ self._node_id_to_index: Dict[str, int] = {}
+ # Mapping from node index (int) to node ID (string)
+ self._index_to_node_id: Dict[int, str] = {}
+
+ def _get_or_create_node_index(self, node_id: str) -> int:
+ """
+ Get the node index for a given node ID, creating it if necessary.
+
+ Args:
+ node_id (str): The node ID.
+
+ Returns:
+ int: The node index.
+ """
+ if node_id not in self._node_id_to_index:
+ node_index = self.graph.add_node(node_id)
+ self._node_id_to_index[node_id] = node_index
+ self._index_to_node_id[node_index] = node_id
+ return self._node_id_to_index[node_id]
+
+ def add_node(self, node_id: str, **attrs) -> None:
+ """
+ Add a node to the Rustworkx graph.
+
+ Args:
+ node_id (str): The unique identifier of the node.
+ **attrs: Additional attributes for the node (stored in node data).
+ """
+ if node_id not in self._node_id_to_index:
+ # Store node data as a dict with the node_id and attributes
+ node_data = {"node_id": node_id, **attrs}
+ node_index = self.graph.add_node(node_data)
+ self._node_id_to_index[node_id] = node_index
+ self._index_to_node_id[node_index] = node_id
+ else:
+ # Update existing node data
+ node_index = self._node_id_to_index[node_id]
+ node_data = self.graph[node_index]
+ if isinstance(node_data, dict):
+ node_data.update(attrs)
+ else:
+ self.graph[node_index] = {"node_id": node_id, **attrs}
+
+ def add_edge(self, source: str, target: str, **attrs) -> None:
+ """
+ Add an edge to the Rustworkx graph.
+
+ Args:
+ source (str): The source node ID.
+ target (str): The target node ID.
+ **attrs: Additional attributes for the edge (stored in edge data).
+ """
+ source_idx = self._get_or_create_node_index(source)
+ target_idx = self._get_or_create_node_index(target)
+ edge_data = attrs if attrs else None
+ self.graph.add_edge(source_idx, target_idx, edge_data)
+
+ def in_degree(self, node_id: str) -> int:
+ """
+ Get the in-degree of a node.
+
+ Args:
+ node_id (str): The node ID.
+
+ Returns:
+ int: The in-degree of the node.
+ """
+ if node_id not in self._node_id_to_index:
+ return 0
+ node_index = self._node_id_to_index[node_id]
+ return self.graph.in_degree(node_index)
+
+ def out_degree(self, node_id: str) -> int:
+ """
+ Get the out-degree of a node.
+
+ Args:
+ node_id (str): The node ID.
+
+ Returns:
+ int: The out-degree of the node.
+ """
+ if node_id not in self._node_id_to_index:
+ return 0
+ node_index = self._node_id_to_index[node_id]
+ return self.graph.out_degree(node_index)
+
+ def predecessors(self, node_id: str) -> Iterator[str]:
+ """
+ Get the predecessors of a node.
+
+ Args:
+ node_id (str): The node ID.
+
+ Returns:
+ Iterator[str]: Iterator of predecessor node IDs.
+ """
+ if node_id not in self._node_id_to_index:
+ return iter([])
+ target_index = self._node_id_to_index[node_id]
+ # Use edge list to find predecessors (more reliable than predecessors() method)
+ result = []
+ for edge in self.graph.edge_list():
+ source_idx, target_idx = edge
+ if target_idx == target_index:
+ result.append(self._index_to_node_id[source_idx])
+ return iter(result)
+
+ def reverse(self) -> "RustworkxBackend":
+ """
+ Return a reversed copy of the graph.
+
+ Returns:
+ RustworkxBackend: A new backend instance with reversed edges.
+ """
+ reversed_backend = RustworkxBackend()
+ # Copy the graph structure
+ reversed_backend.graph = self.graph.copy()
+ # Reverse the edges
+ reversed_backend.graph.reverse()
+ # Copy the mappings
+ reversed_backend._node_id_to_index = (
+ self._node_id_to_index.copy()
+ )
+ reversed_backend._index_to_node_id = (
+ self._index_to_node_id.copy()
+ )
+ return reversed_backend
+
+ def topological_generations(self) -> List[List[str]]:
+ """
+ Get topological generations (layers) of the graph.
+
+ Returns:
+ List[List[str]]: List of layers, where each layer is a list of node IDs.
+ """
+ try:
+ # Get all node indices
+ all_indices = list(self._node_id_to_index.values())
+ if not all_indices:
+ return []
+
+ # Use layer-by-layer approach similar to NetworkX topological_generations
+ layers = []
+ remaining = set(all_indices)
+ processed = set()
+
+ while remaining:
+ # Find all nodes with in-degree 0 considering only edges from processed nodes
+ # In rustworkx, we need to check if all predecessors are in processed set
+ layer = []
+ # First pass: identify nodes that can be added to this layer
+ # (without modifying remaining/processed during iteration)
+ nodes_to_add = []
+ for idx in list(remaining):
+ # Get all predecessors using edge list
+ pred_indices = []
+ for edge in self.graph.edge_list():
+ source_idx, target_idx = edge
+ if target_idx == idx:
+ pred_indices.append(source_idx)
+ # Check if all predecessors have been processed (or node has no predecessors)
+ # A node can be added to the layer if:
+ # 1. It has no predecessors (entry node), OR
+ # 2. All its predecessors have already been processed (from previous layers)
+ if not pred_indices:
+ # No predecessors - this is an entry node
+ nodes_to_add.append(idx)
+ elif all(
+ pred_idx in processed
+ for pred_idx in pred_indices
+ ):
+ # All predecessors have been processed in previous layers
+ nodes_to_add.append(idx)
+
+ # Second pass: add identified nodes to the layer and update sets
+ for idx in nodes_to_add:
+ layer.append(self._index_to_node_id[idx])
+ remaining.remove(idx)
+ processed.add(idx)
+
+ if not layer:
+ # Cycle detected or error, break
+ break
+
+ layers.append(layer)
+
+ # If there are remaining nodes, they form a cycle - add them as a final layer
+ if remaining:
+ cycle_layer = [
+ self._index_to_node_id[idx] for idx in remaining
+ ]
+ layers.append(cycle_layer)
+
+ return (
+ layers
+ if layers
+ else [
+ [
+ self._index_to_node_id[idx]
+ for idx in all_indices
+ ]
+ ]
+ )
+ except Exception as e:
+ logger.warning(
+ f"Error in rustworkx topological_generations: {e}, falling back to simple approach"
+ )
+ # Fallback: return all nodes in one layer
+ return [
+ [node_id for node_id in self._node_id_to_index.keys()]
+ ]
+
+ def simple_cycles(self) -> List[List[str]]:
+ """
+ Find simple cycles in the graph.
+
+ Returns:
+ List[List[str]]: List of cycles, where each cycle is a list of node IDs.
+ """
+ try:
+ # Convert to NetworkX temporarily for cycle detection
+ # This is a limitation of rustworkx - it doesn't have simple_cycles
+ # We'll use a workaround by converting temporarily
+ import networkx as nx
+
+ nx_graph = nx.DiGraph()
+ for node_id in self._node_id_to_index.keys():
+ nx_graph.add_node(node_id)
+ for edge in self.graph.edge_list():
+ source_idx, target_idx = edge
+ source_id = self._index_to_node_id[source_idx]
+ target_id = self._index_to_node_id[target_idx]
+ nx_graph.add_edge(source_id, target_id)
+
+ cycles = list(nx.simple_cycles(nx_graph))
+ return cycles
+ except Exception as e:
+ logger.warning(
+ f"Error in rustworkx simple_cycles: {e}, returning empty list"
+ )
+ return []
+
+ def descendants(self, node_id: str) -> Set[str]:
+ """
+ Get all descendants of a node.
+
+ Args:
+ node_id (str): The node ID.
+
+ Returns:
+ Set[str]: Set of descendant node IDs.
+ """
+ if node_id not in self._node_id_to_index:
+ return set()
+ node_index = self._node_id_to_index[node_id]
+ # Use BFS to find all descendants
+ descendants = set()
+ queue = [node_index]
+ visited = {node_index}
+
+ while queue:
+ current_idx = queue.pop(0)
+ succ_data = self.graph.successors(current_idx)
+ for succ in succ_data:
+ # Handle both dict (node data) and int (index) returns
+ if isinstance(succ, dict):
+ succ_node_id = succ.get("node_id")
+ if (
+ succ_node_id
+ and succ_node_id in self._node_id_to_index
+ ):
+ succ_idx = self._node_id_to_index[
+ succ_node_id
+ ]
+ else:
+ continue
+ elif isinstance(succ, int):
+ succ_idx = succ
+ else:
+ continue
+
+ if succ_idx not in visited:
+ visited.add(succ_idx)
+ descendants.add(self._index_to_node_id[succ_idx])
+ queue.append(succ_idx)
+
+ return descendants
+
+
class NodeType(str, Enum):
AGENT: Agent = "agent"
@@ -129,17 +656,37 @@ class Edge:
Returns:
Edge: A new Edge instance.
"""
- src = (
- source_node.id
- if isinstance(source_node, Node)
- else source_node
- )
- tgt = (
- target_node.id
- if isinstance(target_node, Node)
- else target_node
- )
- return cls(source=src, target=tgt, **kwargs)
+ # Handle source node: extract ID from Node, Agent, or use string directly
+ if isinstance(source_node, Node):
+ src = source_node.id
+ elif hasattr(source_node, "agent_name"):
+ # Agent object - extract agent_name
+ src = getattr(source_node, "agent_name", None)
+ if src is None:
+ raise ValueError(
+ "Source agent does not have an agent_name attribute"
+ )
+ else:
+ # Assume it's already a string ID
+ src = source_node
+
+ # Handle target node: extract ID from Node, Agent, or use string directly
+ if isinstance(target_node, Node):
+ tgt = target_node.id
+ elif hasattr(target_node, "agent_name"):
+ # Agent object - extract agent_name
+ tgt = getattr(target_node, "agent_name", None)
+ if tgt is None:
+ raise ValueError(
+ "Target agent does not have an agent_name attribute"
+ )
+ else:
+ # Assume it's already a string ID
+ tgt = target_node
+
+ # Put all kwargs into metadata dict
+ metadata = kwargs if kwargs else None
+ return cls(source=src, target=tgt, metadata=metadata)
class GraphWorkflow:
@@ -151,7 +698,7 @@ class GraphWorkflow:
edges (List[Edge]): A list of edges in the graph, where each edge is represented by an Edge object.
entry_points (List[str]): A list of node IDs that serve as entry points to the graph.
end_points (List[str]): A list of node IDs that serve as end points of the graph.
- graph (nx.DiGraph): A directed graph object from the NetworkX library representing the workflow graph.
+ graph_backend (GraphBackend): A graph backend object (NetworkX or Rustworkx) representing the workflow graph.
task (str): The task to be executed by the workflow.
_compiled (bool): Whether the graph has been compiled for optimization.
_sorted_layers (List[List[str]]): Pre-computed topological layers for faster execution.
@@ -174,6 +721,7 @@ class GraphWorkflow:
task: Optional[str] = None,
auto_compile: bool = True,
verbose: bool = False,
+ backend: str = "networkx",
):
self.id = id
self.verbose = verbose
@@ -181,14 +729,30 @@ class GraphWorkflow:
if self.verbose:
logger.info("Initializing GraphWorkflow")
logger.debug(
- f"GraphWorkflow parameters: nodes={len(nodes) if nodes else 0}, edges={len(edges) if edges else 0}, max_loops={max_loops}, auto_compile={auto_compile}"
+ f"GraphWorkflow parameters: nodes={len(nodes) if nodes else 0}, edges={len(edges) if edges else 0}, max_loops={max_loops}, auto_compile={auto_compile}, backend={backend}"
)
self.nodes = nodes or {}
self.edges = edges or []
self.entry_points = entry_points or []
self.end_points = end_points or []
- self.graph = nx.DiGraph()
+
+ # Initialize graph backend
+ if backend.lower() == "rustworkx":
+ if not RUSTWORKX_AVAILABLE:
+ logger.warning(
+ "rustworkx is not available, falling back to networkx. Install with: pip install rustworkx"
+ )
+ self.graph_backend = NetworkXBackend()
+ else:
+ self.graph_backend = RustworkxBackend()
+ if self.verbose:
+ logger.info("Using rustworkx backend")
+ else:
+ self.graph_backend = NetworkXBackend()
+ if self.verbose:
+ logger.info("Using networkx backend")
+
self.max_loops = max_loops
self.task = task
self.name = name
@@ -208,15 +772,20 @@ class GraphWorkflow:
self.conversation = Conversation()
- # Rebuild the NetworkX graph from nodes and edges if provided
+ # Rebuild the graph from nodes and edges if provided
if self.nodes:
+ backend_name = (
+ "rustworkx"
+ if isinstance(self.graph_backend, RustworkxBackend)
+ else "networkx"
+ )
if self.verbose:
logger.info(
- f"Adding {len(self.nodes)} nodes to NetworkX graph"
+ f"Adding {len(self.nodes)} nodes to {backend_name} graph"
)
for node_id, node in self.nodes.items():
- self.graph.add_node(
+ self.graph_backend.add_node(
node_id,
type=node.type,
agent=node.agent,
@@ -228,9 +797,14 @@ class GraphWorkflow:
)
if self.edges:
+ backend_name = (
+ "rustworkx"
+ if isinstance(self.graph_backend, RustworkxBackend)
+ else "networkx"
+ )
if self.verbose:
logger.info(
- f"Adding {len(self.edges)} edges to NetworkX graph"
+ f"Adding {len(self.edges)} edges to {backend_name} graph"
)
valid_edges = 0
@@ -239,7 +813,7 @@ class GraphWorkflow:
edge.source in self.nodes
and edge.target in self.nodes
):
- self.graph.add_edge(
+ self.graph_backend.add_edge(
edge.source,
edge.target,
**(edge.metadata or {}),
@@ -328,8 +902,8 @@ class GraphWorkflow:
if self.verbose:
logger.debug("Computing topological layers")
- sorted_layers = list(
- nx.topological_generations(self.graph)
+ sorted_layers = (
+ self.graph_backend.topological_generations()
)
self._sorted_layers = sorted_layers
@@ -380,7 +954,7 @@ class GraphWorkflow:
raise ValueError(error_msg)
self.nodes[node.id] = node
- self.graph.add_node(
+ self.graph_backend.add_node(
node.id,
type=node.type,
agent=node.agent,
@@ -434,7 +1008,7 @@ class GraphWorkflow:
raise ValueError(error_msg)
self.edges.append(edge)
- self.graph.add_edge(
+ self.graph_backend.add_edge(
edge.source, edge.target, **(edge.metadata or {})
)
self._invalidate_compilation()
@@ -492,7 +1066,7 @@ class GraphWorkflow:
raise ValueError(error_msg)
self.edges.append(edge)
- self.graph.add_edge(
+ self.graph_backend.add_edge(
edge.source, edge.target, **(edge.metadata or {})
)
created_edges.append(edge)
@@ -560,7 +1134,7 @@ class GraphWorkflow:
raise ValueError(error_msg)
self.edges.append(edge)
- self.graph.add_edge(
+ self.graph_backend.add_edge(
edge.source, edge.target, **(edge.metadata or {})
)
created_edges.append(edge)
@@ -629,7 +1203,7 @@ class GraphWorkflow:
raise ValueError(error_msg)
self.edges.append(edge)
- self.graph.add_edge(
+ self.graph_backend.add_edge(
edge.source,
edge.target,
**(edge.metadata or {}),
@@ -860,7 +1434,9 @@ class GraphWorkflow:
try:
self.entry_points = [
- n for n in self.nodes if self.graph.in_degree(n) == 0
+ n
+ for n in self.nodes
+ if self.graph_backend.in_degree(n) == 0
]
if self.verbose:
@@ -888,7 +1464,9 @@ class GraphWorkflow:
try:
self.end_points = [
- n for n in self.nodes if self.graph.out_degree(n) == 0
+ n
+ for n in self.nodes
+ if self.graph_backend.out_degree(n) == 0
]
if self.verbose:
@@ -921,7 +1499,7 @@ class GraphWorkflow:
if node_id not in self._predecessors_cache:
self._predecessors_cache[node_id] = tuple(
- self.graph.predecessors(node_id)
+ self.graph_backend.predecessors(node_id)
)
return self._predecessors_cache[node_id]
@@ -2228,8 +2806,8 @@ class GraphWorkflow:
isolated = [
n
for n in self.nodes
- if self.graph.in_degree(n) == 0
- and self.graph.out_degree(n) == 0
+ if self.graph_backend.in_degree(n) == 0
+ and self.graph_backend.out_degree(n) == 0
]
if isolated:
result["warnings"].append(
@@ -2238,7 +2816,7 @@ class GraphWorkflow:
# Check for cyclic dependencies
try:
- cycles = list(nx.simple_cycles(self.graph))
+ cycles = self.graph_backend.simple_cycles()
if cycles:
result["warnings"].append(
f"Found {len(cycles)} cycles in workflow"
@@ -2268,7 +2846,7 @@ class GraphWorkflow:
reachable = set()
for entry in self.entry_points:
reachable.update(
- nx.descendants(self.graph, entry)
+ self.graph_backend.descendants(entry)
)
reachable.add(entry)
@@ -2289,11 +2867,11 @@ class GraphWorkflow:
# Check for dead-end nodes (cannot reach any exit point)
if self.end_points:
- reverse_graph = self.graph.reverse()
+ reverse_graph = self.graph_backend.reverse()
reachable_to_exit = set()
for exit_point in self.end_points:
reachable_to_exit.update(
- nx.descendants(reverse_graph, exit_point)
+ reverse_graph.descendants(exit_point)
)
reachable_to_exit.add(exit_point)
diff --git a/swarms/structs/hiearchical_swarm.py b/swarms/structs/hiearchical_swarm.py
index 1501ccb6..49b63595 100644
--- a/swarms/structs/hiearchical_swarm.py
+++ b/swarms/structs/hiearchical_swarm.py
@@ -914,6 +914,7 @@ class HierarchicalSwarm:
logger.error(
f"{error_msg}\n[TRACE] Traceback: {traceback.format_exc()}\n[BUG] If this issue persists, please report it at: https://github.com/kyegomez/swarms/issues"
)
+ raise e
def agents_no_print(self):
for agent in self.agents:
diff --git a/swarms/structs/llm_council.py b/swarms/structs/llm_council.py
new file mode 100644
index 00000000..a303fa12
--- /dev/null
+++ b/swarms/structs/llm_council.py
@@ -0,0 +1,516 @@
+"""
+LLM Council - A Swarms implementation inspired by Andrej Karpathy's llm-council.
+
+This implementation creates a council of specialized LLM agents that:
+1. Each agent responds to the user query independently
+2. All agents review and rank each other's (anonymized) responses
+3. A Chairman LLM synthesizes all responses and rankings into a final answer
+
+The council demonstrates how different models evaluate and rank each other's work,
+often selecting responses from other models as superior to their own.
+"""
+
+from typing import Dict, List, Optional
+import random
+from swarms.structs.agent import Agent
+from swarms.structs.multi_agent_exec import (
+ run_agents_concurrently,
+ batched_grid_agent_execution,
+)
+from swarms.utils.history_output_formatter import HistoryOutputType, history_output_formatter
+from swarms.structs.conversation import Conversation
+from swarms.structs.swarm_id import swarm_id
+
+def get_gpt_councilor_prompt() -> str:
+ """
+ Get system prompt for GPT-5.1 councilor.
+
+ Returns:
+ System prompt string for GPT-5.1 councilor agent.
+ """
+ return """You are a member of the LLM Council, representing GPT-5.1. Your role is to provide comprehensive, analytical, and thorough responses to user queries.
+
+Your strengths:
+- Deep analytical thinking and comprehensive coverage
+- Ability to break down complex topics into detailed components
+- Thorough exploration of multiple perspectives
+- Rich contextual understanding
+
+Your approach:
+- Provide detailed, well-structured responses
+- Include relevant context and background information
+- Consider multiple angles and perspectives
+- Be thorough but clear in your explanations
+
+Remember: You are part of a council where multiple AI models will respond to the same query, and then evaluate each other's responses. Focus on quality, depth, and clarity."""
+
+
+def get_gemini_councilor_prompt() -> str:
+ """
+ Get system prompt for Gemini 3 Pro councilor.
+
+ Returns:
+ System prompt string for Gemini 3 Pro councilor agent.
+ """
+ return """You are a member of the LLM Council, representing Gemini 3 Pro. Your role is to provide concise, well-processed, and structured responses to user queries.
+
+Your strengths:
+- Clear and structured communication
+- Efficient information processing
+- Condensed yet comprehensive responses
+- Well-organized presentation
+
+Your approach:
+- Provide concise but complete answers
+- Structure information clearly and logically
+- Focus on key points without unnecessary verbosity
+- Present information in an easily digestible format
+
+Remember: You are part of a council where multiple AI models will respond to the same query, and then evaluate each other's responses. Focus on clarity, structure, and efficiency."""
+
+
+def get_claude_councilor_prompt() -> str:
+ """
+ Get system prompt for Claude Sonnet 4.5 councilor.
+
+ Returns:
+ System prompt string for Claude Sonnet 4.5 councilor agent.
+ """
+ return """You are a member of the LLM Council, representing Claude Sonnet 4.5. Your role is to provide thoughtful, balanced, and nuanced responses to user queries.
+
+Your strengths:
+- Nuanced understanding and balanced perspectives
+- Thoughtful consideration of trade-offs
+- Clear reasoning and logical structure
+- Ethical and responsible analysis
+
+Your approach:
+- Provide balanced, well-reasoned responses
+- Consider multiple viewpoints and implications
+- Be thoughtful about potential limitations or edge cases
+- Maintain clarity while showing depth of thought
+
+Remember: You are part of a council where multiple AI models will respond to the same query, and then evaluate each other's responses. Focus on thoughtfulness, balance, and nuanced reasoning."""
+
+
+def get_grok_councilor_prompt() -> str:
+ """
+ Get system prompt for Grok-4 councilor.
+
+ Returns:
+ System prompt string for Grok-4 councilor agent.
+ """
+ return """You are a member of the LLM Council, representing Grok-4. Your role is to provide creative, innovative, and unique perspectives on user queries.
+
+Your strengths:
+- Creative problem-solving and innovative thinking
+- Unique perspectives and out-of-the-box approaches
+- Engaging and dynamic communication style
+- Ability to connect seemingly unrelated concepts
+
+Your approach:
+- Provide creative and innovative responses
+- Offer unique perspectives and fresh insights
+- Be engaging and dynamic in your communication
+- Think creatively while maintaining accuracy
+
+Remember: You are part of a council where multiple AI models will respond to the same query, and then evaluate each other's responses. Focus on creativity, innovation, and unique insights."""
+
+
+def get_chairman_prompt() -> str:
+ """
+ Get system prompt for the Chairman agent.
+
+ Returns:
+ System prompt string for the Chairman agent.
+ """
+ return """You are the Chairman of the LLM Council. Your role is to synthesize responses from all council members along with their evaluations and rankings into a final, comprehensive answer.
+
+Your responsibilities:
+1. Review all council member responses to the user's query
+2. Consider the rankings and evaluations provided by each council member
+3. Synthesize the best elements from all responses
+4. Create a final, comprehensive answer that incorporates the strengths of different approaches
+5. Provide transparency about which perspectives influenced the final answer
+
+Your approach:
+- Synthesize rather than simply aggregate
+- Identify the strongest elements from each response
+- Create a cohesive final answer that benefits from multiple perspectives
+- Acknowledge the diversity of approaches taken by council members
+- Provide a balanced, comprehensive response that serves the user's needs
+
+Remember: You have access to all original responses and all evaluations. Use this rich context to create the best possible final answer."""
+
+
+def get_evaluation_prompt(
+ query: str, responses: Dict[str, str], evaluator_name: str
+) -> str:
+ """
+ Create evaluation prompt for council members to review and rank responses.
+
+ Args:
+ query: The original user query
+ responses: Dictionary mapping anonymous IDs to response texts
+ evaluator_name: Name of the agent doing the evaluation
+
+ Returns:
+ Formatted evaluation prompt string
+ """
+ responses_text = "\n\n".join(
+ [
+ f"Response {response_id}:\n{response_text}"
+ for response_id, response_text in responses.items()
+ ]
+ )
+
+ return f"""You are evaluating responses from your fellow LLM Council members to the following query:
+
+QUERY: {query}
+
+Below are the anonymized responses from all council members (including potentially your own):
+
+{responses_text}
+
+Your task:
+1. Carefully read and analyze each response
+2. Evaluate the quality, accuracy, completeness, and usefulness of each response
+3. Rank the responses from best to worst (1 = best, {len(responses)} = worst)
+4. Provide brief reasoning for your rankings
+5. Be honest and objective - you may find another model's response superior to your own
+
+Format your evaluation as follows:
+
+RANKINGS:
+1. Response [ID]: [Brief reason why this is the best]
+2. Response [ID]: [Brief reason]
+...
+{len(responses)}. Response [ID]: [Brief reason why this ranks lowest]
+
+ADDITIONAL OBSERVATIONS:
+[Any additional insights about the responses, common themes, strengths/weaknesses, etc.]
+
+Remember: The goal is honest, objective evaluation. If another model's response is genuinely better, acknowledge it."""
+
+
+def get_synthesis_prompt(
+ query: str,
+ original_responses: Dict[str, str],
+ evaluations: Dict[str, str],
+ id_to_member: Dict[str, str],
+) -> str:
+ """
+ Create synthesis prompt for the Chairman.
+
+ Args:
+ query: Original user query
+ original_responses: Dict mapping member names to their responses
+ evaluations: Dict mapping evaluator names to their evaluation texts
+ id_to_member: Mapping from anonymous IDs to member names
+
+ Returns:
+ Formatted synthesis prompt
+ """
+ responses_section = "\n\n".join(
+ [
+ f"=== {name} ===\n{response}"
+ for name, response in original_responses.items()
+ ]
+ )
+
+ evaluations_section = "\n\n".join(
+ [
+ f"=== Evaluation by {name} ===\n{evaluation}"
+ for name, evaluation in evaluations.items()
+ ]
+ )
+
+ return f"""As the Chairman of the LLM Council, synthesize the following information into a final, comprehensive answer.
+
+ORIGINAL QUERY:
+{query}
+
+COUNCIL MEMBER RESPONSES:
+{responses_section}
+
+COUNCIL MEMBER EVALUATIONS AND RANKINGS:
+{evaluations_section}
+
+ANONYMOUS ID MAPPING (for reference):
+{chr(10).join([f" {aid} = {name}" for aid, name in id_to_member.items()])}
+
+Your task:
+1. Review all council member responses
+2. Consider the evaluations and rankings provided by each member
+3. Identify the strongest elements from each response
+4. Synthesize a final, comprehensive answer that:
+ - Incorporates the best insights from multiple perspectives
+ - Addresses the query thoroughly and accurately
+ - Benefits from the diversity of approaches taken
+ - Is clear, well-structured, and useful
+
+Provide your final synthesized response below. You may reference which perspectives or approaches influenced different parts of your answer."""
+
+
+class LLMCouncil:
+ """
+ An LLM Council that orchestrates multiple specialized agents to collaboratively
+ answer queries through independent responses, peer review, and synthesis.
+
+ The council follows this workflow:
+ 1. Dispatch query to all council members in parallel
+ 2. Collect all responses (anonymized)
+ 3. Have each member review and rank all responses
+ 4. Chairman synthesizes everything into final response
+ """
+
+ def __init__(
+ self,
+ id: str = swarm_id(),
+ name: str = "LLM Council",
+ description: str = "A collaborative council of LLM agents where each member independently answers a query, reviews and ranks anonymized peer responses, and a chairman synthesizes the best elements into a final answer.",
+ council_members: Optional[List[Agent]] = None,
+ chairman_model: str = "gpt-5.1",
+ verbose: bool = True,
+ output_type: HistoryOutputType = "dict",
+ ):
+ """
+ Initialize the LLM Council.
+
+ Args:
+ council_members: List of Agent instances representing council members.
+ If None, creates default council with GPT-5.1, Gemini 3 Pro,
+ Claude Sonnet 4.5, and Grok-4.
+ chairman_model: Model name for the Chairman agent that synthesizes responses.
+ verbose: Whether to print progress and intermediate results.
+ output_type: Format for the output. Options: "list", "dict", "string", "final", "json", "yaml", etc.
+ """
+ self.name = name
+ self.description = description
+ self.verbose = verbose
+ self.output_type = output_type
+
+ # Create default council members if none provided
+ if council_members is None:
+ self.council_members = self._create_default_council()
+ else:
+ self.council_members = council_members
+
+ # Create Chairman agent
+ self.chairman = Agent(
+ agent_name="Chairman",
+ agent_description="Chairman of the LLM Council, responsible for synthesizing all responses and rankings into a final answer",
+ system_prompt=get_chairman_prompt(),
+ model_name=chairman_model,
+ max_loops=1,
+ verbose=verbose,
+ temperature=0.7,
+ )
+
+ self.conversation = Conversation(name=f"[LLM Council] [Conversation][{name}]")
+
+ if self.verbose:
+ print(
+ f"🏛️ LLM Council initialized with {len(self.council_members)} members"
+ )
+ for i, member in enumerate(self.council_members, 1):
+ print(
+ f" {i}. {member.agent_name} ({member.model_name})"
+ )
+
+ def _create_default_council(self) -> List[Agent]:
+ """
+ Create default council members with specialized prompts and models.
+
+ Returns:
+ List of Agent instances configured as council members.
+ """
+
+ # GPT-5.1 Agent - Analytical and comprehensive
+ gpt_agent = Agent(
+ agent_name="GPT-5.1-Councilor",
+ agent_description="Analytical and comprehensive AI councilor specializing in deep analysis and thorough responses",
+ system_prompt=get_gpt_councilor_prompt(),
+ model_name="gpt-5.1",
+ max_loops=1,
+ verbose=False,
+ temperature=0.7,
+ )
+
+ # Gemini 3 Pro Agent - Concise and processed
+ gemini_agent = Agent(
+ agent_name="Gemini-3-Pro-Councilor",
+ agent_description="Concise and well-processed AI councilor specializing in clear, structured responses",
+ system_prompt=get_gemini_councilor_prompt(),
+ model_name="gemini-2.5-flash", # Using available Gemini model
+ max_loops=1,
+ verbose=False,
+ temperature=0.7,
+ )
+
+ # Claude Sonnet 4.5 Agent - Balanced and thoughtful
+ claude_agent = Agent(
+ agent_name="Claude-Sonnet-4.5-Councilor",
+ agent_description="Thoughtful and balanced AI councilor specializing in nuanced and well-reasoned responses",
+ system_prompt=get_claude_councilor_prompt(),
+ model_name="anthropic/claude-sonnet-4-5", # Using available Claude model
+ max_loops=1,
+ verbose=False,
+ temperature=0.0,
+ top_p=None,
+ )
+
+ # Grok-4 Agent - Creative and innovative
+ grok_agent = Agent(
+ agent_name="Grok-4-Councilor",
+ agent_description="Creative and innovative AI councilor specializing in unique perspectives and creative solutions",
+ system_prompt=get_grok_councilor_prompt(),
+ model_name="x-ai/grok-4", # Using available model as proxy for Grok-4
+ max_loops=1,
+ verbose=False,
+ temperature=0.8,
+ )
+
+ members = [gpt_agent, gemini_agent, claude_agent, grok_agent]
+
+ return members
+
+ def run(self, query: str):
+ """
+ Execute the full LLM Council workflow.
+
+ Args:
+ query: The user's query to process
+
+ Returns:
+ Formatted output based on output_type, containing conversation history
+ with all council member responses, evaluations, and final synthesis.
+ """
+ if self.verbose:
+ print(f"\n{'='*80}")
+ print("🏛️ LLM COUNCIL SESSION")
+ print("=" * 80)
+ print(f"\n📝 Query: {query}\n")
+
+ # Add user query to conversation
+ self.conversation.add(role="User", content=query)
+
+ # Step 1: Get responses from all council members in parallel
+ if self.verbose:
+ print("📤 Dispatching query to all council members...")
+
+ results_dict = run_agents_concurrently(
+ self.council_members,
+ task=query,
+ return_agent_output_dict=True,
+ )
+
+ # Map results to member names
+ original_responses = {
+ member.agent_name: response
+ for member, response in zip(
+ self.council_members,
+ [
+ results_dict.get(member.agent_name, "")
+ for member in self.council_members
+ ],
+ )
+ }
+
+ # Add each council member's response to conversation
+ for member_name, response in original_responses.items():
+ self.conversation.add(role=member_name, content=response)
+
+ if self.verbose:
+ print(
+ f"✅ Received {len(original_responses)} responses\n"
+ )
+ for name, response in original_responses.items():
+ print(f" {name}: {response[:100]}...")
+
+ # Step 2: Anonymize responses for evaluation
+ # Create anonymous IDs (A, B, C, D, etc.)
+ anonymous_ids = [
+ chr(65 + i) for i in range(len(self.council_members))
+ ]
+ random.shuffle(anonymous_ids) # Shuffle to ensure anonymity
+
+ anonymous_responses = {
+ anonymous_ids[i]: original_responses[member.agent_name]
+ for i, member in enumerate(self.council_members)
+ }
+
+ # Create mapping from anonymous ID to member name (for later reference)
+ id_to_member = {
+ anonymous_ids[i]: member.agent_name
+ for i, member in enumerate(self.council_members)
+ }
+
+ if self.verbose:
+ print(
+ "\n🔍 Council members evaluating each other's responses..."
+ )
+
+ # Step 3: Have each member evaluate and rank all responses concurrently
+ # Create evaluation tasks for each member
+ evaluation_tasks = [
+ get_evaluation_prompt(
+ query, anonymous_responses, member.agent_name
+ )
+ for member in self.council_members
+ ]
+
+ # Run evaluations concurrently using batched_grid_agent_execution
+ evaluation_results = batched_grid_agent_execution(
+ self.council_members, evaluation_tasks
+ )
+
+ # Map results to member names
+ evaluations = {
+ member.agent_name: evaluation_results[i]
+ for i, member in enumerate(self.council_members)
+ }
+
+ # Add each council member's evaluation to conversation
+ for member_name, evaluation in evaluations.items():
+ self.conversation.add(
+ role=f"{member_name}-Evaluation", content=evaluation
+ )
+
+ if self.verbose:
+ print(f"✅ Received {len(evaluations)} evaluations\n")
+
+ # Step 4: Chairman synthesizes everything
+ if self.verbose:
+ print("👔 Chairman synthesizing final response...\n")
+
+ synthesis_prompt = get_synthesis_prompt(
+ query, original_responses, evaluations, id_to_member
+ )
+
+ final_response = self.chairman.run(task=synthesis_prompt)
+
+ # Add chairman's final response to conversation
+ self.conversation.add(role="Chairman", content=final_response)
+
+ if self.verbose:
+ print(f"{'='*80}")
+ print("✅ FINAL RESPONSE")
+ print(f"{'='*80}\n")
+
+ # Format and return output using history_output_formatter
+ return history_output_formatter(
+ conversation=self.conversation, type=self.output_type
+ )
+
+ def batched_run(self, tasks: List[str]):
+ """
+ Run the LLM Council workflow for a batch of tasks.
+
+ Args:
+ tasks: List of tasks to process
+
+ Returns:
+ List of formatted outputs based on output_type
+ """
+ return [self.run(task) for task in tasks]
\ No newline at end of file
diff --git a/swarms/structs/swarm_router.py b/swarms/structs/swarm_router.py
index 92903f57..dd13ee08 100644
--- a/swarms/structs/swarm_router.py
+++ b/swarms/structs/swarm_router.py
@@ -37,6 +37,7 @@ from swarms.telemetry.log_executions import log_execution
from swarms.utils.generate_keys import generate_api_key
from swarms.utils.loguru_logger import initialize_logger
from swarms.utils.output_types import OutputType
+from swarms.structs.llm_council import LLMCouncil
logger = initialize_logger(log_folder="swarm_router")
@@ -56,6 +57,7 @@ SwarmType = Literal[
"InteractiveGroupChat",
"HeavySwarm",
"BatchedGridWorkflow",
+ "LLMCouncil",
]
@@ -210,6 +212,7 @@ class SwarmRouter:
verbose: bool = False,
worker_tools: List[Callable] = None,
aggregation_strategy: str = "synthesis",
+ chairman_model: str = "gpt-5.1",
*args,
**kwargs,
):
@@ -252,7 +255,8 @@ class SwarmRouter:
self.heavy_swarm_swarm_show_output = (
heavy_swarm_swarm_show_output
)
-
+ self.chairman_model = chairman_model
+
# Initialize swarm factory for O(1) lookup performance
self._swarm_factory = self._initialize_swarm_factory()
self._swarm_cache = {} # Cache for created swarms
@@ -425,6 +429,7 @@ class SwarmRouter:
"SequentialWorkflow": self._create_sequential_workflow,
"ConcurrentWorkflow": self._create_concurrent_workflow,
"BatchedGridWorkflow": self._create_batched_grid_workflow,
+ "LLMCouncil": self._create_llm_council,
}
def _create_heavy_swarm(self, *args, **kwargs):
@@ -441,6 +446,16 @@ class SwarmRouter:
aggregation_strategy=self.aggregation_strategy,
show_dashboard=False,
)
+
+ def _create_llm_council(self, *args, **kwargs):
+ """Factory function for LLMCouncil."""
+ return LLMCouncil(
+ name=self.name,
+ description=self.description,
+ output_type=self.output_type,
+ verbose=self.verbose,
+ chairman_model=self.chairman_model,
+ )
def _create_agent_rearrange(self, *args, **kwargs):
"""Factory function for AgentRearrange."""
diff --git a/tests/structs/test_auto_swarms_builder.py b/tests/structs/test_auto_swarms_builder.py
index a1e9085a..1d6e8762 100644
--- a/tests/structs/test_auto_swarms_builder.py
+++ b/tests/structs/test_auto_swarms_builder.py
@@ -41,21 +41,27 @@ def test_initialization():
def test_agent_building():
- """Test building individual agents"""
+ """Test building individual agents from specs"""
print_separator()
print("Testing Agent Building")
try:
swarm = AutoSwarmBuilder()
- agent = swarm.build_agent(
- agent_name="TestAgent",
- agent_description="A test agent",
- agent_system_prompt="You are a test agent",
- max_loops=1,
- )
+ specs = {
+ "agents": [
+ {
+ "agent_name": "TestAgent",
+ "description": "A test agent",
+ "system_prompt": "You are a test agent",
+ "max_loops": 1,
+ }
+ ]
+ }
+ agents = swarm.create_agents_from_specs(specs)
+ agent = agents[0]
print("✓ Built agent with configuration:")
print(f" - Name: {agent.agent_name}")
- print(f" - Description: {agent.description}")
+ print(f" - Description: {agent.agent_description}")
print(f" - Max loops: {agent.max_loops}")
print("✓ Agent building test passed")
return agent
@@ -69,18 +75,25 @@ def test_agent_creation():
print_separator()
print("Testing Agent Creation from Task")
try:
+ import json
+
swarm = AutoSwarmBuilder(
name="ResearchSwarm",
description="A swarm for research tasks",
)
task = "Research the latest developments in quantum computing"
- agents = swarm._create_agents(task)
+ # create_agents returns a JSON string
+ agent_specs_json = swarm.create_agents(task)
+ # Parse JSON string to dict
+ agent_specs = json.loads(agent_specs_json)
+ # Convert specs to actual Agent objects
+ agents = swarm.create_agents_from_specs(agent_specs)
print("✓ Created agents for research task:")
for i, agent in enumerate(agents, 1):
print(f" Agent {i}:")
print(f" - Name: {agent.agent_name}")
- print(f" - Description: {agent.description}")
+ print(f" - Description: {agent.agent_description}")
print(f"✓ Created {len(agents)} agents successfully")
return agents
except Exception as e:
@@ -155,7 +168,9 @@ def test_error_handling():
# Test with invalid agent configuration
print("Testing invalid agent configuration...")
try:
- swarm.build_agent("", "", "")
+ swarm.create_agents_from_specs(
+ {"agents": [{"agent_name": ""}]}
+ )
print(
"✗ Should have raised an error for empty agent configuration"
)
diff --git a/tests/structs/test_custom_agent.py b/tests/structs/test_custom_agent.py
index 3cdeda25..63969b97 100644
--- a/tests/structs/test_custom_agent.py
+++ b/tests/structs/test_custom_agent.py
@@ -6,6 +6,7 @@ from swarms.structs.custom_agent import CustomAgent, AgentResponse
try:
import pytest_asyncio
+
ASYNC_AVAILABLE = True
except ImportError:
ASYNC_AVAILABLE = False
@@ -40,7 +41,10 @@ def test_custom_agent_initialization():
timeout=30.0,
verify_ssl=True,
)
- assert custom_agent_instance.base_url == "https://api.example.com"
+ assert (
+ custom_agent_instance.base_url
+ == "https://api.example.com"
+ )
assert custom_agent_instance.endpoint == "v1/endpoint"
assert custom_agent_instance.timeout == 30.0
assert custom_agent_instance.verify_ssl is True
@@ -51,7 +55,9 @@ def test_custom_agent_initialization():
raise
-def test_custom_agent_initialization_with_default_headers(sample_custom_agent):
+def test_custom_agent_initialization_with_default_headers(
+ sample_custom_agent,
+):
try:
custom_agent_no_headers = CustomAgent(
name="TestAgent",
@@ -59,7 +65,9 @@ def test_custom_agent_initialization_with_default_headers(sample_custom_agent):
base_url="https://api.test.com",
endpoint="test",
)
- assert "Content-Type" in custom_agent_no_headers.default_headers
+ assert (
+ "Content-Type" in custom_agent_no_headers.default_headers
+ )
assert (
custom_agent_no_headers.default_headers["Content-Type"]
== "application/json"
@@ -78,7 +86,10 @@ def test_custom_agent_url_normalization():
base_url="https://api.test.com/",
endpoint="/v1/test",
)
- assert custom_agent_with_slashes.base_url == "https://api.test.com"
+ assert (
+ custom_agent_with_slashes.base_url
+ == "https://api.test.com"
+ )
assert custom_agent_with_slashes.endpoint == "v1/test"
logger.debug("URL normalization works correctly")
except Exception as e:
@@ -90,14 +101,22 @@ def test_prepare_headers(sample_custom_agent):
try:
prepared_headers = sample_custom_agent._prepare_headers()
assert "Authorization" in prepared_headers
- assert prepared_headers["Authorization"] == "Bearer test-token"
+ assert (
+ prepared_headers["Authorization"] == "Bearer test-token"
+ )
additional_headers = {"X-Custom-Header": "custom-value"}
prepared_headers_with_additional = (
sample_custom_agent._prepare_headers(additional_headers)
)
- assert prepared_headers_with_additional["X-Custom-Header"] == "custom-value"
- assert prepared_headers_with_additional["Authorization"] == "Bearer test-token"
+ assert (
+ prepared_headers_with_additional["X-Custom-Header"]
+ == "custom-value"
+ )
+ assert (
+ prepared_headers_with_additional["Authorization"]
+ == "Bearer test-token"
+ )
logger.debug("Header preparation works correctly")
except Exception as e:
logger.error(f"Failed to test prepare_headers: {e}")
@@ -107,7 +126,9 @@ def test_prepare_headers(sample_custom_agent):
def test_prepare_payload_dict(sample_custom_agent):
try:
payload_dict = {"key": "value", "number": 123}
- prepared_payload = sample_custom_agent._prepare_payload(payload_dict)
+ prepared_payload = sample_custom_agent._prepare_payload(
+ payload_dict
+ )
assert isinstance(prepared_payload, str)
parsed = json.loads(prepared_payload)
assert parsed["key"] == "value"
@@ -121,22 +142,30 @@ def test_prepare_payload_dict(sample_custom_agent):
def test_prepare_payload_string(sample_custom_agent):
try:
payload_string = '{"test": "value"}'
- prepared_payload = sample_custom_agent._prepare_payload(payload_string)
+ prepared_payload = sample_custom_agent._prepare_payload(
+ payload_string
+ )
assert prepared_payload == payload_string
logger.debug("String payload prepared correctly")
except Exception as e:
- logger.error(f"Failed to test prepare_payload with string: {e}")
+ logger.error(
+ f"Failed to test prepare_payload with string: {e}"
+ )
raise
def test_prepare_payload_bytes(sample_custom_agent):
try:
payload_bytes = b'{"test": "value"}'
- prepared_payload = sample_custom_agent._prepare_payload(payload_bytes)
+ prepared_payload = sample_custom_agent._prepare_payload(
+ payload_bytes
+ )
assert prepared_payload == payload_bytes
logger.debug("Bytes payload prepared correctly")
except Exception as e:
- logger.error(f"Failed to test prepare_payload with bytes: {e}")
+ logger.error(
+ f"Failed to test prepare_payload with bytes: {e}"
+ )
raise
@@ -148,7 +177,9 @@ def test_parse_response_success(sample_custom_agent):
mock_response.headers = {"content-type": "application/json"}
mock_response.json.return_value = {"message": "success"}
- parsed_response = sample_custom_agent._parse_response(mock_response)
+ parsed_response = sample_custom_agent._parse_response(
+ mock_response
+ )
assert isinstance(parsed_response, AgentResponse)
assert parsed_response.status_code == 200
assert parsed_response.success is True
@@ -167,7 +198,9 @@ def test_parse_response_error(sample_custom_agent):
mock_response.text = "Not Found"
mock_response.headers = {"content-type": "text/plain"}
- parsed_response = sample_custom_agent._parse_response(mock_response)
+ parsed_response = sample_custom_agent._parse_response(
+ mock_response
+ )
assert isinstance(parsed_response, AgentResponse)
assert parsed_response.status_code == 404
assert parsed_response.success is False
@@ -189,11 +222,15 @@ def test_extract_content_openai_format(sample_custom_agent):
}
]
}
- extracted_content = sample_custom_agent._extract_content(openai_response)
+ extracted_content = sample_custom_agent._extract_content(
+ openai_response
+ )
assert extracted_content == "This is the response content"
logger.debug("OpenAI format content extracted correctly")
except Exception as e:
- logger.error(f"Failed to test extract_content OpenAI format: {e}")
+ logger.error(
+ f"Failed to test extract_content OpenAI format: {e}"
+ )
raise
@@ -202,25 +239,33 @@ def test_extract_content_anthropic_format(sample_custom_agent):
anthropic_response = {
"content": [
{"text": "First part "},
- {"text": "second part"}
+ {"text": "second part"},
]
}
- extracted_content = sample_custom_agent._extract_content(anthropic_response)
+ extracted_content = sample_custom_agent._extract_content(
+ anthropic_response
+ )
assert extracted_content == "First part second part"
logger.debug("Anthropic format content extracted correctly")
except Exception as e:
- logger.error(f"Failed to test extract_content Anthropic format: {e}")
+ logger.error(
+ f"Failed to test extract_content Anthropic format: {e}"
+ )
raise
def test_extract_content_generic_format(sample_custom_agent):
try:
generic_response = {"text": "Generic response text"}
- extracted_content = sample_custom_agent._extract_content(generic_response)
+ extracted_content = sample_custom_agent._extract_content(
+ generic_response
+ )
assert extracted_content == "Generic response text"
logger.debug("Generic format content extracted correctly")
except Exception as e:
- logger.error(f"Failed to test extract_content generic format: {e}")
+ logger.error(
+ f"Failed to test extract_content generic format: {e}"
+ )
raise
@@ -229,14 +274,18 @@ def test_run_success(mock_client_class, sample_custom_agent):
try:
mock_response = Mock()
mock_response.status_code = 200
- mock_response.text = '{"choices": [{"message": {"content": "Success"}}]}'
+ mock_response.text = (
+ '{"choices": [{"message": {"content": "Success"}}]}'
+ )
mock_response.json.return_value = {
"choices": [{"message": {"content": "Success"}}]
}
mock_response.headers = {"content-type": "application/json"}
mock_client_instance = Mock()
- mock_client_instance.__enter__ = Mock(return_value=mock_client_instance)
+ mock_client_instance.__enter__ = Mock(
+ return_value=mock_client_instance
+ )
mock_client_instance.__exit__ = Mock(return_value=None)
mock_client_instance.post.return_value = mock_response
mock_client_class.return_value = mock_client_instance
@@ -259,7 +308,9 @@ def test_run_error_response(mock_client_class, sample_custom_agent):
mock_response.text = "Internal Server Error"
mock_client_instance = Mock()
- mock_client_instance.__enter__ = Mock(return_value=mock_client_instance)
+ mock_client_instance.__enter__ = Mock(
+ return_value=mock_client_instance
+ )
mock_client_instance.__exit__ = Mock(return_value=None)
mock_client_instance.post.return_value = mock_response
mock_client_class.return_value = mock_client_instance
@@ -280,9 +331,13 @@ def test_run_request_error(mock_client_class, sample_custom_agent):
import httpx
mock_client_instance = Mock()
- mock_client_instance.__enter__ = Mock(return_value=mock_client_instance)
+ mock_client_instance.__enter__ = Mock(
+ return_value=mock_client_instance
+ )
mock_client_instance.__exit__ = Mock(return_value=None)
- mock_client_instance.post.side_effect = httpx.RequestError("Connection failed")
+ mock_client_instance.post.side_effect = httpx.RequestError(
+ "Connection failed"
+ )
mock_client_class.return_value = mock_client_instance
test_payload = {"message": "test"}
@@ -295,23 +350,33 @@ def test_run_request_error(mock_client_class, sample_custom_agent):
raise
-@pytest.mark.skipif(not ASYNC_AVAILABLE, reason="pytest-asyncio not installed")
+@pytest.mark.skipif(
+ not ASYNC_AVAILABLE, reason="pytest-asyncio not installed"
+)
@pytest.mark.asyncio
@patch("swarms.structs.custom_agent.httpx.AsyncClient")
-async def test_run_async_success(mock_async_client_class, sample_custom_agent):
+async def test_run_async_success(
+ mock_async_client_class, sample_custom_agent
+):
try:
mock_response = Mock()
mock_response.status_code = 200
- mock_response.text = '{"content": [{"text": "Async Success"}]}'
+ mock_response.text = (
+ '{"content": [{"text": "Async Success"}]}'
+ )
mock_response.json.return_value = {
"content": [{"text": "Async Success"}]
}
mock_response.headers = {"content-type": "application/json"}
mock_client_instance = AsyncMock()
- mock_client_instance.__aenter__ = AsyncMock(return_value=mock_client_instance)
+ mock_client_instance.__aenter__ = AsyncMock(
+ return_value=mock_client_instance
+ )
mock_client_instance.__aexit__ = AsyncMock(return_value=None)
- mock_client_instance.post = AsyncMock(return_value=mock_response)
+ mock_client_instance.post = AsyncMock(
+ return_value=mock_response
+ )
mock_async_client_class.return_value = mock_client_instance
test_payload = {"message": "test"}
@@ -324,19 +389,27 @@ async def test_run_async_success(mock_async_client_class, sample_custom_agent):
raise
-@pytest.mark.skipif(not ASYNC_AVAILABLE, reason="pytest-asyncio not installed")
+@pytest.mark.skipif(
+ not ASYNC_AVAILABLE, reason="pytest-asyncio not installed"
+)
@pytest.mark.asyncio
@patch("swarms.structs.custom_agent.httpx.AsyncClient")
-async def test_run_async_error_response(mock_async_client_class, sample_custom_agent):
+async def test_run_async_error_response(
+ mock_async_client_class, sample_custom_agent
+):
try:
mock_response = Mock()
mock_response.status_code = 400
mock_response.text = "Bad Request"
mock_client_instance = AsyncMock()
- mock_client_instance.__aenter__ = AsyncMock(return_value=mock_client_instance)
+ mock_client_instance.__aenter__ = AsyncMock(
+ return_value=mock_client_instance
+ )
mock_client_instance.__aexit__ = AsyncMock(return_value=None)
- mock_client_instance.post = AsyncMock(return_value=mock_response)
+ mock_client_instance.post = AsyncMock(
+ return_value=mock_response
+ )
mock_async_client_class.return_value = mock_client_instance
test_payload = {"message": "test"}
@@ -367,4 +440,3 @@ def test_agent_response_dataclass():
except Exception as e:
logger.error(f"Failed to test AgentResponse dataclass: {e}")
raise
-
diff --git a/tests/structs/test_deep_discussion.py b/tests/structs/test_deep_discussion.py
index f83a00c5..76aecd00 100644
--- a/tests/structs/test_deep_discussion.py
+++ b/tests/structs/test_deep_discussion.py
@@ -6,8 +6,10 @@ from swarms.structs.agent import Agent
def create_function_agent(name: str, system_prompt: str = None):
if system_prompt is None:
- system_prompt = f"You are {name}. Provide thoughtful responses."
-
+ system_prompt = (
+ f"You are {name}. Provide thoughtful responses."
+ )
+
agent = Agent(
agent_name=name,
agent_description=f"Test agent {name}",
@@ -23,11 +25,11 @@ def create_function_agent(name: str, system_prompt: str = None):
def sample_agents():
agent1 = create_function_agent(
"Debater1",
- "You are a debater who argues for the affirmative position. Be concise and direct."
+ "You are a debater who argues for the affirmative position. Be concise and direct.",
)
agent2 = create_function_agent(
"Debater2",
- "You are a debater who argues for the negative position. Be concise and direct."
+ "You are a debater who argues for the negative position. Be concise and direct.",
)
return [agent1, agent2]
@@ -64,7 +66,7 @@ def test_one_on_one_debate_multiple_loops(sample_agents, sample_task):
assert result is not None
assert isinstance(result, str)
assert len(result) > 0
-
+
result_list = one_on_one_debate(
max_loops=max_loops,
task=sample_task,
@@ -80,7 +82,9 @@ def test_one_on_one_debate_multiple_loops(sample_agents, sample_task):
raise
-def test_one_on_one_debate_agent_alternation(sample_agents, sample_task):
+def test_one_on_one_debate_agent_alternation(
+ sample_agents, sample_task
+):
try:
max_loops = 4
result = one_on_one_debate(
@@ -92,7 +96,7 @@ def test_one_on_one_debate_agent_alternation(sample_agents, sample_task):
assert result is not None
assert isinstance(result, list)
assert len(result) == max_loops
-
+
agent_names = []
for msg in result:
if isinstance(msg, dict):
@@ -105,8 +109,10 @@ def test_one_on_one_debate_agent_alternation(sample_agents, sample_task):
assert agent_names is not None
assert len(agent_names) >= 0
if len(agent_names) > 0:
- assert "Debater1" in agent_names or "Debater2" in agent_names
-
+ assert (
+ "Debater1" in agent_names or "Debater2" in agent_names
+ )
+
if len(agent_names) > 0:
debater1_count = agent_names.count("Debater1")
debater2_count = agent_names.count("Debater2")
@@ -137,7 +143,9 @@ def test_one_on_one_debate_with_image(sample_agents):
raise
-def test_one_on_one_debate_custom_output_types(sample_agents, sample_task):
+def test_one_on_one_debate_custom_output_types(
+ sample_agents, sample_task
+):
try:
output_type_checks = {
"str": str,
@@ -163,7 +171,9 @@ def test_one_on_one_debate_custom_output_types(sample_agents, sample_task):
raise
-def test_one_on_one_debate_list_output_structure(sample_agents, sample_task):
+def test_one_on_one_debate_list_output_structure(
+ sample_agents, sample_task
+):
try:
result = one_on_one_debate(
max_loops=2,
@@ -174,7 +184,7 @@ def test_one_on_one_debate_list_output_structure(sample_agents, sample_task):
assert result is not None
assert isinstance(result, list)
assert len(result) == 2
-
+
for message in result:
assert message is not None
assert isinstance(message, (str, dict))
@@ -191,7 +201,9 @@ def test_one_on_one_debate_list_output_structure(sample_agents, sample_task):
def test_one_on_one_debate_too_few_agents(sample_task):
try:
single_agent = [create_function_agent("SoloAgent")]
- with pytest.raises(ValueError, match="There must be exactly two agents"):
+ with pytest.raises(
+ ValueError, match="There must be exactly two agents"
+ ):
one_on_one_debate(
max_loops=1,
task=sample_task,
@@ -210,7 +222,9 @@ def test_one_on_one_debate_too_many_agents(sample_task):
create_function_agent("Agent2"),
create_function_agent("Agent3"),
]
- with pytest.raises(ValueError, match="There must be exactly two agents"):
+ with pytest.raises(
+ ValueError, match="There must be exactly two agents"
+ ):
one_on_one_debate(
max_loops=1,
task=sample_task,
@@ -225,7 +239,9 @@ def test_one_on_one_debate_too_many_agents(sample_task):
def test_one_on_one_debate_empty_agents(sample_task):
try:
empty_agents = []
- with pytest.raises(ValueError, match="There must be exactly two agents"):
+ with pytest.raises(
+ ValueError, match="There must be exactly two agents"
+ ):
one_on_one_debate(
max_loops=1,
task=sample_task,
@@ -265,7 +281,9 @@ def test_one_on_one_debate_none_task(sample_agents):
raise
-def test_one_on_one_debate_invalid_output_type(sample_agents, sample_task):
+def test_one_on_one_debate_invalid_output_type(
+ sample_agents, sample_task
+):
try:
with pytest.raises((ValueError, TypeError)):
one_on_one_debate(
@@ -289,7 +307,7 @@ def test_one_on_one_debate_zero_loops(sample_agents, sample_task):
)
assert result is not None
assert isinstance(result, str)
-
+
result_list = one_on_one_debate(
max_loops=0,
task=sample_task,
@@ -327,7 +345,9 @@ def test_one_on_one_debate_different_topics(sample_agents):
raise
-def test_one_on_one_debate_long_conversation(sample_agents, sample_task):
+def test_one_on_one_debate_long_conversation(
+ sample_agents, sample_task
+):
try:
max_loops = 5
result = one_on_one_debate(
@@ -349,11 +369,11 @@ def test_one_on_one_debate_different_agent_personalities():
try:
agent1 = create_function_agent(
"Optimist",
- "You are an optimist. Always see the positive side. Be concise."
+ "You are an optimist. Always see the positive side. Be concise.",
)
agent2 = create_function_agent(
"Pessimist",
- "You are a pessimist. Always see the negative side. Be concise."
+ "You are a pessimist. Always see the negative side. Be concise.",
)
agents = [agent1, agent2]
task = "What is the future of AI?"
@@ -366,7 +386,7 @@ def test_one_on_one_debate_different_agent_personalities():
assert result is not None
assert isinstance(result, list)
assert len(result) == 2
-
+
agent_names = []
for msg in result:
if isinstance(msg, dict):
@@ -379,14 +399,19 @@ def test_one_on_one_debate_different_agent_personalities():
assert agent_names is not None
assert len(agent_names) >= 0
if len(agent_names) > 0:
- assert "Optimist" in agent_names or "Pessimist" in agent_names
+ assert (
+ "Optimist" in agent_names
+ or "Pessimist" in agent_names
+ )
logger.info("Different agent personalities test passed")
except Exception as e:
logger.error(f"Failed to test different personalities: {e}")
raise
-def test_one_on_one_debate_conversation_length_matches_loops(sample_agents, sample_task):
+def test_one_on_one_debate_conversation_length_matches_loops(
+ sample_agents, sample_task
+):
try:
for max_loops in [1, 2, 3, 4]:
result = one_on_one_debate(
@@ -404,7 +429,9 @@ def test_one_on_one_debate_conversation_length_matches_loops(sample_agents, samp
raise
-def test_one_on_one_debate_both_agents_participate(sample_agents, sample_task):
+def test_one_on_one_debate_both_agents_participate(
+ sample_agents, sample_task
+):
try:
result = one_on_one_debate(
max_loops=2,
@@ -415,7 +442,7 @@ def test_one_on_one_debate_both_agents_participate(sample_agents, sample_task):
assert result is not None
assert isinstance(result, list)
assert len(result) == 2
-
+
roles = []
for msg in result:
if isinstance(msg, dict) and "role" in msg:
diff --git a/tests/structs/test_graph_workflow.py b/tests/structs/test_graph_workflow.py
new file mode 100644
index 00000000..a00eecb0
--- /dev/null
+++ b/tests/structs/test_graph_workflow.py
@@ -0,0 +1,552 @@
+import pytest
+from swarms.structs.graph_workflow import (
+ GraphWorkflow,
+ Node,
+ NodeType,
+)
+from swarms.structs.agent import Agent
+
+try:
+ import rustworkx as rx
+
+ RUSTWORKX_AVAILABLE = True
+except ImportError:
+ RUSTWORKX_AVAILABLE = False
+
+
+def create_test_agent(name: str, description: str = None) -> Agent:
+ """Create a real agent for testing"""
+ if description is None:
+ description = f"Test agent for {name} operations"
+
+ return Agent(
+ agent_name=name,
+ agent_description=description,
+ model_name="gpt-4o-mini",
+ verbose=False,
+ print_on=False,
+ max_loops=1,
+ )
+
+
+def test_graph_workflow_basic_node_creation():
+ """Test basic GraphWorkflow node creation with real agents"""
+ # Test basic node creation
+ agent = create_test_agent(
+ "TestAgent", "Test agent for node creation"
+ )
+ node = Node.from_agent(agent)
+ assert node.id == "TestAgent"
+ assert node.type == NodeType.AGENT
+ assert node.agent == agent
+
+ # Test node with custom id
+ node2 = Node(id="CustomID", type=NodeType.AGENT, agent=agent)
+ assert node2.id == "CustomID"
+
+
+def test_graph_workflow_multi_agent_collaboration():
+ """Test GraphWorkflow with multiple agents in a collaboration scenario"""
+ # Create specialized agents for a business analysis workflow
+ market_researcher = create_test_agent(
+ "Market-Researcher",
+ "Specialist in market analysis and trend identification",
+ )
+
+ data_analyst = create_test_agent(
+ "Data-Analyst",
+ "Expert in data processing and statistical analysis",
+ )
+
+ strategy_consultant = create_test_agent(
+ "Strategy-Consultant",
+ "Senior consultant for strategic planning and recommendations",
+ )
+
+ # Create workflow with linear execution path
+ workflow = GraphWorkflow(name="Business-Analysis-Workflow")
+ workflow.add_node(market_researcher)
+ workflow.add_node(data_analyst)
+ workflow.add_node(strategy_consultant)
+
+ # Add edges to define execution order
+ workflow.add_edge("Market-Researcher", "Data-Analyst")
+ workflow.add_edge("Data-Analyst", "Strategy-Consultant")
+
+ # Test workflow execution
+ result = workflow.run(
+ "Analyze market opportunities for AI in healthcare"
+ )
+ assert result is not None
+
+
+def test_graph_workflow_parallel_execution():
+ """Test GraphWorkflow with parallel execution paths"""
+ # Create agents for parallel analysis
+ technical_analyst = create_test_agent(
+ "Technical-Analyst",
+ "Technical feasibility and implementation analysis",
+ )
+
+ market_analyst = create_test_agent(
+ "Market-Analyst",
+ "Market positioning and competitive analysis",
+ )
+
+ financial_analyst = create_test_agent(
+ "Financial-Analyst", "Financial modeling and ROI analysis"
+ )
+
+ risk_assessor = create_test_agent(
+ "Risk-Assessor", "Risk assessment and mitigation planning"
+ )
+
+ # Create workflow with parallel execution
+ workflow = GraphWorkflow(name="Parallel-Analysis-Workflow")
+ workflow.add_node(technical_analyst)
+ workflow.add_node(market_analyst)
+ workflow.add_node(financial_analyst)
+ workflow.add_node(risk_assessor)
+
+ # Add edges for fan-out execution (one to many)
+ workflow.add_edges_from_source(
+ "Technical-Analyst",
+ ["Market-Analyst", "Financial-Analyst", "Risk-Assessor"],
+ )
+
+ # Test parallel execution
+ result = workflow.run(
+ "Evaluate feasibility of launching a new fintech platform"
+ )
+ assert result is not None
+
+
+def test_graph_workflow_complex_topology():
+ """Test GraphWorkflow with complex node topology"""
+ # Create agents for a comprehensive product development workflow
+ product_manager = create_test_agent(
+ "Product-Manager", "Product strategy and roadmap management"
+ )
+
+ ux_designer = create_test_agent(
+ "UX-Designer", "User experience design and research"
+ )
+
+ backend_developer = create_test_agent(
+ "Backend-Developer",
+ "Backend system architecture and development",
+ )
+
+ frontend_developer = create_test_agent(
+ "Frontend-Developer",
+ "Frontend interface and user interaction development",
+ )
+
+ qa_engineer = create_test_agent(
+ "QA-Engineer", "Quality assurance and testing specialist"
+ )
+
+ devops_engineer = create_test_agent(
+ "DevOps-Engineer", "Deployment and infrastructure management"
+ )
+
+ # Create workflow with complex dependencies
+ workflow = GraphWorkflow(name="Product-Development-Workflow")
+ workflow.add_node(product_manager)
+ workflow.add_node(ux_designer)
+ workflow.add_node(backend_developer)
+ workflow.add_node(frontend_developer)
+ workflow.add_node(qa_engineer)
+ workflow.add_node(devops_engineer)
+
+ # Define complex execution topology
+ workflow.add_edge("Product-Manager", "UX-Designer")
+ workflow.add_edge("UX-Designer", "Frontend-Developer")
+ workflow.add_edge("Product-Manager", "Backend-Developer")
+ workflow.add_edge("Backend-Developer", "QA-Engineer")
+ workflow.add_edge("Frontend-Developer", "QA-Engineer")
+ workflow.add_edge("QA-Engineer", "DevOps-Engineer")
+
+ # Test complex workflow execution
+ result = workflow.run(
+ "Develop a comprehensive e-commerce platform with AI recommendations"
+ )
+ assert result is not None
+
+
+def test_graph_workflow_error_handling():
+ """Test GraphWorkflow error handling and validation"""
+ # Test with empty workflow
+ workflow = GraphWorkflow()
+ result = workflow.run("Test task")
+ # Empty workflow should handle gracefully
+ assert result is not None
+
+ # Test workflow compilation and caching
+ researcher = create_test_agent(
+ "Researcher", "Research specialist"
+ )
+ workflow.add_node(researcher)
+
+ # First run should compile
+ result1 = workflow.run("Research task")
+ assert result1 is not None
+
+ # Second run should use cached compilation
+ result2 = workflow.run("Another research task")
+ assert result2 is not None
+
+
+def test_graph_workflow_node_metadata():
+ """Test GraphWorkflow with node metadata"""
+ # Create agents with different priorities and requirements
+ high_priority_agent = create_test_agent(
+ "High-Priority-Analyst", "High priority analysis specialist"
+ )
+
+ standard_agent = create_test_agent(
+ "Standard-Analyst", "Standard analysis agent"
+ )
+
+ # Create workflow and add nodes with metadata
+ workflow = GraphWorkflow(name="Metadata-Workflow")
+ workflow.add_node(
+ high_priority_agent,
+ metadata={"priority": "high", "timeout": 60},
+ )
+ workflow.add_node(
+ standard_agent, metadata={"priority": "normal", "timeout": 30}
+ )
+
+ # Add execution dependency
+ workflow.add_edge("High-Priority-Analyst", "Standard-Analyst")
+
+ # Test execution with metadata
+ result = workflow.run(
+ "Analyze business requirements with different priorities"
+ )
+ assert result is not None
+
+
+@pytest.mark.parametrize("backend", ["networkx", "rustworkx"])
+def test_graph_workflow_backend_basic(backend):
+ """Test GraphWorkflow basic functionality with both backends"""
+ if backend == "rustworkx" and not RUSTWORKX_AVAILABLE:
+ pytest.skip("rustworkx not available")
+
+ agent1 = create_test_agent("Agent1", "First agent")
+ agent2 = create_test_agent("Agent2", "Second agent")
+
+ workflow = GraphWorkflow(
+ name=f"Backend-Test-{backend}", backend=backend
+ )
+ workflow.add_node(agent1)
+ workflow.add_node(agent2)
+ workflow.add_edge(agent1, agent2)
+
+ assert len(workflow.nodes) == 2
+ assert len(workflow.edges) == 1
+
+ result = workflow.run("Test task")
+ assert result is not None
+ assert "Agent1" in result
+ assert "Agent2" in result
+
+
+@pytest.mark.parametrize("backend", ["networkx", "rustworkx"])
+def test_graph_workflow_backend_parallel_execution(backend):
+ """Test parallel execution with both backends"""
+ if backend == "rustworkx" and not RUSTWORKX_AVAILABLE:
+ pytest.skip("rustworkx not available")
+
+ coordinator = create_test_agent(
+ "Coordinator", "Coordinates tasks"
+ )
+ analyst1 = create_test_agent("Analyst1", "First analyst")
+ analyst2 = create_test_agent("Analyst2", "Second analyst")
+ analyst3 = create_test_agent("Analyst3", "Third analyst")
+
+ workflow = GraphWorkflow(
+ name=f"Parallel-Test-{backend}", backend=backend
+ )
+ workflow.add_node(coordinator)
+ workflow.add_node(analyst1)
+ workflow.add_node(analyst2)
+ workflow.add_node(analyst3)
+
+ workflow.add_edges_from_source(
+ coordinator, [analyst1, analyst2, analyst3]
+ )
+
+ workflow.compile()
+ assert len(workflow._sorted_layers) >= 1
+ assert (
+ len(workflow._sorted_layers[0]) == 1
+ ) # Coordinator in first layer
+
+ result = workflow.run("Analyze data in parallel")
+ assert result is not None
+
+
+@pytest.mark.parametrize("backend", ["networkx", "rustworkx"])
+def test_graph_workflow_backend_fan_in_pattern(backend):
+ """Test fan-in pattern with both backends"""
+ if backend == "rustworkx" and not RUSTWORKX_AVAILABLE:
+ pytest.skip("rustworkx not available")
+
+ analyst1 = create_test_agent("Analyst1", "First analyst")
+ analyst2 = create_test_agent("Analyst2", "Second analyst")
+ analyst3 = create_test_agent("Analyst3", "Third analyst")
+ synthesizer = create_test_agent(
+ "Synthesizer", "Synthesizes results"
+ )
+
+ workflow = GraphWorkflow(
+ name=f"FanIn-Test-{backend}", backend=backend
+ )
+ workflow.add_node(analyst1)
+ workflow.add_node(analyst2)
+ workflow.add_node(analyst3)
+ workflow.add_node(synthesizer)
+
+ workflow.add_edges_to_target(
+ [analyst1, analyst2, analyst3], synthesizer
+ )
+
+ workflow.compile()
+ assert len(workflow._sorted_layers) >= 2
+ assert synthesizer.agent_name in workflow.end_points
+
+ result = workflow.run("Synthesize multiple analyses")
+ assert result is not None
+
+
+@pytest.mark.parametrize("backend", ["networkx", "rustworkx"])
+def test_graph_workflow_backend_parallel_chain(backend):
+ """Test parallel chain pattern with both backends"""
+ if backend == "rustworkx" and not RUSTWORKX_AVAILABLE:
+ pytest.skip("rustworkx not available")
+
+ collector1 = create_test_agent("Collector1", "First collector")
+ collector2 = create_test_agent("Collector2", "Second collector")
+ processor1 = create_test_agent("Processor1", "First processor")
+ processor2 = create_test_agent("Processor2", "Second processor")
+
+ workflow = GraphWorkflow(
+ name=f"ParallelChain-Test-{backend}", backend=backend
+ )
+ workflow.add_node(collector1)
+ workflow.add_node(collector2)
+ workflow.add_node(processor1)
+ workflow.add_node(processor2)
+
+ workflow.add_parallel_chain(
+ [collector1, collector2], [processor1, processor2]
+ )
+
+ workflow.compile()
+ assert len(workflow.edges) == 4 # 2x2 = 4 edges
+
+ result = workflow.run("Process data from multiple collectors")
+ assert result is not None
+
+
+@pytest.mark.parametrize("backend", ["networkx", "rustworkx"])
+def test_graph_workflow_backend_complex_topology(backend):
+ """Test complex topology with both backends"""
+ if backend == "rustworkx" and not RUSTWORKX_AVAILABLE:
+ pytest.skip("rustworkx not available")
+
+ agents = [
+ create_test_agent(f"Agent{i}", f"Agent {i}") for i in range(5)
+ ]
+
+ workflow = GraphWorkflow(
+ name=f"Complex-Topology-{backend}", backend=backend
+ )
+ for agent in agents:
+ workflow.add_node(agent)
+
+ workflow.add_edge(agents[0], agents[1])
+ workflow.add_edge(agents[0], agents[2])
+ workflow.add_edge(agents[1], agents[3])
+ workflow.add_edge(agents[2], agents[3])
+ workflow.add_edge(agents[3], agents[4])
+
+ workflow.compile()
+ assert len(workflow._sorted_layers) >= 3
+
+ result = workflow.run("Execute complex workflow")
+ assert result is not None
+
+
+@pytest.mark.parametrize("backend", ["networkx", "rustworkx"])
+def test_graph_workflow_backend_validation(backend):
+ """Test workflow validation with both backends"""
+ if backend == "rustworkx" and not RUSTWORKX_AVAILABLE:
+ pytest.skip("rustworkx not available")
+
+ agent1 = create_test_agent("Agent1", "First agent")
+ agent2 = create_test_agent("Agent2", "Second agent")
+ isolated = create_test_agent("Isolated", "Isolated agent")
+
+ workflow = GraphWorkflow(
+ name=f"Validation-Test-{backend}", backend=backend
+ )
+ workflow.add_node(agent1)
+ workflow.add_node(agent2)
+ workflow.add_node(isolated)
+ workflow.add_edge(agent1, agent2)
+
+ validation = workflow.validate(auto_fix=False)
+ assert isinstance(validation, dict)
+ assert "is_valid" in validation
+
+ validation_fixed = workflow.validate(auto_fix=True)
+ assert isinstance(validation_fixed, dict)
+
+
+@pytest.mark.parametrize("backend", ["networkx", "rustworkx"])
+def test_graph_workflow_backend_entry_end_points(backend):
+ """Test entry and end points with both backends"""
+ if backend == "rustworkx" and not RUSTWORKX_AVAILABLE:
+ pytest.skip("rustworkx not available")
+
+ agent1 = create_test_agent("Agent1", "Entry agent")
+ agent2 = create_test_agent("Agent2", "Middle agent")
+ agent3 = create_test_agent("Agent3", "End agent")
+
+ workflow = GraphWorkflow(
+ name=f"EntryEnd-Test-{backend}", backend=backend
+ )
+ workflow.add_node(agent1)
+ workflow.add_node(agent2)
+ workflow.add_node(agent3)
+ workflow.add_edge(agent1, agent2)
+ workflow.add_edge(agent2, agent3)
+
+ workflow.auto_set_entry_points()
+ workflow.auto_set_end_points()
+
+ assert agent1.agent_name in workflow.entry_points
+ assert agent3.agent_name in workflow.end_points
+
+
+def test_graph_workflow_rustworkx_specific():
+ """Test rustworkx-specific features"""
+ if not RUSTWORKX_AVAILABLE:
+ pytest.skip("rustworkx not available")
+
+ agent1 = create_test_agent("Agent1", "First agent")
+ agent2 = create_test_agent("Agent2", "Second agent")
+ agent3 = create_test_agent("Agent3", "Third agent")
+
+ workflow = GraphWorkflow(
+ name="Rustworkx-Specific-Test", backend="rustworkx"
+ )
+ workflow.add_node(agent1)
+ workflow.add_node(agent2)
+ workflow.add_node(agent3)
+ workflow.add_edge(agent1, agent2)
+ workflow.add_edge(agent2, agent3)
+
+ assert (
+ workflow.graph_backend.__class__.__name__
+ == "RustworkxBackend"
+ )
+ assert hasattr(workflow.graph_backend, "_node_id_to_index")
+ assert hasattr(workflow.graph_backend, "_index_to_node_id")
+
+ workflow.compile()
+ assert len(workflow._sorted_layers) == 3
+
+ predecessors = list(
+ workflow.graph_backend.predecessors(agent2.agent_name)
+ )
+ assert agent1.agent_name in predecessors
+
+ descendants = workflow.graph_backend.descendants(
+ agent1.agent_name
+ )
+ assert agent2.agent_name in descendants
+ assert agent3.agent_name in descendants
+
+ result = workflow.run("Test rustworkx backend")
+ assert result is not None
+
+
+def test_graph_workflow_rustworkx_large_scale():
+ """Test rustworkx with larger workflow"""
+ if not RUSTWORKX_AVAILABLE:
+ pytest.skip("rustworkx not available")
+
+ agents = [
+ create_test_agent(f"Agent{i}", f"Agent {i}")
+ for i in range(10)
+ ]
+
+ workflow = GraphWorkflow(
+ name="Rustworkx-Large-Scale", backend="rustworkx"
+ )
+ for agent in agents:
+ workflow.add_node(agent)
+
+ for i in range(len(agents) - 1):
+ workflow.add_edge(agents[i], agents[i + 1])
+
+ workflow.compile()
+ assert len(workflow._sorted_layers) == 10
+
+ result = workflow.run("Test large scale workflow")
+ assert result is not None
+ assert len(result) == 10
+
+
+def test_graph_workflow_rustworkx_agent_objects():
+ """Test rustworkx with Agent objects directly in edges"""
+ if not RUSTWORKX_AVAILABLE:
+ pytest.skip("rustworkx not available")
+
+ agent1 = create_test_agent("Agent1", "First agent")
+ agent2 = create_test_agent("Agent2", "Second agent")
+ agent3 = create_test_agent("Agent3", "Third agent")
+
+ workflow = GraphWorkflow(
+ name="Rustworkx-Agent-Objects", backend="rustworkx"
+ )
+ workflow.add_node(agent1)
+ workflow.add_node(agent2)
+ workflow.add_node(agent3)
+
+ workflow.add_edges_from_source(agent1, [agent2, agent3])
+ workflow.add_edges_to_target([agent2, agent3], agent1)
+
+ workflow.compile()
+ assert len(workflow.edges) == 4
+
+ result = workflow.run("Test agent objects in edges")
+ assert result is not None
+
+
+def test_graph_workflow_backend_fallback():
+ """Test backend fallback when rustworkx unavailable"""
+ workflow = GraphWorkflow(
+ name="Fallback-Test", backend="rustworkx"
+ )
+ agent = create_test_agent("Agent", "Test agent")
+ workflow.add_node(agent)
+
+ if not RUSTWORKX_AVAILABLE:
+ assert (
+ workflow.graph_backend.__class__.__name__
+ == "NetworkXBackend"
+ )
+ else:
+ assert (
+ workflow.graph_backend.__class__.__name__
+ == "RustworkxBackend"
+ )
+
+
+if __name__ == "__main__":
+ pytest.main([__file__, "-v"])
diff --git a/tests/structs/test_graph_workflow_comprehensive.py b/tests/structs/test_graph_workflow_comprehensive.py
deleted file mode 100644
index 5cb6a4a6..00000000
--- a/tests/structs/test_graph_workflow_comprehensive.py
+++ /dev/null
@@ -1,225 +0,0 @@
-import pytest
-from swarms.structs.graph_workflow import (
- GraphWorkflow,
- Node,
- NodeType,
-)
-from swarms.structs.agent import Agent
-
-
-def create_test_agent(name: str, description: str = None) -> Agent:
- """Create a real agent for testing"""
- if description is None:
- description = f"Test agent for {name} operations"
-
- return Agent(
- agent_name=name,
- agent_description=description,
- model_name="gpt-4o-mini",
- verbose=False,
- print_on=False,
- max_loops=1,
- )
-
-
-def test_graph_workflow_basic_node_creation():
- """Test basic GraphWorkflow node creation with real agents"""
- # Test basic node creation
- agent = create_test_agent(
- "TestAgent", "Test agent for node creation"
- )
- node = Node.from_agent(agent)
- assert node.id == "TestAgent"
- assert node.type == NodeType.AGENT
- assert node.agent == agent
-
- # Test node with custom id
- node2 = Node(id="CustomID", type=NodeType.AGENT, agent=agent)
- assert node2.id == "CustomID"
-
-
-def test_graph_workflow_multi_agent_collaboration():
- """Test GraphWorkflow with multiple agents in a collaboration scenario"""
- # Create specialized agents for a business analysis workflow
- market_researcher = create_test_agent(
- "Market-Researcher",
- "Specialist in market analysis and trend identification",
- )
-
- data_analyst = create_test_agent(
- "Data-Analyst",
- "Expert in data processing and statistical analysis",
- )
-
- strategy_consultant = create_test_agent(
- "Strategy-Consultant",
- "Senior consultant for strategic planning and recommendations",
- )
-
- # Create workflow with linear execution path
- workflow = GraphWorkflow(name="Business-Analysis-Workflow")
- workflow.add_node(market_researcher)
- workflow.add_node(data_analyst)
- workflow.add_node(strategy_consultant)
-
- # Add edges to define execution order
- workflow.add_edge("Market-Researcher", "Data-Analyst")
- workflow.add_edge("Data-Analyst", "Strategy-Consultant")
-
- # Test workflow execution
- result = workflow.run(
- "Analyze market opportunities for AI in healthcare"
- )
- assert result is not None
-
-
-def test_graph_workflow_parallel_execution():
- """Test GraphWorkflow with parallel execution paths"""
- # Create agents for parallel analysis
- technical_analyst = create_test_agent(
- "Technical-Analyst",
- "Technical feasibility and implementation analysis",
- )
-
- market_analyst = create_test_agent(
- "Market-Analyst",
- "Market positioning and competitive analysis",
- )
-
- financial_analyst = create_test_agent(
- "Financial-Analyst", "Financial modeling and ROI analysis"
- )
-
- risk_assessor = create_test_agent(
- "Risk-Assessor", "Risk assessment and mitigation planning"
- )
-
- # Create workflow with parallel execution
- workflow = GraphWorkflow(name="Parallel-Analysis-Workflow")
- workflow.add_node(technical_analyst)
- workflow.add_node(market_analyst)
- workflow.add_node(financial_analyst)
- workflow.add_node(risk_assessor)
-
- # Add edges for fan-out execution (one to many)
- workflow.add_edges_from_source(
- "Technical-Analyst",
- ["Market-Analyst", "Financial-Analyst", "Risk-Assessor"],
- )
-
- # Test parallel execution
- result = workflow.run(
- "Evaluate feasibility of launching a new fintech platform"
- )
- assert result is not None
-
-
-def test_graph_workflow_complex_topology():
- """Test GraphWorkflow with complex node topology"""
- # Create agents for a comprehensive product development workflow
- product_manager = create_test_agent(
- "Product-Manager", "Product strategy and roadmap management"
- )
-
- ux_designer = create_test_agent(
- "UX-Designer", "User experience design and research"
- )
-
- backend_developer = create_test_agent(
- "Backend-Developer",
- "Backend system architecture and development",
- )
-
- frontend_developer = create_test_agent(
- "Frontend-Developer",
- "Frontend interface and user interaction development",
- )
-
- qa_engineer = create_test_agent(
- "QA-Engineer", "Quality assurance and testing specialist"
- )
-
- devops_engineer = create_test_agent(
- "DevOps-Engineer", "Deployment and infrastructure management"
- )
-
- # Create workflow with complex dependencies
- workflow = GraphWorkflow(name="Product-Development-Workflow")
- workflow.add_node(product_manager)
- workflow.add_node(ux_designer)
- workflow.add_node(backend_developer)
- workflow.add_node(frontend_developer)
- workflow.add_node(qa_engineer)
- workflow.add_node(devops_engineer)
-
- # Define complex execution topology
- workflow.add_edge("Product-Manager", "UX-Designer")
- workflow.add_edge("UX-Designer", "Frontend-Developer")
- workflow.add_edge("Product-Manager", "Backend-Developer")
- workflow.add_edge("Backend-Developer", "QA-Engineer")
- workflow.add_edge("Frontend-Developer", "QA-Engineer")
- workflow.add_edge("QA-Engineer", "DevOps-Engineer")
-
- # Test complex workflow execution
- result = workflow.run(
- "Develop a comprehensive e-commerce platform with AI recommendations"
- )
- assert result is not None
-
-
-def test_graph_workflow_error_handling():
- """Test GraphWorkflow error handling and validation"""
- # Test with empty workflow
- workflow = GraphWorkflow()
- result = workflow.run("Test task")
- # Empty workflow should handle gracefully
- assert result is not None
-
- # Test workflow compilation and caching
- researcher = create_test_agent(
- "Researcher", "Research specialist"
- )
- workflow.add_node(researcher)
-
- # First run should compile
- result1 = workflow.run("Research task")
- assert result1 is not None
-
- # Second run should use cached compilation
- result2 = workflow.run("Another research task")
- assert result2 is not None
-
-
-def test_graph_workflow_node_metadata():
- """Test GraphWorkflow with node metadata"""
- # Create agents with different priorities and requirements
- high_priority_agent = create_test_agent(
- "High-Priority-Analyst", "High priority analysis specialist"
- )
-
- standard_agent = create_test_agent(
- "Standard-Analyst", "Standard analysis agent"
- )
-
- # Create workflow and add nodes with metadata
- workflow = GraphWorkflow(name="Metadata-Workflow")
- workflow.add_node(
- high_priority_agent,
- metadata={"priority": "high", "timeout": 60},
- )
- workflow.add_node(
- standard_agent, metadata={"priority": "normal", "timeout": 30}
- )
-
- # Add execution dependency
- workflow.add_edge("High-Priority-Analyst", "Standard-Analyst")
-
- # Test execution with metadata
- result = workflow.run(
- "Analyze business requirements with different priorities"
- )
- assert result is not None
-
-
-if __name__ == "__main__":
- pytest.main([__file__, "-v"])
diff --git a/tests/structs/test_i_agent.py b/tests/structs/test_i_agent.py
new file mode 100644
index 00000000..1c1f95c5
--- /dev/null
+++ b/tests/structs/test_i_agent.py
@@ -0,0 +1,84 @@
+from swarms.agents.i_agent import IterativeReflectiveExpansion
+
+
+def test_ire_agent_initialization():
+ """Test IRE agent initialization with default parameters"""
+ agent = IterativeReflectiveExpansion()
+
+ assert agent is not None
+ assert agent.agent_name == "General-Reasoning-Agent"
+ assert agent.max_iterations == 5
+ assert agent.output_type == "dict"
+ assert agent.agent is not None
+
+
+def test_ire_agent_custom_initialization():
+ """Test IRE agent initialization with custom parameters"""
+ agent = IterativeReflectiveExpansion(
+ agent_name="Custom-IRE-Agent",
+ description="A custom reasoning agent",
+ max_iterations=3,
+ model_name="gpt-4o",
+ output_type="string",
+ )
+
+ assert agent.agent_name == "Custom-IRE-Agent"
+ assert agent.description == "A custom reasoning agent"
+ assert agent.max_iterations == 3
+ assert agent.output_type == "string"
+
+
+def test_ire_agent_execution():
+ """Test IRE agent execution with a simple problem"""
+ agent = IterativeReflectiveExpansion(
+ agent_name="Test-IRE-Agent",
+ model_name="gpt-4o",
+ max_iterations=2,
+ output_type="dict",
+ )
+
+ # Test with a simple reasoning task
+ task = "What are three main benefits of renewable energy?"
+ result = agent.run(task)
+
+ # Result should not be None
+ assert result is not None
+ # Result should be dict or string based on output_type
+ assert isinstance(result, (str, dict))
+
+
+def test_ire_agent_generate_hypotheses():
+ """Test IRE agent hypothesis generation"""
+ agent = IterativeReflectiveExpansion(
+ agent_name="Hypothesis-Test-Agent",
+ max_iterations=1,
+ )
+
+ task = "How can we reduce carbon emissions?"
+ hypotheses = agent.generate_initial_hypotheses(task)
+
+ assert hypotheses is not None
+ assert isinstance(hypotheses, list)
+ assert len(hypotheses) > 0
+
+
+def test_ire_agent_workflow():
+ """Test complete IRE agent workflow with iterative refinement"""
+ agent = IterativeReflectiveExpansion(
+ agent_name="Workflow-Test-Agent",
+ description="Agent for testing complete workflow",
+ model_name="gpt-4o",
+ max_iterations=2,
+ output_type="dict",
+ )
+
+ # Test with a problem that requires iterative refinement
+ task = "Design an efficient public transportation system for a small city"
+ result = agent.run(task)
+
+ # Verify the result is valid
+ assert result is not None
+ assert isinstance(result, (str, dict))
+
+ # Check that conversation was populated during execution
+ assert agent.conversation is not None
diff --git a/tests/structs/test_multi_agent_debate.py b/tests/structs/test_multi_agent_debate.py
index 12737b3b..806a94c6 100644
--- a/tests/structs/test_multi_agent_debate.py
+++ b/tests/structs/test_multi_agent_debate.py
@@ -18,8 +18,10 @@ from swarms.structs.agent import Agent
def create_function_agent(name: str, system_prompt: str = None):
if system_prompt is None:
- system_prompt = f"You are {name}. Provide concise and direct responses."
-
+ system_prompt = (
+ f"You are {name}. Provide concise and direct responses."
+ )
+
agent = Agent(
agent_name=name,
agent_description=f"Test agent {name}",
@@ -34,12 +36,10 @@ def create_function_agent(name: str, system_prompt: str = None):
@pytest.fixture
def sample_two_agents():
agent1 = create_function_agent(
- "Agent1",
- "You are Agent1. Provide concise responses."
+ "Agent1", "You are Agent1. Provide concise responses."
)
agent2 = create_function_agent(
- "Agent2",
- "You are Agent2. Provide concise responses."
+ "Agent2", "You are Agent2. Provide concise responses."
)
return [agent1, agent2]
@@ -71,7 +71,9 @@ def test_one_on_one_debate_initialization(sample_two_agents):
assert debate.output_type == "str-all-except-first"
logger.info("OneOnOneDebate initialization test passed")
except Exception as e:
- logger.error(f"Failed to test OneOnOneDebate initialization: {e}")
+ logger.error(
+ f"Failed to test OneOnOneDebate initialization: {e}"
+ )
raise
@@ -95,7 +97,9 @@ def test_one_on_one_debate_run(sample_two_agents, sample_task):
raise
-def test_one_on_one_debate_wrong_number_of_agents(sample_three_agents, sample_task):
+def test_one_on_one_debate_wrong_number_of_agents(
+ sample_three_agents, sample_task
+):
try:
debate = OneOnOneDebate(
max_loops=2,
@@ -104,13 +108,19 @@ def test_one_on_one_debate_wrong_number_of_agents(sample_three_agents, sample_ta
)
with pytest.raises(ValueError, match="exactly two agents"):
debate.run(sample_task)
- logger.info("OneOnOneDebate wrong number of agents test passed")
+ logger.info(
+ "OneOnOneDebate wrong number of agents test passed"
+ )
except Exception as e:
- logger.error(f"Failed to test OneOnOneDebate wrong number of agents: {e}")
+ logger.error(
+ f"Failed to test OneOnOneDebate wrong number of agents: {e}"
+ )
raise
-def test_one_on_one_debate_output_types(sample_two_agents, sample_task):
+def test_one_on_one_debate_output_types(
+ sample_two_agents, sample_task
+):
try:
assert sample_two_agents is not None
assert sample_task is not None
@@ -133,7 +143,9 @@ def test_one_on_one_debate_output_types(sample_two_agents, sample_task):
assert isinstance(result, str)
logger.info("OneOnOneDebate output types test passed")
except Exception as e:
- logger.error(f"Failed to test OneOnOneDebate output types: {e}")
+ logger.error(
+ f"Failed to test OneOnOneDebate output types: {e}"
+ )
raise
@@ -175,13 +187,19 @@ def test_expert_panel_discussion_initialization(sample_three_agents):
assert panel.max_rounds == 2
assert len(panel.agents) == 3
assert panel.moderator is not None
- logger.info("ExpertPanelDiscussion initialization test passed")
+ logger.info(
+ "ExpertPanelDiscussion initialization test passed"
+ )
except Exception as e:
- logger.error(f"Failed to test ExpertPanelDiscussion initialization: {e}")
+ logger.error(
+ f"Failed to test ExpertPanelDiscussion initialization: {e}"
+ )
raise
-def test_expert_panel_discussion_run(sample_three_agents, sample_task):
+def test_expert_panel_discussion_run(
+ sample_three_agents, sample_task
+):
try:
moderator = create_function_agent("Moderator")
assert moderator is not None
@@ -217,15 +235,23 @@ def test_expert_panel_discussion_insufficient_agents(sample_task):
output_type="str-all-except-first",
)
assert panel is not None
- with pytest.raises(ValueError, match="At least two expert agents"):
+ with pytest.raises(
+ ValueError, match="At least two expert agents"
+ ):
panel.run(sample_task)
- logger.info("ExpertPanelDiscussion insufficient agents test passed")
+ logger.info(
+ "ExpertPanelDiscussion insufficient agents test passed"
+ )
except Exception as e:
- logger.error(f"Failed to test ExpertPanelDiscussion insufficient agents: {e}")
+ logger.error(
+ f"Failed to test ExpertPanelDiscussion insufficient agents: {e}"
+ )
raise
-def test_expert_panel_discussion_no_moderator(sample_three_agents, sample_task):
+def test_expert_panel_discussion_no_moderator(
+ sample_three_agents, sample_task
+):
try:
panel = ExpertPanelDiscussion(
max_rounds=2,
@@ -233,11 +259,15 @@ def test_expert_panel_discussion_no_moderator(sample_three_agents, sample_task):
moderator=None,
output_type="str-all-except-first",
)
- with pytest.raises(ValueError, match="moderator agent is required"):
+ with pytest.raises(
+ ValueError, match="moderator agent is required"
+ ):
panel.run(sample_task)
logger.info("ExpertPanelDiscussion no moderator test passed")
except Exception as e:
- logger.error(f"Failed to test ExpertPanelDiscussion no moderator: {e}")
+ logger.error(
+ f"Failed to test ExpertPanelDiscussion no moderator: {e}"
+ )
raise
@@ -257,7 +287,9 @@ def test_round_table_discussion_initialization(sample_three_agents):
assert round_table.facilitator is not None
logger.info("RoundTableDiscussion initialization test passed")
except Exception as e:
- logger.error(f"Failed to test RoundTableDiscussion initialization: {e}")
+ logger.error(
+ f"Failed to test RoundTableDiscussion initialization: {e}"
+ )
raise
@@ -292,15 +324,23 @@ def test_round_table_discussion_insufficient_agents(sample_task):
facilitator=facilitator,
output_type="str-all-except-first",
)
- with pytest.raises(ValueError, match="At least two participants"):
+ with pytest.raises(
+ ValueError, match="At least two participants"
+ ):
round_table.run(sample_task)
- logger.info("RoundTableDiscussion insufficient agents test passed")
+ logger.info(
+ "RoundTableDiscussion insufficient agents test passed"
+ )
except Exception as e:
- logger.error(f"Failed to test RoundTableDiscussion insufficient agents: {e}")
+ logger.error(
+ f"Failed to test RoundTableDiscussion insufficient agents: {e}"
+ )
raise
-def test_round_table_discussion_no_facilitator(sample_three_agents, sample_task):
+def test_round_table_discussion_no_facilitator(
+ sample_three_agents, sample_task
+):
try:
round_table = RoundTableDiscussion(
max_cycles=2,
@@ -308,11 +348,15 @@ def test_round_table_discussion_no_facilitator(sample_three_agents, sample_task)
facilitator=None,
output_type="str-all-except-first",
)
- with pytest.raises(ValueError, match="facilitator agent is required"):
+ with pytest.raises(
+ ValueError, match="facilitator agent is required"
+ ):
round_table.run(sample_task)
logger.info("RoundTableDiscussion no facilitator test passed")
except Exception as e:
- logger.error(f"Failed to test RoundTableDiscussion no facilitator: {e}")
+ logger.error(
+ f"Failed to test RoundTableDiscussion no facilitator: {e}"
+ )
raise
@@ -338,7 +382,9 @@ def test_interview_series_initialization():
assert interview.follow_up_depth == 1
logger.info("InterviewSeries initialization test passed")
except Exception as e:
- logger.error(f"Failed to test InterviewSeries initialization: {e}")
+ logger.error(
+ f"Failed to test InterviewSeries initialization: {e}"
+ )
raise
@@ -378,11 +424,15 @@ def test_interview_series_no_interviewer(sample_task):
follow_up_depth=1,
output_type="str-all-except-first",
)
- with pytest.raises(ValueError, match="Both interviewer and interviewee"):
+ with pytest.raises(
+ ValueError, match="Both interviewer and interviewee"
+ ):
interview.run(sample_task)
logger.info("InterviewSeries no interviewer test passed")
except Exception as e:
- logger.error(f"Failed to test InterviewSeries no interviewer: {e}")
+ logger.error(
+ f"Failed to test InterviewSeries no interviewer: {e}"
+ )
raise
@@ -396,11 +446,15 @@ def test_interview_series_no_interviewee(sample_task):
follow_up_depth=1,
output_type="str-all-except-first",
)
- with pytest.raises(ValueError, match="Both interviewer and interviewee"):
+ with pytest.raises(
+ ValueError, match="Both interviewer and interviewee"
+ ):
interview.run(sample_task)
logger.info("InterviewSeries no interviewee test passed")
except Exception as e:
- logger.error(f"Failed to test InterviewSeries no interviewee: {e}")
+ logger.error(
+ f"Failed to test InterviewSeries no interviewee: {e}"
+ )
raise
@@ -425,13 +479,18 @@ def test_interview_series_default_questions(sample_task):
assert len(result) >= 0
logger.info("InterviewSeries default questions test passed")
except Exception as e:
- logger.error(f"Failed to test InterviewSeries default questions: {e}")
+ logger.error(
+ f"Failed to test InterviewSeries default questions: {e}"
+ )
raise
def test_peer_review_process_initialization():
try:
- reviewers = [create_function_agent("Reviewer1"), create_function_agent("Reviewer2")]
+ reviewers = [
+ create_function_agent("Reviewer1"),
+ create_function_agent("Reviewer2"),
+ ]
assert reviewers is not None
assert len(reviewers) == 2
assert reviewers[0] is not None
@@ -450,13 +509,18 @@ def test_peer_review_process_initialization():
assert peer_review.review_rounds == 2
logger.info("PeerReviewProcess initialization test passed")
except Exception as e:
- logger.error(f"Failed to test PeerReviewProcess initialization: {e}")
+ logger.error(
+ f"Failed to test PeerReviewProcess initialization: {e}"
+ )
raise
def test_peer_review_process_run(sample_task):
try:
- reviewers = [create_function_agent("Reviewer1"), create_function_agent("Reviewer2")]
+ reviewers = [
+ create_function_agent("Reviewer1"),
+ create_function_agent("Reviewer2"),
+ ]
assert reviewers is not None
assert len(reviewers) == 2
author = create_function_agent("Author")
@@ -491,7 +555,9 @@ def test_peer_review_process_no_reviewers(sample_task):
peer_review.run(sample_task)
logger.info("PeerReviewProcess no reviewers test passed")
except Exception as e:
- logger.error(f"Failed to test PeerReviewProcess no reviewers: {e}")
+ logger.error(
+ f"Failed to test PeerReviewProcess no reviewers: {e}"
+ )
raise
@@ -504,11 +570,15 @@ def test_peer_review_process_no_author(sample_task):
review_rounds=2,
output_type="str-all-except-first",
)
- with pytest.raises(ValueError, match="author agent is required"):
+ with pytest.raises(
+ ValueError, match="author agent is required"
+ ):
peer_review.run(sample_task)
logger.info("PeerReviewProcess no author test passed")
except Exception as e:
- logger.error(f"Failed to test PeerReviewProcess no author: {e}")
+ logger.error(
+ f"Failed to test PeerReviewProcess no author: {e}"
+ )
raise
@@ -529,7 +599,9 @@ def test_mediation_session_initialization(sample_two_agents):
assert mediation.max_sessions == 2
logger.info("MediationSession initialization test passed")
except Exception as e:
- logger.error(f"Failed to test MediationSession initialization: {e}")
+ logger.error(
+ f"Failed to test MediationSession initialization: {e}"
+ )
raise
@@ -567,13 +639,19 @@ def test_mediation_session_insufficient_parties(sample_task):
)
with pytest.raises(ValueError, match="At least two parties"):
mediation.run(sample_task)
- logger.info("MediationSession insufficient parties test passed")
+ logger.info(
+ "MediationSession insufficient parties test passed"
+ )
except Exception as e:
- logger.error(f"Failed to test MediationSession insufficient parties: {e}")
+ logger.error(
+ f"Failed to test MediationSession insufficient parties: {e}"
+ )
raise
-def test_mediation_session_no_mediator(sample_two_agents, sample_task):
+def test_mediation_session_no_mediator(
+ sample_two_agents, sample_task
+):
try:
mediation = MediationSession(
parties=sample_two_agents,
@@ -581,11 +659,15 @@ def test_mediation_session_no_mediator(sample_two_agents, sample_task):
max_sessions=2,
output_type="str-all-except-first",
)
- with pytest.raises(ValueError, match="mediator agent is required"):
+ with pytest.raises(
+ ValueError, match="mediator agent is required"
+ ):
mediation.run(sample_task)
logger.info("MediationSession no mediator test passed")
except Exception as e:
- logger.error(f"Failed to test MediationSession no mediator: {e}")
+ logger.error(
+ f"Failed to test MediationSession no mediator: {e}"
+ )
raise
@@ -608,7 +690,9 @@ def test_brainstorming_session_initialization(sample_three_agents):
assert brainstorming.build_on_ideas is True
logger.info("BrainstormingSession initialization test passed")
except Exception as e:
- logger.error(f"Failed to test BrainstormingSession initialization: {e}")
+ logger.error(
+ f"Failed to test BrainstormingSession initialization: {e}"
+ )
raise
@@ -646,15 +730,23 @@ def test_brainstorming_session_insufficient_participants(sample_task):
build_on_ideas=True,
output_type="str-all-except-first",
)
- with pytest.raises(ValueError, match="At least two participants"):
+ with pytest.raises(
+ ValueError, match="At least two participants"
+ ):
brainstorming.run(sample_task)
- logger.info("BrainstormingSession insufficient participants test passed")
+ logger.info(
+ "BrainstormingSession insufficient participants test passed"
+ )
except Exception as e:
- logger.error(f"Failed to test BrainstormingSession insufficient participants: {e}")
+ logger.error(
+ f"Failed to test BrainstormingSession insufficient participants: {e}"
+ )
raise
-def test_brainstorming_session_no_facilitator(sample_three_agents, sample_task):
+def test_brainstorming_session_no_facilitator(
+ sample_three_agents, sample_task
+):
try:
brainstorming = BrainstormingSession(
participants=sample_three_agents,
@@ -663,11 +755,15 @@ def test_brainstorming_session_no_facilitator(sample_three_agents, sample_task):
build_on_ideas=True,
output_type="str-all-except-first",
)
- with pytest.raises(ValueError, match="facilitator agent is required"):
+ with pytest.raises(
+ ValueError, match="facilitator agent is required"
+ ):
brainstorming.run(sample_task)
logger.info("BrainstormingSession no facilitator test passed")
except Exception as e:
- logger.error(f"Failed to test BrainstormingSession no facilitator: {e}")
+ logger.error(
+ f"Failed to test BrainstormingSession no facilitator: {e}"
+ )
raise
@@ -699,7 +795,9 @@ def test_trial_simulation_initialization():
assert trial.phases == ["opening", "closing"]
logger.info("TrialSimulation initialization test passed")
except Exception as e:
- logger.error(f"Failed to test TrialSimulation initialization: {e}")
+ logger.error(
+ f"Failed to test TrialSimulation initialization: {e}"
+ )
raise
@@ -746,7 +844,9 @@ def test_trial_simulation_no_prosecution(sample_task):
trial.run(sample_task)
logger.info("TrialSimulation no prosecution test passed")
except Exception as e:
- logger.error(f"Failed to test TrialSimulation no prosecution: {e}")
+ logger.error(
+ f"Failed to test TrialSimulation no prosecution: {e}"
+ )
raise
@@ -774,7 +874,9 @@ def test_trial_simulation_default_phases(sample_task):
assert len(result) >= 0
logger.info("TrialSimulation default phases test passed")
except Exception as e:
- logger.error(f"Failed to test TrialSimulation default phases: {e}")
+ logger.error(
+ f"Failed to test TrialSimulation default phases: {e}"
+ )
raise
@@ -797,7 +899,9 @@ def test_council_meeting_initialization(sample_three_agents):
assert council.require_consensus is False
logger.info("CouncilMeeting initialization test passed")
except Exception as e:
- logger.error(f"Failed to test CouncilMeeting initialization: {e}")
+ logger.error(
+ f"Failed to test CouncilMeeting initialization: {e}"
+ )
raise
@@ -835,15 +939,21 @@ def test_council_meeting_insufficient_members(sample_task):
require_consensus=False,
output_type="str-all-except-first",
)
- with pytest.raises(ValueError, match="At least two council members"):
+ with pytest.raises(
+ ValueError, match="At least two council members"
+ ):
council.run(sample_task)
logger.info("CouncilMeeting insufficient members test passed")
except Exception as e:
- logger.error(f"Failed to test CouncilMeeting insufficient members: {e}")
+ logger.error(
+ f"Failed to test CouncilMeeting insufficient members: {e}"
+ )
raise
-def test_council_meeting_no_chairperson(sample_three_agents, sample_task):
+def test_council_meeting_no_chairperson(
+ sample_three_agents, sample_task
+):
try:
council = CouncilMeeting(
council_members=sample_three_agents,
@@ -852,11 +962,15 @@ def test_council_meeting_no_chairperson(sample_three_agents, sample_task):
require_consensus=False,
output_type="str-all-except-first",
)
- with pytest.raises(ValueError, match="chairperson agent is required"):
+ with pytest.raises(
+ ValueError, match="chairperson agent is required"
+ ):
council.run(sample_task)
logger.info("CouncilMeeting no chairperson test passed")
except Exception as e:
- logger.error(f"Failed to test CouncilMeeting no chairperson: {e}")
+ logger.error(
+ f"Failed to test CouncilMeeting no chairperson: {e}"
+ )
raise
@@ -880,7 +994,9 @@ def test_mentorship_session_initialization():
assert mentorship.include_feedback is True
logger.info("MentorshipSession initialization test passed")
except Exception as e:
- logger.error(f"Failed to test MentorshipSession initialization: {e}")
+ logger.error(
+ f"Failed to test MentorshipSession initialization: {e}"
+ )
raise
@@ -918,11 +1034,15 @@ def test_mentorship_session_no_mentor(sample_task):
include_feedback=True,
output_type="str-all-except-first",
)
- with pytest.raises(ValueError, match="Both mentor and mentee"):
+ with pytest.raises(
+ ValueError, match="Both mentor and mentee"
+ ):
mentorship.run(sample_task)
logger.info("MentorshipSession no mentor test passed")
except Exception as e:
- logger.error(f"Failed to test MentorshipSession no mentor: {e}")
+ logger.error(
+ f"Failed to test MentorshipSession no mentor: {e}"
+ )
raise
@@ -936,11 +1056,15 @@ def test_mentorship_session_no_mentee(sample_task):
include_feedback=True,
output_type="str-all-except-first",
)
- with pytest.raises(ValueError, match="Both mentor and mentee"):
+ with pytest.raises(
+ ValueError, match="Both mentor and mentee"
+ ):
mentorship.run(sample_task)
logger.info("MentorshipSession no mentee test passed")
except Exception as e:
- logger.error(f"Failed to test MentorshipSession no mentee: {e}")
+ logger.error(
+ f"Failed to test MentorshipSession no mentee: {e}"
+ )
raise
@@ -963,7 +1087,9 @@ def test_negotiation_session_initialization(sample_two_agents):
assert negotiation.include_concessions is True
logger.info("NegotiationSession initialization test passed")
except Exception as e:
- logger.error(f"Failed to test NegotiationSession initialization: {e}")
+ logger.error(
+ f"Failed to test NegotiationSession initialization: {e}"
+ )
raise
@@ -1003,13 +1129,19 @@ def test_negotiation_session_insufficient_parties(sample_task):
)
with pytest.raises(ValueError, match="At least two parties"):
negotiation.run(sample_task)
- logger.info("NegotiationSession insufficient parties test passed")
+ logger.info(
+ "NegotiationSession insufficient parties test passed"
+ )
except Exception as e:
- logger.error(f"Failed to test NegotiationSession insufficient parties: {e}")
+ logger.error(
+ f"Failed to test NegotiationSession insufficient parties: {e}"
+ )
raise
-def test_negotiation_session_no_mediator(sample_two_agents, sample_task):
+def test_negotiation_session_no_mediator(
+ sample_two_agents, sample_task
+):
try:
negotiation = NegotiationSession(
parties=sample_two_agents,
@@ -1018,15 +1150,21 @@ def test_negotiation_session_no_mediator(sample_two_agents, sample_task):
include_concessions=True,
output_type="str-all-except-first",
)
- with pytest.raises(ValueError, match="mediator agent is required"):
+ with pytest.raises(
+ ValueError, match="mediator agent is required"
+ ):
negotiation.run(sample_task)
logger.info("NegotiationSession no mediator test passed")
except Exception as e:
- logger.error(f"Failed to test NegotiationSession no mediator: {e}")
+ logger.error(
+ f"Failed to test NegotiationSession no mediator: {e}"
+ )
raise
-def test_negotiation_session_without_concessions(sample_two_agents, sample_task):
+def test_negotiation_session_without_concessions(
+ sample_two_agents, sample_task
+):
try:
mediator = create_function_agent("Mediator")
assert mediator is not None
@@ -1043,13 +1181,19 @@ def test_negotiation_session_without_concessions(sample_two_agents, sample_task)
assert result is not None
assert isinstance(result, str)
assert len(result) >= 0
- logger.info("NegotiationSession without concessions test passed")
+ logger.info(
+ "NegotiationSession without concessions test passed"
+ )
except Exception as e:
- logger.error(f"Failed to test NegotiationSession without concessions: {e}")
+ logger.error(
+ f"Failed to test NegotiationSession without concessions: {e}"
+ )
raise
-def test_one_on_one_debate_multiple_loops(sample_two_agents, sample_task):
+def test_one_on_one_debate_multiple_loops(
+ sample_two_agents, sample_task
+):
try:
assert sample_two_agents is not None
debate = OneOnOneDebate(
@@ -1064,11 +1208,15 @@ def test_one_on_one_debate_multiple_loops(sample_two_agents, sample_task):
assert len(result) >= 0
logger.info("OneOnOneDebate multiple loops test passed")
except Exception as e:
- logger.error(f"Failed to test OneOnOneDebate multiple loops: {e}")
+ logger.error(
+ f"Failed to test OneOnOneDebate multiple loops: {e}"
+ )
raise
-def test_expert_panel_discussion_output_types(sample_three_agents, sample_task):
+def test_expert_panel_discussion_output_types(
+ sample_three_agents, sample_task
+):
try:
moderator = create_function_agent("Moderator")
assert moderator is not None
@@ -1093,5 +1241,7 @@ def test_expert_panel_discussion_output_types(sample_three_agents, sample_task):
assert isinstance(result, str)
logger.info("ExpertPanelDiscussion output types test passed")
except Exception as e:
- logger.error(f"Failed to test ExpertPanelDiscussion output types: {e}")
- raise
\ No newline at end of file
+ logger.error(
+ f"Failed to test ExpertPanelDiscussion output types: {e}"
+ )
+ raise
diff --git a/tests/structs/test_reasoning_agent_router.py b/tests/structs/test_reasoning_agent_router.py
index cf5a8782..2507058c 100644
--- a/tests/structs/test_reasoning_agent_router.py
+++ b/tests/structs/test_reasoning_agent_router.py
@@ -6,6 +6,9 @@ from swarms.agents.reasoning_agents import (
ReasoningAgentInitializationError,
ReasoningAgentRouter,
)
+from dotenv import load_dotenv
+
+load_dotenv()
def test_router_initialization():
@@ -55,7 +58,7 @@ def test_router_initialization():
eval=True,
random_models_on=True,
majority_voting_prompt="Custom voting prompt",
- reasoning_model_name="claude-3-5-sonnet-20240620",
+ reasoning_model_name="gpt-4o",
)
assert (
custom_router is not None
diff --git a/tests/structs/test_sequential_workflow.py b/tests/structs/test_sequential_workflow.py
index 6d8f74a1..99dd73ae 100644
--- a/tests/structs/test_sequential_workflow.py
+++ b/tests/structs/test_sequential_workflow.py
@@ -3,21 +3,6 @@ import pytest
from swarms import Agent, SequentialWorkflow
-# Test SequentialWorkflow class
-def test_sequential_workflow_initialization():
- workflow = SequentialWorkflow()
- assert isinstance(workflow, SequentialWorkflow)
- assert len(workflow.tasks) == 0
- assert workflow.max_loops == 1
- assert workflow.autosave is False
- assert (
- workflow.saved_state_filepath
- == "sequential_workflow_state.json"
- )
- assert workflow.restore_state_filepath is None
- assert workflow.dashboard is False
-
-
def test_sequential_workflow_initialization_with_agents():
"""Test SequentialWorkflow initialization with agents"""
agent1 = Agent(