From dd2b0c2a3c6fcc66e0d34426e399d08807a45306 Mon Sep 17 00:00:00 2001 From: harshalmore31 Date: Sun, 10 Aug 2025 20:51:09 +0530 Subject: [PATCH 01/19] added agent_loader --- agent_loader_research_team.py | 165 ++++++++ docs/swarms/utils/agent_loader.md | 353 ++++++++++++++++ examples/agents_loader_example.py | 127 ++++++ .../multi_agent/simple_agent_loader_demo.py | 140 +++++++ swarms/utils/agent_loader.py | 378 ++++++++++++++++++ test_agent_loader.py | 244 +++++++++++ 6 files changed, 1407 insertions(+) create mode 100644 agent_loader_research_team.py create mode 100644 docs/swarms/utils/agent_loader.md create mode 100644 examples/agents_loader_example.py create mode 100644 examples/multi_agent/simple_agent_loader_demo.py create mode 100644 swarms/utils/agent_loader.py create mode 100644 test_agent_loader.py diff --git a/agent_loader_research_team.py b/agent_loader_research_team.py new file mode 100644 index 00000000..1ae02301 --- /dev/null +++ b/agent_loader_research_team.py @@ -0,0 +1,165 @@ +""" +AgentLoader Example: Research Team Collaboration +=============================================== + +This example demonstrates using the AgentLoader to create a research team +from markdown files and orchestrate them in a sequential workflow. +""" + +import os +import sys +import tempfile +from pathlib import Path + +# Add local swarms to path +sys.path.insert(0, str(Path(__file__).parent.parent.parent)) + +from swarms.structs.agent import Agent +from swarms.structs.sequential_workflow import SequentialWorkflow +from swarms.utils.agent_loader import AgentLoader + +def create_research_agents(): + """Create markdown files for research team agents""" + + market_researcher = """| name | description | model | +|------|-------------|-------| +| market-researcher | Expert in market analysis and competitive intelligence | gpt-4 | + +## Focus Areas +- Market size and growth analysis +- Competitive landscape assessment +- Consumer behavior patterns +- Industry trend identification + +## Approach +1. Gather comprehensive market data +2. Analyze quantitative and qualitative indicators +3. Identify key market drivers and barriers +4. Evaluate competitive positioning +5. Assess market opportunities and threats + +## Output +- Market analysis reports with key metrics +- Competitive intelligence briefings +- Market opportunity assessments +- Consumer behavior insights +""" + + financial_analyst = """| name | description | model | +|------|-------------|-------| +| financial-analyst | Specialist in financial modeling and investment analysis | gpt-4 | + +## Focus Areas +- Financial statement analysis +- Valuation modeling techniques +- Investment risk assessment +- Cash flow projections + +## Approach +1. Conduct thorough financial analysis +2. Build comprehensive financial models +3. Perform multiple valuation methods +4. Assess financial risks and sensitivities +5. Provide investment recommendations + +## Output +- Financial analysis reports +- Valuation models with scenarios +- Investment recommendation memos +- Risk assessment matrices +""" + + industry_expert = """| name | description | model | +|------|-------------|-------| +| industry-expert | Domain specialist with deep industry knowledge | gpt-4 | + +## Focus Areas +- Industry structure and dynamics +- Regulatory environment analysis +- Technology trends and disruptions +- Supply chain analysis + +## Approach +1. Map industry structure and stakeholders +2. Analyze regulatory framework +3. Identify technology trends +4. Evaluate supply chain dynamics +5. Assess competitive positioning + +## Output +- Industry landscape reports +- Regulatory compliance assessments +- Technology trend analysis +- Strategic positioning recommendations +""" + + return { + "market_researcher.md": market_researcher, + "financial_analyst.md": financial_analyst, + "industry_expert.md": industry_expert + } + +def main(): + """Main execution function""" + + temp_dir = tempfile.mkdtemp() + + try: + # Create markdown files + agent_definitions = create_research_agents() + file_paths = [] + + for filename, content in agent_definitions.items(): + file_path = os.path.join(temp_dir, filename) + with open(file_path, 'w', encoding='utf-8') as f: + f.write(content) + file_paths.append(file_path) + + # Load agents using AgentLoader + loader = AgentLoader() + agents = loader.load_multiple_agents( + file_paths, + max_loops=1, + verbose=False + ) + + print(f"Loaded {len(agents)} agents") + for i, agent in enumerate(agents): + print(f"Agent {i}: {agent.agent_name} - LLM: {hasattr(agent, 'llm')}") + + # Create sequential workflow + research_workflow = SequentialWorkflow( + agents=agents, + max_loops=1, + ) + + # Define research task + task = """ + Analyze the AI-powered healthcare diagnostics market for a potential $50M investment. + + Focus on: + 1. Market size, growth, and key drivers + 2. Competitive landscape and major players + 3. Financial viability and investment metrics + 4. Industry dynamics and regulatory factors + + Provide strategic recommendations for market entry. + """ + + # Execute workflow + result = research_workflow.run(task) + + return result + + finally: + # Cleanup + for file_path in file_paths: + if os.path.exists(file_path): + os.remove(file_path) + os.rmdir(temp_dir) + +if __name__ == "__main__": + result = main() + print("Research Analysis Complete:") + print("-" * 50) + print(result) \ No newline at end of file diff --git a/docs/swarms/utils/agent_loader.md b/docs/swarms/utils/agent_loader.md new file mode 100644 index 00000000..c3e1fd32 --- /dev/null +++ b/docs/swarms/utils/agent_loader.md @@ -0,0 +1,353 @@ +# AgentLoader - Load Agents from Markdown Files + +The `AgentLoader` is a powerful utility for creating Swarms agents from markdown files. It supports both single and multiple markdown file loading, providing a flexible way to define and deploy agents using a structured markdown format. + +## Overview + +The AgentLoader enables you to: +- Load single agents from markdown files +- Load multiple agents from directories or file lists +- Parse structured markdown content into agent configurations +- Maintain backwards compatibility with existing agent systems +- Provide comprehensive error handling and validation + +## Installation + +The AgentLoader is included with the Swarms framework: + +```python +from swarms.utils import AgentLoader, load_agent_from_markdown, load_agents_from_markdown +``` + +## Markdown Format + +The AgentLoader expects markdown files to follow a specific structure: + +### Required Table Header +```markdown +| name | description | model | +|------|-------------|-------| +| agent-name | Brief description of the agent | gpt-4 | +``` + +### Optional Sections +```markdown +## Focus Areas +- Key responsibility area 1 +- Key responsibility area 2 +- Key responsibility area 3 + +## Approach +1. First step in methodology +2. Second step in methodology +3. Third step in methodology + +## Output +- Expected deliverable 1 +- Expected deliverable 2 +- Expected deliverable 3 +``` + +## Quick Start + +### Loading a Single Agent + +```python +from swarms.utils import load_agent_from_markdown + +# Load agent from markdown file +agent = load_agent_from_markdown( + file_path="path/to/agent.md" +) + +# Use the agent +response = agent.run("What are your capabilities?") +print(response) +``` + +### Loading Multiple Agents + +```python +from swarms.utils import load_agents_from_markdown + +# Load all agents from directory +agents = load_agents_from_markdown( + file_paths="./agents_directory/" +) + +# Load agents from specific files +agents = load_agents_from_markdown( + file_paths=["agent1.md", "agent2.md", "agent3.md"] +) + +print(f"Loaded {len(agents)} agents") +``` + +## Class-Based Usage + +### AgentLoader Class + +For more advanced usage, use the `AgentLoader` class directly: + +```python +from swarms.utils import AgentLoader + +# Initialize loader +loader = AgentLoader() + +# Load single agent +agent = loader.load_single_agent("path/to/agent.md") + +# Load multiple agents +agents = loader.load_multiple_agents("./agents_directory/") + +# Parse markdown file without creating agent +config = loader.parse_markdown_file("path/to/agent.md") +print(config.name, config.description) +``` + +## Configuration Options + +You can override default configuration when loading agents: + +```python +agent = load_agent_from_markdown( + file_path="agent.md", + max_loops=5, + verbose=True, + dashboard=True, + autosave=False, + context_length=200000 +) +``` + +### Available Configuration Parameters + +- `max_loops` (int): Maximum number of reasoning loops (default: 1) +- `autosave` (bool): Enable automatic state saving (default: True) +- `dashboard` (bool): Enable dashboard monitoring (default: False) +- `verbose` (bool): Enable verbose logging (default: False) +- `dynamic_temperature_enabled` (bool): Enable dynamic temperature (default: False) +- `saved_state_path` (str): Path for saving agent state +- `user_name` (str): User identifier (default: "default_user") +- `retry_attempts` (int): Number of retry attempts (default: 3) +- `context_length` (int): Maximum context length (default: 100000) +- `return_step_meta` (bool): Return step metadata (default: False) +- `output_type` (str): Output format type (default: "str") +- `auto_generate_prompt` (bool): Auto-generate prompts (default: False) +- `artifacts_on` (bool): Enable artifacts (default: False) + +## Complete Example + +### Example Markdown File (performance-engineer.md) + +```markdown +| name | description | model | +|------|-------------|-------| +| performance-engineer | Optimize application performance and identify bottlenecks | gpt-4 | + +## Focus Areas +- Application profiling and performance analysis +- Database optimization and query tuning +- Memory and CPU usage optimization +- Load testing and capacity planning +- Infrastructure scaling recommendations + +## Approach +1. Analyze application architecture and identify potential bottlenecks +2. Implement comprehensive monitoring and logging systems +3. Conduct performance testing under various load conditions +4. Profile memory usage and optimize resource consumption +5. Provide actionable recommendations with implementation guides + +## Output +- Detailed performance analysis reports with metrics +- Optimized code recommendations and examples +- Infrastructure scaling and architecture suggestions +- Monitoring and alerting configuration guidelines +- Load testing results and capacity planning documents +``` + +### Loading and Using the Agent + +```python +from swarms.utils import load_agent_from_markdown +from swarms.utils.litellm_wrapper import LiteLLM + +# Initialize model +model = LiteLLM(model_name="gpt-4") + +# Load the performance engineer agent +agent = load_agent_from_markdown( + file_path="performance-engineer.md", + model=model, + max_loops=3, + verbose=True +) + +# Use the agent +task = """ +Analyze the performance of a web application that handles 10,000 concurrent users +but is experiencing slow response times averaging 3 seconds. The application uses +a PostgreSQL database and is deployed on AWS with 4 EC2 instances behind a load balancer. +""" + +analysis = agent.run(task) +print(f"Performance Analysis:\n{analysis}") +``` + +## Error Handling + +The AgentLoader provides comprehensive error handling: + +```python +from swarms.utils import AgentLoader + +loader = AgentLoader() + +try: + # This will raise FileNotFoundError + agent = loader.load_single_agent("nonexistent.md") +except FileNotFoundError as e: + print(f"File not found: {e}") + +try: + # This will handle parsing errors gracefully + agents = loader.load_multiple_agents("./invalid_directory/") + print(f"Successfully loaded {len(agents)} agents") +except Exception as e: + print(f"Error loading agents: {e}") +``` + +## Advanced Features + +### Custom System Prompt Building + +The AgentLoader automatically builds comprehensive system prompts from the markdown structure: + +```python +loader = AgentLoader() +config = loader.parse_markdown_file("agent.md") + +# The system prompt includes: +# - Role description from the table +# - Focus areas as bullet points +# - Approach as numbered steps +# - Expected outputs as deliverables + +print("Generated System Prompt:") +print(config.system_prompt) +``` + +### Batch Processing + +Process multiple agent files efficiently: + +```python +import os +from pathlib import Path +from swarms.utils import AgentLoader + +loader = AgentLoader() + +# Find all markdown files in a directory +agent_dir = Path("./agents") +md_files = list(agent_dir.glob("*.md")) + +# Load all agents +agents = [] +for file_path in md_files: + try: + agent = loader.load_single_agent(str(file_path)) + agents.append(agent) + print(f"✓ Loaded: {agent.agent_name}") + except Exception as e: + print(f"✗ Failed to load {file_path}: {e}") + +print(f"\nSuccessfully loaded {len(agents)} agents") +``` + +## Integration with Swarms + +The loaded agents are fully compatible with Swarms orchestration systems: + +```python +from swarms.utils import load_agents_from_markdown +from swarms.structs import SequentialWorkflow + +# Load multiple specialized agents +agents = load_agents_from_markdown("./specialist_agents/") + +# Create a sequential workflow +workflow = SequentialWorkflow( + agents=agents, + max_loops=1 +) + +# Execute complex task across multiple agents +result = workflow.run("Conduct a comprehensive system audit") +``` + +## Best Practices + +1. **Consistent Naming**: Use clear, descriptive agent names +2. **Detailed Descriptions**: Provide comprehensive role descriptions +3. **Structured Sections**: Use the optional sections to define agent behavior +4. **Error Handling**: Always wrap agent loading in try-catch blocks +5. **Model Selection**: Choose appropriate models based on agent complexity +6. **Configuration**: Override defaults when specific behavior is needed + +## Backwards Compatibility + +The AgentLoader maintains full backwards compatibility with: +- Claude Code sub-agents markdown format +- Existing swarms agent creation patterns +- Legacy configuration systems +- Current workflow orchestration + +## API Reference + +### AgentLoader Class + +```python +class AgentLoader: + def __init__(self, model: Optional[LiteLLM] = None) + def parse_markdown_file(self, file_path: str) -> MarkdownAgentConfig + def load_single_agent(self, file_path: str, **kwargs) -> Agent + def load_multiple_agents(self, file_paths: Union[str, List[str]], **kwargs) -> List[Agent] +``` + +### Convenience Functions + +```python +def load_agent_from_markdown(file_path: str, model: Optional[LiteLLM] = None, **kwargs) -> Agent +def load_agents_from_markdown(file_paths: Union[str, List[str]], model: Optional[LiteLLM] = None, **kwargs) -> List[Agent] +``` + +### Configuration Model + +```python +class MarkdownAgentConfig(BaseModel): + name: str + description: str + model_name: Optional[str] = "gpt-4" + system_prompt: str + focus_areas: Optional[List[str]] = [] + approach: Optional[List[str]] = [] + output: Optional[List[str]] = [] + # ... additional configuration fields +``` + +## Examples Repository + +Find more examples in the Swarms repository: +- `examples/agents_loader_example.py` - Complete usage demonstration +- `test_agent_loader.py` - Test suite with validation examples +- `examples/single_agent/utils/markdown_agent.py` - Markdown agent utilities + +## Support + +For questions and support: +- GitHub Issues: [https://github.com/kyegomez/swarms/issues](https://github.com/kyegomez/swarms/issues) +- Documentation: [https://docs.swarms.world](https://docs.swarms.world) +- Community: Join our Discord for real-time support \ No newline at end of file diff --git a/examples/agents_loader_example.py b/examples/agents_loader_example.py new file mode 100644 index 00000000..12359af6 --- /dev/null +++ b/examples/agents_loader_example.py @@ -0,0 +1,127 @@ +""" +Example demonstrating the AgentLoader for loading agents from markdown files. + +This example shows: +1. Loading a single agent from a markdown file +2. Loading multiple agents from markdown files +3. Using the convenience functions +4. Error handling and validation +""" + +import os +from swarms.utils.agent_loader import AgentLoader, load_agent_from_markdown, load_agents_from_markdown + +def main(): + # Initialize the loader + loader = AgentLoader() + + print("=== AgentLoader Demo ===") + + # Example 1: Create a sample markdown file for testing + sample_md = """| name | description | model | +|------|-------------|-------| +| performance-engineer | Optimize application performance and identify bottlenecks | gpt-4 | + +## Focus Areas +- Application profiling and performance analysis +- Database optimization and query tuning +- Memory and CPU usage optimization +- Load testing and capacity planning + +## Approach +1. Analyze application architecture and identify potential bottlenecks +2. Implement comprehensive monitoring and logging +3. Conduct performance testing under various load conditions +4. Optimize critical paths and resource usage +5. Document findings and provide actionable recommendations + +## Output +- Performance analysis reports with specific metrics +- Optimized code recommendations +- Infrastructure scaling suggestions +- Monitoring and alerting setup guidelines +""" + + # Create sample markdown file + sample_file = "sample_agent.md" + with open(sample_file, 'w') as f: + f.write(sample_md) + + try: + # Example 2: Load single agent using class method + print("\\n1. Loading single agent using AgentLoader class:") + agent = loader.load_single_agent(sample_file) + print(f" Loaded agent: {agent.agent_name}") + print(f" System prompt preview: {agent.system_prompt[:100]}...") + + # Example 3: Load single agent using convenience function + print("\\n2. Loading single agent using convenience function:") + agent2 = load_agent_from_markdown(sample_file) + print(f" Loaded agent: {agent2.agent_name}") + + # Example 4: Load multiple agents (from directory or list) + print("\\n3. Loading multiple agents:") + + # Create another sample file + sample_md2 = """| name | description | model | +|------|-------------|-------| +| security-analyst | Analyze and improve system security | gpt-4 | + +## Focus Areas +- Security vulnerability assessment +- Code security review +- Infrastructure hardening + +## Approach +1. Conduct thorough security audits +2. Identify potential vulnerabilities +3. Recommend security improvements + +## Output +- Security assessment reports +- Vulnerability remediation plans +- Security best practices documentation +""" + + sample_file2 = "security_agent.md" + with open(sample_file2, 'w') as f: + f.write(sample_md2) + + # Load multiple agents from list + agents = loader.load_multiple_agents([sample_file, sample_file2]) + print(f" Loaded {len(agents)} agents:") + for agent in agents: + print(f" - {agent.agent_name}") + + # Example 5: Load agents from directory (current directory) + print("\\n4. Loading agents from current directory:") + current_dir_agents = load_agents_from_markdown(".") + print(f" Found {len(current_dir_agents)} agents in current directory") + + # Example 6: Demonstrate error handling + print("\\n5. Error handling demo:") + try: + loader.load_single_agent("nonexistent.md") + except FileNotFoundError as e: + print(f" Caught expected error: {e}") + + # Example 7: Test agent functionality + print("\\n6. Testing loaded agent functionality:") + test_agent = agents[0] + response = test_agent.run("What are the key steps for performance optimization?") + print(f" Agent response preview: {str(response)[:150]}...") + + except Exception as e: + print(f"Error during demo: {e}") + + finally: + # Cleanup sample files + for file in [sample_file, sample_file2]: + if os.path.exists(file): + os.remove(file) + print("\\n Cleaned up sample files") + + print("\\n=== Demo Complete ===") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/examples/multi_agent/simple_agent_loader_demo.py b/examples/multi_agent/simple_agent_loader_demo.py new file mode 100644 index 00000000..e78eae52 --- /dev/null +++ b/examples/multi_agent/simple_agent_loader_demo.py @@ -0,0 +1,140 @@ +""" +Simple AgentLoader Demo +======================= + +A working demonstration of how to create agents from markdown-like definitions +and use them in workflows. +""" + +import os +import tempfile +from pathlib import Path +import sys + +# Add local swarms to path +sys.path.insert(0, str(Path(__file__).parent.parent.parent)) + +from swarms.structs.agent import Agent +from swarms.structs.sequential_workflow import SequentialWorkflow + +def create_agents_from_configs(): + """Create agents from configuration dictionaries (simulating markdown parsing)""" + + # These would normally come from parsing markdown files + agent_configs = [ + { + "name": "market-researcher", + "description": "Expert in market analysis and competitive intelligence", + "system_prompt": """You are a market research specialist. Your expertise includes: + +Focus Areas: +- Market size and growth analysis +- Competitive landscape assessment +- Consumer behavior patterns +- Industry trend identification + +Approach: +1. Gather comprehensive market data +2. Analyze quantitative and qualitative indicators +3. Identify key market drivers and barriers +4. Evaluate competitive positioning +5. Assess market opportunities and threats + +Provide detailed market analysis reports with key metrics and actionable insights.""", + "model": "gpt-4" + }, + { + "name": "financial-analyst", + "description": "Specialist in financial modeling and investment analysis", + "system_prompt": """You are a financial analysis expert. Your responsibilities include: + +Focus Areas: +- Financial statement analysis +- Valuation modeling techniques +- Investment risk assessment +- Cash flow projections + +Approach: +1. Conduct thorough financial analysis +2. Build comprehensive financial models +3. Perform multiple valuation methods +4. Assess financial risks and sensitivities +5. Provide investment recommendations + +Generate detailed financial reports with valuation models and risk assessments.""", + "model": "gpt-4" + }, + { + "name": "industry-expert", + "description": "Domain specialist with deep industry knowledge", + "system_prompt": """You are an industry analysis expert. Your focus areas include: + +Focus Areas: +- Industry structure and dynamics +- Regulatory environment analysis +- Technology trends and disruptions +- Supply chain analysis + +Approach: +1. Map industry structure and stakeholders +2. Analyze regulatory framework +3. Identify technology trends +4. Evaluate supply chain dynamics +5. Assess competitive positioning + +Provide comprehensive industry landscape reports with strategic recommendations.""", + "model": "gpt-4" + } + ] + + agents = [] + for config in agent_configs: + agent = Agent( + agent_name=config["name"], + system_prompt=config["system_prompt"], + model_name=config["model"], + max_loops=1, + verbose=False + ) + agents.append(agent) + print(f"Created agent: {agent.agent_name}") + + return agents + +def main(): + """Main execution function""" + + # Create agents + agents = create_agents_from_configs() + + # Create sequential workflow + research_workflow = SequentialWorkflow( + agents=agents, + max_loops=1, + ) + + # Define research task + task = """ + Analyze the AI-powered healthcare diagnostics market for a potential $50M investment. + + Focus on: + 1. Market size, growth projections, and key drivers + 2. Competitive landscape and major players + 3. Financial viability and investment attractiveness + 4. Industry dynamics and regulatory considerations + + Provide strategic recommendations for market entry. + """ + + print("Executing research workflow...") + print("=" * 50) + + # Execute workflow + result = research_workflow.run(task) + + print("\nResearch Analysis Complete:") + print("-" * 50) + print(result) + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/swarms/utils/agent_loader.py b/swarms/utils/agent_loader.py new file mode 100644 index 00000000..7b918421 --- /dev/null +++ b/swarms/utils/agent_loader.py @@ -0,0 +1,378 @@ +import os +import re +from pathlib import Path +from typing import Any, Dict, List, Optional, Union +from pydantic import BaseModel, Field, field_validator +from loguru import logger + +from swarms.structs.agent import Agent + + +class MarkdownAgentConfig(BaseModel): + """Configuration model for agents loaded from markdown files.""" + name: str + description: str + model_name: Optional[str] = "gpt-4" + system_prompt: str + focus_areas: Optional[List[str]] = [] + approach: Optional[List[str]] = [] + output: Optional[List[str]] = [] + max_loops: int = Field(default=1, ge=1) + autosave: bool = False + dashboard: bool = False + verbose: bool = False + dynamic_temperature_enabled: bool = False + saved_state_path: Optional[str] = None + user_name: str = "default_user" + retry_attempts: int = Field(default=3, ge=1) + context_length: int = Field(default=100000, ge=1000) + return_step_meta: bool = False + output_type: str = "str" + auto_generate_prompt: bool = False + artifacts_on: bool = False + artifacts_file_extension: str = ".md" + artifacts_output_path: str = "" + + @field_validator("system_prompt") + @classmethod + def validate_system_prompt(cls, v): + if not v or not isinstance(v, str) or len(v.strip()) == 0: + raise ValueError("System prompt must be a non-empty string") + return v + + +class AgentLoader: + """ + Loader for creating agents from markdown files. + + Supports both single markdown file and multiple markdown files. + Maintains backwards compatibility with claude code sub agents markdown format. + + Features: + - Single markdown file loading + - Multiple markdown files loading (batch processing) + - Flexible markdown parsing + - Agent configuration extraction from markdown structure + - Error handling and validation + """ + + def __init__(self): + """ + Initialize the AgentLoader. + """ + pass + + def parse_markdown_table(self, content: str) -> Dict[str, str]: + """ + Parse markdown table to extract agent metadata. + + Args: + content: Markdown content containing a table + + Returns: + Dictionary with parsed table data + """ + table_data = {} + + # Find markdown table pattern + table_pattern = r'\|([^|]+)\|([^|]+)\|([^|]+)\|' + lines = content.split('\n') + + header_found = False + for line in lines: + if '|' in line and not header_found: + # Skip header separator line + if '---' in line: + header_found = True + continue + + # Parse header + if 'name' in line.lower() and 'description' in line.lower(): + continue + + elif header_found and '|' in line: + # Parse data row + match = re.match(table_pattern, line) + if match: + table_data['name'] = match.group(1).strip() + table_data['description'] = match.group(2).strip() + table_data['model_name'] = match.group(3).strip() + break + + return table_data + + def extract_sections(self, content: str) -> Dict[str, List[str]]: + """ + Extract structured sections from markdown content. + + Args: + content: Markdown content + + Returns: + Dictionary with section names as keys and content lists as values + """ + sections = {} + current_section = None + current_content = [] + + lines = content.split('\n') + for line in lines: + # Check for headers (## Section Name) + if line.startswith('## '): + # Save previous section + if current_section: + sections[current_section.lower()] = current_content + + # Start new section + current_section = line[3:].strip() + current_content = [] + + elif current_section and line.strip(): + # Add content to current section + # Remove markdown list markers + clean_line = re.sub(r'^[-*+]\s*', '', line.strip()) + clean_line = re.sub(r'^\d+\.\s*', '', clean_line) + if clean_line: + current_content.append(clean_line) + + # Save last section + if current_section: + sections[current_section.lower()] = current_content + + return sections + + def build_system_prompt(self, config_data: Dict[str, Any]) -> str: + """ + Build comprehensive system prompt from parsed markdown data. + + Args: + config_data: Dictionary containing parsed agent configuration + + Returns: + Complete system prompt string + """ + prompt_parts = [] + + # Add description + if config_data.get('description'): + prompt_parts.append(f"Role: {config_data['description']}") + + # Add focus areas + if config_data.get('focus_areas'): + prompt_parts.append("\nFocus Areas:") + for area in config_data['focus_areas']: + prompt_parts.append(f"- {area}") + + # Add approach + if config_data.get('approach'): + prompt_parts.append("\nApproach:") + for i, step in enumerate(config_data['approach'], 1): + prompt_parts.append(f"{i}. {step}") + + # Add expected output + if config_data.get('output'): + prompt_parts.append("\nExpected Output:") + for output in config_data['output']: + prompt_parts.append(f"- {output}") + + return '\n'.join(prompt_parts) + + def parse_markdown_file(self, file_path: str) -> MarkdownAgentConfig: + """ + Parse a single markdown file to extract agent configuration. + + Args: + file_path: Path to markdown file + + Returns: + MarkdownAgentConfig object with parsed configuration + + Raises: + FileNotFoundError: If file doesn't exist + ValueError: If parsing fails + """ + if not os.path.exists(file_path): + raise FileNotFoundError(f"Markdown file {file_path} not found.") + + try: + with open(file_path, 'r', encoding='utf-8') as file: + content = file.read() + + # Parse table for basic metadata + table_data = self.parse_markdown_table(content) + + # Extract sections + sections = self.extract_sections(content) + + # Build configuration + config_data = { + 'name': table_data.get('name', Path(file_path).stem), + 'description': table_data.get('description', 'Agent loaded from markdown'), + 'model_name': table_data.get('model_name', 'gpt-4'), + 'focus_areas': sections.get('focus areas', []), + 'approach': sections.get('approach', []), + 'output': sections.get('output', []), + } + + # Build system prompt + system_prompt = self.build_system_prompt(config_data) + config_data['system_prompt'] = system_prompt + + logger.info(f"Successfully parsed markdown file: {file_path}") + return MarkdownAgentConfig(**config_data) + + except Exception as e: + logger.error(f"Error parsing markdown file {file_path}: {str(e)}") + raise ValueError(f"Error parsing markdown file {file_path}: {str(e)}") + + def load_agent_from_markdown(self, file_path: str, **kwargs) -> Agent: + """ + Load a single agent from a markdown file. + + Args: + file_path: Path to markdown file + **kwargs: Additional arguments to override default configuration + + Returns: + Configured Agent instance + """ + config = self.parse_markdown_file(file_path) + + # Override with any provided kwargs + config_dict = config.model_dump() + config_dict.update(kwargs) + + # Remove fields not needed for Agent creation + agent_fields = { + 'agent_name': config_dict['name'], + 'system_prompt': config_dict['system_prompt'], + 'model_name': config_dict.get('model_name', 'gpt-4'), + # Don't pass llm explicitly - let Agent handle it internally + 'max_loops': config_dict['max_loops'], + 'autosave': config_dict['autosave'], + 'dashboard': config_dict['dashboard'], + 'verbose': config_dict['verbose'], + 'dynamic_temperature_enabled': config_dict['dynamic_temperature_enabled'], + 'saved_state_path': config_dict['saved_state_path'], + 'user_name': config_dict['user_name'], + 'retry_attempts': config_dict['retry_attempts'], + 'context_length': config_dict['context_length'], + 'return_step_meta': config_dict['return_step_meta'], + 'output_type': config_dict['output_type'], + 'auto_generate_prompt': config_dict['auto_generate_prompt'], + 'artifacts_on': config_dict['artifacts_on'], + 'artifacts_file_extension': config_dict['artifacts_file_extension'], + 'artifacts_output_path': config_dict['artifacts_output_path'], + } + + try: + logger.info(f"Creating agent '{config.name}' from {file_path}") + agent = Agent(**agent_fields) + logger.info(f"Successfully created agent '{config.name}' from {file_path}") + return agent + except Exception as e: + import traceback + logger.error(f"Error creating agent from {file_path}: {str(e)}") + logger.error(f"Traceback: {traceback.format_exc()}") + raise ValueError(f"Error creating agent from {file_path}: {str(e)}") + + def load_agents_from_markdown(self, file_paths: Union[str, List[str]], **kwargs) -> List[Agent]: + """ + Load multiple agents from markdown files. + + Args: + file_paths: Single file path, directory path, or list of file paths + **kwargs: Additional arguments to override default configuration + + Returns: + List of configured Agent instances + """ + agents = [] + paths_to_process = [] + + # Handle different input types + if isinstance(file_paths, str): + if os.path.isdir(file_paths): + # Directory - find all .md files + md_files = list(Path(file_paths).glob('*.md')) + paths_to_process = [str(f) for f in md_files] + elif os.path.isfile(file_paths): + # Single file + paths_to_process = [file_paths] + else: + raise FileNotFoundError(f"Path {file_paths} not found.") + elif isinstance(file_paths, list): + paths_to_process = file_paths + else: + raise ValueError("file_paths must be a string or list of strings") + + # Process each file + for file_path in paths_to_process: + try: + agent = self.load_agent_from_markdown(file_path, **kwargs) + agents.append(agent) + except Exception as e: + logger.warning(f"Skipping {file_path} due to error: {str(e)}") + continue + + logger.info(f"Successfully loaded {len(agents)} agents from markdown files") + return agents + + def load_single_agent(self, file_path: str, **kwargs) -> Agent: + """ + Convenience method for loading a single agent. + Backwards compatible with claude code sub agents markdown. + + Args: + file_path: Path to markdown file + **kwargs: Additional configuration overrides + + Returns: + Configured Agent instance + """ + return self.load_agent_from_markdown(file_path, **kwargs) + + def load_multiple_agents(self, file_paths: Union[str, List[str]], **kwargs) -> List[Agent]: + """ + Convenience method for loading multiple agents. + Backwards compatible with claude code sub agents markdown. + + Args: + file_paths: Directory path or list of file paths + **kwargs: Additional configuration overrides + + Returns: + List of configured Agent instances + """ + return self.load_agents_from_markdown(file_paths, **kwargs) + + +# Convenience functions for backwards compatibility +def load_agent_from_markdown(file_path: str, **kwargs) -> Agent: + """ + Load a single agent from a markdown file. + + Args: + file_path: Path to markdown file + **kwargs: Additional configuration overrides + + Returns: + Configured Agent instance + """ + loader = AgentLoader() + return loader.load_single_agent(file_path, **kwargs) + + +def load_agents_from_markdown(file_paths: Union[str, List[str]], **kwargs) -> List[Agent]: + """ + Load multiple agents from markdown files. + + Args: + file_paths: Directory path or list of file paths + **kwargs: Additional configuration overrides + + Returns: + List of configured Agent instances + """ + loader = AgentLoader() + return loader.load_multiple_agents(file_paths, **kwargs) \ No newline at end of file diff --git a/test_agent_loader.py b/test_agent_loader.py new file mode 100644 index 00000000..3a02693a --- /dev/null +++ b/test_agent_loader.py @@ -0,0 +1,244 @@ +""" +Test script for the AgentLoader functionality. +This tests the core functionality without requiring external models. +""" + +import os +import tempfile +from pathlib import Path +import sys + +# Add swarms to path for local testing +sys.path.insert(0, os.path.join(os.path.dirname(__file__))) + +from swarms.utils.agent_loader import AgentLoader, MarkdownAgentConfig + +def test_markdown_parsing(): + """Test markdown parsing functionality.""" + print("Testing markdown parsing...") + + # Create a sample markdown content + sample_content = """| name | description | model | +|------|-------------|-------| +| test-agent | A test agent for validation | gpt-4 | + +## Focus Areas +- Testing functionality +- Validating implementation +- Ensuring compatibility + +## Approach +1. Parse markdown structure +2. Extract configuration data +3. Validate parsed results +4. Create agent instance + +## Output +- Test results +- Validation reports +- Configuration summary +""" + + # Test parsing functionality + loader = AgentLoader() + + # Test table parsing + table_data = loader.parse_markdown_table(sample_content) + assert table_data['name'] == 'test-agent' + assert table_data['description'] == 'A test agent for validation' + assert table_data['model_name'] == 'gpt-4' + print("[OK] Table parsing successful") + + # Test section extraction + sections = loader.extract_sections(sample_content) + assert 'focus areas' in sections + assert len(sections['focus areas']) == 3 + assert 'approach' in sections + assert len(sections['approach']) == 4 + print("[OK] Section extraction successful") + + # Test system prompt building + config_data = { + 'description': table_data['description'], + 'focus_areas': sections['focus areas'], + 'approach': sections['approach'], + 'output': sections.get('output', []) + } + system_prompt = loader.build_system_prompt(config_data) + assert 'Role:' in system_prompt + assert 'Focus Areas:' in system_prompt + assert 'Approach:' in system_prompt + print("[OK] System prompt building successful") + + print("Markdown parsing tests passed!") + return True + +def test_file_operations(): + """Test file loading operations.""" + print("\\nTesting file operations...") + + # Create temporary markdown file + sample_content = """| name | description | model | +|------|-------------|-------| +| file-test-agent | Agent created from file | gpt-4 | + +## Focus Areas +- File processing +- Configuration validation + +## Approach +1. Load from file +2. Parse content +3. Create configuration + +## Output +- Loaded agent +- Configuration object +""" + + with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f: + f.write(sample_content) + temp_file = f.name + + try: + loader = AgentLoader() + + # Test file parsing + config = loader.parse_markdown_file(temp_file) + assert isinstance(config, MarkdownAgentConfig) + assert config.name == 'file-test-agent' + assert config.description == 'Agent created from file' + print("[OK] File parsing successful") + + # Test configuration validation + assert len(config.focus_areas) == 2 + assert len(config.approach) == 3 + assert config.system_prompt is not None + print("[OK] Configuration validation successful") + + finally: + # Cleanup + if os.path.exists(temp_file): + os.remove(temp_file) + + print("File operations tests passed!") + return True + +def test_multiple_files(): + """Test loading multiple files.""" + print("\\nTesting multiple file loading...") + + # Create multiple temporary files + files = [] + for i in range(3): + content = f"""| name | description | model | +|------|-------------|-------| +| agent-{i} | Test agent number {i} | gpt-4 | + +## Focus Areas +- Multi-agent testing +- Batch processing + +## Approach +1. Process multiple files +2. Create agent configurations +3. Return agent list + +## Output +- Multiple agents +- Batch results +""" + temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False) + temp_file.write(content) + temp_file.close() + files.append(temp_file.name) + + try: + loader = AgentLoader() + + # Test parsing multiple files + configs = [] + for file_path in files: + config = loader.parse_markdown_file(file_path) + configs.append(config) + + assert len(configs) == 3 + for i, config in enumerate(configs): + assert config.name == f'agent-{i}' + + print("[OK] Multiple file parsing successful") + + finally: + # Cleanup + for file_path in files: + if os.path.exists(file_path): + os.remove(file_path) + + print("Multiple file tests passed!") + return True + +def test_error_handling(): + """Test error handling scenarios.""" + print("\\nTesting error handling...") + + loader = AgentLoader() + + # Test non-existent file + try: + loader.parse_markdown_file("nonexistent.md") + assert False, "Should have raised FileNotFoundError" + except FileNotFoundError: + print("[OK] FileNotFoundError handling successful") + + # Test invalid markdown + with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f: + f.write("Invalid markdown content without proper structure") + invalid_file = f.name + + try: + # This should not raise an error, but should handle gracefully + config = loader.parse_markdown_file(invalid_file) + # Should have defaults + assert config.name is not None + print("[OK] Invalid markdown handling successful") + + finally: + if os.path.exists(invalid_file): + os.remove(invalid_file) + + print("Error handling tests passed!") + return True + +def main(): + """Run all tests.""" + print("=== AgentLoader Test Suite ===") + + tests = [ + test_markdown_parsing, + test_file_operations, + test_multiple_files, + test_error_handling + ] + + passed = 0 + total = len(tests) + + for test in tests: + try: + if test(): + passed += 1 + except Exception as e: + print(f"[FAIL] Test {test.__name__} failed: {e}") + + print(f"\\n=== Results: {passed}/{total} tests passed ===") + + if passed == total: + print("All tests passed!") + return True + else: + print("Some tests failed!") + return False + +if __name__ == "__main__": + success = main() + exit(0 if success else 1) \ No newline at end of file From 223d398a20ceddc54d47738c660ca31c0dc2b10e Mon Sep 17 00:00:00 2001 From: harshalmore31 Date: Sat, 16 Aug 2025 16:03:18 +0530 Subject: [PATCH 02/19] updated and fixed ! --- agent_loader_research_team.py | 165 --------- docs/swarms/utils/agent_loader.md | 168 ++++----- examples/agents_loader_example.py | 168 +++++---- .../multi_agent/simple_agent_loader_demo.py | 321 +++++++++++------- swarms/utils/agent_loader.py | 200 ++++------- test_agent_loader.py | 244 ------------- 6 files changed, 468 insertions(+), 798 deletions(-) delete mode 100644 agent_loader_research_team.py delete mode 100644 test_agent_loader.py diff --git a/agent_loader_research_team.py b/agent_loader_research_team.py deleted file mode 100644 index 1ae02301..00000000 --- a/agent_loader_research_team.py +++ /dev/null @@ -1,165 +0,0 @@ -""" -AgentLoader Example: Research Team Collaboration -=============================================== - -This example demonstrates using the AgentLoader to create a research team -from markdown files and orchestrate them in a sequential workflow. -""" - -import os -import sys -import tempfile -from pathlib import Path - -# Add local swarms to path -sys.path.insert(0, str(Path(__file__).parent.parent.parent)) - -from swarms.structs.agent import Agent -from swarms.structs.sequential_workflow import SequentialWorkflow -from swarms.utils.agent_loader import AgentLoader - -def create_research_agents(): - """Create markdown files for research team agents""" - - market_researcher = """| name | description | model | -|------|-------------|-------| -| market-researcher | Expert in market analysis and competitive intelligence | gpt-4 | - -## Focus Areas -- Market size and growth analysis -- Competitive landscape assessment -- Consumer behavior patterns -- Industry trend identification - -## Approach -1. Gather comprehensive market data -2. Analyze quantitative and qualitative indicators -3. Identify key market drivers and barriers -4. Evaluate competitive positioning -5. Assess market opportunities and threats - -## Output -- Market analysis reports with key metrics -- Competitive intelligence briefings -- Market opportunity assessments -- Consumer behavior insights -""" - - financial_analyst = """| name | description | model | -|------|-------------|-------| -| financial-analyst | Specialist in financial modeling and investment analysis | gpt-4 | - -## Focus Areas -- Financial statement analysis -- Valuation modeling techniques -- Investment risk assessment -- Cash flow projections - -## Approach -1. Conduct thorough financial analysis -2. Build comprehensive financial models -3. Perform multiple valuation methods -4. Assess financial risks and sensitivities -5. Provide investment recommendations - -## Output -- Financial analysis reports -- Valuation models with scenarios -- Investment recommendation memos -- Risk assessment matrices -""" - - industry_expert = """| name | description | model | -|------|-------------|-------| -| industry-expert | Domain specialist with deep industry knowledge | gpt-4 | - -## Focus Areas -- Industry structure and dynamics -- Regulatory environment analysis -- Technology trends and disruptions -- Supply chain analysis - -## Approach -1. Map industry structure and stakeholders -2. Analyze regulatory framework -3. Identify technology trends -4. Evaluate supply chain dynamics -5. Assess competitive positioning - -## Output -- Industry landscape reports -- Regulatory compliance assessments -- Technology trend analysis -- Strategic positioning recommendations -""" - - return { - "market_researcher.md": market_researcher, - "financial_analyst.md": financial_analyst, - "industry_expert.md": industry_expert - } - -def main(): - """Main execution function""" - - temp_dir = tempfile.mkdtemp() - - try: - # Create markdown files - agent_definitions = create_research_agents() - file_paths = [] - - for filename, content in agent_definitions.items(): - file_path = os.path.join(temp_dir, filename) - with open(file_path, 'w', encoding='utf-8') as f: - f.write(content) - file_paths.append(file_path) - - # Load agents using AgentLoader - loader = AgentLoader() - agents = loader.load_multiple_agents( - file_paths, - max_loops=1, - verbose=False - ) - - print(f"Loaded {len(agents)} agents") - for i, agent in enumerate(agents): - print(f"Agent {i}: {agent.agent_name} - LLM: {hasattr(agent, 'llm')}") - - # Create sequential workflow - research_workflow = SequentialWorkflow( - agents=agents, - max_loops=1, - ) - - # Define research task - task = """ - Analyze the AI-powered healthcare diagnostics market for a potential $50M investment. - - Focus on: - 1. Market size, growth, and key drivers - 2. Competitive landscape and major players - 3. Financial viability and investment metrics - 4. Industry dynamics and regulatory factors - - Provide strategic recommendations for market entry. - """ - - # Execute workflow - result = research_workflow.run(task) - - return result - - finally: - # Cleanup - for file_path in file_paths: - if os.path.exists(file_path): - os.remove(file_path) - os.rmdir(temp_dir) - -if __name__ == "__main__": - result = main() - print("Research Analysis Complete:") - print("-" * 50) - print(result) \ No newline at end of file diff --git a/docs/swarms/utils/agent_loader.md b/docs/swarms/utils/agent_loader.md index c3e1fd32..b8f1e94f 100644 --- a/docs/swarms/utils/agent_loader.md +++ b/docs/swarms/utils/agent_loader.md @@ -1,14 +1,14 @@ # AgentLoader - Load Agents from Markdown Files -The `AgentLoader` is a powerful utility for creating Swarms agents from markdown files. It supports both single and multiple markdown file loading, providing a flexible way to define and deploy agents using a structured markdown format. +The `AgentLoader` is a powerful utility for creating Swarms agents from markdown files using the Claude Code sub-agent format. It supports both single and multiple markdown file loading, providing a flexible way to define and deploy agents using YAML frontmatter configuration. ## Overview The AgentLoader enables you to: -- Load single agents from markdown files +- Load single agents from markdown files with YAML frontmatter - Load multiple agents from directories or file lists -- Parse structured markdown content into agent configurations -- Maintain backwards compatibility with existing agent systems +- Parse Claude Code sub-agent YAML frontmatter configurations +- Extract system prompts from markdown content - Provide comprehensive error handling and validation ## Installation @@ -21,32 +21,33 @@ from swarms.utils import AgentLoader, load_agent_from_markdown, load_agents_from ## Markdown Format -The AgentLoader expects markdown files to follow a specific structure: +The AgentLoader uses the Claude Code sub-agent YAML frontmatter format: -### Required Table Header ```markdown -| name | description | model | -|------|-------------|-------| -| agent-name | Brief description of the agent | gpt-4 | +--- +name: your-sub-agent-name +description: Description of when this subagent should be invoked +model_name: gpt-4 +temperature: 0.3 +max_loops: 2 +mcp_url: http://example.com/mcp # optional +--- + +Your subagent's system prompt goes here. This can be multiple paragraphs +and should clearly define the subagent's role, capabilities, and approach +to solving problems. + +Include specific instructions, best practices, and any constraints +the subagent should follow. ``` -### Optional Sections -```markdown -## Focus Areas -- Key responsibility area 1 -- Key responsibility area 2 -- Key responsibility area 3 - -## Approach -1. First step in methodology -2. Second step in methodology -3. Third step in methodology - -## Output -- Expected deliverable 1 -- Expected deliverable 2 -- Expected deliverable 3 -``` +**Schema Fields:** +- `name` (required): Your sub-agent name +- `description` (required): Description of when this subagent should be invoked +- `model_name` (optional): Name of model (defaults to random selection if not provided) +- `temperature` (optional): Float value for model temperature (0.0-2.0) +- `max_loops` (optional): Integer for maximum reasoning loops +- `mcp_url` (optional): MCP server URL if needed ## Quick Start @@ -55,13 +56,17 @@ The AgentLoader expects markdown files to follow a specific structure: ```python from swarms.utils import load_agent_from_markdown -# Load agent from markdown file +# Load Claude Code format agent (YAML frontmatter) agent = load_agent_from_markdown( - file_path="path/to/agent.md" + file_path="performance-engineer.md" # Uses YAML frontmatter format ) -# Use the agent -response = agent.run("What are your capabilities?") +# The agent automatically gets configured with: +# - Name, description from frontmatter +# - Temperature, max_loops, model settings +# - System prompt from content after frontmatter + +response = agent.run("Analyze application performance issues") print(response) ``` @@ -70,17 +75,23 @@ print(response) ```python from swarms.utils import load_agents_from_markdown -# Load all agents from directory +# Load all agents from directory (YAML frontmatter format) agents = load_agents_from_markdown( - file_paths="./agents_directory/" + file_paths="./agents_directory/" # Directory with Claude Code format files ) # Load agents from specific files agents = load_agents_from_markdown( - file_paths=["agent1.md", "agent2.md", "agent3.md"] + file_paths=[ + "performance-engineer.md", # Claude Code YAML format + "financial-analyst.md", # Claude Code YAML format + "security-analyst.md" # Claude Code YAML format + ] ) print(f"Loaded {len(agents)} agents") +for agent in agents: + print(f"- {agent.agent_name}: {getattr(agent, 'temperature', 'default temp')}") ``` ## Class-Based Usage @@ -139,61 +150,60 @@ agent = load_agent_from_markdown( ## Complete Example -### Example Markdown File (performance-engineer.md) +### Example: Claude Code Sub-Agent Format + +Create a file `performance-engineer.md`: ```markdown -| name | description | model | -|------|-------------|-------| -| performance-engineer | Optimize application performance and identify bottlenecks | gpt-4 | - -## Focus Areas -- Application profiling and performance analysis -- Database optimization and query tuning -- Memory and CPU usage optimization -- Load testing and capacity planning -- Infrastructure scaling recommendations - -## Approach -1. Analyze application architecture and identify potential bottlenecks -2. Implement comprehensive monitoring and logging systems -3. Conduct performance testing under various load conditions -4. Profile memory usage and optimize resource consumption -5. Provide actionable recommendations with implementation guides - -## Output -- Detailed performance analysis reports with metrics -- Optimized code recommendations and examples -- Infrastructure scaling and architecture suggestions -- Monitoring and alerting configuration guidelines -- Load testing results and capacity planning documents +--- +name: performance-engineer +description: Optimize application performance and identify bottlenecks +model_name: gpt-4 +temperature: 0.3 +max_loops: 2 +mcp_url: http://example.com/mcp +--- + +You are a Performance Engineer specializing in application optimization and scalability. + +Your role involves analyzing system performance, identifying bottlenecks, and implementing +solutions to improve efficiency and user experience. + +Key responsibilities: +- Profile applications to identify performance issues +- Optimize database queries and caching strategies +- Implement load testing and monitoring solutions +- Recommend infrastructure improvements +- Provide actionable optimization recommendations + +Always provide specific, measurable recommendations with implementation details. +Focus on both immediate wins and long-term architectural improvements. ``` ### Loading and Using the Agent ```python from swarms.utils import load_agent_from_markdown -from swarms.utils.litellm_wrapper import LiteLLM -# Initialize model -model = LiteLLM(model_name="gpt-4") - -# Load the performance engineer agent -agent = load_agent_from_markdown( - file_path="performance-engineer.md", - model=model, - max_loops=3, - verbose=True +# Load Claude Code format agent (YAML frontmatter) +performance_agent = load_agent_from_markdown( + file_path="performance-engineer.md" ) -# Use the agent +print(f"Agent: {performance_agent.agent_name}") +print(f"Temperature: {getattr(performance_agent, 'temperature', 'default')}") +print(f"Max loops: {performance_agent.max_loops}") +print(f"System prompt preview: {performance_agent.system_prompt[:100]}...") + +# Use the performance agent task = """ Analyze the performance of a web application that handles 10,000 concurrent users but is experiencing slow response times averaging 3 seconds. The application uses a PostgreSQL database and is deployed on AWS with 4 EC2 instances behind a load balancer. """ -analysis = agent.run(task) -print(f"Performance Analysis:\n{analysis}") +# Note: Actual agent.run() would make API calls +print(f"\nTask for {performance_agent.agent_name}: {task[:100]}...") ``` ## Error Handling @@ -297,13 +307,6 @@ result = workflow.run("Conduct a comprehensive system audit") 5. **Model Selection**: Choose appropriate models based on agent complexity 6. **Configuration**: Override defaults when specific behavior is needed -## Backwards Compatibility - -The AgentLoader maintains full backwards compatibility with: -- Claude Code sub-agents markdown format -- Existing swarms agent creation patterns -- Legacy configuration systems -- Current workflow orchestration ## API Reference @@ -331,10 +334,13 @@ class MarkdownAgentConfig(BaseModel): name: str description: str model_name: Optional[str] = "gpt-4" + temperature: Optional[float] = 0.1 # Model temperature (0.0-2.0) + mcp_url: Optional[str] = None # Optional MCP server URL system_prompt: str - focus_areas: Optional[List[str]] = [] - approach: Optional[List[str]] = [] - output: Optional[List[str]] = [] + max_loops: int = 1 + autosave: bool = False + dashboard: bool = False + verbose: bool = False # ... additional configuration fields ``` diff --git a/examples/agents_loader_example.py b/examples/agents_loader_example.py index 12359af6..f70e4435 100644 --- a/examples/agents_loader_example.py +++ b/examples/agents_loader_example.py @@ -17,106 +17,154 @@ def main(): print("=== AgentLoader Demo ===") - # Example 1: Create a sample markdown file for testing - sample_md = """| name | description | model | -|------|-------------|-------| -| performance-engineer | Optimize application performance and identify bottlenecks | gpt-4 | + # Example 1: Create sample markdown files for testing - Claude Code format + + # Performance Engineer agent + performance_md = """--- +name: performance-engineer +description: Optimize application performance and identify bottlenecks +model_name: gpt-4 +temperature: 0.3 +max_loops: 2 +mcp_url: http://example.com/mcp +--- -## Focus Areas -- Application profiling and performance analysis -- Database optimization and query tuning -- Memory and CPU usage optimization -- Load testing and capacity planning +You are a Performance Engineer specializing in application optimization and scalability. -## Approach -1. Analyze application architecture and identify potential bottlenecks -2. Implement comprehensive monitoring and logging -3. Conduct performance testing under various load conditions -4. Optimize critical paths and resource usage -5. Document findings and provide actionable recommendations +Your role involves: +- Analyzing application architecture and identifying potential bottlenecks +- Implementing comprehensive monitoring and logging +- Conducting performance testing under various load conditions +- Optimizing critical paths and resource usage +- Documenting findings and providing actionable recommendations -## Output +Expected output: - Performance analysis reports with specific metrics - Optimized code recommendations - Infrastructure scaling suggestions - Monitoring and alerting setup guidelines """ - # Create sample markdown file - sample_file = "sample_agent.md" - with open(sample_file, 'w') as f: - f.write(sample_md) + # Data Analyst agent + data_analyst_md = """--- +name: data-analyst +description: Analyze data and provide business insights +model_name: gpt-4 +temperature: 0.2 +max_loops: 1 +--- + +You are a Data Analyst specializing in extracting insights from complex datasets. + +Your responsibilities include: +- Collecting and cleaning data from various sources +- Performing exploratory data analysis and statistical modeling +- Creating compelling visualizations and interactive dashboards +- Applying statistical methods and machine learning techniques +- Presenting findings and actionable business recommendations + +Focus on providing data-driven insights that support strategic decision making. +""" + + # Create sample markdown files + performance_file = "performance_engineer.md" + data_file = "data_analyst.md" + + with open(performance_file, 'w') as f: + f.write(performance_md) + + with open(data_file, 'w') as f: + f.write(data_analyst_md) try: - # Example 2: Load single agent using class method - print("\\n1. Loading single agent using AgentLoader class:") - agent = loader.load_single_agent(sample_file) - print(f" Loaded agent: {agent.agent_name}") - print(f" System prompt preview: {agent.system_prompt[:100]}...") + # Example 2: Load Performance Engineer agent + print("\\n1. Loading Performance Engineer agent (YAML frontmatter):") + perf_agent = loader.load_single_agent(performance_file) + print(f" Loaded agent: {perf_agent.agent_name}") + print(f" Model: {perf_agent.model_name}") + print(f" Temperature: {getattr(perf_agent, 'temperature', 'Not set')}") + print(f" Max loops: {perf_agent.max_loops}") + print(f" System prompt preview: {perf_agent.system_prompt[:100]}...") - # Example 3: Load single agent using convenience function - print("\\n2. Loading single agent using convenience function:") - agent2 = load_agent_from_markdown(sample_file) + # Example 3: Load Data Analyst agent + print("\\n2. Loading Data Analyst agent:") + data_agent = loader.load_single_agent(data_file) + print(f" Loaded agent: {data_agent.agent_name}") + print(f" Temperature: {getattr(data_agent, 'temperature', 'Not set')}") + print(f" System prompt preview: {data_agent.system_prompt[:100]}...") + + # Example 4: Load single agent using convenience function + print("\\n3. Loading single agent using convenience function:") + agent2 = load_agent_from_markdown(performance_file) print(f" Loaded agent: {agent2.agent_name}") - # Example 4: Load multiple agents (from directory or list) - print("\\n3. Loading multiple agents:") + # Example 5: Load multiple agents (from directory or list) + print("\\n4. Loading multiple agents:") - # Create another sample file - sample_md2 = """| name | description | model | -|------|-------------|-------| -| security-analyst | Analyze and improve system security | gpt-4 | + # Create another sample file - Security Analyst + security_md = """--- +name: security-analyst +description: Analyze and improve system security +model_name: gpt-4 +temperature: 0.1 +max_loops: 3 +--- + +You are a Security Analyst specializing in cybersecurity assessment and protection. -## Focus Areas -- Security vulnerability assessment -- Code security review -- Infrastructure hardening +Your expertise includes: +- Conducting comprehensive security vulnerability assessments +- Performing detailed code security reviews and penetration testing +- Implementing robust infrastructure hardening measures +- Developing incident response and recovery procedures -## Approach -1. Conduct thorough security audits -2. Identify potential vulnerabilities -3. Recommend security improvements +Key methodology: +1. Conduct thorough security audits across all system components +2. Identify and classify potential vulnerabilities and threats +3. Recommend and implement security improvements and controls +4. Develop comprehensive security policies and best practices +5. Monitor and respond to security incidents -## Output -- Security assessment reports -- Vulnerability remediation plans -- Security best practices documentation +Provide detailed security reports with specific remediation steps and risk assessments. """ - sample_file2 = "security_agent.md" - with open(sample_file2, 'w') as f: - f.write(sample_md2) + security_file = "security_analyst.md" + with open(security_file, 'w') as f: + f.write(security_md) # Load multiple agents from list - agents = loader.load_multiple_agents([sample_file, sample_file2]) + agents = loader.load_multiple_agents([performance_file, data_file, security_file]) print(f" Loaded {len(agents)} agents:") for agent in agents: - print(f" - {agent.agent_name}") + temp_attr = getattr(agent, 'temperature', 'default') + print(f" - {agent.agent_name} (temp: {temp_attr})") - # Example 5: Load agents from directory (current directory) - print("\\n4. Loading agents from current directory:") + # Example 6: Load agents from directory (current directory) + print("\\n5. Loading agents from current directory:") current_dir_agents = load_agents_from_markdown(".") print(f" Found {len(current_dir_agents)} agents in current directory") - # Example 6: Demonstrate error handling - print("\\n5. Error handling demo:") + # Example 7: Demonstrate error handling + print("\\n6. Error handling demo:") try: loader.load_single_agent("nonexistent.md") except FileNotFoundError as e: print(f" Caught expected error: {e}") - # Example 7: Test agent functionality - print("\\n6. Testing loaded agent functionality:") + # Example 8: Test agent functionality + print("\\n7. Testing loaded agent functionality:") test_agent = agents[0] - response = test_agent.run("What are the key steps for performance optimization?") - print(f" Agent response preview: {str(response)[:150]}...") + print(f" Agent: {test_agent.agent_name}") + print(f" Temperature: {getattr(test_agent, 'temperature', 'default')}") + print(f" Max loops: {test_agent.max_loops}") + print(f" Ready for task execution") except Exception as e: print(f"Error during demo: {e}") finally: # Cleanup sample files - for file in [sample_file, sample_file2]: + for file in [performance_file, data_file, security_file]: if os.path.exists(file): os.remove(file) print("\\n Cleaned up sample files") diff --git a/examples/multi_agent/simple_agent_loader_demo.py b/examples/multi_agent/simple_agent_loader_demo.py index e78eae52..481a5aea 100644 --- a/examples/multi_agent/simple_agent_loader_demo.py +++ b/examples/multi_agent/simple_agent_loader_demo.py @@ -1,9 +1,15 @@ """ -Simple AgentLoader Demo -======================= +Simple AgentLoader Demo - Claude Code Format +============================================= -A working demonstration of how to create agents from markdown-like definitions -and use them in workflows. +A comprehensive demonstration of the AgentLoader using the Claude Code +sub-agent YAML frontmatter format. + +This example shows: +1. Creating agents using Claude Code YAML frontmatter format +2. Loading agents from markdown files with YAML frontmatter +3. Using loaded agents in multi-agent workflows +4. Demonstrating different agent configurations """ import os @@ -16,125 +22,212 @@ sys.path.insert(0, str(Path(__file__).parent.parent.parent)) from swarms.structs.agent import Agent from swarms.structs.sequential_workflow import SequentialWorkflow +from swarms.utils.agent_loader import AgentLoader, load_agents_from_markdown -def create_agents_from_configs(): - """Create agents from configuration dictionaries (simulating markdown parsing)""" +def create_markdown_agent_files(): + """Create markdown files demonstrating Claude Code YAML frontmatter format""" - # These would normally come from parsing markdown files - agent_configs = [ - { - "name": "market-researcher", - "description": "Expert in market analysis and competitive intelligence", - "system_prompt": """You are a market research specialist. Your expertise includes: - -Focus Areas: -- Market size and growth analysis -- Competitive landscape assessment -- Consumer behavior patterns -- Industry trend identification - -Approach: -1. Gather comprehensive market data -2. Analyze quantitative and qualitative indicators -3. Identify key market drivers and barriers -4. Evaluate competitive positioning -5. Assess market opportunities and threats - -Provide detailed market analysis reports with key metrics and actionable insights.""", - "model": "gpt-4" - }, - { - "name": "financial-analyst", - "description": "Specialist in financial modeling and investment analysis", - "system_prompt": """You are a financial analysis expert. Your responsibilities include: - -Focus Areas: -- Financial statement analysis -- Valuation modeling techniques -- Investment risk assessment -- Cash flow projections - -Approach: -1. Conduct thorough financial analysis -2. Build comprehensive financial models -3. Perform multiple valuation methods -4. Assess financial risks and sensitivities -5. Provide investment recommendations - -Generate detailed financial reports with valuation models and risk assessments.""", - "model": "gpt-4" - }, - { - "name": "industry-expert", - "description": "Domain specialist with deep industry knowledge", - "system_prompt": """You are an industry analysis expert. Your focus areas include: - -Focus Areas: -- Industry structure and dynamics -- Regulatory environment analysis -- Technology trends and disruptions -- Supply chain analysis - -Approach: -1. Map industry structure and stakeholders -2. Analyze regulatory framework -3. Identify technology trends -4. Evaluate supply chain dynamics -5. Assess competitive positioning - -Provide comprehensive industry landscape reports with strategic recommendations.""", - "model": "gpt-4" - } - ] + # Claude Code YAML frontmatter format + agent_files = { + "market_researcher.md": """--- +name: market-researcher +description: Expert in market analysis and competitive intelligence +model_name: gpt-4 +temperature: 0.2 +max_loops: 2 +mcp_url: http://example.com/market-data +--- + +You are a market research specialist with deep expertise in analyzing market dynamics and competitive landscapes. + +Your core responsibilities include: +- Conducting comprehensive market size and growth analysis +- Performing detailed competitive landscape assessments +- Analyzing consumer behavior patterns and preferences +- Identifying emerging industry trends and opportunities + +Methodology: +1. Gather comprehensive quantitative and qualitative market data +2. Analyze key market drivers, barriers, and success factors +3. Evaluate competitive positioning and market share dynamics +4. Assess market opportunities, threats, and entry strategies +5. Provide actionable insights with data-driven recommendations + +Always provide detailed market analysis reports with specific metrics, growth projections, and strategic recommendations for market entry or expansion. +""", + + "financial_analyst.md": """--- +name: financial-analyst +description: Specialist in financial modeling and investment analysis +model_name: gpt-4 +temperature: 0.1 +max_loops: 3 +--- + +You are a financial analysis expert specializing in investment evaluation and financial modeling. + +Your areas of expertise include: +- Financial statement analysis and ratio interpretation +- DCF modeling and valuation techniques (DCF, comparable company analysis, precedent transactions) +- Investment risk assessment and sensitivity analysis +- Cash flow projections and working capital analysis + +Analytical approach: +1. Conduct thorough financial statement analysis +2. Build comprehensive financial models with multiple scenarios +3. Perform detailed valuation using multiple methodologies +4. Assess financial risks and conduct sensitivity analysis +5. Generate investment recommendations with clear rationale + +Provide detailed financial reports with valuation models, risk assessments, and investment recommendations supported by quantitative analysis. +""", + + "industry_expert.md": """--- +name: industry-expert +description: Domain specialist with deep industry knowledge and regulatory expertise +model_name: gpt-4 +temperature: 0.3 +max_loops: 2 +--- + +You are an industry analysis expert with comprehensive knowledge of market structures, regulatory environments, and technology trends. + +Your specialization areas: +- Industry structure analysis and value chain mapping +- Regulatory environment assessment and compliance requirements +- Technology trends identification and disruption analysis +- Supply chain dynamics and operational considerations + +Research methodology: +1. Map industry structure, key players, and stakeholder relationships +2. Analyze current and emerging regulatory framework +3. Identify technology trends and potential market disruptions +4. Evaluate supply chain dynamics and operational requirements +5. Assess competitive positioning and strategic opportunities + +Generate comprehensive industry landscape reports with regulatory insights, technology trend analysis, and strategic recommendations for market positioning. +""" + "risk_analyst.md": """--- +name: risk-analyst +description: Specialist in investment risk assessment and mitigation strategies +model_name: gpt-4 +temperature: 0.15 +max_loops: 2 +--- + +You are a Risk Analyst specializing in comprehensive investment risk assessment and portfolio management. + +Your core competencies include: +- Conducting detailed investment risk evaluation and categorization +- Implementing sophisticated portfolio risk management strategies +- Ensuring regulatory compliance and conducting compliance assessments +- Performing advanced scenario analysis and stress testing methodologies + +Analytical framework: +1. Systematically identify and categorize all investment risks +2. Quantify risk exposure using advanced statistical methods and models +3. Develop comprehensive risk mitigation strategies and frameworks +4. Conduct rigorous scenario analysis and stress testing procedures +5. Provide actionable risk management recommendations with implementation roadmaps + +Deliver comprehensive risk assessment reports with quantitative analysis, compliance guidelines, and strategic risk management recommendations. +""" + } - agents = [] - for config in agent_configs: - agent = Agent( - agent_name=config["name"], - system_prompt=config["system_prompt"], - model_name=config["model"], - max_loops=1, - verbose=False - ) - agents.append(agent) - print(f"Created agent: {agent.agent_name}") + temp_files = [] + + # Create Claude Code format files + for filename, content in agent_files.items(): + temp_file = os.path.join(tempfile.gettempdir(), filename) + with open(temp_file, 'w', encoding='utf-8') as f: + f.write(content) + temp_files.append(temp_file) - return agents + return temp_files def main(): - """Main execution function""" - - # Create agents - agents = create_agents_from_configs() + """Main execution function demonstrating AgentLoader with Claude Code format""" - # Create sequential workflow - research_workflow = SequentialWorkflow( - agents=agents, - max_loops=1, - ) + print("AgentLoader Demo - Claude Code YAML Frontmatter Format") + print("=" * 60) - # Define research task - task = """ - Analyze the AI-powered healthcare diagnostics market for a potential $50M investment. + # Create markdown files demonstrating both formats + print("\n1. Creating markdown files...") + temp_files = create_markdown_agent_files() - Focus on: - 1. Market size, growth projections, and key drivers - 2. Competitive landscape and major players - 3. Financial viability and investment attractiveness - 4. Industry dynamics and regulatory considerations - - Provide strategic recommendations for market entry. - """ - - print("Executing research workflow...") - print("=" * 50) - - # Execute workflow - result = research_workflow.run(task) - - print("\nResearch Analysis Complete:") - print("-" * 50) - print(result) + try: + # Load agents using AgentLoader + print("\n2. Loading agents using AgentLoader...") + agents = load_agents_from_markdown(temp_files) + + print(f" Successfully loaded {len(agents)} agents:") + for agent in agents: + temp_attr = getattr(agent, 'temperature', 'default') + max_loops = getattr(agent, 'max_loops', 1) + print(f" - {agent.agent_name} (temp: {temp_attr}, loops: {max_loops})") + + # Demonstrate individual agent configuration + print("\n3. Agent Configuration Details:") + for i, agent in enumerate(agents, 1): + print(f" Agent {i}: {agent.agent_name}") + print(f" Model: {getattr(agent, 'model_name', 'default')}") + print(f" System prompt preview: {agent.system_prompt[:100]}...") + print() + + # Create sequential workflow with loaded agents + print("4. Creating sequential workflow...") + research_workflow = SequentialWorkflow( + agents=agents, + max_loops=1, + ) + + # Define research task + task = """ + Analyze the AI-powered healthcare diagnostics market for a potential $50M investment. + + Focus on: + 1. Market size, growth projections, and key drivers + 2. Competitive landscape and major players + 3. Financial viability and investment attractiveness + 4. Industry dynamics and regulatory considerations + 5. Risk assessment and mitigation strategies + + Provide comprehensive strategic recommendations for market entry. + """ + + print("5. Executing research workflow...") + print("=" * 50) + + # Note: In a real scenario, this would execute the workflow + # For demo purposes, we'll show the task distribution + print(f"Task distributed to {len(agents)} specialized agents:") + for i, agent in enumerate(agents, 1): + print(f" Agent {i} ({agent.agent_name}): Ready to process") + + print(f"\nTask preview: {task[:150]}...") + print("\n[Demo mode - actual workflow execution would call LLM APIs]") + + print("\nDemo Summary:") + print("-" * 50) + print("✓ Successfully loaded agents using Claude Code YAML frontmatter format") + print("✓ Agents configured with different temperatures and max_loops from YAML") + print("✓ Multi-agent workflow created with specialized investment analysis agents") + print("✓ Workflow ready for comprehensive market analysis execution") + + except Exception as e: + print(f"Error during demo: {e}") + import traceback + traceback.print_exc() + + finally: + # Cleanup temporary files + print("\n6. Cleaning up temporary files...") + for temp_file in temp_files: + try: + os.remove(temp_file) + print(f" Removed: {os.path.basename(temp_file)}") + except OSError: + pass if __name__ == "__main__": main() \ No newline at end of file diff --git a/swarms/utils/agent_loader.py b/swarms/utils/agent_loader.py index 7b918421..e6dce69d 100644 --- a/swarms/utils/agent_loader.py +++ b/swarms/utils/agent_loader.py @@ -1,5 +1,7 @@ import os import re +import random +import yaml from pathlib import Path from typing import Any, Dict, List, Optional, Union from pydantic import BaseModel, Field, field_validator @@ -9,14 +11,13 @@ from swarms.structs.agent import Agent class MarkdownAgentConfig(BaseModel): - """Configuration model for agents loaded from markdown files.""" + """Configuration model for agents loaded from Claude Code markdown files.""" name: str description: str model_name: Optional[str] = "gpt-4" + temperature: Optional[float] = Field(default=0.1, ge=0.0, le=2.0) + mcp_url: Optional[str] = None system_prompt: str - focus_areas: Optional[List[str]] = [] - approach: Optional[List[str]] = [] - output: Optional[List[str]] = [] max_loops: int = Field(default=1, ge=1) autosave: bool = False dashboard: bool = False @@ -43,16 +44,16 @@ class MarkdownAgentConfig(BaseModel): class AgentLoader: """ - Loader for creating agents from markdown files. + Loader for creating agents from markdown files using Claude Code sub-agent format. Supports both single markdown file and multiple markdown files. - Maintains backwards compatibility with claude code sub agents markdown format. + Uses YAML frontmatter format for agent configuration. Features: - Single markdown file loading - Multiple markdown files loading (batch processing) - - Flexible markdown parsing - - Agent configuration extraction from markdown structure + - YAML frontmatter parsing + - Agent configuration extraction from YAML metadata - Error handling and validation """ @@ -62,124 +63,51 @@ class AgentLoader: """ pass - def parse_markdown_table(self, content: str) -> Dict[str, str]: + def parse_yaml_frontmatter(self, content: str) -> Dict[str, Any]: """ - Parse markdown table to extract agent metadata. + Parse YAML frontmatter from markdown content. Args: - content: Markdown content containing a table + content: Markdown content with potential YAML frontmatter Returns: - Dictionary with parsed table data + Dictionary with parsed YAML data and remaining content """ - table_data = {} - - # Find markdown table pattern - table_pattern = r'\|([^|]+)\|([^|]+)\|([^|]+)\|' lines = content.split('\n') - header_found = False - for line in lines: - if '|' in line and not header_found: - # Skip header separator line - if '---' in line: - header_found = True - continue - - # Parse header - if 'name' in line.lower() and 'description' in line.lower(): - continue - - elif header_found and '|' in line: - # Parse data row - match = re.match(table_pattern, line) - if match: - table_data['name'] = match.group(1).strip() - table_data['description'] = match.group(2).strip() - table_data['model_name'] = match.group(3).strip() - break - - return table_data - - def extract_sections(self, content: str) -> Dict[str, List[str]]: - """ - Extract structured sections from markdown content. - - Args: - content: Markdown content - - Returns: - Dictionary with section names as keys and content lists as values - """ - sections = {} - current_section = None - current_content = [] - - lines = content.split('\n') - for line in lines: - # Check for headers (## Section Name) - if line.startswith('## '): - # Save previous section - if current_section: - sections[current_section.lower()] = current_content - - # Start new section - current_section = line[3:].strip() - current_content = [] - - elif current_section and line.strip(): - # Add content to current section - # Remove markdown list markers - clean_line = re.sub(r'^[-*+]\s*', '', line.strip()) - clean_line = re.sub(r'^\d+\.\s*', '', clean_line) - if clean_line: - current_content.append(clean_line) - - # Save last section - if current_section: - sections[current_section.lower()] = current_content - - return sections - - def build_system_prompt(self, config_data: Dict[str, Any]) -> str: - """ - Build comprehensive system prompt from parsed markdown data. - - Args: - config_data: Dictionary containing parsed agent configuration - - Returns: - Complete system prompt string - """ - prompt_parts = [] + # Check if content starts with YAML frontmatter + if not lines[0].strip() == '---': + return {"frontmatter": {}, "content": content} - # Add description - if config_data.get('description'): - prompt_parts.append(f"Role: {config_data['description']}") + # Find end of frontmatter + end_marker = -1 + for i, line in enumerate(lines[1:], 1): + if line.strip() == '---': + end_marker = i + break - # Add focus areas - if config_data.get('focus_areas'): - prompt_parts.append("\nFocus Areas:") - for area in config_data['focus_areas']: - prompt_parts.append(f"- {area}") + if end_marker == -1: + return {"frontmatter": {}, "content": content} - # Add approach - if config_data.get('approach'): - prompt_parts.append("\nApproach:") - for i, step in enumerate(config_data['approach'], 1): - prompt_parts.append(f"{i}. {step}") + # Extract frontmatter and content + frontmatter_text = '\n'.join(lines[1:end_marker]) + remaining_content = '\n'.join(lines[end_marker + 1:]).strip() - # Add expected output - if config_data.get('output'): - prompt_parts.append("\nExpected Output:") - for output in config_data['output']: - prompt_parts.append(f"- {output}") + try: + frontmatter_data = yaml.safe_load(frontmatter_text) or {} + except yaml.YAMLError as e: + logger.warning(f"Failed to parse YAML frontmatter: {e}") + return {"frontmatter": {}, "content": content} - return '\n'.join(prompt_parts) + return {"frontmatter": frontmatter_data, "content": remaining_content} + + + def parse_markdown_file(self, file_path: str) -> MarkdownAgentConfig: """ Parse a single markdown file to extract agent configuration. + Uses Claude Code sub-agent YAML frontmatter format. Args: file_path: Path to markdown file @@ -189,7 +117,7 @@ class AgentLoader: Raises: FileNotFoundError: If file doesn't exist - ValueError: If parsing fails + ValueError: If parsing fails or no YAML frontmatter found """ if not os.path.exists(file_path): raise FileNotFoundError(f"Markdown file {file_path} not found.") @@ -198,25 +126,29 @@ class AgentLoader: with open(file_path, 'r', encoding='utf-8') as file: content = file.read() - # Parse table for basic metadata - table_data = self.parse_markdown_table(content) + # Parse YAML frontmatter (Claude Code sub-agent format) + yaml_result = self.parse_yaml_frontmatter(content) + frontmatter = yaml_result["frontmatter"] + remaining_content = yaml_result["content"] - # Extract sections - sections = self.extract_sections(content) + if not frontmatter: + raise ValueError(f"No YAML frontmatter found in {file_path}. File must use Claude Code sub-agent format with YAML frontmatter.") - # Build configuration + # Use YAML frontmatter data config_data = { - 'name': table_data.get('name', Path(file_path).stem), - 'description': table_data.get('description', 'Agent loaded from markdown'), - 'model_name': table_data.get('model_name', 'gpt-4'), - 'focus_areas': sections.get('focus areas', []), - 'approach': sections.get('approach', []), - 'output': sections.get('output', []), + 'name': frontmatter.get('name', Path(file_path).stem), + 'description': frontmatter.get('description', 'Agent loaded from markdown'), + 'model_name': frontmatter.get('model_name') or frontmatter.get('model', 'gpt-4'), + 'temperature': frontmatter.get('temperature', 0.1), + 'max_loops': frontmatter.get('max_loops', 1), + 'mcp_url': frontmatter.get('mcp_url'), + 'system_prompt': remaining_content.strip(), } - # Build system prompt - system_prompt = self.build_system_prompt(config_data) - config_data['system_prompt'] = system_prompt + # Generate random model if not specified + if not config_data['model_name'] or config_data['model_name'] == 'random': + models = ['gpt-4', 'gpt-4-turbo', 'claude-3-sonnet', 'claude-3-haiku'] + config_data['model_name'] = random.choice(models) logger.info(f"Successfully parsed markdown file: {file_path}") return MarkdownAgentConfig(**config_data) @@ -247,7 +179,7 @@ class AgentLoader: 'agent_name': config_dict['name'], 'system_prompt': config_dict['system_prompt'], 'model_name': config_dict.get('model_name', 'gpt-4'), - # Don't pass llm explicitly - let Agent handle it internally + 'temperature': config_dict.get('temperature', 0.1), 'max_loops': config_dict['max_loops'], 'autosave': config_dict['autosave'], 'dashboard': config_dict['dashboard'], @@ -321,10 +253,10 @@ class AgentLoader: def load_single_agent(self, file_path: str, **kwargs) -> Agent: """ Convenience method for loading a single agent. - Backwards compatible with claude code sub agents markdown. + Uses Claude Code sub-agent YAML frontmatter format. Args: - file_path: Path to markdown file + file_path: Path to markdown file with YAML frontmatter **kwargs: Additional configuration overrides Returns: @@ -335,10 +267,10 @@ class AgentLoader: def load_multiple_agents(self, file_paths: Union[str, List[str]], **kwargs) -> List[Agent]: """ Convenience method for loading multiple agents. - Backwards compatible with claude code sub agents markdown. + Uses Claude Code sub-agent YAML frontmatter format. Args: - file_paths: Directory path or list of file paths + file_paths: Directory path or list of file paths with YAML frontmatter **kwargs: Additional configuration overrides Returns: @@ -347,13 +279,13 @@ class AgentLoader: return self.load_agents_from_markdown(file_paths, **kwargs) -# Convenience functions for backwards compatibility +# Convenience functions def load_agent_from_markdown(file_path: str, **kwargs) -> Agent: """ - Load a single agent from a markdown file. + Load a single agent from a markdown file with Claude Code YAML frontmatter format. Args: - file_path: Path to markdown file + file_path: Path to markdown file with YAML frontmatter **kwargs: Additional configuration overrides Returns: @@ -365,10 +297,10 @@ def load_agent_from_markdown(file_path: str, **kwargs) -> Agent: def load_agents_from_markdown(file_paths: Union[str, List[str]], **kwargs) -> List[Agent]: """ - Load multiple agents from markdown files. + Load multiple agents from markdown files with Claude Code YAML frontmatter format. Args: - file_paths: Directory path or list of file paths + file_paths: Directory path or list of file paths with YAML frontmatter **kwargs: Additional configuration overrides Returns: diff --git a/test_agent_loader.py b/test_agent_loader.py deleted file mode 100644 index 3a02693a..00000000 --- a/test_agent_loader.py +++ /dev/null @@ -1,244 +0,0 @@ -""" -Test script for the AgentLoader functionality. -This tests the core functionality without requiring external models. -""" - -import os -import tempfile -from pathlib import Path -import sys - -# Add swarms to path for local testing -sys.path.insert(0, os.path.join(os.path.dirname(__file__))) - -from swarms.utils.agent_loader import AgentLoader, MarkdownAgentConfig - -def test_markdown_parsing(): - """Test markdown parsing functionality.""" - print("Testing markdown parsing...") - - # Create a sample markdown content - sample_content = """| name | description | model | -|------|-------------|-------| -| test-agent | A test agent for validation | gpt-4 | - -## Focus Areas -- Testing functionality -- Validating implementation -- Ensuring compatibility - -## Approach -1. Parse markdown structure -2. Extract configuration data -3. Validate parsed results -4. Create agent instance - -## Output -- Test results -- Validation reports -- Configuration summary -""" - - # Test parsing functionality - loader = AgentLoader() - - # Test table parsing - table_data = loader.parse_markdown_table(sample_content) - assert table_data['name'] == 'test-agent' - assert table_data['description'] == 'A test agent for validation' - assert table_data['model_name'] == 'gpt-4' - print("[OK] Table parsing successful") - - # Test section extraction - sections = loader.extract_sections(sample_content) - assert 'focus areas' in sections - assert len(sections['focus areas']) == 3 - assert 'approach' in sections - assert len(sections['approach']) == 4 - print("[OK] Section extraction successful") - - # Test system prompt building - config_data = { - 'description': table_data['description'], - 'focus_areas': sections['focus areas'], - 'approach': sections['approach'], - 'output': sections.get('output', []) - } - system_prompt = loader.build_system_prompt(config_data) - assert 'Role:' in system_prompt - assert 'Focus Areas:' in system_prompt - assert 'Approach:' in system_prompt - print("[OK] System prompt building successful") - - print("Markdown parsing tests passed!") - return True - -def test_file_operations(): - """Test file loading operations.""" - print("\\nTesting file operations...") - - # Create temporary markdown file - sample_content = """| name | description | model | -|------|-------------|-------| -| file-test-agent | Agent created from file | gpt-4 | - -## Focus Areas -- File processing -- Configuration validation - -## Approach -1. Load from file -2. Parse content -3. Create configuration - -## Output -- Loaded agent -- Configuration object -""" - - with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f: - f.write(sample_content) - temp_file = f.name - - try: - loader = AgentLoader() - - # Test file parsing - config = loader.parse_markdown_file(temp_file) - assert isinstance(config, MarkdownAgentConfig) - assert config.name == 'file-test-agent' - assert config.description == 'Agent created from file' - print("[OK] File parsing successful") - - # Test configuration validation - assert len(config.focus_areas) == 2 - assert len(config.approach) == 3 - assert config.system_prompt is not None - print("[OK] Configuration validation successful") - - finally: - # Cleanup - if os.path.exists(temp_file): - os.remove(temp_file) - - print("File operations tests passed!") - return True - -def test_multiple_files(): - """Test loading multiple files.""" - print("\\nTesting multiple file loading...") - - # Create multiple temporary files - files = [] - for i in range(3): - content = f"""| name | description | model | -|------|-------------|-------| -| agent-{i} | Test agent number {i} | gpt-4 | - -## Focus Areas -- Multi-agent testing -- Batch processing - -## Approach -1. Process multiple files -2. Create agent configurations -3. Return agent list - -## Output -- Multiple agents -- Batch results -""" - temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False) - temp_file.write(content) - temp_file.close() - files.append(temp_file.name) - - try: - loader = AgentLoader() - - # Test parsing multiple files - configs = [] - for file_path in files: - config = loader.parse_markdown_file(file_path) - configs.append(config) - - assert len(configs) == 3 - for i, config in enumerate(configs): - assert config.name == f'agent-{i}' - - print("[OK] Multiple file parsing successful") - - finally: - # Cleanup - for file_path in files: - if os.path.exists(file_path): - os.remove(file_path) - - print("Multiple file tests passed!") - return True - -def test_error_handling(): - """Test error handling scenarios.""" - print("\\nTesting error handling...") - - loader = AgentLoader() - - # Test non-existent file - try: - loader.parse_markdown_file("nonexistent.md") - assert False, "Should have raised FileNotFoundError" - except FileNotFoundError: - print("[OK] FileNotFoundError handling successful") - - # Test invalid markdown - with tempfile.NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f: - f.write("Invalid markdown content without proper structure") - invalid_file = f.name - - try: - # This should not raise an error, but should handle gracefully - config = loader.parse_markdown_file(invalid_file) - # Should have defaults - assert config.name is not None - print("[OK] Invalid markdown handling successful") - - finally: - if os.path.exists(invalid_file): - os.remove(invalid_file) - - print("Error handling tests passed!") - return True - -def main(): - """Run all tests.""" - print("=== AgentLoader Test Suite ===") - - tests = [ - test_markdown_parsing, - test_file_operations, - test_multiple_files, - test_error_handling - ] - - passed = 0 - total = len(tests) - - for test in tests: - try: - if test(): - passed += 1 - except Exception as e: - print(f"[FAIL] Test {test.__name__} failed: {e}") - - print(f"\\n=== Results: {passed}/{total} tests passed ===") - - if passed == total: - print("All tests passed!") - return True - else: - print("Some tests failed!") - return False - -if __name__ == "__main__": - success = main() - exit(0 if success else 1) \ No newline at end of file From f9ffd102f5b3e46032bf5bdf445b864ed8a0b4e3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 15:00:39 +0000 Subject: [PATCH 03/19] Bump actions/checkout from 4 to 5 Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/RELEASE.yml | 2 +- .github/workflows/codacy.yml | 2 +- .github/workflows/code-quality-and-tests.yml | 2 +- .github/workflows/codeql.yml | 2 +- .github/workflows/dependency-review.yml | 2 +- .github/workflows/docs.yml | 2 +- .github/workflows/lint.yml | 2 +- .github/workflows/pyre.yml | 2 +- .github/workflows/pysa.yml | 2 +- .github/workflows/python-package.yml | 2 +- .github/workflows/tests.yml | 2 +- .github/workflows/trivy.yml | 2 +- 12 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/RELEASE.yml b/.github/workflows/RELEASE.yml index 059ec93e..2c7663af 100644 --- a/.github/workflows/RELEASE.yml +++ b/.github/workflows/RELEASE.yml @@ -17,7 +17,7 @@ jobs: && ${{ contains(github.event.pull_request.labels.*.name, 'release') }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install poetry run: pipx install poetry==$POETRY_VERSION - name: Set up Python 3.9 diff --git a/.github/workflows/codacy.yml b/.github/workflows/codacy.yml index 23466431..28950171 100644 --- a/.github/workflows/codacy.yml +++ b/.github/workflows/codacy.yml @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 # Execute Codacy Analysis CLI and generate a SARIF output with the security issues identified during the analysis - name: Run Codacy Analysis CLI uses: codacy/codacy-analysis-cli-action@562ee3e92b8e92df8b67e0a5ff8aa8e261919c08 diff --git a/.github/workflows/code-quality-and-tests.yml b/.github/workflows/code-quality-and-tests.yml index a6fec774..7e56373e 100644 --- a/.github/workflows/code-quality-and-tests.yml +++ b/.github/workflows/code-quality-and-tests.yml @@ -16,7 +16,7 @@ jobs: steps: # Step 1: Check out the repository - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 # Step 2: Set up Python - name: Set up Python ${{ matrix.python-version }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index b93db343..0228db1b 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -28,7 +28,7 @@ jobs: language: ["python"] steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml index 9bbf3ba2..507a2882 100644 --- a/.github/workflows/dependency-review.yml +++ b/.github/workflows/dependency-review.yml @@ -28,7 +28,7 @@ jobs: runs-on: ubuntu-latest steps: - name: 'Checkout repository' - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: 'Dependency Review' uses: actions/dependency-review-action@v4 # Commonly enabled options, see https://github.com/actions/dependency-review-action#configuration-options for all available options. diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 17e8b500..8d2d2d58 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -9,7 +9,7 @@ jobs: deploy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - uses: actions/setup-python@v5 with: python-version: 3.11 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 964a012a..f09618f5 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -6,7 +6,7 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python uses: actions/setup-python@v5 diff --git a/.github/workflows/pyre.yml b/.github/workflows/pyre.yml index 53aca44d..336569b5 100644 --- a/.github/workflows/pyre.yml +++ b/.github/workflows/pyre.yml @@ -33,7 +33,7 @@ jobs: security-events: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: submodules: true diff --git a/.github/workflows/pysa.yml b/.github/workflows/pysa.yml index 6c301e80..5f913465 100644 --- a/.github/workflows/pysa.yml +++ b/.github/workflows/pysa.yml @@ -35,7 +35,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 with: submodules: true diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index c0ad132e..ec58ac6d 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -21,7 +21,7 @@ jobs: python-version: ["3.10", "3.11", "3.12"] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v5 with: diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index fb6f4c5e..f27c181c 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Set up Python 3.10 uses: actions/setup-python@v5 diff --git a/.github/workflows/trivy.yml b/.github/workflows/trivy.yml index 54d83af8..1a92d0ee 100644 --- a/.github/workflows/trivy.yml +++ b/.github/workflows/trivy.yml @@ -27,7 +27,7 @@ jobs: runs-on: "ubuntu-20.04" steps: - name: Checkout code - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Build an image from Dockerfile run: | From 20819aae80a0b83ab67c6ea3742fd8aa1fb710e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 18 Aug 2025 15:42:29 +0000 Subject: [PATCH 04/19] Update asyncio requirement from <4.0,>=3.4.3 to >=3.4.3,<5.0 Updates the requirements on [asyncio](https://github.com/python/asyncio) to permit the latest version. - [Commits](https://github.com/python/asyncio/commits) --- updated-dependencies: - dependency-name: asyncio dependency-version: 4.0.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index ead3af6e..d920af57 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ torch>=2.1.1,<3.0 transformers>=4.39.0,<4.51.0 -asyncio>=3.4.3,<4.0 +asyncio>=3.4.3,<5.0 toml pypdf==5.1.0 ratelimit==2.2.1 From bb46bd9f949c4d2187e4f78854a4d27e38db6a2a Mon Sep 17 00:00:00 2001 From: Kye Gomez Date: Mon, 18 Aug 2025 15:01:35 -0700 Subject: [PATCH 05/19] [REMOVE UN-USED FILES] [Cleanup Concurrent Dashboard] [file renaming] --- docs/examples/smart_database.md | 4 +- docs/llm.txt | 4 +- docs/swarms/concept/vision.md | 2 +- docs/swarms/structs/swarm_rearrange.md | 2 +- example.py | 5 + .../smart_database_swarm.py | 7 +- examples/models/claude_4.py | 2 +- .../streaming_concurrent_workflow.py | 2 +- .../council/council_judge_evaluation.py | 2 +- .../council/council_judge_example.py | 2 +- .../council/council_of_judges_eval.py | 2 +- .../council_judge_complex_example.py | 2 +- .../council_judge_custom_example.py | 2 +- .../council_judge_example.py | 2 +- ...dation.py => graph_workflow_validation.py} | 0 .../paper_implementations}/long_agent.py | 0 examples/sims/simulation_vote_example.py | 19 - examples/utils/misc/swarm_eval_deepseek.py | 170 --------- graph_workflow_basic.py | 51 +++ scripts/docker/test_docker.py | 34 +- simulation_vote_example.py | 27 ++ swarms/structs/__init__.py | 6 +- swarms/structs/agent.py | 28 +- .../{rearrange.py => agent_rearrange.py} | 8 +- swarms/structs/batch_agent_execution.py | 12 +- swarms/structs/concurrent_workflow.py | 7 +- .../{council_judge.py => council_as_judge.py} | 0 swarms/structs/matrix_swarm.py | 306 ---------------- swarms/structs/sequential_workflow.py | 2 +- swarms/structs/swarm_eval.py | 326 ------------------ swarms/structs/swarm_id_generator.py | 2 +- .../{swarm_arange.py => swarm_rearrange.py} | 0 swarms/structs/swarm_router.py | 38 +- swarms/utils/formatter.py | 2 +- tests/structs/test_agentrearrange.py | 2 +- tests/structs/test_matrix_swarm.py | 216 ------------ 36 files changed, 164 insertions(+), 1132 deletions(-) rename examples/{guides/smart_database => apps}/smart_database_swarm.py (99%) rename examples/multi_agent/graphworkflow_examples/{test_graphworlfolw_validation.py => graph_workflow_validation.py} (100%) rename {swarms/structs => examples/paper_implementations}/long_agent.py (100%) delete mode 100644 examples/sims/simulation_vote_example.py delete mode 100644 examples/utils/misc/swarm_eval_deepseek.py create mode 100644 graph_workflow_basic.py create mode 100644 simulation_vote_example.py rename swarms/structs/{rearrange.py => agent_rearrange.py} (99%) rename swarms/structs/{council_judge.py => council_as_judge.py} (100%) delete mode 100644 swarms/structs/matrix_swarm.py delete mode 100644 swarms/structs/swarm_eval.py rename swarms/structs/{swarm_arange.py => swarm_rearrange.py} (100%) delete mode 100644 tests/structs/test_matrix_swarm.py diff --git a/docs/examples/smart_database.md b/docs/examples/smart_database.md index f8681d55..a49ac22f 100644 --- a/docs/examples/smart_database.md +++ b/docs/examples/smart_database.md @@ -1,8 +1,6 @@ # Smart Database Powered by Hierarchical Multi-Agent Workflow -This module implements a fully autonomous database management system using a hierarchical -multi-agent architecture. The system includes specialized agents for different database -operations coordinated by a Database Director agent. +This module implements a fully autonomous database management system using a hierarchical multi-agent architecture. The system includes specialized agents for different database operations coordinated by a Database Director agent. ## Features diff --git a/docs/llm.txt b/docs/llm.txt index 3dc0048a..692944af 100644 --- a/docs/llm.txt +++ b/docs/llm.txt @@ -14643,7 +14643,7 @@ The following example showcases how to use the `AgentRearrange` class to manage ```python from swarms.structs.agent import Agent -from swarms.structs.rearrange import AgentRearrange +from swarms.structs.agent_rearrange import AgentRearrange # Initialize the Director agent using Anthropic model via model_name director = Agent( @@ -44327,7 +44327,7 @@ The flow pattern uses arrow notation (`->`) to define execution order: ### Basic Sequential Flow ```python -from swarms.structs.swarm_arange import SwarmRearrange +from swarms.structs.swarm_rearrange import SwarmRearrange import os from swarms import Agent, AgentRearrange from swarm_models import OpenAIChat diff --git a/docs/swarms/concept/vision.md b/docs/swarms/concept/vision.md index 678b495d..e0ce08be 100644 --- a/docs/swarms/concept/vision.md +++ b/docs/swarms/concept/vision.md @@ -49,7 +49,7 @@ The following example showcases how to use the `AgentRearrange` class to manage ```python from swarms.structs.agent import Agent -from swarms.structs.rearrange import AgentRearrange +from swarms.structs.agent_rearrange import AgentRearrange # Initialize the Director agent using Anthropic model via model_name director = Agent( diff --git a/docs/swarms/structs/swarm_rearrange.md b/docs/swarms/structs/swarm_rearrange.md index c40aa5b5..9297f117 100644 --- a/docs/swarms/structs/swarm_rearrange.md +++ b/docs/swarms/structs/swarm_rearrange.md @@ -46,7 +46,7 @@ The flow pattern uses arrow notation (`->`) to define execution order: ### Basic Sequential Flow ```python -from swarms.structs.swarm_arange import SwarmRearrange +from swarms.structs.swarm_rearrange import SwarmRearrange import os from swarms import Agent, AgentRearrange from swarm_models import OpenAIChat diff --git a/example.py b/example.py index 561f22fc..17398527 100644 --- a/example.py +++ b/example.py @@ -1,5 +1,10 @@ from swarms import Agent +import litellm + +litellm._turn_on_debug() # 👈 this is the 1-line change you need to make + + # Initialize the agent agent = Agent( agent_name="Quantitative-Trading-Agent", diff --git a/examples/guides/smart_database/smart_database_swarm.py b/examples/apps/smart_database_swarm.py similarity index 99% rename from examples/guides/smart_database/smart_database_swarm.py rename to examples/apps/smart_database_swarm.py index b3f07c41..a4b5cb55 100644 --- a/examples/guides/smart_database/smart_database_swarm.py +++ b/examples/apps/smart_database_swarm.py @@ -23,6 +23,9 @@ from loguru import logger from swarms import Agent, HierarchicalSwarm +from dotenv import load_dotenv + +load_dotenv() # ============================================================================= # DATABASE TOOLS - Core Functions for Database Operations @@ -901,6 +904,7 @@ smart_database_swarm = HierarchicalSwarm( description="A comprehensive database management system with specialized agents for creation, schema management, data operations, and querying, coordinated by a database director", director_model_name="gpt-4.1", agents=database_specialists, + director_reasoning_enabled=False, max_loops=1, verbose=True, ) @@ -917,7 +921,8 @@ if __name__ == "__main__": print("SMART DATABASE SWARM - E-COMMERCE SYSTEM EXAMPLE") print("=" * 80) - task1 = """Create a comprehensive e-commerce database system with the following requirements: + task1 = """ + Create a comprehensive e-commerce database system with the following requirements: 1. Create a database called 'ecommerce_db' 2. Create tables for: diff --git a/examples/models/claude_4.py b/examples/models/claude_4.py index 491d5c83..f053e682 100644 --- a/examples/models/claude_4.py +++ b/examples/models/claude_4.py @@ -1,5 +1,5 @@ from swarms.structs.agent import Agent -from swarms.structs.council_judge import CouncilAsAJudge +from swarms.structs.council_as_judge import CouncilAsAJudge # ========== USAGE EXAMPLE ========== diff --git a/examples/multi_agent/concurrent_examples/streaming_concurrent_workflow.py b/examples/multi_agent/concurrent_examples/streaming_concurrent_workflow.py index 9a0fc0d5..9318650f 100644 --- a/examples/multi_agent/concurrent_examples/streaming_concurrent_workflow.py +++ b/examples/multi_agent/concurrent_examples/streaming_concurrent_workflow.py @@ -46,7 +46,7 @@ technical_analyst = Agent( ) # Create list of agents -agents = [market_researcher, financial_analyst, technical_analyst] +agents = [market_researcher, financial_analyst] # Initialize the concurrent workflow workflow = ConcurrentWorkflow( diff --git a/examples/multi_agent/council/council_judge_evaluation.py b/examples/multi_agent/council/council_judge_evaluation.py index 8e0694d5..2188c4f8 100644 --- a/examples/multi_agent/council/council_judge_evaluation.py +++ b/examples/multi_agent/council/council_judge_evaluation.py @@ -8,7 +8,7 @@ from loguru import logger from tqdm import tqdm from swarms.structs.agent import Agent -from swarms.structs.council_judge import CouncilAsAJudge +from swarms.structs.council_as_judge import CouncilAsAJudge # Dataset configurations DATASET_CONFIGS = { diff --git a/examples/multi_agent/council/council_judge_example.py b/examples/multi_agent/council/council_judge_example.py index 634eba28..f862e274 100644 --- a/examples/multi_agent/council/council_judge_example.py +++ b/examples/multi_agent/council/council_judge_example.py @@ -1,5 +1,5 @@ from swarms.structs.agent import Agent -from swarms.structs.council_judge import CouncilAsAJudge +from swarms.structs.council_as_judge import CouncilAsAJudge if __name__ == "__main__": diff --git a/examples/multi_agent/council/council_of_judges_eval.py b/examples/multi_agent/council/council_of_judges_eval.py index ad2e9781..aec19131 100644 --- a/examples/multi_agent/council/council_of_judges_eval.py +++ b/examples/multi_agent/council/council_of_judges_eval.py @@ -1,5 +1,5 @@ from swarms.structs.agent import Agent -from swarms.structs.council_judge import CouncilAsAJudge +from swarms.structs.council_as_judge import CouncilAsAJudge if __name__ == "__main__": diff --git a/examples/multi_agent/council_of_judges/council_judge_complex_example.py b/examples/multi_agent/council_of_judges/council_judge_complex_example.py index e072f593..c4654bfb 100644 --- a/examples/multi_agent/council_of_judges/council_judge_complex_example.py +++ b/examples/multi_agent/council_of_judges/council_judge_complex_example.py @@ -5,7 +5,7 @@ This example shows how to use the CouncilAsAJudge to evaluate various types of responses including technical explanations, creative writing, and problem-solving. """ -from swarms.structs.council_judge import CouncilAsAJudge +from swarms.structs.council_as_judge import CouncilAsAJudge def evaluate_technical_response(): diff --git a/examples/multi_agent/council_of_judges/council_judge_custom_example.py b/examples/multi_agent/council_of_judges/council_judge_custom_example.py index f456a824..edf69186 100644 --- a/examples/multi_agent/council_of_judges/council_judge_custom_example.py +++ b/examples/multi_agent/council_of_judges/council_judge_custom_example.py @@ -5,7 +5,7 @@ This example shows how to use the CouncilAsAJudge with different output types, custom worker configurations, and focused evaluation scenarios. """ -from swarms.structs.council_judge import CouncilAsAJudge +from swarms.structs.council_as_judge import CouncilAsAJudge def evaluate_with_final_output(): diff --git a/examples/multi_agent/council_of_judges/council_judge_example.py b/examples/multi_agent/council_of_judges/council_judge_example.py index 64ad1e9a..aa1bf3cd 100644 --- a/examples/multi_agent/council_of_judges/council_judge_example.py +++ b/examples/multi_agent/council_of_judges/council_judge_example.py @@ -6,7 +6,7 @@ across multiple dimensions including accuracy, helpfulness, harmlessness, coherence, conciseness, and instruction adherence. """ -from swarms.structs.council_judge import CouncilAsAJudge +from swarms.structs.council_as_judge import CouncilAsAJudge def main(): diff --git a/examples/multi_agent/graphworkflow_examples/test_graphworlfolw_validation.py b/examples/multi_agent/graphworkflow_examples/graph_workflow_validation.py similarity index 100% rename from examples/multi_agent/graphworkflow_examples/test_graphworlfolw_validation.py rename to examples/multi_agent/graphworkflow_examples/graph_workflow_validation.py diff --git a/swarms/structs/long_agent.py b/examples/paper_implementations/long_agent.py similarity index 100% rename from swarms/structs/long_agent.py rename to examples/paper_implementations/long_agent.py diff --git a/examples/sims/simulation_vote_example.py b/examples/sims/simulation_vote_example.py deleted file mode 100644 index 46a99933..00000000 --- a/examples/sims/simulation_vote_example.py +++ /dev/null @@ -1,19 +0,0 @@ -from swarms.sims.senator_assembly import SenatorAssembly - - -def main(): - """ - Simulate a Senate vote on a bill to invade Cuba and claim it as the 51st state. - - This function initializes the SenatorAssembly and runs a concurrent vote simulation - on the specified bill. - """ - senator_simulation = SenatorAssembly() - senator_simulation.simulate_vote_concurrent( - "A bill proposing to deregulate the IPO (Initial Public Offering) market in the United States as extensively as possible. The bill seeks to remove or significantly reduce existing regulatory requirements and oversight for companies seeking to go public, with the aim of increasing market efficiency and access to capital. Senators must consider the potential economic, legal, and ethical consequences of such broad deregulation, and cast their votes accordingly.", - batch_size=10, - ) - - -if __name__ == "__main__": - main() diff --git a/examples/utils/misc/swarm_eval_deepseek.py b/examples/utils/misc/swarm_eval_deepseek.py deleted file mode 100644 index ac4a9408..00000000 --- a/examples/utils/misc/swarm_eval_deepseek.py +++ /dev/null @@ -1,170 +0,0 @@ -from loguru import logger -from swarms.structs.swarm_eval import ( - SwarmEvaluator, - PRESET_DATASETS, -) - -import os -from swarms import Agent -from dotenv import load_dotenv - -from swarm_models import OpenAIChat - -load_dotenv() - - -model = OpenAIChat( - model_name="deepseek-ai/DeepSeek-R1-Distill-Llama-70B-free", - openai_api_key=os.getenv("TOGETHER_API_KEY"), - base_url="https://api.together.xyz/v1", -) - -# Define system prompts for reasoning agents -THINKING_AGENT_PROMPT = """You are a sophisticated analytical and strategic thinking agent focused on deep problem analysis and solution design. - -Your core capabilities include: -1. Comprehensive Problem Analysis - - Break down complex problems into constituent elements - - Map relationships and dependencies between components - - Identify root causes and underlying patterns - - Consider historical context and precedents - -2. Multi-Perspective Evaluation - - Examine issues from multiple stakeholder viewpoints - - Consider short-term and long-term implications - - Evaluate social, economic, technical, and ethical dimensions - - Challenge assumptions and identify potential biases - -3. Risk Assessment and Mitigation - - Conduct thorough risk analysis across scenarios - - Identify potential failure modes and edge cases - - Develop contingency plans and mitigation strategies - - Assess probability and impact of various outcomes - -4. Strategic Solution Development - - Generate multiple solution approaches - - Evaluate trade-offs between different strategies - - Consider resource constraints and limitations - - Design scalable and sustainable solutions - -5. Decision Framework Creation - - Establish clear evaluation criteria - - Weight competing priorities appropriately - - Create structured decision matrices - - Document reasoning and key decision factors - -6. Systems Thinking - - Map interconnections between system elements - - Identify feedback loops and cascade effects - - Consider emergent properties and behaviors - - Account for dynamic system evolution - -Your output should always include: -- Clear articulation of your analytical process -- Key assumptions and their justification -- Potential risks and mitigation strategies -- Multiple solution options with pros/cons -- Specific recommendations with supporting rationale -- Areas of uncertainty requiring further investigation - -Focus on developing robust, well-reasoned strategies that account for complexity while remaining practical and actionable.""" - -ACTION_AGENT_PROMPT = """You are an advanced implementation and execution agent focused on turning strategic plans into concrete results. - -Your core capabilities include: -1. Strategic Implementation Planning - - Break down high-level strategies into specific actions - - Create detailed project roadmaps and timelines - - Identify critical path dependencies - - Establish clear milestones and success metrics - - Design feedback and monitoring mechanisms - -2. Resource Optimization - - Assess resource requirements and constraints - - Optimize resource allocation and scheduling - - Identify efficiency opportunities - - Plan for scalability and flexibility - - Manage competing priorities effectively - -3. Execution Management - - Develop detailed implementation procedures - - Create clear operational guidelines - - Establish quality control measures - - Design progress tracking systems - - Build in review and adjustment points - -4. Risk Management - - Implement specific risk mitigation measures - - Create early warning systems - - Develop contingency procedures - - Establish fallback positions - - Monitor risk indicators - -5. Stakeholder Management - - Identify key stakeholders and their needs - - Create communication plans - - Establish feedback mechanisms - - Manage expectations effectively - - Build support and buy-in - -6. Continuous Improvement - - Monitor implementation effectiveness - - Gather and analyze performance data - - Identify improvement opportunities - - Implement iterative enhancements - - Document lessons learned - -Your output should always include: -- Detailed action plans with specific steps -- Resource requirements and allocation plans -- Timeline with key milestones -- Success metrics and monitoring approach -- Risk mitigation procedures -- Communication and stakeholder management plans -- Quality control measures -- Feedback and adjustment mechanisms - -Focus on practical, efficient, and effective implementation while maintaining high quality standards and achieving desired outcomes.""" - -# Initialize the thinking agent -thinking_agent = Agent( - agent_name="Strategic-Thinker", - agent_description="Deep analysis and strategic planning agent", - system_prompt=THINKING_AGENT_PROMPT, - max_loops=1, - llm=model, - dynamic_temperature_enabled=True, -) - - -class DeepSeekSwarm: - def __init__(self): - self.thinking_agent = thinking_agent - - def run(self, task: str): - first_one = self.thinking_agent.run(task) - - return self.thinking_agent.run(first_one) - - -if __name__ == "__main__": - # Initialize the swarm (replace with your actual multi-agent system) - swarm = DeepSeekSwarm() - - # Initialize the evaluator with the swarm instance - evaluator = SwarmEvaluator(swarm) - - logger.info("Starting evaluation for dataset: gsm8k") - - # For demonstration, we use 4 concurrent workers, show progress, and save results. - results = evaluator.evaluate( - "gsm8k", - split="train", - config=PRESET_DATASETS["gsm8k"], - max_workers=os.cpu_count(), - max_retries=3, - show_progress=True, - output_file="gsm8k_results.txt", - ) - - logger.info(f"Results for gsm8k: {results}") diff --git a/graph_workflow_basic.py b/graph_workflow_basic.py new file mode 100644 index 00000000..afb3bd92 --- /dev/null +++ b/graph_workflow_basic.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python3 +""" +Basic Graph Workflow Example + +A minimal example showing how to use GraphWorkflow with backend selection. +""" + +from swarms.structs.graph_workflow import GraphWorkflow +from swarms.structs.agent import Agent + +agent_one = Agent(agent_name="research_agent", model="gpt-4o-mini") +agent_two = Agent( + agent_name="research_agent_two", model="gpt-4o-mini" +) +agent_three = Agent( + agent_name="research_agent_three", model="gpt-4o-mini" +) + + +def main(): + """ + Run a basic graph workflow example without print statements. + """ + # Create agents + + # Create workflow with backend selection + workflow = GraphWorkflow( + name="Basic Example", + verbose=True, + ) + + # Add agents to workflow + workflow.add_node(agent_one) + workflow.add_node(agent_two) + workflow.add_node(agent_three) + + # Create simple chain using the actual agent names + workflow.add_edge("research_agent", "research_agent_two") + workflow.add_edge("research_agent_two", "research_agent_three") + + # Compile the workflow + workflow.compile() + + # Run the workflow + task = "Complete a simple task" + results = workflow.run(task) + return results + + +if __name__ == "__main__": + main() diff --git a/scripts/docker/test_docker.py b/scripts/docker/test_docker.py index 70e9060b..a50a17b9 100644 --- a/scripts/docker/test_docker.py +++ b/scripts/docker/test_docker.py @@ -1,4 +1,3 @@ - #!/usr/bin/env python3 """ Test script to verify Swarms installation in Docker container. @@ -7,53 +6,59 @@ Test script to verify Swarms installation in Docker container. import sys from typing import Dict, Any + def test_swarms_import() -> Dict[str, Any]: """ Test that swarms can be imported and basic functionality works. - + Returns: Dict[str, Any]: Test results """ try: import swarms - print(f" Swarms imported successfully. Version: {swarms.__version__}") - + + print( + f" Swarms imported successfully. Version: {swarms.__version__}" + ) + # Test basic functionality from swarms import Agent + print(" Agent class imported successfully") - + return { "status": "success", "version": swarms.__version__, - "message": "Swarms package is working correctly" + "message": "Swarms package is working correctly", } - + except ImportError as e: print(f" Failed to import swarms: {e}") return { "status": "error", "error": str(e), - "message": "Swarms package import failed" + "message": "Swarms package import failed", } except Exception as e: print(f" Unexpected error: {e}") return { - "status": "error", + "status": "error", "error": str(e), - "message": "Unexpected error occurred" + "message": "Unexpected error occurred", } + def main() -> None: """Main function to run tests.""" print(" Testing Swarms Docker Image...") print("=" * 50) - + # Test Python version print(f"Python version: {sys.version}") - + # Test swarms import result = test_swarms_import() - + print("=" * 50) if result["status"] == "success": print(" All tests passed! Docker image is working correctly.") @@ -62,5 +67,6 @@ def main() -> None: print(" Tests failed! Please check the Docker image.") sys.exit(1) + if __name__ == "__main__": - main() + main() diff --git a/simulation_vote_example.py b/simulation_vote_example.py new file mode 100644 index 00000000..377728c9 --- /dev/null +++ b/simulation_vote_example.py @@ -0,0 +1,27 @@ +from swarms.sims.senator_assembly import SenatorAssembly + + +def main(): + """ + Runs a simulation of a Senate vote on a bill proposing significant tax cuts for all Americans. + The bill is described in realistic legislative terms, and the simulation uses a concurrent voting model. + """ + senator_simulation = SenatorAssembly( + model_name="claude-sonnet-4-20250514" + ) + senator_simulation.simulate_vote_concurrent( + ( + "A bill proposing a significant reduction in federal income tax rates for all American citizens. " + "The legislation aims to lower tax brackets across the board, increase the standard deduction, " + "and provide additional tax relief for middle- and lower-income families. Proponents argue that " + "the bill will stimulate economic growth, increase disposable income, and enhance consumer spending. " + "Opponents raise concerns about the potential impact on the federal deficit, funding for public services, " + "and long-term fiscal responsibility. Senators must weigh the economic, social, and budgetary implications " + "before casting their votes." + ), + batch_size=10, + ) + + +if __name__ == "__main__": + main() diff --git a/swarms/structs/__init__.py b/swarms/structs/__init__.py index 9ea95998..8da55bf3 100644 --- a/swarms/structs/__init__.py +++ b/swarms/structs/__init__.py @@ -9,7 +9,7 @@ from swarms.structs.board_of_directors_swarm import ( ) from swarms.structs.concurrent_workflow import ConcurrentWorkflow from swarms.structs.conversation import Conversation -from swarms.structs.council_judge import CouncilAsAJudge +from swarms.structs.council_as_judge import CouncilAsAJudge from swarms.structs.cron_job import CronJob from swarms.structs.de_hallucination_swarm import DeHallucinationSwarm from swarms.structs.deep_research_swarm import DeepResearchSwarm @@ -66,11 +66,11 @@ from swarms.structs.multi_agent_exec import ( run_single_agent, ) from swarms.structs.multi_agent_router import MultiAgentRouter -from swarms.structs.rearrange import AgentRearrange, rearrange +from swarms.structs.agent_rearrange import AgentRearrange, rearrange from swarms.structs.round_robin import RoundRobinSwarm from swarms.structs.sequential_workflow import SequentialWorkflow from swarms.structs.spreadsheet_swarm import SpreadSheetSwarm -from swarms.structs.swarm_arange import SwarmRearrange +from swarms.structs.swarm_rearrange import SwarmRearrange from swarms.structs.swarm_router import ( SwarmRouter, SwarmType, diff --git a/swarms/structs/agent.py b/swarms/structs/agent.py index 73435c30..914c0adb 100644 --- a/swarms/structs/agent.py +++ b/swarms/structs/agent.py @@ -660,11 +660,13 @@ class Agent: # Add agent name, description, and instructions to the prompt if self.agent_name is not None: - prompt += f"\n Name: {self.agent_name}" + prompt += f"\n Your Name: {self.agent_name} \n" elif self.agent_description is not None: - prompt += f"\n Description: {self.agent_description}" + prompt += ( + f"\n Your Description: {self.agent_description} \n" + ) elif self.system_prompt is not None: - prompt += f"\n Instructions: {self.system_prompt}" + prompt += f"\n Your Instructions: {self.system_prompt} \n" else: prompt = self.system_prompt @@ -676,26 +678,14 @@ class Agent: name=f"{self.agent_name}_conversation", user=self.user_name, rules=self.rules, - token_count=( - self.conversation_schema.count_tokens - if self.conversation_schema - else False - ), - message_id_on=( - self.conversation_schema.message_id_on - if self.conversation_schema - else False - ), - time_enabled=( - self.conversation_schema.time_enabled - if self.conversation_schema - else False - ), + token_count=False, + message_id_on=False, + time_enabled=True, ) # Add the system prompt to the conversation memory.add( - role="System", + role="system", content=prompt, ) diff --git a/swarms/structs/rearrange.py b/swarms/structs/agent_rearrange.py similarity index 99% rename from swarms/structs/rearrange.py rename to swarms/structs/agent_rearrange.py index dd9a65c7..459b1b2e 100644 --- a/swarms/structs/rearrange.py +++ b/swarms/structs/agent_rearrange.py @@ -3,19 +3,17 @@ import uuid from concurrent.futures import ThreadPoolExecutor from typing import Any, Callable, Dict, List, Optional, Union - from swarms.structs.agent import Agent from swarms.structs.base_swarm import BaseSwarm - +from swarms.structs.conversation import Conversation +from swarms.structs.multi_agent_exec import get_agents_info +from swarms.telemetry.main import log_agent_data from swarms.utils.any_to_str import any_to_str from swarms.utils.history_output_formatter import ( history_output_formatter, ) from swarms.utils.loguru_logger import initialize_logger -from swarms.telemetry.main import log_agent_data -from swarms.structs.conversation import Conversation from swarms.utils.output_types import OutputType -from swarms.structs.multi_agent_exec import get_agents_info logger = initialize_logger(log_folder="rearrange") diff --git a/swarms/structs/batch_agent_execution.py b/swarms/structs/batch_agent_execution.py index 7b2a926d..7be9e1f7 100644 --- a/swarms/structs/batch_agent_execution.py +++ b/swarms/structs/batch_agent_execution.py @@ -7,10 +7,15 @@ from loguru import logger import traceback +class BatchAgentExecutionError(Exception): + pass + + def batch_agent_execution( agents: List[Union[Agent, Callable]], tasks: List[str] = None, imgs: List[str] = None, + max_workers: int = max(1, int(os.cpu_count() * 0.9)), ): """ Execute a batch of agents on a list of tasks concurrently. @@ -38,9 +43,6 @@ def batch_agent_execution( results = [] - # Calculate max workers as 90% of available CPU cores - max_workers = max(1, int(os.cpu_count() * 0.9)) - formatter.print_panel( f"Executing {len(agents)} agents on {len(tasks)} tasks using {max_workers} workers" ) @@ -78,5 +80,7 @@ def batch_agent_execution( return results except Exception as e: log = f"Batch agent execution failed Error: {str(e)} Traceback: {traceback.format_exc()}" + logger.error(log) - raise e + + raise BatchAgentExecutionError(log) diff --git a/swarms/structs/concurrent_workflow.py b/swarms/structs/concurrent_workflow.py index 4ceb62e0..57b77f9b 100644 --- a/swarms/structs/concurrent_workflow.py +++ b/swarms/structs/concurrent_workflow.py @@ -295,7 +295,7 @@ class ConcurrentWorkflow(BaseSwarm): def display_agent_dashboard( self, - title: str = "🤖 Agent Dashboard", + title: str = "ConcurrentWorkflow Dashboard", is_final: bool = False, ) -> None: """ @@ -307,7 +307,7 @@ class ConcurrentWorkflow(BaseSwarm): Args: title (str, optional): The dashboard title to display at the top. - Defaults to "🤖 Agent Dashboard". + Defaults to "🤖 ConcurrentWorkflow Dashboard". is_final (bool, optional): Whether this is the final dashboard display after all agents have completed. Changes formatting and styling. Defaults to False. @@ -543,7 +543,8 @@ class ConcurrentWorkflow(BaseSwarm): # Display final dashboard if enabled if self.show_dashboard: self.display_agent_dashboard( - "🎉 Final Agent Dashboard", is_final=True + "Final ConcurrentWorkflow Dashboard", + is_final=True, ) return history_output_formatter( diff --git a/swarms/structs/council_judge.py b/swarms/structs/council_as_judge.py similarity index 100% rename from swarms/structs/council_judge.py rename to swarms/structs/council_as_judge.py diff --git a/swarms/structs/matrix_swarm.py b/swarms/structs/matrix_swarm.py deleted file mode 100644 index 179e88b5..00000000 --- a/swarms/structs/matrix_swarm.py +++ /dev/null @@ -1,306 +0,0 @@ -import json -from typing import Any, List - -from loguru import logger -from pydantic import BaseModel, Field - -from swarms import Agent - - -class AgentOutput(BaseModel): - """ - Schema for capturing metadata and results of an agent run. - """ - - agent_name: str = Field(..., description="Name of the agent.") - input_query: str = Field( - ..., description="Input query provided to the agent." - ) - output_result: Any = Field( - ..., description="Result produced by the agent." - ) - metadata: dict = Field( - ..., description="Additional metadata about the agent run." - ) - - -class MatrixSwarm: - """ - A class to manage a matrix of agents and perform matrix operations similar to linear algebra. - """ - - def __init__(self, agents: List[List[Agent]]): - """ - Initializes the MatrixSwarm with a 2D list of agents. - Args: - agents (List[List[Agent]]): 2D list of agents representing the matrix. - """ - if not agents or not all( - isinstance(row, list) for row in agents - ): - raise ValueError("Agents must be provided as a 2D list.") - if not all( - isinstance(agent, Agent) - for row in agents - for agent in row - ): - raise ValueError( - "All elements of the matrix must be instances of `Agent`." - ) - self.agents = agents - self.outputs = [] # List to store outputs as AgentOutput - - def validate_dimensions(self, other: "MatrixSwarm") -> None: - """ - Validates that two matrices have compatible dimensions for operations. - - Args: - other (MatrixSwarm): Another MatrixSwarm. - - Raises: - ValueError: If dimensions are incompatible. - """ - if len(self.agents) != len(other.agents) or len( - self.agents[0] - ) != len(other.agents[0]): - raise ValueError( - "Matrix dimensions are incompatible for this operation." - ) - - def transpose(self) -> "MatrixSwarm": - """ - Transposes the matrix of agents (swap rows and columns). - - Returns: - MatrixSwarm: A new transposed MatrixSwarm. - """ - transposed_agents = [ - [self.agents[j][i] for j in range(len(self.agents))] - for i in range(len(self.agents[0])) - ] - return MatrixSwarm(transposed_agents) - - def add(self, other: "MatrixSwarm") -> "MatrixSwarm": - """ - Adds two matrices element-wise. - - Args: - other (MatrixSwarm): Another MatrixSwarm to add. - - Returns: - MatrixSwarm: A new MatrixSwarm resulting from the addition. - """ - self.validate_dimensions(other) - added_agents = [ - [self.agents[i][j] for j in range(len(self.agents[i]))] - for i in range(len(self.agents)) - ] - return MatrixSwarm(added_agents) - - def scalar_multiply(self, scalar: int) -> "MatrixSwarm": - """ - Scales the agents by duplicating them scalar times along the row. - - Args: - scalar (int): The scalar multiplier. - - Returns: - MatrixSwarm: A new MatrixSwarm where each agent is repeated scalar times along the row. - """ - scaled_agents = [ - [agent for _ in range(scalar) for agent in row] - for row in self.agents - ] - return MatrixSwarm(scaled_agents) - - def multiply( - self, other: "MatrixSwarm", inputs: List[str] - ) -> List[List[AgentOutput]]: - """ - Multiplies two matrices (dot product between rows and columns). - - Args: - other (MatrixSwarm): Another MatrixSwarm for multiplication. - inputs (List[str]): A list of input queries for the agents. - - Returns: - List[List[AgentOutput]]: A resulting matrix of outputs after multiplication. - """ - if len(self.agents[0]) != len(other.agents): - raise ValueError( - "Matrix dimensions are incompatible for multiplication." - ) - - results = [] - for i, row in enumerate(self.agents): - row_results = [] - for col_idx in range(len(other.agents[0])): - col = [ - other.agents[row_idx][col_idx] - for row_idx in range(len(other.agents)) - ] - query = inputs[ - i - ] # Input query for the corresponding row - intermediate_result = [] - - for agent_r, agent_c in zip(row, col): - try: - result = agent_r.run(query) - intermediate_result.append(result) - except Exception as e: - intermediate_result.append(f"Error: {e}") - - # Aggregate outputs from dot product - combined_result = " ".join( - intermediate_result - ) # Example aggregation - row_results.append( - AgentOutput( - agent_name=f"DotProduct-{i}-{col_idx}", - input_query=query, - output_result=combined_result, - metadata={"row": i, "col": col_idx}, - ) - ) - results.append(row_results) - return results - - def subtract(self, other: "MatrixSwarm") -> "MatrixSwarm": - """ - Subtracts two matrices element-wise. - - Args: - other (MatrixSwarm): Another MatrixSwarm to subtract. - - Returns: - MatrixSwarm: A new MatrixSwarm resulting from the subtraction. - """ - self.validate_dimensions(other) - subtracted_agents = [ - [self.agents[i][j] for j in range(len(self.agents[i]))] - for i in range(len(self.agents)) - ] - return MatrixSwarm(subtracted_agents) - - def identity(self, size: int) -> "MatrixSwarm": - """ - Creates an identity matrix of agents with size `size`. - - Args: - size (int): Size of the identity matrix (NxN). - - Returns: - MatrixSwarm: An identity MatrixSwarm. - """ - identity_agents = [ - [ - ( - self.agents[i][j] - if i == j - else Agent( - agent_name=f"Zero-Agent-{i}-{j}", - system_prompt="", - ) - ) - for j in range(size) - ] - for i in range(size) - ] - return MatrixSwarm(identity_agents) - - def determinant(self) -> Any: - """ - Computes the determinant of a square MatrixSwarm. - - Returns: - Any: Determinant of the matrix (as agent outputs). - """ - if len(self.agents) != len(self.agents[0]): - raise ValueError( - "Determinant can only be computed for square matrices." - ) - - # Recursive determinant calculation (example using placeholder logic) - if len(self.agents) == 1: - return self.agents[0][0].run("Compute determinant") - - det_result = 0 - for i in range(len(self.agents)): - submatrix = MatrixSwarm( - [row[:i] + row[i + 1 :] for row in self.agents[1:]] - ) - cofactor = ((-1) ** i) * self.agents[0][i].run( - "Compute determinant" - ) - det_result += cofactor * submatrix.determinant() - return det_result - - def save_to_file(self, path: str) -> None: - """ - Saves the agent matrix structure and metadata to a file. - - Args: - path (str): File path to save the matrix. - """ - try: - matrix_data = { - "agents": [ - [agent.agent_name for agent in row] - for row in self.agents - ], - "outputs": [output.dict() for output in self.outputs], - } - with open(path, "w") as f: - json.dump(matrix_data, f, indent=4) - logger.info(f"MatrixSwarm saved to {path}") - except Exception as e: - logger.error(f"Error saving MatrixSwarm: {e}") - - -# # Example usage -# if __name__ == "__main__": -# from swarms.prompts.finance_agent_sys_prompt import ( -# FINANCIAL_AGENT_SYS_PROMPT, -# ) - -# # Create a 3x3 matrix of agents -# agents = [ -# [ -# Agent( -# agent_name=f"Agent-{i}-{j}", -# system_prompt=FINANCIAL_AGENT_SYS_PROMPT, -# model_name="gpt-4o-mini", -# max_loops=1, -# autosave=True, -# dashboard=False, -# verbose=True, -# dynamic_temperature_enabled=True, -# saved_state_path=f"agent_{i}_{j}.json", -# user_name="swarms_corp", -# retry_attempts=1, -# context_length=200000, -# return_step_meta=False, -# output_type="string", -# streaming_on=False, -# ) -# for j in range(3) -# ] -# for i in range(3) -# ] - -# # Initialize the matrix -# agent_matrix = MatrixSwarm(agents) - -# # Example queries -# inputs = [ -# "Explain Roth IRA benefits", -# "Differences between ETFs and mutual funds", -# "How to create a diversified portfolio", -# ] - -# # Run agents -# outputs = agent_matrix.multiply(agent_matrix.transpose(), inputs) - -# # Save results -# agent_matrix.save_to_file("agent_matrix_results.json") diff --git a/swarms/structs/sequential_workflow.py b/swarms/structs/sequential_workflow.py index c845d508..32036f7f 100644 --- a/swarms/structs/sequential_workflow.py +++ b/swarms/structs/sequential_workflow.py @@ -2,7 +2,7 @@ from concurrent.futures import ThreadPoolExecutor, as_completed from typing import Callable, List, Optional, Union from swarms.structs.agent import Agent -from swarms.structs.rearrange import AgentRearrange +from swarms.structs.agent_rearrange import AgentRearrange from swarms.utils.loguru_logger import initialize_logger from swarms.utils.output_types import OutputType diff --git a/swarms/structs/swarm_eval.py b/swarms/structs/swarm_eval.py deleted file mode 100644 index ac47b291..00000000 --- a/swarms/structs/swarm_eval.py +++ /dev/null @@ -1,326 +0,0 @@ -import math -import time -from concurrent.futures import ThreadPoolExecutor, as_completed -from typing import Any, Callable, Dict, Optional, Tuple - -from datasets import Dataset, load_dataset -from loguru import logger -from tqdm import tqdm - -# ----------------------------------------------------------------------------- -# Logging configuration: log to console and file (rotating by size) -# ----------------------------------------------------------------------------- - -# ----------------------------------------------------------------------------- -# Swarm interface example -# ----------------------------------------------------------------------------- - - -# ----------------------------------------------------------------------------- -# Benchmark configuration -# ----------------------------------------------------------------------------- -class BenchmarkConfig: - """ - Configuration for a benchmark dataset. - - Attributes: - input_column (str): The column containing the task prompt. - answer_column (str): The column containing the expected answer. - answer_extractor (Optional[Callable[[Any], str]]): Function to extract - a string answer from the dataset's raw answer format. - answer_matcher (Optional[Callable[[str, str], bool]]): Function to compare - the expected answer and the swarm output. If None, a simple substring - containment is used. - """ - - def __init__( - self, - input_column: str, - answer_column: str, - answer_extractor: Optional[Callable[[Any], str]] = None, - answer_matcher: Optional[Callable[[str, str], bool]] = None, - ): - self.input_column = input_column - self.answer_column = answer_column - self.answer_extractor = answer_extractor - self.answer_matcher = answer_matcher - - -# ----------------------------------------------------------------------------- -# Preset dataset configurations for popular benchmarks -# ----------------------------------------------------------------------------- -PRESET_DATASETS: Dict[str, BenchmarkConfig] = { - "gsm8k": BenchmarkConfig( - input_column="question", - answer_column="answer", - ), - "squad": BenchmarkConfig( - input_column="question", - answer_column="answers", - answer_extractor=lambda ans: ( - ans["text"][0] - if isinstance(ans, dict) - and "text" in ans - and isinstance(ans["text"], list) - and ans["text"] - else str(ans) - ), - ), - "winogrande": BenchmarkConfig( - input_column="sentence", - answer_column="answer", - ), - "commonsense_qa": BenchmarkConfig( - input_column="question", - answer_column="answerKey", - ), - # Add additional presets here. -} - - -# ----------------------------------------------------------------------------- -# SwarmEvaluator with extended features -# ----------------------------------------------------------------------------- -class SwarmEvaluator: - """ - Evaluator that uses a swarm of agents to process benchmark datasets - from Hugging Face, with concurrency, retries, progress display, performance timing, - and customizable answer matching. - - Example: - swarm = Swarm() - evaluator = SwarmEvaluator(swarm) - results = evaluator.evaluate("gsm8k", split="test", max_workers=4) - print(results) - """ - - def __init__(self, swarm: callable) -> None: - """ - Initialize the evaluator with a given swarm. - - Args: - swarm (Swarm): A swarm instance with a callable run(task: str) method. - """ - self.swarm = swarm - - def evaluate( - self, - dataset_name: str, - split: str = "test", - config: Optional[BenchmarkConfig] = None, - max_workers: int = 1, - max_retries: int = 3, - show_progress: bool = True, - output_file: Optional[str] = None, - ) -> Dict[str, Any]: - """ - Evaluate the specified benchmark dataset using the swarm. - - Args: - dataset_name (str): The dataset name (from Hugging Face). - split (str): The dataset split (e.g., "test", "validation"). - config (Optional[BenchmarkConfig]): Benchmark configuration. If None, - a preset config is used. - max_workers (int): Number of concurrent workers. - max_retries (int): Number of retries for swarm tasks on failure. - show_progress (bool): If True, display a progress bar. - output_file (Optional[str]): Path to a file to write the results. - - Returns: - Dict[str, Any]: Evaluation metrics including total examples, correct answers, - accuracy, and total evaluation time. - """ - if config is None: - config = PRESET_DATASETS.get(dataset_name) - if config is None: - raise ValueError( - f"No preset config for dataset '{dataset_name}'. Provide a BenchmarkConfig." - ) - - logger.info( - f"Loading dataset '{dataset_name}' (split: {split})..." - ) - dataset: Dataset = load_dataset(dataset_name, split=split) - total_examples = len(dataset) - logger.info(f"Total examples to evaluate: {total_examples}") - - start_time = time.time() - correct = 0 - - # Function to process a single example. - def _process_example( - example: Dict[str, Any], idx: int - ) -> Tuple[bool, float]: - task_start = time.time() - task_text = example.get(config.input_column) - expected_answer = example.get(config.answer_column) - - if task_text is None or expected_answer is None: - logger.warning( - f"Example {idx}: Missing '{config.input_column}' or '{config.answer_column}', skipping." - ) - return (False, 0.0) - - # Use answer_extractor if provided. - if config.answer_extractor: - try: - expected_answer = config.answer_extractor( - expected_answer - ) - except Exception as e: - logger.error( - f"Example {idx}: Error extracting answer: {e}" - ) - return (False, 0.0) - - logger.debug(f"Example {idx} - Task: {task_text}") - logger.debug( - f"Example {idx} - Expected Answer: {expected_answer}" - ) - - try: - swarm_output = self._run_with_retry( - task_text, max_retries - ) - except Exception as e: - logger.error( - f"Example {idx}: Failed after retries. Error: {e}" - ) - return (False, time.time() - task_start) - - logger.debug( - f"Example {idx} - Swarm Output: {swarm_output}" - ) - - # Use custom matcher if provided; otherwise, default matching. - if config.answer_matcher: - is_correct = config.answer_matcher( - expected_answer, swarm_output - ) - else: - is_correct = self._default_matcher( - expected_answer, swarm_output - ) - - task_time = time.time() - task_start - logger.info( - f"Example {idx}: {'Correct' if is_correct else 'Incorrect'} in {task_time:.2f}s" - ) - return (is_correct, task_time) - - # Use ThreadPoolExecutor for concurrency. - futures = [] - total_time = 0.0 - with ThreadPoolExecutor(max_workers=max_workers) as executor: - # Optionally wrap the dataset with tqdm for a progress bar. - examples_iter = enumerate(dataset, start=1) - if show_progress: - examples_iter = tqdm( - list(examples_iter), - total=total_examples, - desc="Evaluating", - ) - - for idx, example in examples_iter: - futures.append( - executor.submit(_process_example, example, idx) - ) - - for future in as_completed(futures): - try: - is_correct, elapsed = future.result() - total_time += elapsed - if is_correct: - correct += 1 - except Exception as e: - logger.error(f"Error processing an example: {e}") - - overall_time = time.time() - start_time - accuracy = ( - correct / total_examples if total_examples > 0 else 0.0 - ) - - logger.info( - f"Evaluation complete. Total examples: {total_examples}, Correct: {correct}, " - f"Accuracy: {accuracy:.2%}, Overall Time: {overall_time:.2f}s, " - f"Average per-example time: {total_time/total_examples if total_examples else 0:.2f}s" - ) - - results = { - "total": total_examples, - "correct": correct, - "accuracy": accuracy, - "overall_time": overall_time, - "average_example_time": ( - total_time / total_examples - if total_examples - else math.nan - ), - } - - # Optionally save results to a file. - if output_file: - try: - with open(output_file, "w") as f: - for key, value in results.items(): - f.write(f"{key}: {value}\n") - logger.info(f"Results saved to {output_file}") - except Exception as e: - logger.error( - f"Error saving results to {output_file}: {e}" - ) - - return results - - def _run_with_retry(self, task: str, max_retries: int) -> str: - """ - Runs the swarm task with a retry mechanism. - - Args: - task (str): The task string. - max_retries (int): Maximum number of retries. - - Returns: - str: Swarm output. - - Raises: - Exception: If all retries fail. - """ - attempt = 0 - while attempt <= max_retries: - try: - start = time.time() - result = self.swarm.run(task) - elapsed = time.time() - start - logger.debug( - f"Task succeeded in {elapsed:.2f}s on attempt {attempt + 1}" - ) - return result - except Exception as e: - logger.warning( - f"Task failed on attempt {attempt + 1}: {e}" - ) - attempt += 1 - time.sleep(0.5 * attempt) # Exponential backoff - raise Exception("Max retries exceeded for task.") - - @staticmethod - def _default_matcher(expected: str, output: str) -> bool: - """ - Default answer matching using a normalized substring check. - - Args: - expected (str): The expected answer. - output (str): The swarm output. - - Returns: - bool: True if expected is found in output; otherwise, False. - """ - expected_norm = " ".join(expected.strip().split()) - output_norm = " ".join(output.strip().split()) - return expected_norm in output_norm - - -# ----------------------------------------------------------------------------- -# Example usage -# ----------------------------------------------------------------------------- diff --git a/swarms/structs/swarm_id_generator.py b/swarms/structs/swarm_id_generator.py index c05e039d..aeaa5999 100644 --- a/swarms/structs/swarm_id_generator.py +++ b/swarms/structs/swarm_id_generator.py @@ -2,4 +2,4 @@ import uuid def generate_swarm_id(): - return str(uuid.uuid4()) + return f"swarm-{uuid.uuid4().hex}" diff --git a/swarms/structs/swarm_arange.py b/swarms/structs/swarm_rearrange.py similarity index 100% rename from swarms/structs/swarm_arange.py rename to swarms/structs/swarm_rearrange.py diff --git a/swarms/structs/swarm_router.py b/swarms/structs/swarm_router.py index 5edf3b13..cae43b9c 100644 --- a/swarms/structs/swarm_router.py +++ b/swarms/structs/swarm_router.py @@ -11,33 +11,31 @@ from swarms.prompts.multi_agent_collab_prompt import ( ) from swarms.structs.agent import Agent from swarms.structs.concurrent_workflow import ConcurrentWorkflow +from swarms.structs.council_as_judge import CouncilAsAJudge from swarms.structs.csv_to_agent import AgentLoader +from swarms.structs.deep_research_swarm import DeepResearchSwarm from swarms.structs.groupchat import GroupChat +from swarms.structs.heavy_swarm import HeavySwarm from swarms.structs.hiearchical_swarm import HierarchicalSwarm +from swarms.structs.interactive_groupchat import InteractiveGroupChat +from swarms.structs.ma_utils import list_all_agents from swarms.structs.majority_voting import MajorityVoting +from swarms.structs.malt import MALT from swarms.structs.mixture_of_agents import MixtureOfAgents from swarms.structs.multi_agent_router import MultiAgentRouter -from swarms.structs.rearrange import AgentRearrange +from swarms.structs.agent_rearrange import AgentRearrange from swarms.structs.sequential_workflow import SequentialWorkflow -from swarms.structs.spreadsheet_swarm import SpreadSheetSwarm from swarms.structs.swarm_matcher import swarm_matcher from swarms.telemetry.log_executions import log_execution -from swarms.utils.output_types import OutputType -from swarms.utils.loguru_logger import initialize_logger -from swarms.structs.malt import MALT -from swarms.structs.deep_research_swarm import DeepResearchSwarm -from swarms.structs.council_judge import CouncilAsAJudge -from swarms.structs.interactive_groupchat import InteractiveGroupChat -from swarms.structs.heavy_swarm import HeavySwarm -from swarms.structs.ma_utils import list_all_agents from swarms.utils.generate_keys import generate_api_key +from swarms.utils.loguru_logger import initialize_logger +from swarms.utils.output_types import OutputType logger = initialize_logger(log_folder="swarm_router") SwarmType = Literal[ "AgentRearrange", "MixtureOfAgents", - "SpreadSheetSwarm", "SequentialWorkflow", "ConcurrentWorkflow", "GroupChat", @@ -146,7 +144,6 @@ class SwarmRouter: Available Swarm Types: - AgentRearrange: Optimizes agent arrangement for task execution - MixtureOfAgents: Combines multiple agent types for diverse tasks - - SpreadSheetSwarm: Uses spreadsheet-like operations for task management - SequentialWorkflow: Executes tasks sequentially - ConcurrentWorkflow: Executes tasks in parallel - "auto": Automatically selects best swarm type via embedding search @@ -179,7 +176,7 @@ class SwarmRouter: description: str = "Routes your task to the desired swarm", max_loops: int = 1, agents: List[Union[Agent, Callable]] = [], - swarm_type: SwarmType = "SequentialWorkflow", # "SpreadSheetSwarm" # "auto" + swarm_type: SwarmType = "SequentialWorkflow", # "ConcurrentWorkflow" # "auto" autosave: bool = False, rearrange_flow: str = None, return_json: bool = False, @@ -396,7 +393,6 @@ class SwarmRouter: "MajorityVoting": self._create_majority_voting, "GroupChat": self._create_group_chat, "MultiAgentRouter": self._create_multi_agent_router, - "SpreadSheetSwarm": self._create_spreadsheet_swarm, "SequentialWorkflow": self._create_sequential_workflow, "ConcurrentWorkflow": self._create_concurrent_workflow, } @@ -528,18 +524,6 @@ class SwarmRouter: output_type=self.output_type, ) - def _create_spreadsheet_swarm(self, *args, **kwargs): - """Factory function for SpreadSheetSwarm.""" - return SpreadSheetSwarm( - name=self.name, - description=self.description, - agents=self.agents, - max_loops=self.max_loops, - autosave_on=self.autosave, - *args, - **kwargs, - ) - def _create_sequential_workflow(self, *args, **kwargs): """Factory function for SequentialWorkflow.""" return SequentialWorkflow( @@ -580,7 +564,7 @@ class SwarmRouter: **kwargs: Arbitrary keyword arguments. Returns: - Union[AgentRearrange, MixtureOfAgents, SpreadSheetSwarm, SequentialWorkflow, ConcurrentWorkflow]: + Union[AgentRearrange, MixtureOfAgents, SequentialWorkflow, ConcurrentWorkflow]: The instantiated swarm object. Raises: diff --git a/swarms/utils/formatter.py b/swarms/utils/formatter.py index 0b546be5..0fb3f4f0 100644 --- a/swarms/utils/formatter.py +++ b/swarms/utils/formatter.py @@ -397,7 +397,7 @@ class Formatter: def print_agent_dashboard( self, agents_data: List[Dict[str, Any]], - title: str = "🤖 Agent Dashboard", + title: str = "ConcurrentWorkflow Dashboard", is_final: bool = False, ) -> None: """ diff --git a/tests/structs/test_agentrearrange.py b/tests/structs/test_agentrearrange.py index abb23dd2..2110cf60 100644 --- a/tests/structs/test_agentrearrange.py +++ b/tests/structs/test_agentrearrange.py @@ -7,7 +7,7 @@ from loguru import logger from swarm_models import OpenAIChat from swarms.structs.agent import Agent -from swarms.structs.rearrange import AgentRearrange +from swarms.structs.agent_rearrange import AgentRearrange class TestResult: diff --git a/tests/structs/test_matrix_swarm.py b/tests/structs/test_matrix_swarm.py deleted file mode 100644 index 4556c693..00000000 --- a/tests/structs/test_matrix_swarm.py +++ /dev/null @@ -1,216 +0,0 @@ -from swarms.structs.matrix_swarm import AgentMatrix, AgentOutput -from swarms import Agent - - -def create_test_matrix(rows: int, cols: int) -> AgentMatrix: - """Helper function to create a test agent matrix""" - agents = [ - [ - Agent( - agent_name=f"TestAgent-{i}-{j}", - system_prompt="Test prompt", - ) - for j in range(cols) - ] - for i in range(rows) - ] - return AgentMatrix(agents) - - -def test_init(): - """Test AgentMatrix initialization""" - # Test valid initialization - matrix = create_test_matrix(2, 2) - assert isinstance(matrix, AgentMatrix) - assert len(matrix.agents) == 2 - assert len(matrix.agents[0]) == 2 - - # Test invalid initialization - try: - AgentMatrix([[1, 2], [3, 4]]) # Non-agent elements - assert False, "Should raise ValueError" - except ValueError: - pass - - try: - AgentMatrix([]) # Empty matrix - assert False, "Should raise ValueError" - except ValueError: - pass - - -def test_transpose(): - """Test matrix transpose operation""" - matrix = create_test_matrix(2, 3) - transposed = matrix.transpose() - - assert len(transposed.agents) == 3 # Original cols become rows - assert len(transposed.agents[0]) == 2 # Original rows become cols - - # Verify agent positions - for i in range(2): - for j in range(3): - assert ( - matrix.agents[i][j].agent_name - == transposed.agents[j][i].agent_name - ) - - -def test_add(): - """Test matrix addition""" - matrix1 = create_test_matrix(2, 2) - matrix2 = create_test_matrix(2, 2) - - result = matrix1.add(matrix2) - assert len(result.agents) == 2 - assert len(result.agents[0]) == 2 - - # Test incompatible dimensions - matrix3 = create_test_matrix(2, 3) - try: - matrix1.add(matrix3) - assert False, "Should raise ValueError" - except ValueError: - pass - - -def test_scalar_multiply(): - """Test scalar multiplication""" - matrix = create_test_matrix(2, 2) - scalar = 3 - result = matrix.scalar_multiply(scalar) - - assert len(result.agents) == 2 - assert len(result.agents[0]) == 2 * scalar - - # Verify agent duplication - for i in range(len(result.agents)): - for j in range(0, len(result.agents[0]), scalar): - original_agent = matrix.agents[i][j // scalar] - for k in range(scalar): - assert ( - result.agents[i][j + k].agent_name - == original_agent.agent_name - ) - - -def test_multiply(): - """Test matrix multiplication""" - matrix1 = create_test_matrix(2, 3) - matrix2 = create_test_matrix(3, 2) - inputs = ["test query 1", "test query 2"] - - result = matrix1.multiply(matrix2, inputs) - assert len(result) == 2 # Number of rows in first matrix - assert len(result[0]) == 2 # Number of columns in second matrix - - # Verify output structure - for row in result: - for output in row: - assert isinstance(output, AgentOutput) - assert isinstance(output.input_query, str) - assert isinstance(output.metadata, dict) - - -def test_subtract(): - """Test matrix subtraction""" - matrix1 = create_test_matrix(2, 2) - matrix2 = create_test_matrix(2, 2) - - result = matrix1.subtract(matrix2) - assert len(result.agents) == 2 - assert len(result.agents[0]) == 2 - - -def test_identity(): - """Test identity matrix creation""" - matrix = create_test_matrix(3, 3) - identity = matrix.identity(3) - - assert len(identity.agents) == 3 - assert len(identity.agents[0]) == 3 - - # Verify diagonal elements are from original matrix - for i in range(3): - assert ( - identity.agents[i][i].agent_name - == matrix.agents[i][i].agent_name - ) - - # Verify non-diagonal elements are zero agents - for j in range(3): - if i != j: - assert identity.agents[i][j].agent_name.startswith( - "Zero-Agent" - ) - - -def test_determinant(): - """Test determinant calculation""" - # Test 1x1 matrix - matrix1 = create_test_matrix(1, 1) - det1 = matrix1.determinant() - assert det1 is not None - - # Test 2x2 matrix - matrix2 = create_test_matrix(2, 2) - det2 = matrix2.determinant() - assert det2 is not None - - # Test non-square matrix - matrix3 = create_test_matrix(2, 3) - try: - matrix3.determinant() - assert False, "Should raise ValueError" - except ValueError: - pass - - -def test_save_to_file(tmp_path): - """Test saving matrix to file""" - import os - - matrix = create_test_matrix(2, 2) - file_path = os.path.join(tmp_path, "test_matrix.json") - - matrix.save_to_file(file_path) - assert os.path.exists(file_path) - - # Verify file contents - import json - - with open(file_path, "r") as f: - data = json.load(f) - assert "agents" in data - assert "outputs" in data - assert len(data["agents"]) == 2 - assert len(data["agents"][0]) == 2 - - -def run_all_tests(): - """Run all test functions""" - test_functions = [ - test_init, - test_transpose, - test_add, - test_scalar_multiply, - test_multiply, - test_subtract, - test_identity, - test_determinant, - ] - - for test_func in test_functions: - try: - test_func() - print(f"✅ {test_func.__name__} passed") - except AssertionError as e: - print(f"❌ {test_func.__name__} failed: {str(e)}") - except Exception as e: - print( - f"❌ {test_func.__name__} failed with exception: {str(e)}" - ) - - -if __name__ == "__main__": - run_all_tests() From 67b380dc8a36c66f6a843c796ad46281dfe8c124 Mon Sep 17 00:00:00 2001 From: Kye Gomez Date: Mon, 18 Aug 2025 15:26:47 -0700 Subject: [PATCH 06/19] remove-unused-params from agent.py --- .../graph_workflow_basic.py | 0 .../euroswarm_parliament/__init__.py | 3 +- .../euroswarm_parliament.py | 2461 +++++++++++------ .../euroswarm_parliament_example.py | 553 ++-- .../mass_agent_template.py | 736 +++-- .../euroswarm_parliament/test_mass_agents.py | 63 +- .../wikipedia_personality_scraper.py | 608 ++-- swarms/structs/__init__.py | 2 +- swarms/structs/agent.py | 37 +- swarms/structs/conversation.py | 86 +- 10 files changed, 2893 insertions(+), 1656 deletions(-) rename graph_workflow_basic.py => examples/multi_agent/graphworkflow_examples/graph_workflow_basic.py (100%) diff --git a/graph_workflow_basic.py b/examples/multi_agent/graphworkflow_examples/graph_workflow_basic.py similarity index 100% rename from graph_workflow_basic.py rename to examples/multi_agent/graphworkflow_examples/graph_workflow_basic.py diff --git a/examples/simulations/euroswarm_parliament/__init__.py b/examples/simulations/euroswarm_parliament/__init__.py index 381233f0..863b6d26 100644 --- a/examples/simulations/euroswarm_parliament/__init__.py +++ b/examples/simulations/euroswarm_parliament/__init__.py @@ -30,6 +30,7 @@ try: WikipediaPersonalityScraper, MEPPersonalityProfile, ) + WIKIPEDIA_PERSONALITY_AVAILABLE = True except ImportError: WIKIPEDIA_PERSONALITY_AVAILABLE = False @@ -52,4 +53,4 @@ __all__ = [ "WikipediaPersonalityScraper", "MEPPersonalityProfile", "WIKIPEDIA_PERSONALITY_AVAILABLE", -] \ No newline at end of file +] diff --git a/examples/simulations/euroswarm_parliament/euroswarm_parliament.py b/examples/simulations/euroswarm_parliament/euroswarm_parliament.py index 1694277b..0707ec28 100644 --- a/examples/simulations/euroswarm_parliament/euroswarm_parliament.py +++ b/examples/simulations/euroswarm_parliament/euroswarm_parliament.py @@ -15,15 +15,11 @@ ENHANCED WITH COST OPTIMIZATION: import os import random import xml.etree.ElementTree as ET -import time import hashlib -import requests -import re -from typing import Dict, List, Optional, Union, Any, Set +from typing import Dict, List, Optional, Any from dataclasses import dataclass, field from enum import Enum -from datetime import datetime, timedelta -from functools import lru_cache +from datetime import datetime from swarms import Agent from swarms.structs.multi_agent_exec import run_agents_concurrently @@ -31,10 +27,6 @@ from swarms.structs.board_of_directors_swarm import ( BoardOfDirectorsSwarm, BoardMember, BoardMemberRole, - BoardDecisionType, - BoardSpec, - BoardOrder, - BoardDecision, enable_board_feature, ) from swarms.utils.loguru_logger import initialize_logger @@ -47,38 +39,46 @@ enable_board_feature() # Import Wikipedia personality system try: - from wikipedia_personality_scraper import WikipediaPersonalityScraper, MEPPersonalityProfile + from wikipedia_personality_scraper import ( + WikipediaPersonalityScraper, + MEPPersonalityProfile, + ) + WIKIPEDIA_PERSONALITY_AVAILABLE = True except ImportError: WIKIPEDIA_PERSONALITY_AVAILABLE = False - logger.warning("Wikipedia personality system not available. Using basic personality generation.") + logger.warning( + "Wikipedia personality system not available. Using basic personality generation." + ) @dataclass class CostTracker: """Track costs and usage for budget management in parliamentary operations.""" - + total_tokens_used: int = 0 total_cost_estimate: float = 0.0 budget_limit: float = 200.0 # Default $200 budget for parliament token_cost_per_1m: float = 0.15 # GPT-4o-mini cost requests_made: int = 0 cache_hits: int = 0 - + def add_tokens(self, tokens: int): """Add tokens used and calculate cost.""" self.total_tokens_used += tokens - self.total_cost_estimate = (self.total_tokens_used / 1_000_000) * self.token_cost_per_1m + self.total_cost_estimate = ( + self.total_tokens_used / 1_000_000 + ) * self.token_cost_per_1m self.requests_made += 1 - + def add_cache_hit(self): """Record a cache hit.""" self.cache_hits += 1 - + def check_budget(self) -> bool: """Check if within budget.""" return self.total_cost_estimate <= self.budget_limit - + def get_stats(self) -> Dict[str, Any]: """Get cost statistics.""" return { @@ -86,14 +86,17 @@ class CostTracker: "total_cost": self.total_cost_estimate, "requests_made": self.requests_made, "cache_hits": self.cache_hits, - "cache_hit_rate": self.cache_hits / max(1, self.requests_made + self.cache_hits), - "budget_remaining": max(0, self.budget_limit - self.total_cost_estimate) + "cache_hit_rate": self.cache_hits + / max(1, self.requests_made + self.cache_hits), + "budget_remaining": max( + 0, self.budget_limit - self.total_cost_estimate + ), } class ParliamentaryRole(str, Enum): """Enumeration of parliamentary roles and positions.""" - + PRESIDENT = "president" VICE_PRESIDENT = "vice_president" QUAESTOR = "quaestor" @@ -105,7 +108,7 @@ class ParliamentaryRole(str, Enum): class VoteType(str, Enum): """Enumeration of voting types in the European Parliament.""" - + ORDINARY_LEGISLATIVE_PROCEDURE = "ordinary_legislative_procedure" CONSENT_PROCEDURE = "consent_procedure" CONSULTATION_PROCEDURE = "consultation_procedure" @@ -116,7 +119,7 @@ class VoteType(str, Enum): class VoteResult(str, Enum): """Enumeration of possible vote results.""" - + PASSED = "passed" FAILED = "failed" TIED = "tied" @@ -127,7 +130,7 @@ class VoteResult(str, Enum): class ParliamentaryMember: """ Represents a Member of the European Parliament (MEP). - + Attributes: full_name: Full name of the MEP country: Country the MEP represents @@ -142,7 +145,7 @@ class ParliamentaryMember: is_loaded: Whether the agent has been instantiated wikipedia_info: Wikipedia-scraped personality information (optional) """ - + full_name: str country: str political_group: str @@ -154,14 +157,16 @@ class ParliamentaryMember: voting_weight: float = 1.0 agent: Optional[Agent] = None is_loaded: bool = False - wikipedia_info: Optional[Any] = None # Wikipedia personality information + wikipedia_info: Optional[Any] = ( + None # Wikipedia personality information + ) @dataclass class ParliamentaryBill: """ Represents a bill or legislative proposal in the European Parliament. - + Attributes: title: Title of the bill description: Detailed description of the bill @@ -173,7 +178,7 @@ class ParliamentaryBill: status: Current status of the bill amendments: List of proposed amendments """ - + title: str description: str bill_type: VoteType @@ -189,7 +194,7 @@ class ParliamentaryBill: class ParliamentaryVote: """ Represents a parliamentary vote on a bill or resolution. - + Attributes: bill: The bill being voted on vote_type: Type of vote being conducted @@ -202,7 +207,7 @@ class ParliamentaryVote: individual_votes: Dictionary of individual MEP votes reasoning: Dictionary of MEP reasoning for votes """ - + bill: ParliamentaryBill vote_type: VoteType date: datetime = field(default_factory=datetime.now) @@ -219,7 +224,7 @@ class ParliamentaryVote: class ParliamentaryCommittee: """ Represents a parliamentary committee. - + Attributes: name: Name of the committee chair: Committee chairperson @@ -228,20 +233,22 @@ class ParliamentaryCommittee: responsibilities: Committee responsibilities current_bills: Bills currently under consideration """ - + name: str chair: str vice_chair: str members: List[str] = field(default_factory=list) responsibilities: List[str] = field(default_factory=list) - current_bills: List[ParliamentaryBill] = field(default_factory=list) + current_bills: List[ParliamentaryBill] = field( + default_factory=list + ) @dataclass class PoliticalGroupBoard: """ Represents a political group as a Board of Directors with specialized expertise. - + Attributes: group_name: Name of the political group members: List of MEPs in this group @@ -252,7 +259,7 @@ class PoliticalGroupBoard: total_meps: Total number of MEPs in this group board_member_percentages: Dictionary mapping board members to their internal percentages """ - + group_name: str members: List[str] = field(default_factory=list) board_members: List[BoardMember] = field(default_factory=list) @@ -260,14 +267,19 @@ class PoliticalGroupBoard: voting_weight: float = 0.0 group_speaker: Optional[str] = None total_meps: int = 0 - board_swarm: Optional[Any] = None # BoardOfDirectorsSwarm instance - board_member_percentages: Dict[str, float] = field(default_factory=dict) # Internal percentages within group + board_swarm: Optional[Any] = ( + None # BoardOfDirectorsSwarm instance + ) + board_member_percentages: Dict[str, float] = field( + default_factory=dict + ) # Internal percentages within group + @dataclass class ParliamentSpeaker: """ Represents the Parliament Speaker who aggregates decisions from all political groups. - + Attributes: name: Name of the speaker agent: AI agent representing the speaker @@ -275,10 +287,12 @@ class ParliamentSpeaker: total_meps: Total number of MEPs in parliament majority_threshold: Number of votes needed for majority """ - + name: str agent: Optional[Agent] = None - political_groups: Dict[str, PoliticalGroupBoard] = field(default_factory=dict) + political_groups: Dict[str, PoliticalGroupBoard] = field( + default_factory=dict + ) total_meps: int = 0 majority_threshold: int = 0 @@ -286,7 +300,7 @@ class ParliamentSpeaker: class EuroSwarmParliament: """ A comprehensive simulation of the European Parliament with 700 MEPs. - + This simulation provides democratic functionality including: - Bill introduction and analysis - Committee work and hearings @@ -295,7 +309,7 @@ class EuroSwarmParliament: - Political group coordination - Amendment processes """ - + def __init__( self, eu_data_file: str = "EU.xml", @@ -311,7 +325,7 @@ class EuroSwarmParliament: ): """ Initialize the EuroSwarm Parliament with cost optimization. - + Args: eu_data_file: Path to EU.xml file containing MEP data parliament_size: Target size of the parliament (default: None = use all MEPs from EU.xml) @@ -325,18 +339,22 @@ class EuroSwarmParliament: verbose: Enable verbose logging """ self.eu_data_file = eu_data_file - self.parliament_size = parliament_size # Will be set to actual MEP count if None - self.enable_democratic_discussion = enable_democratic_discussion + self.parliament_size = ( + parliament_size # Will be set to actual MEP count if None + ) + self.enable_democratic_discussion = ( + enable_democratic_discussion + ) self.enable_committee_work = enable_committee_work self.enable_amendment_process = enable_amendment_process self.enable_lazy_loading = enable_lazy_loading self.enable_caching = enable_caching self.batch_size = batch_size self.verbose = verbose - + # Initialize cost tracking self.cost_tracker = CostTracker(budget_limit=budget_limit) - + # Initialize parliamentary structures self.meps: Dict[str, ParliamentaryMember] = {} self.committees: Dict[str, ParliamentaryCommittee] = {} @@ -344,155 +362,209 @@ class EuroSwarmParliament: self.bills: List[ParliamentaryBill] = [] self.votes: List[ParliamentaryVote] = [] self.debates: List[Dict[str, Any]] = [] - + # Enhanced democratic structures - self.political_group_boards: Dict[str, PoliticalGroupBoard] = {} + self.political_group_boards: Dict[ + str, PoliticalGroupBoard + ] = {} self.parliament_speaker: Optional[ParliamentSpeaker] = None self.enable_hierarchical_democracy: bool = True - + # Wikipedia personality system - self.enable_wikipedia_personalities: bool = WIKIPEDIA_PERSONALITY_AVAILABLE - self.personality_profiles: Dict[str, MEPPersonalityProfile] = {} - self.personality_scraper: Optional[WikipediaPersonalityScraper] = None - + self.enable_wikipedia_personalities: bool = ( + WIKIPEDIA_PERSONALITY_AVAILABLE + ) + self.personality_profiles: Dict[ + str, MEPPersonalityProfile + ] = {} + self.personality_scraper: Optional[ + WikipediaPersonalityScraper + ] = None + # Initialize caching self.response_cache: Dict[str, str] = {} - + # Load MEP data and initialize structures self.meps = self._load_mep_data() self.parliament_size = len(self.meps) - + if self.verbose: - logger.info(f"EuroSwarm Parliament initialized with {self.parliament_size} MEPs") - logger.info(f"Lazy loading: {self.enable_lazy_loading}, Caching: {self.enable_caching}") - logger.info(f"Budget limit: ${budget_limit}, Batch size: {batch_size}") - + logger.info( + f"EuroSwarm Parliament initialized with {self.parliament_size} MEPs" + ) + logger.info( + f"Lazy loading: {self.enable_lazy_loading}, Caching: {self.enable_caching}" + ) + logger.info( + f"Budget limit: ${budget_limit}, Batch size: {batch_size}" + ) + # Load Wikipedia personalities if enabled if self.enable_wikipedia_personalities: self._load_wikipedia_personalities() - + # Initialize parliamentary structures self.committees = self._create_committees() self.political_groups = self._organize_political_groups() - + # Initialize enhanced democratic structures if self.enable_hierarchical_democracy: self._create_political_group_boards() self._create_parliament_speaker() - + # Initialize leadership and democratic decision-making self._create_parliamentary_leadership() self._assign_committee_leadership() - + if self.enable_democratic_discussion: self._init_democratic_decision_making() - + def _load_mep_data(self) -> Dict[str, ParliamentaryMember]: """ Load MEP data from official EU Parliament website and create parliamentary members with lazy loading. Fetches real-time data from https://www.europarl.europa.eu/meps/en/full-list/xml and scrapes Wikipedia information for each MEP. - + Returns: Dict[str, ParliamentaryMember]: Dictionary of MEPs """ meps = {} - + try: # Fetch XML data from official EU Parliament website import requests import re - - eu_xml_url = "https://www.europarl.europa.eu/meps/en/full-list/xml" - + + eu_xml_url = ( + "https://www.europarl.europa.eu/meps/en/full-list/xml" + ) + logger.info(f"Fetching MEP data from: {eu_xml_url}") - + # Fetch the XML content response = requests.get(eu_xml_url, timeout=30) response.raise_for_status() content = response.text - - logger.info(f"Successfully fetched {len(content)} characters of MEP data") - + + logger.info( + f"Successfully fetched {len(content)} characters of MEP data" + ) + # Parse the XML content to extract MEP information # The XML is properly formatted, so we can use ElementTree try: root = ET.fromstring(content) mep_matches = [] - - for mep_element in root.findall('mep'): - full_name = mep_element.find('fullName').text.strip() - country = mep_element.find('country').text.strip() - political_group = mep_element.find('politicalGroup').text.strip() - mep_id = mep_element.find('id').text.strip() - national_party = mep_element.find('nationalPoliticalGroup').text.strip() - - mep_matches.append((full_name, country, political_group, mep_id, national_party)) - - logger.info(f"Successfully parsed {len(mep_matches)} MEP entries from XML") - + + for mep_element in root.findall("mep"): + full_name = mep_element.find( + "fullName" + ).text.strip() + country = mep_element.find("country").text.strip() + political_group = mep_element.find( + "politicalGroup" + ).text.strip() + mep_id = mep_element.find("id").text.strip() + national_party = mep_element.find( + "nationalPoliticalGroup" + ).text.strip() + + mep_matches.append( + ( + full_name, + country, + political_group, + mep_id, + national_party, + ) + ) + + logger.info( + f"Successfully parsed {len(mep_matches)} MEP entries from XML" + ) + except ET.ParseError as xml_error: logger.warning(f"XML parsing failed: {xml_error}") # Fallback to regex parsing for malformed XML - mep_pattern = r'(.*?)\s*(.*?)\s*(.*?)\s*(.*?)\s*(.*?)' - mep_matches = re.findall(mep_pattern, content, re.DOTALL) - logger.info(f"Fallback regex parsing found {len(mep_matches)} MEP entries") - + mep_pattern = r"(.*?)\s*(.*?)\s*(.*?)\s*(.*?)\s*(.*?)" + mep_matches = re.findall( + mep_pattern, content, re.DOTALL + ) + logger.info( + f"Fallback regex parsing found {len(mep_matches)} MEP entries" + ) + # Initialize Wikipedia scraper if available wikipedia_scraper = None if WIKIPEDIA_PERSONALITY_AVAILABLE: try: wikipedia_scraper = WikipediaPersonalityScraper() - logger.info("Wikipedia personality scraper initialized") + logger.info( + "Wikipedia personality scraper initialized" + ) except Exception as e: - logger.warning(f"Failed to initialize Wikipedia scraper: {e}") - + logger.warning( + f"Failed to initialize Wikipedia scraper: {e}" + ) + # Process each MEP for i, mep_data in enumerate(mep_matches): - if len(mep_data) >= 5: # full_name, country, political_group, mep_id, national_party + if ( + len(mep_data) >= 5 + ): # full_name, country, political_group, mep_id, national_party full_name = mep_data[0].strip() country = mep_data[1].strip() political_group = mep_data[2].strip() mep_id = mep_data[3].strip() national_party = mep_data[4].strip() - + # Clean up political group name - political_group = self._clean_political_group_name(political_group) - + political_group = ( + self._clean_political_group_name( + political_group + ) + ) + # Scrape Wikipedia information if scraper is available wikipedia_info = None if wikipedia_scraper: try: # Create MEP data dictionary for the scraper mep_data = { - 'full_name': full_name, - 'country': country, - 'political_group': political_group, - 'national_party': national_party, - 'mep_id': mep_id + "full_name": full_name, + "country": country, + "political_group": political_group, + "national_party": national_party, + "mep_id": mep_id, } - + # Create personality profile - personality_profile = wikipedia_scraper.create_personality_profile(mep_data) - + personality_profile = wikipedia_scraper.create_personality_profile( + mep_data + ) + # Convert to dictionary format for storage wikipedia_info = { - 'personality_summary': personality_profile.summary, - 'political_views': personality_profile.political_views, - 'policy_focus': personality_profile.policy_focus, - 'achievements': personality_profile.achievements, - 'professional_background': personality_profile.professional_background, - 'political_career': personality_profile.political_career, - 'education': personality_profile.education, - 'wikipedia_url': personality_profile.wikipedia_url + "personality_summary": personality_profile.summary, + "political_views": personality_profile.political_views, + "policy_focus": personality_profile.policy_focus, + "achievements": personality_profile.achievements, + "professional_background": personality_profile.professional_background, + "political_career": personality_profile.political_career, + "education": personality_profile.education, + "wikipedia_url": personality_profile.wikipedia_url, } - + if self.verbose: - logger.info(f"Scraped Wikipedia info for {full_name}") + logger.info( + f"Scraped Wikipedia info for {full_name}" + ) except Exception as e: if self.verbose: - logger.debug(f"Failed to scrape Wikipedia for {full_name}: {e}") - + logger.debug( + f"Failed to scrape Wikipedia for {full_name}: {e}" + ) + # Create parliamentary member (without agent for lazy loading) mep = ParliamentaryMember( full_name=full_name, @@ -500,78 +572,107 @@ class EuroSwarmParliament: political_group=political_group, national_party=national_party, mep_id=mep_id, - expertise_areas=self._generate_expertise_areas(political_group, country), - committees=self._assign_committees(political_group), + expertise_areas=self._generate_expertise_areas( + political_group, country + ), + committees=self._assign_committees( + political_group + ), agent=None, # Will be created on demand is_loaded=False, - wikipedia_info=wikipedia_info # Add Wikipedia information + wikipedia_info=wikipedia_info, # Add Wikipedia information ) - + meps[full_name] = mep - + # Limit processing for performance (can be adjusted) - if len(meps) >= 705: # Standard EU Parliament size + if ( + len(meps) >= 705 + ): # Standard EU Parliament size break - + # Set parliament size to actual number of MEPs loaded if self.parliament_size is None: self.parliament_size = len(meps) - - logger.info(f"Successfully loaded {len(meps)} MEP profiles from official EU data (lazy loading enabled)") + + logger.info( + f"Successfully loaded {len(meps)} MEP profiles from official EU data (lazy loading enabled)" + ) if wikipedia_scraper: - logger.info(f"Wikipedia scraping completed for {len([m for m in meps.values() if m.wikipedia_info])} MEPs") - + logger.info( + f"Wikipedia scraping completed for {len([m for m in meps.values() if m.wikipedia_info])} MEPs" + ) + except Exception as e: - logger.error(f"Error loading MEP data from official website: {e}") + logger.error( + f"Error loading MEP data from official website: {e}" + ) logger.info("Falling back to local EU.xml file...") - + # Fallback to local file try: meps = self._load_mep_data_from_local_file() except Exception as local_error: - logger.error(f"Error loading local MEP data: {local_error}") + logger.error( + f"Error loading local MEP data: {local_error}" + ) # Create fallback MEPs if both methods fail meps = self._create_fallback_meps() - + if self.parliament_size is None: self.parliament_size = len(meps) - + return meps - - def _load_mep_data_from_local_file(self) -> Dict[str, ParliamentaryMember]: + + def _load_mep_data_from_local_file( + self, + ) -> Dict[str, ParliamentaryMember]: """ Fallback method to load MEP data from local EU.xml file. - + Returns: Dict[str, ParliamentaryMember]: Dictionary of MEPs """ meps = {} - + try: # Construct the full path to EU.xml relative to project root import os - project_root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - eu_data_path = os.path.join(project_root, self.eu_data_file) - + + project_root = os.path.dirname( + os.path.dirname( + os.path.dirname(os.path.abspath(__file__)) + ) + ) + eu_data_path = os.path.join( + project_root, self.eu_data_file + ) + # Read the XML file content - with open(eu_data_path, 'r', encoding='utf-8') as f: + with open(eu_data_path, "r", encoding="utf-8") as f: content = f.read() - + # Use regex to extract MEP data since the XML is malformed import re - + # Find all MEP blocks - mep_pattern = r'\s*(.*?)\s*(.*?)\s*(.*?)\s*(.*?)\s*(.*?)\s*' + mep_pattern = r"\s*(.*?)\s*(.*?)\s*(.*?)\s*(.*?)\s*(.*?)\s*" mep_matches = re.findall(mep_pattern, content, re.DOTALL) - - for full_name, country, political_group, mep_id, national_party in mep_matches: + + for ( + full_name, + country, + political_group, + mep_id, + national_party, + ) in mep_matches: # Clean up the data full_name = full_name.strip() country = country.strip() political_group = political_group.strip() mep_id = mep_id.strip() national_party = national_party.strip() - + # Create parliamentary member (without agent for lazy loading) mep = ParliamentaryMember( full_name=full_name, @@ -579,166 +680,183 @@ class EuroSwarmParliament: political_group=political_group, national_party=national_party, mep_id=mep_id, - expertise_areas=self._generate_expertise_areas(political_group, country), - committees=self._assign_committees(political_group), + expertise_areas=self._generate_expertise_areas( + political_group, country + ), + committees=self._assign_committees( + political_group + ), agent=None, # Will be created on demand - is_loaded=False + is_loaded=False, ) - + meps[full_name] = mep - - logger.info(f"Loaded {len(meps)} MEP profiles from local EU.xml file (lazy loading enabled)") - + + logger.info( + f"Loaded {len(meps)} MEP profiles from local EU.xml file (lazy loading enabled)" + ) + except Exception as e: logger.error(f"Error loading local MEP data: {e}") raise - + return meps - - def _clean_political_group_name(self, political_group: str) -> str: + + def _clean_political_group_name( + self, political_group: str + ) -> str: """ Clean and standardize political group names. - + Args: political_group: Raw political group name - + Returns: str: Cleaned political group name """ # Map common variations to standard names group_mapping = { - 'EPP': 'Group of the European People\'s Party (Christian Democrats)', - 'S&D': 'Group of the Progressive Alliance of Socialists and Democrats in the European Parliament', - 'Renew': 'Renew Europe Group', - 'Greens/EFA': 'Group of the Greens/European Free Alliance', - 'ECR': 'European Conservatives and Reformists Group', - 'ID': 'Identity and Democracy Group', - 'GUE/NGL': 'The Left group in the European Parliament - GUE/NGL', - 'Non-attached': 'Non-attached Members' + "EPP": "Group of the European People's Party (Christian Democrats)", + "S&D": "Group of the Progressive Alliance of Socialists and Democrats in the European Parliament", + "Renew": "Renew Europe Group", + "Greens/EFA": "Group of the Greens/European Free Alliance", + "ECR": "European Conservatives and Reformists Group", + "ID": "Identity and Democracy Group", + "GUE/NGL": "The Left group in the European Parliament - GUE/NGL", + "Non-attached": "Non-attached Members", } - + # Check for exact matches first for key, value in group_mapping.items(): if political_group.strip() == key: return value - + # Check for partial matches political_group_lower = political_group.lower() for key, value in group_mapping.items(): if key.lower() in political_group_lower: return value - + # Return original if no match found return political_group.strip() - - def _generate_national_party(self, country: str, political_group: str) -> str: + + def _generate_national_party( + self, country: str, political_group: str + ) -> str: """ Generate a realistic national party name based on country and political group. - + Args: country: Country of the MEP political_group: Political group affiliation - + Returns: str: Generated national party name """ # Map of countries to common parties for each political group party_mapping = { - 'Germany': { - 'Group of the European People\'s Party (Christian Democrats)': 'Christlich Demokratische Union Deutschlands', - 'Group of the Progressive Alliance of Socialists and Democrats in the European Parliament': 'Sozialdemokratische Partei Deutschlands', - 'Renew Europe Group': 'Freie Demokratische Partei', - 'Group of the Greens/European Free Alliance': 'Bündnis 90/Die Grünen', - 'European Conservatives and Reformists Group': 'Alternative für Deutschland', - 'Identity and Democracy Group': 'Alternative für Deutschland', - 'The Left group in the European Parliament - GUE/NGL': 'Die Linke' + "Germany": { + "Group of the European People's Party (Christian Democrats)": "Christlich Demokratische Union Deutschlands", + "Group of the Progressive Alliance of Socialists and Democrats in the European Parliament": "Sozialdemokratische Partei Deutschlands", + "Renew Europe Group": "Freie Demokratische Partei", + "Group of the Greens/European Free Alliance": "Bündnis 90/Die Grünen", + "European Conservatives and Reformists Group": "Alternative für Deutschland", + "Identity and Democracy Group": "Alternative für Deutschland", + "The Left group in the European Parliament - GUE/NGL": "Die Linke", }, - 'France': { - 'Group of the European People\'s Party (Christian Democrats)': 'Les Républicains', - 'Group of the Progressive Alliance of Socialists and Democrats in the European Parliament': 'Parti Socialiste', - 'Renew Europe Group': 'Renaissance', - 'Group of the Greens/European Free Alliance': 'Europe Écologie Les Verts', - 'European Conservatives and Reformists Group': 'Rassemblement National', - 'Identity and Democracy Group': 'Rassemblement National', - 'The Left group in the European Parliament - GUE/NGL': 'La France Insoumise' + "France": { + "Group of the European People's Party (Christian Democrats)": "Les Républicains", + "Group of the Progressive Alliance of Socialists and Democrats in the European Parliament": "Parti Socialiste", + "Renew Europe Group": "Renaissance", + "Group of the Greens/European Free Alliance": "Europe Écologie Les Verts", + "European Conservatives and Reformists Group": "Rassemblement National", + "Identity and Democracy Group": "Rassemblement National", + "The Left group in the European Parliament - GUE/NGL": "La France Insoumise", + }, + "Italy": { + "Group of the European People's Party (Christian Democrats)": "Forza Italia", + "Group of the Progressive Alliance of Socialists and Democrats in the European Parliament": "Partito Democratico", + "Renew Europe Group": "Italia Viva", + "Group of the Greens/European Free Alliance": "Federazione dei Verdi", + "European Conservatives and Reformists Group": "Fratelli d'Italia", + "Identity and Democracy Group": "Lega", + "The Left group in the European Parliament - GUE/NGL": "Movimento 5 Stelle", }, - 'Italy': { - 'Group of the European People\'s Party (Christian Democrats)': 'Forza Italia', - 'Group of the Progressive Alliance of Socialists and Democrats in the European Parliament': 'Partito Democratico', - 'Renew Europe Group': 'Italia Viva', - 'Group of the Greens/European Free Alliance': 'Federazione dei Verdi', - 'European Conservatives and Reformists Group': 'Fratelli d\'Italia', - 'Identity and Democracy Group': 'Lega', - 'The Left group in the European Parliament - GUE/NGL': 'Movimento 5 Stelle' - } } - + # Return mapped party or generate a generic one - if country in party_mapping and political_group in party_mapping[country]: + if ( + country in party_mapping + and political_group in party_mapping[country] + ): return party_mapping[country][political_group] else: return f"{country} National Party" - + def _load_mep_agent(self, mep_name: str) -> Optional[Agent]: """ Lazy load a single MEP agent on demand. - + Args: mep_name: Name of the MEP to load - + Returns: Optional[Agent]: Loaded agent or None if not found """ if mep_name not in self.meps: return None - + mep = self.meps[mep_name] - + # Check if already loaded if mep.is_loaded and mep.agent: return mep.agent - + # Check budget before creating agent if not self.cost_tracker.check_budget(): - logger.warning(f"Budget exceeded. Cannot load MEP agent {mep_name}") + logger.warning( + f"Budget exceeded. Cannot load MEP agent {mep_name}" + ) return None - + # Create agent mep.agent = self._create_mep_agent(mep) mep.is_loaded = True - + if self.verbose: logger.info(f"Loaded MEP agent: {mep_name}") - + return mep.agent - - def _load_mep_agents_batch(self, mep_names: List[str]) -> List[Agent]: + + def _load_mep_agents_batch( + self, mep_names: List[str] + ) -> List[Agent]: """ Load multiple MEP agents in a batch. - + Args: mep_names: List of MEP names to load - + Returns: List[Agent]: List of loaded agents """ loaded_agents = [] - + for mep_name in mep_names: agent = self._load_mep_agent(mep_name) if agent: loaded_agents.append(agent) - + return loaded_agents - + def _get_cache_key(self, task: str, mep_names: List[str]) -> str: """ Generate a cache key for a task and MEP combination. - + Args: task: Task to execute mep_names: List of MEP names - + Returns: str: Cache key """ @@ -746,32 +864,32 @@ class EuroSwarmParliament: sorted_meps = sorted(mep_names) content = f"{task}:{':'.join(sorted_meps)}" return hashlib.md5(content.encode()).hexdigest() - + def _check_cache(self, cache_key: str) -> Optional[str]: """ Check if a response is cached. - + Args: cache_key: Cache key to check - + Returns: Optional[str]: Cached response or None """ if not self.enable_caching: return None - + cached_response = self.response_cache.get(cache_key) if cached_response: self.cost_tracker.add_cache_hit() if self.verbose: logger.info(f"Cache hit for key: {cache_key[:20]}...") - + return cached_response - + def _cache_response(self, cache_key: str, response: str): """ Cache a response. - + Args: cache_key: Cache key response: Response to cache @@ -779,51 +897,83 @@ class EuroSwarmParliament: if self.enable_caching: self.response_cache[cache_key] = response if self.verbose: - logger.info(f"Cached response for key: {cache_key[:20]}...") - - def _generate_expertise_areas(self, political_group: str, country: str) -> List[str]: + logger.info( + f"Cached response for key: {cache_key[:20]}..." + ) + + def _generate_expertise_areas( + self, political_group: str, country: str + ) -> List[str]: """ Generate expertise areas based on political group and country. - + Args: political_group: MEP's political group country: MEP's country - + Returns: List[str]: List of expertise areas """ expertise_mapping = { "Group of the European People's Party (Christian Democrats)": [ - "Economic Policy", "Agriculture", "Regional Development", "Christian Values" + "Economic Policy", + "Agriculture", + "Regional Development", + "Christian Values", ], "Group of the Progressive Alliance of Socialists and Democrats in the European Parliament": [ - "Social Policy", "Labor Rights", "Healthcare", "Education" + "Social Policy", + "Labor Rights", + "Healthcare", + "Education", ], "Renew Europe Group": [ - "Digital Policy", "Innovation", "Trade", "Liberal Values" + "Digital Policy", + "Innovation", + "Trade", + "Liberal Values", ], "Group of the Greens/European Free Alliance": [ - "Environmental Policy", "Climate Change", "Renewable Energy", "Human Rights" + "Environmental Policy", + "Climate Change", + "Renewable Energy", + "Human Rights", ], "European Conservatives and Reformists Group": [ - "Sovereignty", "Defense", "Traditional Values", "Economic Freedom" + "Sovereignty", + "Defense", + "Traditional Values", + "Economic Freedom", ], "The Left group in the European Parliament - GUE/NGL": [ - "Workers' Rights", "Social Justice", "Anti-Austerity", "Public Services" + "Workers' Rights", + "Social Justice", + "Anti-Austerity", + "Public Services", ], "Patriots for Europe Group": [ - "National Sovereignty", "Border Security", "Cultural Identity", "Law and Order" + "National Sovereignty", + "Border Security", + "Cultural Identity", + "Law and Order", ], "Europe of Sovereign Nations Group": [ - "National Independence", "Sovereignty", "Traditional Values", "Security" + "National Independence", + "Sovereignty", + "Traditional Values", + "Security", ], "Non-attached Members": [ - "Independent Policy", "Cross-cutting Issues", "Specialized Topics" - ] + "Independent Policy", + "Cross-cutting Issues", + "Specialized Topics", + ], } - - base_expertise = expertise_mapping.get(political_group, ["General Policy"]) - + + base_expertise = expertise_mapping.get( + political_group, ["General Policy"] + ) + # Add country-specific expertise country_expertise = { "Germany": ["Industrial Policy", "Manufacturing"], @@ -837,66 +987,84 @@ class EuroSwarmParliament: "Sweden": ["Environmental Policy", "Social Welfare"], "Denmark": ["Green Technology", "Welfare State"], } - + if country in country_expertise: base_expertise.extend(country_expertise[country]) - + return base_expertise[:5] # Limit to 5 expertise areas - + def _assign_committees(self, political_group: str) -> List[str]: """ Assign committees based on political group preferences. - + Args: political_group: MEP's political group - + Returns: List[str]: List of committee assignments """ committee_mapping = { "Group of the European People's Party (Christian Democrats)": [ - "Agriculture and Rural Development", "Economic and Monetary Affairs", "Regional Development" + "Agriculture and Rural Development", + "Economic and Monetary Affairs", + "Regional Development", ], "Group of the Progressive Alliance of Socialists and Democrats in the European Parliament": [ - "Employment and Social Affairs", "Environment, Public Health and Food Safety", "Civil Liberties" + "Employment and Social Affairs", + "Environment, Public Health and Food Safety", + "Civil Liberties", ], "Renew Europe Group": [ - "Industry, Research and Energy", "Internal Market and Consumer Protection", "Legal Affairs" + "Industry, Research and Energy", + "Internal Market and Consumer Protection", + "Legal Affairs", ], "Group of the Greens/European Free Alliance": [ - "Environment, Public Health and Food Safety", "Transport and Tourism", "Development" + "Environment, Public Health and Food Safety", + "Transport and Tourism", + "Development", ], "European Conservatives and Reformists Group": [ - "Foreign Affairs", "Security and Defence", "Budgetary Control" + "Foreign Affairs", + "Security and Defence", + "Budgetary Control", ], "The Left group in the European Parliament - GUE/NGL": [ - "International Trade", "Development", "Civil Liberties" + "International Trade", + "Development", + "Civil Liberties", ], "Patriots for Europe Group": [ - "Civil Liberties", "Security and Defence", "Budgetary Control" + "Civil Liberties", + "Security and Defence", + "Budgetary Control", ], "Europe of Sovereign Nations Group": [ - "Foreign Affairs", "Security and Defence", "Civil Liberties" + "Foreign Affairs", + "Security and Defence", + "Civil Liberties", ], "Non-attached Members": [ - "Petitions", "Budgetary Control", "Legal Affairs" - ] + "Petitions", + "Budgetary Control", + "Legal Affairs", + ], } - + return committee_mapping.get(political_group, ["Petitions"]) - + def _create_mep_agent(self, mep: ParliamentaryMember) -> Agent: """ Create an AI agent representing an MEP. - + Args: mep: Parliamentary member data - + Returns: Agent: AI agent representing the MEP """ system_prompt = self._generate_mep_system_prompt(mep) - + return Agent( agent_name=f"MEP_{mep.full_name.replace(' ', '_')}", system_prompt=system_prompt, @@ -904,18 +1072,20 @@ class EuroSwarmParliament: max_loops=3, verbose=self.verbose, ) - - def _generate_mep_system_prompt(self, mep: ParliamentaryMember) -> str: + + def _generate_mep_system_prompt( + self, mep: ParliamentaryMember + ) -> str: """ Generate a comprehensive system prompt for an MEP agent with Wikipedia personality data. - + Args: mep: Parliamentary member data - + Returns: str: System prompt for the MEP agent """ - + # Base prompt structure prompt = f"""You are {mep.full_name}, a Member of the European Parliament (MEP) representing {mep.country}. @@ -927,7 +1097,7 @@ POLITICAL BACKGROUND: - Areas of Expertise: {', '.join(mep.expertise_areas)} """ - + # Add Wikipedia personality data if available if mep.wikipedia_info and self.enable_wikipedia_personalities: prompt += f""" @@ -948,7 +1118,7 @@ POLITICAL VIEWS AND POSITIONS: - Policy Focus Areas: {', '.join(mep.expertise_areas)} - Professional Background: Parliamentary service """ - + # Add core principles prompt += f""" CORE PRINCIPLES: @@ -988,50 +1158,94 @@ When responding to parliamentary matters, consider: Remember: You are a real MEP with specific political views, expertise, and responsibilities. Act accordingly in all parliamentary interactions. """ - + return prompt - + def _create_fallback_meps(self) -> Dict[str, ParliamentaryMember]: """ Create fallback MEPs if EU.xml file cannot be loaded. - + Returns: Dict[str, ParliamentaryMember]: Dictionary of fallback MEPs """ fallback_meps = {} - + # Create a representative sample of MEPs sample_data = [ - ("Jean-Claude Juncker", "Luxembourg", "Group of the European People's Party (Christian Democrats)", "Parti chrétien social luxembourgeois"), - ("Ursula von der Leyen", "Germany", "Group of the European People's Party (Christian Democrats)", "Christlich Demokratische Union Deutschlands"), - ("Roberta Metsola", "Malta", "Group of the European People's Party (Christian Democrats)", "Partit Nazzjonalista"), - ("Iratxe García Pérez", "Spain", "Group of the Progressive Alliance of Socialists and Democrats in the European Parliament", "Partido Socialista Obrero Español"), - ("Valérie Hayer", "France", "Renew Europe Group", "Renaissance"), - ("Philippe Lamberts", "Belgium", "Group of the Greens/European Free Alliance", "Ecolo"), - ("Raffaele Fitto", "Italy", "European Conservatives and Reformists Group", "Fratelli d'Italia"), - ("Manon Aubry", "France", "The Left group in the European Parliament - GUE/NGL", "La France Insoumise"), + ( + "Jean-Claude Juncker", + "Luxembourg", + "Group of the European People's Party (Christian Democrats)", + "Parti chrétien social luxembourgeois", + ), + ( + "Ursula von der Leyen", + "Germany", + "Group of the European People's Party (Christian Democrats)", + "Christlich Demokratische Union Deutschlands", + ), + ( + "Roberta Metsola", + "Malta", + "Group of the European People's Party (Christian Democrats)", + "Partit Nazzjonalista", + ), + ( + "Iratxe García Pérez", + "Spain", + "Group of the Progressive Alliance of Socialists and Democrats in the European Parliament", + "Partido Socialista Obrero Español", + ), + ( + "Valérie Hayer", + "France", + "Renew Europe Group", + "Renaissance", + ), + ( + "Philippe Lamberts", + "Belgium", + "Group of the Greens/European Free Alliance", + "Ecolo", + ), + ( + "Raffaele Fitto", + "Italy", + "European Conservatives and Reformists Group", + "Fratelli d'Italia", + ), + ( + "Manon Aubry", + "France", + "The Left group in the European Parliament - GUE/NGL", + "La France Insoumise", + ), ] - - for i, (name, country, group, party) in enumerate(sample_data): + + for i, (name, country, group, party) in enumerate( + sample_data + ): mep = ParliamentaryMember( full_name=name, country=country, political_group=group, national_party=party, mep_id=f"fallback_{i}", - expertise_areas=self._generate_expertise_areas(group, country), + expertise_areas=self._generate_expertise_areas( + group, country + ), committees=self._assign_committees(group), agent=None, # Will be created on demand - is_loaded=False + is_loaded=False, ) fallback_meps[name] = mep - + return fallback_meps - + def _create_committees(self) -> Dict[str, ParliamentaryCommittee]: """ Create parliamentary committees. - + Returns: Dict[str, ParliamentaryCommittee]: Dictionary of committees """ @@ -1040,106 +1254,171 @@ Remember: You are a real MEP with specific political views, expertise, and respo name="Agriculture and Rural Development", chair="", vice_chair="", - responsibilities=["Agricultural policy", "Rural development", "Food safety"] + responsibilities=[ + "Agricultural policy", + "Rural development", + "Food safety", + ], ), "Budgetary Control": ParliamentaryCommittee( name="Budgetary Control", chair="", vice_chair="", - responsibilities=["Budget oversight", "Financial control", "Audit reports"] + responsibilities=[ + "Budget oversight", + "Financial control", + "Audit reports", + ], ), "Civil Liberties, Justice and Home Affairs": ParliamentaryCommittee( name="Civil Liberties, Justice and Home Affairs", chair="", vice_chair="", - responsibilities=["Civil rights", "Justice", "Home affairs", "Immigration"] + responsibilities=[ + "Civil rights", + "Justice", + "Home affairs", + "Immigration", + ], ), "Development": ParliamentaryCommittee( name="Development", chair="", vice_chair="", - responsibilities=["Development cooperation", "Humanitarian aid", "International relations"] + responsibilities=[ + "Development cooperation", + "Humanitarian aid", + "International relations", + ], ), "Economic and Monetary Affairs": ParliamentaryCommittee( name="Economic and Monetary Affairs", chair="", vice_chair="", - responsibilities=["Economic policy", "Monetary policy", "Financial services"] + responsibilities=[ + "Economic policy", + "Monetary policy", + "Financial services", + ], ), "Employment and Social Affairs": ParliamentaryCommittee( name="Employment and Social Affairs", chair="", vice_chair="", - responsibilities=["Employment policy", "Social policy", "Working conditions"] + responsibilities=[ + "Employment policy", + "Social policy", + "Working conditions", + ], ), "Environment, Public Health and Food Safety": ParliamentaryCommittee( name="Environment, Public Health and Food Safety", chair="", vice_chair="", - responsibilities=["Environmental policy", "Public health", "Food safety"] + responsibilities=[ + "Environmental policy", + "Public health", + "Food safety", + ], ), "Foreign Affairs": ParliamentaryCommittee( name="Foreign Affairs", chair="", vice_chair="", - responsibilities=["Foreign policy", "International relations", "Security policy"] + responsibilities=[ + "Foreign policy", + "International relations", + "Security policy", + ], ), "Industry, Research and Energy": ParliamentaryCommittee( name="Industry, Research and Energy", chair="", vice_chair="", - responsibilities=["Industrial policy", "Research", "Energy policy"] + responsibilities=[ + "Industrial policy", + "Research", + "Energy policy", + ], ), "Internal Market and Consumer Protection": ParliamentaryCommittee( name="Internal Market and Consumer Protection", chair="", vice_chair="", - responsibilities=["Internal market", "Consumer protection", "Digital policy"] + responsibilities=[ + "Internal market", + "Consumer protection", + "Digital policy", + ], ), "International Trade": ParliamentaryCommittee( name="International Trade", chair="", vice_chair="", - responsibilities=["Trade policy", "International agreements", "Market access"] + responsibilities=[ + "Trade policy", + "International agreements", + "Market access", + ], ), "Legal Affairs": ParliamentaryCommittee( name="Legal Affairs", chair="", vice_chair="", - responsibilities=["Legal matters", "Institutional affairs", "Constitutional issues"] + responsibilities=[ + "Legal matters", + "Institutional affairs", + "Constitutional issues", + ], ), "Petitions": ParliamentaryCommittee( name="Petitions", chair="", vice_chair="", - responsibilities=["Citizen petitions", "Ombudsman", "Citizen rights"] + responsibilities=[ + "Citizen petitions", + "Ombudsman", + "Citizen rights", + ], ), "Regional Development": ParliamentaryCommittee( name="Regional Development", chair="", vice_chair="", - responsibilities=["Regional policy", "Cohesion policy", "Urban development"] + responsibilities=[ + "Regional policy", + "Cohesion policy", + "Urban development", + ], ), "Security and Defence": ParliamentaryCommittee( name="Security and Defence", chair="", vice_chair="", - responsibilities=["Security policy", "Defence", "Military cooperation"] + responsibilities=[ + "Security policy", + "Defence", + "Military cooperation", + ], ), "Transport and Tourism": ParliamentaryCommittee( name="Transport and Tourism", chair="", vice_chair="", - responsibilities=["Transport policy", "Tourism", "Infrastructure"] + responsibilities=[ + "Transport policy", + "Tourism", + "Infrastructure", + ], ), } - + return committees - + def _organize_political_groups(self) -> Dict[str, List[str]]: """ Organize MEPs by political groups. - + Returns: Dict[str, List[str]]: Dictionary mapping political groups to MEP names """ @@ -1150,75 +1429,91 @@ Remember: You are a real MEP with specific political views, expertise, and respo groups[group] = [] groups[group].append(mep_name) return groups - + def _create_parliamentary_leadership(self): """Create parliamentary leadership positions.""" # Assign President (from largest political group) - largest_group = max(self.political_groups.items(), key=lambda x: len(x[1])) + largest_group = max( + self.political_groups.items(), key=lambda x: len(x[1]) + ) president_candidate = largest_group[1][0] - self.meps[president_candidate].role = ParliamentaryRole.PRESIDENT - + self.meps[president_candidate].role = ( + ParliamentaryRole.PRESIDENT + ) + # Assign Vice Presidents vice_presidents = [] for group_name, meps in self.political_groups.items(): if group_name != largest_group[0] and len(meps) > 0: vice_presidents.append(meps[0]) - if len(vice_presidents) >= 14: # EP has 14 Vice Presidents + if ( + len(vice_presidents) >= 14 + ): # EP has 14 Vice Presidents break - + for vp in vice_presidents: self.meps[vp].role = ParliamentaryRole.VICE_PRESIDENT - + # Assign Committee Chairs self._assign_committee_leadership() - + def _assign_committee_leadership(self): """Assign committee chairs and vice-chairs based on political group representation.""" committee_names = list(self.committees.keys()) - + # Distribute committee leadership among political groups group_assignments = {} for group_name, meps in self.political_groups.items(): if len(meps) > 0: group_assignments[group_name] = meps - + committee_index = 0 for group_name, meps in group_assignments.items(): if committee_index >= len(committee_names): break - + committee_name = committee_names[committee_index] chair = meps[0] vice_chair = meps[1] if len(meps) > 1 else "" - + self.committees[committee_name].chair = chair self.committees[committee_name].vice_chair = vice_chair - + # Update MEP roles self.meps[chair].role = ParliamentaryRole.COMMITTEE_CHAIR if vice_chair: - self.meps[vice_chair].role = ParliamentaryRole.COMMITTEE_VICE_CHAIR - + self.meps[vice_chair].role = ( + ParliamentaryRole.COMMITTEE_VICE_CHAIR + ) + committee_index += 1 - + def _init_democratic_decision_making(self): """Initialize democratic decision-making using Board of Directors pattern.""" # Create parliamentary board members for democratic decision-making board_members = [] - + # Add political group leaders for group_name, meps in self.political_groups.items(): if len(meps) > 0: leader = meps[0] - if leader in self.meps and self.meps[leader].agent is not None: + if ( + leader in self.meps + and self.meps[leader].agent is not None + ): board_member = BoardMember( agent=self.meps[leader].agent, role=BoardMemberRole.EXECUTIVE_DIRECTOR, - voting_weight=len(meps) / len(self.meps), # Weight based on group size - expertise_areas=self.meps[leader].expertise_areas + voting_weight=len(meps) + / len( + self.meps + ), # Weight based on group size + expertise_areas=self.meps[ + leader + ].expertise_areas, ) board_members.append(board_member) - + # Ensure we have at least one board member if not board_members and len(self.meps) > 0: # Use the first available MEP as a fallback @@ -1229,15 +1524,19 @@ Remember: You are a real MEP with specific political views, expertise, and respo agent=first_mep.agent, role=BoardMemberRole.EXECUTIVE_DIRECTOR, voting_weight=1.0, - expertise_areas=first_mep.expertise_areas + expertise_areas=first_mep.expertise_areas, ) board_members.append(board_member) - + # Create the democratic decision-making swarm if board_members: # Extract agents from board members for the parent class - agents = [member.agent for member in board_members if member.agent is not None] - + agents = [ + member.agent + for member in board_members + if member.agent is not None + ] + self.democratic_swarm = BoardOfDirectorsSwarm( name="EuroSwarm Parliament Democratic Council", description="Democratic decision-making body for the European Parliament", @@ -1250,87 +1549,147 @@ Remember: You are a real MEP with specific political views, expertise, and respo enable_consensus=True, ) else: - logger.warning("No valid board members found for democratic decision-making") + logger.warning( + "No valid board members found for democratic decision-making" + ) self.democratic_swarm = None - + def _create_political_group_boards(self): """Create Board of Directors for each political group with specialized expertise and individual percentages.""" - + # Define specialized expertise areas for governance expertise_areas = { - "economics": ["Economic Policy", "Trade", "Budget", "Taxation", "Financial Services"], - "law": ["Legal Affairs", "Justice", "Civil Liberties", "Constitutional Affairs"], - "environment": ["Environment", "Climate Action", "Energy", "Transport"], - "social": ["Employment", "Social Affairs", "Health", "Education", "Culture"], - "foreign": ["Foreign Affairs", "Security", "Defense", "International Trade"], - "agriculture": ["Agriculture", "Rural Development", "Food Safety"], - "technology": ["Digital Affairs", "Industry", "Research", "Innovation"], - "regional": ["Regional Development", "Cohesion Policy", "Urban Planning"] + "economics": [ + "Economic Policy", + "Trade", + "Budget", + "Taxation", + "Financial Services", + ], + "law": [ + "Legal Affairs", + "Justice", + "Civil Liberties", + "Constitutional Affairs", + ], + "environment": [ + "Environment", + "Climate Action", + "Energy", + "Transport", + ], + "social": [ + "Employment", + "Social Affairs", + "Health", + "Education", + "Culture", + ], + "foreign": [ + "Foreign Affairs", + "Security", + "Defense", + "International Trade", + ], + "agriculture": [ + "Agriculture", + "Rural Development", + "Food Safety", + ], + "technology": [ + "Digital Affairs", + "Industry", + "Research", + "Innovation", + ], + "regional": [ + "Regional Development", + "Cohesion Policy", + "Urban Planning", + ], } - + total_meps = len(self.meps) - + for group_name, mep_list in self.political_groups.items(): if not mep_list: continue - + # Calculate voting weight (percentage of parliament) voting_weight = len(mep_list) / total_meps - + # Assign specialized expertise areas based on political group - group_expertise = self._assign_group_expertise(group_name, expertise_areas) - + group_expertise = self._assign_group_expertise( + group_name, expertise_areas + ) + # Create board members with specialized roles and individual percentages board_members = [] group_speaker = None board_member_percentages = {} - + # Select group speaker (CEO) - usually the first MEP in the group if mep_list and mep_list[0] in self.meps: group_speaker = mep_list[0] speaker_mep = self.meps[group_speaker] - + # Create group speaker board member with highest percentage if speaker_mep.agent: speaker_board_member = BoardMember( agent=speaker_mep.agent, role=BoardMemberRole.CHAIRMAN, voting_weight=1.0, - expertise_areas=group_expertise + expertise_areas=group_expertise, ) board_members.append(speaker_board_member) # Group speaker gets 35% of the group's internal voting power board_member_percentages[group_speaker] = 0.35 - + # Create specialized board members for each expertise area with weighted percentages - expertise_percentages = self._calculate_expertise_percentages(group_name, len(group_expertise)) - - for i, expertise_area in enumerate(group_expertise[:5]): # Limit to 5 main areas + expertise_percentages = ( + self._calculate_expertise_percentages( + group_name, len(group_expertise) + ) + ) + + for i, expertise_area in enumerate( + group_expertise[:5] + ): # Limit to 5 main areas # Find MEPs with relevant expertise specialized_meps = [ - mep_name for mep_name in mep_list - if mep_name in self.meps and - any(exp.lower() in expertise_area.lower() for exp in self.meps[mep_name].expertise_areas) + mep_name + for mep_name in mep_list + if mep_name in self.meps + and any( + exp.lower() in expertise_area.lower() + for exp in self.meps[mep_name].expertise_areas + ) ] - - if specialized_meps and i < len(expertise_percentages): + + if specialized_meps and i < len( + expertise_percentages + ): # Select the first specialized MEP specialized_mep_name = specialized_meps[0] specialized_mep = self.meps[specialized_mep_name] - + if specialized_mep.agent: # Assign percentage based on expertise importance - expertise_percentage = expertise_percentages[i] - + expertise_percentage = expertise_percentages[ + i + ] + board_member = BoardMember( agent=specialized_mep.agent, role=BoardMemberRole.EXECUTIVE_DIRECTOR, voting_weight=expertise_percentage, - expertise_areas=[expertise_area] + expertise_areas=[expertise_area], ) board_members.append(board_member) - board_member_percentages[specialized_mep_name] = expertise_percentage - + board_member_percentages[ + specialized_mep_name + ] = expertise_percentage + # Create the political group board with individual percentages political_group_board = PoliticalGroupBoard( group_name=group_name, @@ -1340,13 +1699,17 @@ Remember: You are a real MEP with specific political views, expertise, and respo voting_weight=voting_weight, group_speaker=group_speaker, total_meps=len(mep_list), - board_member_percentages=board_member_percentages + board_member_percentages=board_member_percentages, ) - + # Create BoardOfDirectorsSwarm for this political group if board_members: - agents = [member.agent for member in board_members if member.agent is not None] - + agents = [ + member.agent + for member in board_members + if member.agent is not None + ] + political_group_board.board_swarm = BoardOfDirectorsSwarm( name=f"{group_name} Board", description=f"Specialized board for {group_name} with expertise in {', '.join(group_expertise)}", @@ -1356,111 +1719,194 @@ Remember: You are a real MEP with specific political views, expertise, and respo verbose=self.verbose, decision_threshold=0.6, enable_voting=True, - enable_consensus=True + enable_consensus=True, ) - - self.political_group_boards[group_name] = political_group_board - + + self.political_group_boards[group_name] = ( + political_group_board + ) + if self.verbose: - logger.info(f"Created {group_name} board with {len(board_members)} members, " - f"voting weight: {voting_weight:.1%}, expertise: {', '.join(group_expertise[:3])}") - logger.info(f"Board member percentages: {board_member_percentages}") + logger.info( + f"Created {group_name} board with {len(board_members)} members, " + f"voting weight: {voting_weight:.1%}, expertise: {', '.join(group_expertise[:3])}" + ) + logger.info( + f"Board member percentages: {board_member_percentages}" + ) - def _assign_group_expertise(self, group_name: str, expertise_areas: Dict[str, List[str]]) -> List[str]: + def _assign_group_expertise( + self, group_name: str, expertise_areas: Dict[str, List[str]] + ) -> List[str]: """Assign specialized expertise areas based on political group ideology.""" - + # Map political groups to their primary expertise areas group_expertise_mapping = { "Group of the European People's Party (Christian Democrats)": [ - "economics", "law", "foreign", "social" + "economics", + "law", + "foreign", + "social", ], "Group of the Progressive Alliance of Socialists and Democrats in the European Parliament": [ - "social", "economics", "environment", "law" + "social", + "economics", + "environment", + "law", ], "Renew Europe Group": [ - "economics", "technology", "environment", "foreign" + "economics", + "technology", + "environment", + "foreign", ], "European Conservatives and Reformists Group": [ - "law", "foreign", "economics", "regional" + "law", + "foreign", + "economics", + "regional", ], "Group of the Greens/European Free Alliance": [ - "environment", "social", "technology", "agriculture" + "environment", + "social", + "technology", + "agriculture", ], "The Left group in the European Parliament - GUE/NGL": [ - "social", "economics", "environment", "law" + "social", + "economics", + "environment", + "law", ], "Patriots for Europe Group": [ - "foreign", "law", "regional", "social" + "foreign", + "law", + "regional", + "social", ], "Europe of Sovereign Nations Group": [ - "foreign", "law", "regional", "economics" + "foreign", + "law", + "regional", + "economics", ], "Non-attached Members": [ - "law", "foreign", "economics", "social" - ] + "law", + "foreign", + "economics", + "social", + ], } - + # Get primary expertise areas for this group - primary_areas = group_expertise_mapping.get(group_name, ["economics", "law", "social"]) - + primary_areas = group_expertise_mapping.get( + group_name, ["economics", "law", "social"] + ) + # Expand to specific expertise topics specific_expertise = [] for area in primary_areas: if area in expertise_areas: specific_expertise.extend(expertise_areas[area]) - + return specific_expertise[:8] # Limit to 8 areas - def _calculate_expertise_percentages(self, group_name: str, num_expertise_areas: int) -> List[float]: + def _calculate_expertise_percentages( + self, group_name: str, num_expertise_areas: int + ) -> List[float]: """Calculate individual percentages for board members based on political group and expertise areas.""" - + # Define percentage distributions based on political group characteristics percentage_distributions = { - "Group of the European People's Party (Christian Democrats)": [0.25, 0.20, 0.15, 0.05], # CEO gets 35% - "Group of the Progressive Alliance of Socialists and Democrats in the European Parliament": [0.25, 0.20, 0.15, 0.05], - "Renew Europe Group": [0.30, 0.20, 0.10, 0.05], # More emphasis on first expertise - "Group of the Greens/European Free Alliance": [0.30, 0.20, 0.10, 0.05], - "European Conservatives and Reformists Group": [0.25, 0.20, 0.15, 0.05], - "The Left group in the European Parliament - GUE/NGL": [0.25, 0.20, 0.15, 0.05], + "Group of the European People's Party (Christian Democrats)": [ + 0.25, + 0.20, + 0.15, + 0.05, + ], # CEO gets 35% + "Group of the Progressive Alliance of Socialists and Democrats in the European Parliament": [ + 0.25, + 0.20, + 0.15, + 0.05, + ], + "Renew Europe Group": [ + 0.30, + 0.20, + 0.10, + 0.05, + ], # More emphasis on first expertise + "Group of the Greens/European Free Alliance": [ + 0.30, + 0.20, + 0.10, + 0.05, + ], + "European Conservatives and Reformists Group": [ + 0.25, + 0.20, + 0.15, + 0.05, + ], + "The Left group in the European Parliament - GUE/NGL": [ + 0.25, + 0.20, + 0.15, + 0.05, + ], "Patriots for Europe Group": [0.30, 0.20, 0.10, 0.05], - "Europe of Sovereign Nations Group": [0.30, 0.20, 0.10, 0.05], - "Non-attached Members": [0.40, 0.20, 0.05, 0.00] # More concentrated power + "Europe of Sovereign Nations Group": [ + 0.30, + 0.20, + 0.10, + 0.05, + ], + "Non-attached Members": [ + 0.40, + 0.20, + 0.05, + 0.00, + ], # More concentrated power } - + # Get the distribution for this group - distribution = percentage_distributions.get(group_name, [0.25, 0.20, 0.15, 0.05]) - + distribution = percentage_distributions.get( + group_name, [0.25, 0.20, 0.15, 0.05] + ) + # Return the appropriate number of percentages return distribution[:num_expertise_areas] def _create_parliament_speaker(self): """Create the Parliament Speaker who aggregates decisions from all political groups.""" - + # Create parliament speaker agent speaker_agent = Agent( name="Parliament Speaker", system_prompt=self._generate_speaker_system_prompt(), llm="gpt-4", - verbose=self.verbose + verbose=self.verbose, ) - + # Calculate majority threshold majority_threshold = (len(self.meps) // 2) + 1 - + self.parliament_speaker = ParliamentSpeaker( name="Parliament Speaker", agent=speaker_agent, political_groups=self.political_group_boards, total_meps=len(self.meps), - majority_threshold=majority_threshold + majority_threshold=majority_threshold, ) - + if self.verbose: - logger.info(f"Created Parliament Speaker with majority threshold: {majority_threshold}") + logger.info( + f"Created Parliament Speaker with majority threshold: {majority_threshold}" + ) def _generate_speaker_system_prompt(self) -> str: """Generate system prompt for the Parliament Speaker.""" - + return f"""You are the Parliament Speaker of the European Parliament, responsible for: 1. **Aggregating Political Group Decisions**: Collect and analyze decisions from all political groups @@ -1483,17 +1929,19 @@ Remember: You are a real MEP with specific political views, expertise, and respo def _format_political_group_distribution(self) -> str: """Format political group distribution for the speaker prompt.""" - + if not self.political_group_boards: return "No political groups available" - + lines = [] for group_name, board in self.political_group_boards.items(): percentage = board.voting_weight * 100 - lines.append(f"- {group_name}: {board.total_meps} MEPs ({percentage:.1f}%)") - + lines.append( + f"- {group_name}: {board.total_meps} MEPs ({percentage:.1f}%)" + ) + return "\n".join(lines) - + def introduce_bill( self, title: str, @@ -1501,11 +1949,11 @@ Remember: You are a real MEP with specific political views, expertise, and respo bill_type: VoteType, committee: str, sponsor: str, - co_sponsors: List[str] = None + co_sponsors: List[str] = None, ) -> ParliamentaryBill: """ Introduce a new bill to the parliament. - + Args: title: Bill title description: Bill description @@ -1513,61 +1961,71 @@ Remember: You are a real MEP with specific political views, expertise, and respo committee: Primary committee sponsor: Sponsoring MEP co_sponsors: List of co-sponsoring MEPs - + Returns: ParliamentaryBill: The introduced bill """ if sponsor not in self.meps: raise ValueError(f"Sponsor {sponsor} is not a valid MEP") - + if committee not in self.committees: raise ValueError(f"Committee {committee} does not exist") - + bill = ParliamentaryBill( title=title, description=description, bill_type=bill_type, committee=committee, sponsor=sponsor, - co_sponsors=co_sponsors or [] + co_sponsors=co_sponsors or [], ) - + self.bills.append(bill) self.committees[committee].current_bills.append(bill) - - logger.info(f"Bill '{title}' introduced by {sponsor} in {committee} committee") + + logger.info( + f"Bill '{title}' introduced by {sponsor} in {committee} committee" + ) return bill - + def conduct_committee_hearing( self, committee: str, bill: ParliamentaryBill, - participants: List[str] = None + participants: List[str] = None, ) -> Dict[str, Any]: """ Conduct a committee hearing on a bill with cost optimization. - + Args: committee: Committee name bill: Bill under consideration participants: List of MEPs to participate - + Returns: Dict[str, Any]: Hearing results and transcript """ if committee not in self.committees: raise ValueError(f"Committee {committee} does not exist") - + # Check budget before starting if not self.cost_tracker.check_budget(): - return {"error": "Budget exceeded", "cost_stats": self.cost_tracker.get_stats()} - + return { + "error": "Budget exceeded", + "cost_stats": self.cost_tracker.get_stats(), + } + committee_meps = self.committees[committee].members if not participants: - participants = committee_meps[:10] # Limit to 10 participants - + participants = committee_meps[ + :10 + ] # Limit to 10 participants + # Check cache first - cache_key = self._get_cache_key(f"committee_hearing_{committee}_{bill.title}", participants) + cache_key = self._get_cache_key( + f"committee_hearing_{committee}_{bill.title}", + participants, + ) cached_result = self._check_cache(cache_key) if cached_result: return { @@ -1577,9 +2035,9 @@ Remember: You are a real MEP with specific political views, expertise, and respo "responses": cached_result, "date": datetime.now(), "cached": True, - "cost_stats": self.cost_tracker.get_stats() + "cost_stats": self.cost_tracker.get_stats(), } - + hearing_prompt = f""" Committee Hearing: {committee} Bill: {bill.title} @@ -1595,51 +2053,65 @@ Remember: You are a real MEP with specific political views, expertise, and respo Provide a detailed analysis with specific recommendations. """ - + # Load MEP agents in batches all_responses = {} total_processed = 0 - + for i in range(0, len(participants), self.batch_size): - batch_participants = participants[i:i + self.batch_size] - + batch_participants = participants[i : i + self.batch_size] + # Check budget for this batch if not self.cost_tracker.check_budget(): - logger.warning(f"Budget exceeded after processing {total_processed} participants") + logger.warning( + f"Budget exceeded after processing {total_processed} participants" + ) break - + # Load agents for this batch - batch_agents = self._load_mep_agents_batch(batch_participants) - + batch_agents = self._load_mep_agents_batch( + batch_participants + ) + if not batch_agents: continue - + # Run batch try: - batch_results = run_agents_concurrently(batch_agents, hearing_prompt) - + batch_results = run_agents_concurrently( + batch_agents, hearing_prompt + ) + # Map results back to participant names for j, agent in enumerate(batch_agents): if j < len(batch_results): participant_name = batch_participants[j] - all_responses[participant_name] = batch_results[j] + all_responses[participant_name] = ( + batch_results[j] + ) total_processed += 1 - + # Estimate tokens used - estimated_tokens = len(batch_agents) * 500 # ~500 tokens per response + estimated_tokens = ( + len(batch_agents) * 500 + ) # ~500 tokens per response self.cost_tracker.add_tokens(estimated_tokens) - + if self.verbose: - logger.info(f"Processed committee hearing batch {i//self.batch_size + 1}: {len(batch_agents)} participants") - + logger.info( + f"Processed committee hearing batch {i//self.batch_size + 1}: {len(batch_agents)} participants" + ) + except Exception as e: - logger.error(f"Error processing committee hearing batch: {e}") + logger.error( + f"Error processing committee hearing batch: {e}" + ) continue - + # Cache the results if all_responses: self._cache_response(cache_key, str(all_responses)) - + hearing_result = { "committee": committee, "bill": bill.title, @@ -1648,19 +2120,25 @@ Remember: You are a real MEP with specific political views, expertise, and respo "date": datetime.now(), "cached": False, "cost_stats": self.cost_tracker.get_stats(), - "recommendations": self._synthesize_committee_recommendations(all_responses) + "recommendations": self._synthesize_committee_recommendations( + all_responses + ), } - - logger.info(f"Committee hearing completed for {bill.title} in {committee}") + + logger.info( + f"Committee hearing completed for {bill.title} in {committee}" + ) return hearing_result - - def _synthesize_committee_recommendations(self, responses: Dict[str, str]) -> Dict[str, Any]: + + def _synthesize_committee_recommendations( + self, responses: Dict[str, str] + ) -> Dict[str, Any]: """ Synthesize committee recommendations from individual responses. - + Args: responses: Dictionary of MEP responses - + Returns: Dict[str, Any]: Synthesized recommendations """ @@ -1668,46 +2146,82 @@ Remember: You are a real MEP with specific political views, expertise, and respo support_count = 0 oppose_count = 0 amend_count = 0 - + for response in responses.values(): response_lower = response.lower() - if any(word in response_lower for word in ["support", "approve", "recommend", "favorable"]): + if any( + word in response_lower + for word in [ + "support", + "approve", + "recommend", + "favorable", + ] + ): support_count += 1 - elif any(word in response_lower for word in ["oppose", "reject", "against", "unfavorable"]): + elif any( + word in response_lower + for word in [ + "oppose", + "reject", + "against", + "unfavorable", + ] + ): oppose_count += 1 - elif any(word in response_lower for word in ["amend", "modify", "improve", "revise"]): + elif any( + word in response_lower + for word in ["amend", "modify", "improve", "revise"] + ): amend_count += 1 - + total = len(responses) - + return { - "support_percentage": (support_count / total) * 100 if total > 0 else 0, - "oppose_percentage": (oppose_count / total) * 100 if total > 0 else 0, - "amend_percentage": (amend_count / total) * 100 if total > 0 else 0, - "recommendation": "support" if support_count > oppose_count else "oppose" if oppose_count > support_count else "amend" + "support_percentage": ( + (support_count / total) * 100 if total > 0 else 0 + ), + "oppose_percentage": ( + (oppose_count / total) * 100 if total > 0 else 0 + ), + "amend_percentage": ( + (amend_count / total) * 100 if total > 0 else 0 + ), + "recommendation": ( + "support" + if support_count > oppose_count + else ( + "oppose" + if oppose_count > support_count + else "amend" + ) + ), } - + def conduct_parliamentary_debate( self, bill: ParliamentaryBill, participants: List[str] = None, - max_speakers: int = 20 + max_speakers: int = 20, ) -> Dict[str, Any]: """ Conduct a parliamentary debate on a bill with cost optimization. - + Args: bill: Bill under debate participants: List of MEPs to participate max_speakers: Maximum number of speakers - + Returns: Dict[str, Any]: Debate transcript and analysis """ # Check budget before starting if not self.cost_tracker.check_budget(): - return {"error": "Budget exceeded", "cost_stats": self.cost_tracker.get_stats()} - + return { + "error": "Budget exceeded", + "cost_stats": self.cost_tracker.get_stats(), + } + if not participants: # Select diverse participants from different political groups participants = [] @@ -1716,11 +2230,13 @@ Remember: You are a real MEP with specific political views, expertise, and respo participants.extend(meps[:3]) # 3 MEPs per group if len(participants) >= max_speakers: break - + participants = participants[:max_speakers] - + # Check cache first - cache_key = self._get_cache_key(f"parliamentary_debate_{bill.title}", participants) + cache_key = self._get_cache_key( + f"parliamentary_debate_{bill.title}", participants + ) cached_result = self._check_cache(cache_key) if cached_result: return { @@ -1729,9 +2245,9 @@ Remember: You are a real MEP with specific political views, expertise, and respo "transcript": cached_result, "date": datetime.now(), "cached": True, - "cost_stats": self.cost_tracker.get_stats() + "cost_stats": self.cost_tracker.get_stats(), } - + debate_prompt = f""" Parliamentary Debate: {bill.title} @@ -1749,60 +2265,70 @@ Remember: You are a real MEP with specific political views, expertise, and respo Provide a clear, reasoned argument for your position. """ - + # Conduct debate with batching debate_transcript = [] total_processed = 0 - + for i in range(0, len(participants), self.batch_size): - batch_participants = participants[i:i + self.batch_size] - + batch_participants = participants[i : i + self.batch_size] + # Check budget for this batch if not self.cost_tracker.check_budget(): - logger.warning(f"Budget exceeded after processing {total_processed} speakers") + logger.warning( + f"Budget exceeded after processing {total_processed} speakers" + ) break - + # Load agents for this batch - batch_agents = self._load_mep_agents_batch(batch_participants) - + batch_agents = self._load_mep_agents_batch( + batch_participants + ) + if not batch_agents: continue - + # Run batch try: - batch_results = run_agents_concurrently(batch_agents, debate_prompt) - + batch_results = run_agents_concurrently( + batch_agents, debate_prompt + ) + # Create debate entries for j, agent in enumerate(batch_agents): if j < len(batch_results): participant_name = batch_participants[j] mep = self.meps[participant_name] - + debate_entry = { "speaker": participant_name, "political_group": mep.political_group, "country": mep.country, "position": batch_results[j], - "timestamp": datetime.now() + "timestamp": datetime.now(), } debate_transcript.append(debate_entry) total_processed += 1 - + # Estimate tokens used - estimated_tokens = len(batch_agents) * 500 # ~500 tokens per response + estimated_tokens = ( + len(batch_agents) * 500 + ) # ~500 tokens per response self.cost_tracker.add_tokens(estimated_tokens) - + if self.verbose: - logger.info(f"Processed debate batch {i//self.batch_size + 1}: {len(batch_agents)} speakers") - + logger.info( + f"Processed debate batch {i//self.batch_size + 1}: {len(batch_agents)} speakers" + ) + except Exception as e: logger.error(f"Error processing debate batch: {e}") continue - + # Cache the results if debate_transcript: self._cache_response(cache_key, str(debate_transcript)) - + debate_result = { "bill": bill.title, "participants": participants[:total_processed], @@ -1810,20 +2336,24 @@ Remember: You are a real MEP with specific political views, expertise, and respo "date": datetime.now(), "cached": False, "cost_stats": self.cost_tracker.get_stats(), - "analysis": self._analyze_debate(debate_transcript) + "analysis": self._analyze_debate(debate_transcript), } - + self.debates.append(debate_result) - logger.info(f"Parliamentary debate completed for {bill.title} with {total_processed} speakers") + logger.info( + f"Parliamentary debate completed for {bill.title} with {total_processed} speakers" + ) return debate_result - - def _analyze_debate(self, transcript: List[Dict[str, Any]]) -> Dict[str, Any]: + + def _analyze_debate( + self, transcript: List[Dict[str, Any]] + ) -> Dict[str, Any]: """ Analyze debate transcript for key themes and positions. - + Args: transcript: Debate transcript - + Returns: Dict[str, Any]: Debate analysis """ @@ -1831,39 +2361,49 @@ Remember: You are a real MEP with specific political views, expertise, and respo support_count = 0 oppose_count = 0 neutral_count = 0 - + for entry in transcript: position = entry["position"].lower() - if any(word in position for word in ["support", "approve", "favorable", "yes"]): + if any( + word in position + for word in ["support", "approve", "favorable", "yes"] + ): support_count += 1 - elif any(word in position for word in ["oppose", "reject", "against", "no"]): + elif any( + word in position + for word in ["oppose", "reject", "against", "no"] + ): oppose_count += 1 else: neutral_count += 1 - + total = len(transcript) - + return { "support_count": support_count, "oppose_count": oppose_count, "neutral_count": neutral_count, - "support_percentage": (support_count / total) * 100 if total > 0 else 0, - "oppose_percentage": (oppose_count / total) * 100 if total > 0 else 0, - "neutral_percentage": (neutral_count / total) * 100 if total > 0 else 0 + "support_percentage": ( + (support_count / total) * 100 if total > 0 else 0 + ), + "oppose_percentage": ( + (oppose_count / total) * 100 if total > 0 else 0 + ), + "neutral_percentage": ( + (neutral_count / total) * 100 if total > 0 else 0 + ), } - + def conduct_democratic_vote( - self, - bill: ParliamentaryBill, - participants: List[str] = None + self, bill: ParliamentaryBill, participants: List[str] = None ) -> ParliamentaryVote: """ Conduct a democratic vote on a bill using the Board of Directors pattern with lazy loading. - + Args: bill: Bill to vote on participants: List of MEPs to participate - + Returns: ParliamentaryVote: Vote results """ @@ -1872,12 +2412,12 @@ Remember: You are a real MEP with specific political views, expertise, and respo return ParliamentaryVote( bill=bill, vote_type=bill.bill_type, - result=VoteResult.FAILED + result=VoteResult.FAILED, ) - + if not participants: participants = list(self.meps.keys()) - + # Use democratic swarm for decision-making if available democratic_result = None if self.democratic_swarm is not None: @@ -1896,30 +2436,36 @@ Remember: You are a real MEP with specific political views, expertise, and respo This is a critical legislative decision that will affect all EU citizens. """ - + # Get democratic decision - democratic_result = self.democratic_swarm.run_board_meeting(decision_task) - + democratic_result = ( + self.democratic_swarm.run_board_meeting(decision_task) + ) + # Conduct individual MEP votes with lazy loading individual_votes = {} reasoning = {} total_processed = 0 - + # Process participants in batches for i in range(0, len(participants), self.batch_size): - batch_participants = participants[i:i + self.batch_size] - + batch_participants = participants[i : i + self.batch_size] + # Check budget for this batch if not self.cost_tracker.check_budget(): - logger.warning(f"Budget exceeded after processing {total_processed} voters") + logger.warning( + f"Budget exceeded after processing {total_processed} voters" + ) break - + # Load agents for this batch - batch_agents = self._load_mep_agents_batch(batch_participants) - + batch_agents = self._load_mep_agents_batch( + batch_participants + ) + if not batch_agents: continue - + # Create voting prompt vote_prompt = f""" Vote on Bill: {bill.title} @@ -1936,47 +2482,79 @@ Remember: You are a real MEP with specific political views, expertise, and respo Respond with 'FOR', 'AGAINST', or 'ABSTAIN' and explain your reasoning. """ - + # Run batch voting try: - batch_results = run_agents_concurrently(batch_agents, vote_prompt) - + batch_results = run_agents_concurrently( + batch_agents, vote_prompt + ) + # Process results for j, agent in enumerate(batch_agents): if j < len(batch_results): participant_name = batch_participants[j] response = batch_results[j] - + # Parse vote response_lower = response.lower() - if any(word in response_lower for word in ["for", "support", "yes", "approve"]): + if any( + word in response_lower + for word in [ + "for", + "support", + "yes", + "approve", + ] + ): vote = "FOR" - elif any(word in response_lower for word in ["against", "oppose", "no", "reject"]): + elif any( + word in response_lower + for word in [ + "against", + "oppose", + "no", + "reject", + ] + ): vote = "AGAINST" else: vote = "ABSTAIN" - + individual_votes[participant_name] = vote reasoning[participant_name] = response total_processed += 1 - + # Estimate tokens used - estimated_tokens = len(batch_agents) * 500 # ~500 tokens per response + estimated_tokens = ( + len(batch_agents) * 500 + ) # ~500 tokens per response self.cost_tracker.add_tokens(estimated_tokens) - + if self.verbose: - logger.info(f"Processed voting batch {i//self.batch_size + 1}: {len(batch_agents)} voters") - + logger.info( + f"Processed voting batch {i//self.batch_size + 1}: {len(batch_agents)} voters" + ) + except Exception as e: logger.error(f"Error processing voting batch: {e}") continue - + # Calculate results - votes_for = sum(1 for vote in individual_votes.values() if vote == "FOR") - votes_against = sum(1 for vote in individual_votes.values() if vote == "AGAINST") - abstentions = sum(1 for vote in individual_votes.values() if vote == "ABSTAIN") + votes_for = sum( + 1 for vote in individual_votes.values() if vote == "FOR" + ) + votes_against = sum( + 1 + for vote in individual_votes.values() + if vote == "AGAINST" + ) + abstentions = sum( + 1 + for vote in individual_votes.values() + if vote == "ABSTAIN" + ) absent = len(participants) - len(individual_votes) - + # Determine result if votes_for > votes_against: result = VoteResult.PASSED @@ -1984,7 +2562,7 @@ Remember: You are a real MEP with specific political views, expertise, and respo result = VoteResult.FAILED else: result = VoteResult.TIED - + vote_result = ParliamentaryVote( bill=bill, vote_type=bill.bill_type, @@ -1994,60 +2572,65 @@ Remember: You are a real MEP with specific political views, expertise, and respo absent=absent, result=result, individual_votes=individual_votes, - reasoning=reasoning + reasoning=reasoning, ) - + self.votes.append(vote_result) bill.status = "voted" - - logger.info(f"Democratic vote completed for {bill.title}: {result.value} ({total_processed} voters processed)") + + logger.info( + f"Democratic vote completed for {bill.title}: {result.value} ({total_processed} voters processed)" + ) return vote_result - + def conduct_hierarchical_democratic_vote( - self, - bill: ParliamentaryBill, - participants: List[str] = None + self, bill: ParliamentaryBill, participants: List[str] = None ) -> ParliamentaryVote: """ Conduct a hierarchical democratic vote using political group boards and parliament speaker. - + This enhanced voting system: 1. Each political group votes internally as a specialized board 2. Group speakers (CEOs) synthesize their group's position 3. Parliament Speaker aggregates all group decisions based on percentage representation 4. Final result calculated using weighted voting - + Args: bill: Bill to vote on participants: List of MEPs to participate (optional, uses all by default) - + Returns: ParliamentaryVote: Enhanced vote results with group-level analysis """ - + if not self.enable_hierarchical_democracy: - logger.warning("Hierarchical democracy not enabled, falling back to standard voting") + logger.warning( + "Hierarchical democracy not enabled, falling back to standard voting" + ) return self.conduct_democratic_vote(bill, participants) - - logger.info(f"Conducting hierarchical democratic vote on: {bill.title}") - + + logger.info( + f"Conducting hierarchical democratic vote on: {bill.title}" + ) + # Initialize vote tracking vote = ParliamentaryVote( - bill=bill, - vote_type=bill.bill_type, - date=datetime.now() + bill=bill, vote_type=bill.bill_type, date=datetime.now() ) - + # Step 1: Each political group votes internally group_decisions = {} group_reasoning = {} - - for group_name, group_board in self.political_group_boards.items(): + + for ( + group_name, + group_board, + ) in self.political_group_boards.items(): if not group_board.board_swarm: continue - + logger.info(f"Conducting internal vote for {group_name}") - + # Create voting task for this group voting_task = f""" Parliamentary Vote: {bill.title} @@ -2069,27 +2652,37 @@ Remember: You are a real MEP with specific political views, expertise, and respo Provide your group's decision: POSITIVE, NEGATIVE, or ABSTAIN Include detailed reasoning for your position. """ - + try: # Get group decision using their specialized board - group_result = group_board.board_swarm.run(voting_task) - + group_result = group_board.board_swarm.run( + voting_task + ) + # Parse the group decision - group_decision = self._parse_group_decision(group_result) + group_decision = self._parse_group_decision( + group_result + ) group_decisions[group_name] = group_decision group_reasoning[group_name] = group_result - - logger.info(f"{group_name} decision: {group_decision}") - + + logger.info( + f"{group_name} decision: {group_decision}" + ) + except Exception as e: logger.error(f"Error in {group_name} vote: {e}") group_decisions[group_name] = "ABSTAIN" - group_reasoning[group_name] = f"Error during voting: {str(e)}" - + group_reasoning[group_name] = ( + f"Error during voting: {str(e)}" + ) + # Step 2: Parliament Speaker aggregates group decisions if self.parliament_speaker and self.parliament_speaker.agent: - logger.info("Parliament Speaker aggregating group decisions") - + logger.info( + "Parliament Speaker aggregating group decisions" + ) + aggregation_task = f""" Parliamentary Vote Aggregation: {bill.title} @@ -2110,75 +2703,115 @@ Remember: You are a real MEP with specific political views, expertise, and respo 3. Weighted analysis of each group's contribution 4. Summary of the democratic process """ - + try: - speaker_result = self.parliament_speaker.agent.run(aggregation_task) - + speaker_result = self.parliament_speaker.agent.run( + aggregation_task + ) + # Parse speaker's analysis - final_result = self._parse_speaker_analysis(speaker_result, group_decisions) - + final_result = self._parse_speaker_analysis( + speaker_result, group_decisions + ) + # Update vote with results - vote.result = final_result['result'] - vote.votes_for = final_result['votes_for'] - vote.votes_against = final_result['votes_against'] - vote.abstentions = final_result['abstentions'] + vote.result = final_result["result"] + vote.votes_for = final_result["votes_for"] + vote.votes_against = final_result["votes_against"] + vote.abstentions = final_result["abstentions"] vote.individual_votes = group_decisions vote.reasoning = group_reasoning - + logger.info(f"Final result: {vote.result.value}") - logger.info(f"Votes - For: {vote.votes_for}, Against: {vote.votes_against}, Abstain: {vote.abstentions}") - + logger.info( + f"Votes - For: {vote.votes_for}, Against: {vote.votes_against}, Abstain: {vote.abstentions}" + ) + except Exception as e: logger.error(f"Error in speaker aggregation: {e}") # Fallback to simple counting - vote = self._fallback_vote_calculation(vote, group_decisions) - + vote = self._fallback_vote_calculation( + vote, group_decisions + ) + # Store the vote self.votes.append(vote) - + return vote def _parse_group_decision(self, group_result: str) -> str: """Parse the decision from a political group's voting result.""" - + result_lower = group_result.lower() - - if any(word in result_lower for word in ['positive', 'for', 'support', 'approve', 'pass']): + + if any( + word in result_lower + for word in [ + "positive", + "for", + "support", + "approve", + "pass", + ] + ): return "POSITIVE" - elif any(word in result_lower for word in ['negative', 'against', 'oppose', 'reject', 'fail']): + elif any( + word in result_lower + for word in [ + "negative", + "against", + "oppose", + "reject", + "fail", + ] + ): return "NEGATIVE" else: return "ABSTAIN" - def _format_group_decisions(self, group_decisions: Dict[str, str], group_reasoning: Dict[str, str]) -> str: + def _format_group_decisions( + self, + group_decisions: Dict[str, str], + group_reasoning: Dict[str, str], + ) -> str: """Format group decisions for the speaker's analysis.""" - + lines = [] for group_name, decision in group_decisions.items(): board = self.political_group_boards.get(group_name) if board: percentage = board.voting_weight * 100 - reasoning = group_reasoning.get(group_name, "No reasoning provided") - lines.append(f"- {group_name} ({board.total_meps} MEPs, {percentage:.1f}%): {decision}") + reasoning = group_reasoning.get( + group_name, "No reasoning provided" + ) + lines.append( + f"- {group_name} ({board.total_meps} MEPs, {percentage:.1f}%): {decision}" + ) lines.append(f" Reasoning: {reasoning[:200]}...") - + return "\n".join(lines) - def _parse_speaker_analysis(self, speaker_result: str, group_decisions: Dict[str, str]) -> Dict[str, Any]: + def _parse_speaker_analysis( + self, speaker_result: str, group_decisions: Dict[str, str] + ) -> Dict[str, Any]: """Parse the Parliament Speaker's analysis to extract final vote results using dual-layer percentage system.""" - + # Initialize counters votes_for = 0 votes_against = 0 abstentions = 0 - + # Calculate weighted votes using dual-layer percentage system for group_name, decision in group_decisions.items(): board = self.political_group_boards.get(group_name) if board and board.board_member_percentages: # Calculate weighted votes using individual board member percentages - group_weighted_votes = self._calculate_group_weighted_votes(board, decision) - + group_weighted_votes = ( + self._calculate_group_weighted_votes( + board, decision + ) + ) + if decision == "POSITIVE": votes_for += group_weighted_votes elif decision == "NEGATIVE": @@ -2188,15 +2821,17 @@ Remember: You are a real MEP with specific political views, expertise, and respo else: # Fallback to simple calculation if no individual percentages available if board: - weighted_votes = int(board.total_meps * board.voting_weight) - + weighted_votes = int( + board.total_meps * board.voting_weight + ) + if decision == "POSITIVE": votes_for += weighted_votes elif decision == "NEGATIVE": votes_against += weighted_votes else: # ABSTAIN abstentions += weighted_votes - + # Determine result if votes_for > votes_against: result = VoteResult.PASSED @@ -2204,44 +2839,57 @@ Remember: You are a real MEP with specific political views, expertise, and respo result = VoteResult.FAILED else: result = VoteResult.TIED - + return { - 'result': result, - 'votes_for': votes_for, - 'votes_against': votes_against, - 'abstentions': abstentions + "result": result, + "votes_for": votes_for, + "votes_against": votes_against, + "abstentions": abstentions, } - def _calculate_group_weighted_votes(self, board: PoliticalGroupBoard, decision: str) -> int: + def _calculate_group_weighted_votes( + self, board: PoliticalGroupBoard, decision: str + ) -> int: """Calculate weighted votes for a political group using individual board member percentages.""" - + total_weighted_votes = 0 - + # Calculate votes based on individual board member percentages - for member_name, internal_percentage in board.board_member_percentages.items(): + for ( + member_name, + internal_percentage, + ) in board.board_member_percentages.items(): # Convert internal percentage to parliament percentage # internal_percentage is percentage within the group # board.voting_weight is group's percentage of parliament - parliament_percentage = internal_percentage * board.voting_weight - + parliament_percentage = ( + internal_percentage * board.voting_weight + ) + # Calculate weighted votes for this member - member_weighted_votes = int(board.total_meps * parliament_percentage) + member_weighted_votes = int( + board.total_meps * parliament_percentage + ) total_weighted_votes += member_weighted_votes - + if self.verbose: - logger.debug(f"{member_name}: {internal_percentage:.1%} of {board.group_name} " - f"({board.voting_weight:.1%} of parliament) = {parliament_percentage:.3%} " - f"= {member_weighted_votes} weighted votes") - + logger.debug( + f"{member_name}: {internal_percentage:.1%} of {board.group_name} " + f"({board.voting_weight:.1%} of parliament) = {parliament_percentage:.3%} " + f"= {member_weighted_votes} weighted votes" + ) + return total_weighted_votes - def _fallback_vote_calculation(self, vote: ParliamentaryVote, group_decisions: Dict[str, str]) -> ParliamentaryVote: + def _fallback_vote_calculation( + self, vote: ParliamentaryVote, group_decisions: Dict[str, str] + ) -> ParliamentaryVote: """Fallback vote calculation if speaker analysis fails.""" - + votes_for = 0 votes_against = 0 abstentions = 0 - + for group_name, decision in group_decisions.items(): board = self.political_group_boards.get(group_name) if board: @@ -2251,30 +2899,32 @@ Remember: You are a real MEP with specific political views, expertise, and respo votes_against += board.total_meps else: abstentions += board.total_meps - + vote.votes_for = votes_for vote.votes_against = votes_against vote.abstentions = abstentions - + if votes_for > votes_against: vote.result = VoteResult.PASSED elif votes_against > votes_for: vote.result = VoteResult.FAILED else: vote.result = VoteResult.TIED - + return vote - + def get_parliament_composition(self) -> Dict[str, Any]: """ Get the current composition of the parliament including cost statistics. - + Returns: Dict[str, Any]: Parliament composition statistics """ composition = { "total_meps": len(self.meps), - "loaded_meps": len([mep for mep in self.meps.values() if mep.is_loaded]), + "loaded_meps": len( + [mep for mep in self.meps.values() if mep.is_loaded] + ), "political_groups": {}, "countries": {}, "leadership": {}, @@ -2284,25 +2934,27 @@ Remember: You are a real MEP with specific political views, expertise, and respo "lazy_loading": self.enable_lazy_loading, "caching": self.enable_caching, "batch_size": self.batch_size, - "budget_limit": self.cost_tracker.budget_limit - } + "budget_limit": self.cost_tracker.budget_limit, + }, } - + # Political group breakdown for group_name, meps in self.political_groups.items(): composition["political_groups"][group_name] = { "count": len(meps), - "percentage": (len(meps) / len(self.meps)) * 100 + "percentage": (len(meps) / len(self.meps)) * 100, } - + # Country breakdown country_counts = {} for mep in self.meps.values(): country = mep.country - country_counts[country] = country_counts.get(country, 0) + 1 - + country_counts[country] = ( + country_counts.get(country, 0) + 1 + ) + composition["countries"] = country_counts - + # Leadership positions leadership = {} for mep in self.meps.values(): @@ -2311,44 +2963,63 @@ Remember: You are a real MEP with specific political views, expertise, and respo if role not in leadership: leadership[role] = [] leadership[role].append(mep.full_name) - + composition["leadership"] = leadership - + # Committee composition for committee_name, committee in self.committees.items(): composition["committees"][committee_name] = { "chair": committee.chair, "vice_chair": committee.vice_chair, "member_count": len(committee.members), - "current_bills": len(committee.current_bills) + "current_bills": len(committee.current_bills), } - + return composition - + def get_cost_statistics(self) -> Dict[str, Any]: """ Get detailed cost statistics for the parliamentary operations. - + Returns: Dict[str, Any]: Cost statistics and optimization metrics """ stats = self.cost_tracker.get_stats() - + # Add additional metrics - stats.update({ - "total_meps": len(self.meps), - "loaded_meps": len([mep for mep in self.meps.values() if mep.is_loaded]), - "loading_efficiency": len([mep for mep in self.meps.values() if mep.is_loaded]) / len(self.meps) if self.meps else 0, - "cache_size": len(self.response_cache), - "optimization_enabled": { - "lazy_loading": self.enable_lazy_loading, - "caching": self.enable_caching, - "batching": self.batch_size > 1 + stats.update( + { + "total_meps": len(self.meps), + "loaded_meps": len( + [ + mep + for mep in self.meps.values() + if mep.is_loaded + ] + ), + "loading_efficiency": ( + len( + [ + mep + for mep in self.meps.values() + if mep.is_loaded + ] + ) + / len(self.meps) + if self.meps + else 0 + ), + "cache_size": len(self.response_cache), + "optimization_enabled": { + "lazy_loading": self.enable_lazy_loading, + "caching": self.enable_caching, + "batching": self.batch_size > 1, + }, } - }) - + ) + return stats - + def run_optimized_parliamentary_session( self, bill_title: str, @@ -2356,11 +3027,11 @@ Remember: You are a real MEP with specific political views, expertise, and respo bill_type: VoteType = VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, committee: str = "Legal Affairs", sponsor: str = None, - max_cost: float = 50.0 + max_cost: float = 50.0, ) -> Dict[str, Any]: """ Run a complete parliamentary session with cost optimization. - + Args: bill_title: Title of the bill bill_description: Description of the bill @@ -2368,37 +3039,39 @@ Remember: You are a real MEP with specific political views, expertise, and respo committee: Primary committee sponsor: Sponsoring MEP (random if not specified) max_cost: Maximum cost for this session - + Returns: Dict[str, Any]: Complete session results with cost tracking """ # Set temporary budget for this session original_budget = self.cost_tracker.budget_limit - self.cost_tracker.budget_limit = min(original_budget, max_cost) - + self.cost_tracker.budget_limit = min( + original_budget, max_cost + ) + try: # Select sponsor if not provided if not sponsor: sponsor = random.choice(list(self.meps.keys())) - + # Introduce bill bill = self.introduce_bill( title=bill_title, description=bill_description, bill_type=bill_type, committee=committee, - sponsor=sponsor + sponsor=sponsor, ) - + # Conduct committee hearing hearing = self.conduct_committee_hearing(committee, bill) - + # Conduct parliamentary debate debate = self.conduct_parliamentary_debate(bill) - + # Conduct democratic vote vote = self.conduct_democratic_vote(bill) - + session_result = { "bill": bill, "hearing": hearing, @@ -2409,66 +3082,78 @@ Remember: You are a real MEP with specific political views, expertise, and respo "bill_title": bill_title, "sponsor": sponsor, "committee": committee, - "hearing_recommendation": hearing.get("recommendations", {}).get("recommendation", "unknown"), - "debate_support_percentage": debate.get("analysis", {}).get("support_percentage", 0), + "hearing_recommendation": hearing.get( + "recommendations", {} + ).get("recommendation", "unknown"), + "debate_support_percentage": debate.get( + "analysis", {} + ).get("support_percentage", 0), "vote_result": vote.result.value, - "final_outcome": "PASSED" if vote.result == VoteResult.PASSED else "FAILED", - "total_cost": self.cost_tracker.total_cost_estimate - } + "final_outcome": ( + "PASSED" + if vote.result == VoteResult.PASSED + else "FAILED" + ), + "total_cost": self.cost_tracker.total_cost_estimate, + }, } - - logger.info(f"Optimized parliamentary session completed for {bill_title}: {session_result['session_summary']['final_outcome']}") - logger.info(f"Session cost: ${self.cost_tracker.total_cost_estimate:.2f}") - + + logger.info( + f"Optimized parliamentary session completed for {bill_title}: {session_result['session_summary']['final_outcome']}" + ) + logger.info( + f"Session cost: ${self.cost_tracker.total_cost_estimate:.2f}" + ) + return session_result - + finally: # Restore original budget self.cost_tracker.budget_limit = original_budget - + def run_democratic_session( self, bill_title: str, bill_description: str, bill_type: VoteType = VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, committee: str = "Legal Affairs", - sponsor: str = None + sponsor: str = None, ) -> Dict[str, Any]: """ Run a complete democratic parliamentary session on a bill. - + Args: bill_title: Title of the bill bill_description: Description of the bill bill_type: Type of legislative procedure committee: Primary committee sponsor: Sponsoring MEP (random if not specified) - + Returns: Dict[str, Any]: Complete session results """ # Select sponsor if not provided if not sponsor: sponsor = random.choice(list(self.meps.keys())) - + # Introduce bill bill = self.introduce_bill( title=bill_title, description=bill_description, bill_type=bill_type, committee=committee, - sponsor=sponsor + sponsor=sponsor, ) - + # Conduct committee hearing hearing = self.conduct_committee_hearing(committee, bill) - + # Conduct parliamentary debate debate = self.conduct_parliamentary_debate(bill) - + # Conduct democratic vote vote = self.conduct_democratic_vote(bill) - + session_result = { "bill": bill, "hearing": hearing, @@ -2478,78 +3163,104 @@ Remember: You are a real MEP with specific political views, expertise, and respo "bill_title": bill_title, "sponsor": sponsor, "committee": committee, - "hearing_recommendation": hearing["recommendations"]["recommendation"], - "debate_support_percentage": debate["analysis"]["support_percentage"], + "hearing_recommendation": hearing["recommendations"][ + "recommendation" + ], + "debate_support_percentage": debate["analysis"][ + "support_percentage" + ], "vote_result": vote.result.value, - "final_outcome": "PASSED" if vote.result == VoteResult.PASSED else "FAILED" - } + "final_outcome": ( + "PASSED" + if vote.result == VoteResult.PASSED + else "FAILED" + ), + }, } - - logger.info(f"Democratic session completed for {bill_title}: {session_result['session_summary']['final_outcome']}") + + logger.info( + f"Democratic session completed for {bill_title}: {session_result['session_summary']['final_outcome']}" + ) return session_result - + def run_hierarchical_democratic_session( self, bill_title: str, bill_description: str, bill_type: VoteType = VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, committee: str = "Legal Affairs", - sponsor: str = None + sponsor: str = None, ) -> Dict[str, Any]: """ Run a complete hierarchical democratic session from bill introduction to final vote. - + This enhanced session uses: 1. Political group boards with specialized expertise 2. Group-level internal voting and discussion 3. Parliament Speaker aggregation of group decisions 4. Weighted voting based on political group percentages - + Args: bill_title: Title of the bill bill_description: Description of the bill bill_type: Type of legislative procedure committee: Committee responsible for the bill sponsor: MEP sponsoring the bill - + Returns: Dict[str, Any]: Complete session results including group decisions and final vote """ - + if not self.enable_hierarchical_democracy: - logger.warning("Hierarchical democracy not enabled, falling back to standard session") - return self.run_democratic_session(bill_title, bill_description, bill_type, committee, sponsor) - - logger.info(f"Starting hierarchical democratic session: {bill_title}") - + logger.warning( + "Hierarchical democracy not enabled, falling back to standard session" + ) + return self.run_democratic_session( + bill_title, + bill_description, + bill_type, + committee, + sponsor, + ) + + logger.info( + f"Starting hierarchical democratic session: {bill_title}" + ) + # Step 1: Introduce the bill if not sponsor: - sponsor = list(self.meps.keys())[0] # Use first MEP as sponsor - + sponsor = list(self.meps.keys())[ + 0 + ] # Use first MEP as sponsor + bill = self.introduce_bill( title=bill_title, description=bill_description, bill_type=bill_type, committee=committee, - sponsor=sponsor + sponsor=sponsor, ) - + # Step 2: Conduct committee hearing (if enabled) committee_result = None if self.enable_committee_work: - logger.info(f"Conducting committee hearing in {committee}") - committee_result = self.conduct_committee_hearing(committee, bill) - + logger.info( + f"Conducting committee hearing in {committee}" + ) + committee_result = self.conduct_committee_hearing( + committee, bill + ) + # Step 3: Conduct parliamentary debate (if enabled) debate_result = None if self.enable_democratic_discussion: logger.info("Conducting parliamentary debate") debate_result = self.conduct_parliamentary_debate(bill) - + # Step 4: Conduct hierarchical democratic vote logger.info("Conducting hierarchical democratic vote") vote_result = self.conduct_hierarchical_democratic_vote(bill) - + # Step 5: Compile comprehensive session report session_report = { "session_type": "hierarchical_democratic", @@ -2559,7 +3270,7 @@ Remember: You are a real MEP with specific political views, expertise, and respo "type": bill.bill_type.value, "committee": bill.committee, "sponsor": bill.sponsor, - "status": bill.status + "status": bill.status, }, "committee_work": committee_result, "parliamentary_debate": debate_result, @@ -2568,24 +3279,36 @@ Remember: You are a real MEP with specific political views, expertise, and respo "votes_for": vote_result.votes_for, "votes_against": vote_result.votes_against, "abstentions": vote_result.abstentions, - "total_votes": vote_result.votes_for + vote_result.votes_against + vote_result.abstentions + "total_votes": vote_result.votes_for + + vote_result.votes_against + + vote_result.abstentions, }, "political_group_decisions": vote_result.individual_votes, "group_reasoning": vote_result.reasoning, "parliament_composition": self.get_parliament_composition(), - "session_summary": self._generate_hierarchical_session_summary(bill, vote_result) + "session_summary": self._generate_hierarchical_session_summary( + bill, vote_result + ), } - - logger.info(f"Hierarchical democratic session completed. Final result: {vote_result.result.value}") - + + logger.info( + f"Hierarchical democratic session completed. Final result: {vote_result.result.value}" + ) + return session_report - def _generate_hierarchical_session_summary(self, bill: ParliamentaryBill, vote: ParliamentaryVote) -> str: + def _generate_hierarchical_session_summary( + self, bill: ParliamentaryBill, vote: ParliamentaryVote + ) -> str: """Generate a summary of the hierarchical democratic session with dual-layer percentage breakdown.""" - - total_votes = vote.votes_for + vote.votes_against + vote.abstentions - participation_rate = (total_votes / len(self.meps)) * 100 if self.meps else 0 - + + total_votes = ( + vote.votes_for + vote.votes_against + vote.abstentions + ) + participation_rate = ( + (total_votes / len(self.meps)) * 100 if self.meps else 0 + ) + summary = f""" 🏛️ HIERARCHICAL DEMOCRATIC SESSION SUMMARY @@ -2600,168 +3323,212 @@ Remember: You are a real MEP with specific political views, expertise, and respo 🏛️ POLITICAL GROUP DECISIONS (Dual-Layer Percentage System): """ - + for group_name, decision in vote.individual_votes.items(): board = self.political_group_boards.get(group_name) if board: group_percentage = board.voting_weight * 100 summary += f"\n• {group_name}: {decision} ({board.total_meps} MEPs, {group_percentage:.1f}% of parliament)" - + # Show individual board member percentages if board.board_member_percentages: - summary += f"\n 📊 Board Member Breakdown:" - for member_name, internal_percentage in board.board_member_percentages.items(): - parliament_percentage = internal_percentage * board.voting_weight * 100 + summary += "\n 📊 Board Member Breakdown:" + for ( + member_name, + internal_percentage, + ) in board.board_member_percentages.items(): + parliament_percentage = ( + internal_percentage + * board.voting_weight + * 100 + ) summary += f"\n - {member_name}: {internal_percentage:.1%} of group = {parliament_percentage:.3f}% of parliament" - - summary += f"\n\n🎯 DUAL-LAYER DEMOCRATIC PROCESS:" - summary += f"\n• Each political group operates as a specialized board" - summary += f"\n• Board members have individual percentages within their group" - summary += f"\n• Individual percentages × Group percentage = Parliament percentage" - summary += f"\n• Parliament Speaker aggregates all weighted decisions" + + summary += "\n\n🎯 DUAL-LAYER DEMOCRATIC PROCESS:" + summary += ( + "\n• Each political group operates as a specialized board" + ) + summary += "\n• Board members have individual percentages within their group" + summary += "\n• Individual percentages × Group percentage = Parliament percentage" + summary += ( + "\n• Parliament Speaker aggregates all weighted decisions" + ) summary += f"\n• Final result based on {len(self.political_group_boards)} political groups with {sum(len(board.board_member_percentages) for board in self.political_group_boards.values())} board members" - + return summary - + def get_mep(self, mep_name: str) -> Optional[ParliamentaryMember]: """ Get a specific MEP by name. - + Args: mep_name: Name of the MEP - + Returns: Optional[ParliamentaryMember]: MEP if found, None otherwise """ return self.meps.get(mep_name) - - def get_committee(self, committee_name: str) -> Optional[ParliamentaryCommittee]: + + def get_committee( + self, committee_name: str + ) -> Optional[ParliamentaryCommittee]: """ Get a specific committee by name. - + Args: committee_name: Name of the committee - + Returns: Optional[ParliamentaryCommittee]: Committee if found, None otherwise """ return self.committees.get(committee_name) - - def get_political_group_members(self, group_name: str) -> List[str]: + + def get_political_group_members( + self, group_name: str + ) -> List[str]: """ Get all MEPs in a specific political group. - + Args: group_name: Name of the political group - + Returns: List[str]: List of MEP names in the group """ return self.political_groups.get(group_name, []) - + def get_country_members(self, country: str) -> List[str]: """ Get all MEPs from a specific country. - + Args: country: Name of the country - + Returns: List[str]: List of MEP names from the country """ - return [mep_name for mep_name, mep in self.meps.items() if mep.country == country] + return [ + mep_name + for mep_name, mep in self.meps.items() + if mep.country == country + ] def _load_wikipedia_personalities(self): """Load Wikipedia personality profiles for MEPs.""" - + if not self.enable_wikipedia_personalities: return - + try: # Initialize personality scraper self.personality_scraper = WikipediaPersonalityScraper( - output_dir="mep_personalities", - verbose=self.verbose + output_dir="mep_personalities", verbose=self.verbose ) - + # Load existing personality profiles personality_dir = "mep_personalities" if os.path.exists(personality_dir): - profile_files = [f for f in os.listdir(personality_dir) if f.endswith('.json')] - + profile_files = [ + f + for f in os.listdir(personality_dir) + if f.endswith(".json") + ] + for filename in profile_files: filepath = os.path.join(personality_dir, filename) try: - profile = self.personality_scraper.load_personality_profile(filepath) - self.personality_profiles[profile.full_name] = profile - + profile = self.personality_scraper.load_personality_profile( + filepath + ) + self.personality_profiles[ + profile.full_name + ] = profile + if self.verbose: - logger.debug(f"Loaded personality profile: {profile.full_name}") - + logger.debug( + f"Loaded personality profile: {profile.full_name}" + ) + except Exception as e: - logger.warning(f"Error loading personality profile {filename}: {e}") - + logger.warning( + f"Error loading personality profile {filename}: {e}" + ) + if self.verbose: - logger.info(f"Loaded {len(self.personality_profiles)} Wikipedia personality profiles") + logger.info( + f"Loaded {len(self.personality_profiles)} Wikipedia personality profiles" + ) else: if self.verbose: - logger.info("No existing personality profiles found. Run Wikipedia scraper to create profiles.") - + logger.info( + "No existing personality profiles found. Run Wikipedia scraper to create profiles." + ) + except Exception as e: - logger.error(f"Error loading Wikipedia personalities: {e}") + logger.error( + f"Error loading Wikipedia personalities: {e}" + ) self.enable_wikipedia_personalities = False - def scrape_wikipedia_personalities(self, delay: float = 1.0) -> Dict[str, str]: + def scrape_wikipedia_personalities( + self, delay: float = 1.0 + ) -> Dict[str, str]: """ Scrape Wikipedia personality data for all MEPs. - + Args: delay: Delay between requests to be respectful to Wikipedia - + Returns: Dictionary mapping MEP names to their personality profile file paths """ - + if not self.enable_wikipedia_personalities: logger.error("Wikipedia personality system not available") return {} - + if not self.personality_scraper: self.personality_scraper = WikipediaPersonalityScraper( - output_dir="mep_personalities", - verbose=self.verbose + output_dir="mep_personalities", verbose=self.verbose ) - - logger.info("Starting Wikipedia personality scraping for all MEPs...") - profile_files = self.personality_scraper.scrape_all_mep_personalities( - xml_file=self.eu_data_file, - delay=delay + + logger.info( + "Starting Wikipedia personality scraping for all MEPs..." ) - + profile_files = ( + self.personality_scraper.scrape_all_mep_personalities( + xml_file=self.eu_data_file, delay=delay + ) + ) + # Reload personality profiles self._load_wikipedia_personalities() - + return profile_files - def get_mep_personality_profile(self, mep_name: str) -> Optional[MEPPersonalityProfile]: + def get_mep_personality_profile( + self, mep_name: str + ) -> Optional[MEPPersonalityProfile]: """ Get personality profile for a specific MEP. - + Args: mep_name: Name of the MEP - + Returns: MEPPersonalityProfile if found, None otherwise """ return self.personality_profiles.get(mep_name) - def analyze_political_landscape(self, bill: ParliamentaryBill) -> Dict[str, Any]: + def analyze_political_landscape( + self, bill: ParliamentaryBill + ) -> Dict[str, Any]: """ Analyze the political landscape for a bill to predict voting outcomes. - + Args: bill: Bill to analyze - + Returns: Dict[str, Any]: Political analysis results """ @@ -2769,59 +3536,79 @@ Remember: You are a real MEP with specific political views, expertise, and respo "overall_support": 0.0, "opposition": 0.0, "uncertainty": 0.0, - "group_analysis": {} + "group_analysis": {}, } - + # Analyze by political group for group_name, meps in self.political_groups.items(): if not meps: continue - + # Simple analysis based on political group alignment group_support = 0.0 group_opposition = 0.0 - + # Assign support based on political group characteristics - if "Green" in group_name or "Environment" in bill.description: + if ( + "Green" in group_name + or "Environment" in bill.description + ): group_support = 75.0 group_opposition = 15.0 - elif "Socialist" in group_name or "Social" in bill.description: + elif ( + "Socialist" in group_name + or "Social" in bill.description + ): group_support = 70.0 group_opposition = 20.0 - elif "Conservative" in group_name or "Economic" in bill.description: + elif ( + "Conservative" in group_name + or "Economic" in bill.description + ): group_support = 60.0 group_opposition = 30.0 - elif "Liberal" in group_name or "Digital" in bill.description: + elif ( + "Liberal" in group_name + or "Digital" in bill.description + ): group_support = 65.0 group_opposition = 25.0 else: group_support = 50.0 group_opposition = 30.0 - - group_uncertainty = 100.0 - group_support - group_opposition - + + group_uncertainty = ( + 100.0 - group_support - group_opposition + ) + analysis["group_analysis"][group_name] = { "support": group_support, "opposition": group_opposition, "uncertainty": group_uncertainty, - "mep_count": len(meps) + "mep_count": len(meps), } - + # Calculate overall support weighted by group size total_meps = len(self.meps) if total_meps > 0: weighted_support = 0.0 weighted_opposition = 0.0 weighted_uncertainty = 0.0 - - for group_name, group_data in analysis["group_analysis"].items(): + + for group_name, group_data in analysis[ + "group_analysis" + ].items(): weight = group_data["mep_count"] / total_meps weighted_support += group_data["support"] * weight - weighted_opposition += group_data["opposition"] * weight - weighted_uncertainty += group_data["uncertainty"] * weight - + weighted_opposition += ( + group_data["opposition"] * weight + ) + weighted_uncertainty += ( + group_data["uncertainty"] * weight + ) + analysis["overall_support"] = weighted_support analysis["opposition"] = weighted_opposition analysis["uncertainty"] = weighted_uncertainty - + return analysis diff --git a/examples/simulations/euroswarm_parliament/euroswarm_parliament_example.py b/examples/simulations/euroswarm_parliament/euroswarm_parliament_example.py index 7dc60d02..b2ccf858 100644 --- a/examples/simulations/euroswarm_parliament/euroswarm_parliament_example.py +++ b/examples/simulations/euroswarm_parliament/euroswarm_parliament_example.py @@ -5,25 +5,21 @@ This script demonstrates the comprehensive democratic functionality of the EuroS including bill introduction, committee work, parliamentary debates, and democratic voting. """ -import json -import time -from datetime import datetime - # Import directly from the file from euroswarm_parliament import ( EuroSwarmParliament, VoteType, - ParliamentaryRole, - ParliamentaryMember ) def demonstrate_parliament_initialization(): """Demonstrate parliament initialization and basic functionality with cost optimization.""" - - print("\nEUROSWARM PARLIAMENT INITIALIZATION DEMONSTRATION (COST OPTIMIZED)") + + print( + "\nEUROSWARM PARLIAMENT INITIALIZATION DEMONSTRATION (COST OPTIMIZED)" + ) print("=" * 60) - + # Initialize the parliament with cost optimization parliament = EuroSwarmParliament( eu_data_file="EU.xml", @@ -35,487 +31,632 @@ def demonstrate_parliament_initialization(): enable_caching=True, # NEW: Enable response caching batch_size=25, # NEW: Batch size for concurrent execution budget_limit=100.0, # NEW: Budget limit in dollars - verbose=True + verbose=True, ) - + print(f"Parliament initialized with {len(parliament.meps)} MEPs") - + # Show parliament composition with cost stats composition = parliament.get_parliament_composition() - - print(f"\nPARLIAMENT COMPOSITION:") + + print("\nPARLIAMENT COMPOSITION:") print(f"Total MEPs: {composition['total_meps']}") - print(f"Loaded MEPs: {composition['loaded_meps']} (lazy loading active)") - - print(f"\nCOST OPTIMIZATION:") - cost_stats = composition['cost_stats'] - print(f"Budget Limit: ${cost_stats['budget_remaining'] + cost_stats['total_cost']:.2f}") + print( + f"Loaded MEPs: {composition['loaded_meps']} (lazy loading active)" + ) + + print("\nCOST OPTIMIZATION:") + cost_stats = composition["cost_stats"] + print( + f"Budget Limit: ${cost_stats['budget_remaining'] + cost_stats['total_cost']:.2f}" + ) print(f"Budget Used: ${cost_stats['total_cost']:.2f}") print(f"Budget Remaining: ${cost_stats['budget_remaining']:.2f}") print(f"Cache Hit Rate: {cost_stats['cache_hit_rate']:.1%}") - - print(f"\nPOLITICAL GROUP DISTRIBUTION:") - for group, data in composition['political_groups'].items(): - count = data['count'] - percentage = data['percentage'] + + print("\nPOLITICAL GROUP DISTRIBUTION:") + for group, data in composition["political_groups"].items(): + count = data["count"] + percentage = data["percentage"] print(f" {group}: {count} MEPs ({percentage:.1f}%)") - - print(f"\nCOMMITTEE LEADERSHIP:") - for committee_name, committee_data in composition['committees'].items(): - chair = committee_data['chair'] + + print("\nCOMMITTEE LEADERSHIP:") + for committee_name, committee_data in composition[ + "committees" + ].items(): + chair = committee_data["chair"] if chair: print(f" {committee_name}: {chair}") - + return parliament def demonstrate_individual_mep_interaction(parliament): """Demonstrate individual MEP interaction and personality.""" - + print("\nINDIVIDUAL MEP INTERACTION DEMONSTRATION") print("=" * 60) - + # Get a sample MEP sample_mep_name = list(parliament.meps.keys())[0] sample_mep = parliament.meps[sample_mep_name] - + print(f"Sample MEP: {sample_mep.full_name}") print(f"Country: {sample_mep.country}") print(f"Political Group: {sample_mep.political_group}") print(f"National Party: {sample_mep.national_party}") print(f"Committees: {', '.join(sample_mep.committees)}") print(f"Expertise Areas: {', '.join(sample_mep.expertise_areas)}") - + # Test MEP agent interaction if sample_mep.agent: test_prompt = "What are your views on European integration and how do you approach cross-border cooperation?" - + print(f"\nMEP Response to: '{test_prompt}'") print("-" * 50) - + try: response = sample_mep.agent.run(test_prompt) - print(response[:500] + "..." if len(response) > 500 else response) + print( + response[:500] + "..." + if len(response) > 500 + else response + ) except Exception as e: print(f"Error getting MEP response: {e}") def demonstrate_committee_work(parliament): """Demonstrate committee work and hearings.""" - + print("\nCOMMITTEE WORK DEMONSTRATION") print("=" * 60) - + # Get a real MEP as sponsor sponsor = list(parliament.meps.keys())[0] - + # Create a test bill bill = parliament.introduce_bill( title="European Digital Rights and Privacy Protection Act", description="Comprehensive legislation to strengthen digital rights, enhance privacy protection, and establish clear guidelines for data handling across the European Union.", bill_type=VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, committee="Legal Affairs", - sponsor=sponsor + sponsor=sponsor, ) - + print(f"Bill: {bill.title}") print(f"Committee: {bill.committee}") print(f"Sponsor: {bill.sponsor}") - + # Conduct committee hearing - print(f"\nCONDUCTING COMMITTEE HEARING...") - hearing_result = parliament.conduct_committee_hearing(bill.committee, bill) - + print("\nCONDUCTING COMMITTEE HEARING...") + hearing_result = parliament.conduct_committee_hearing( + bill.committee, bill + ) + print(f"Committee: {hearing_result['committee']}") print(f"Participants: {len(hearing_result['participants'])} MEPs") - print(f"Recommendation: {hearing_result['recommendations']['recommendation']}") - print(f"Support: {hearing_result['recommendations']['support_percentage']:.1f}%") - print(f"Oppose: {hearing_result['recommendations']['oppose_percentage']:.1f}%") - print(f"Amend: {hearing_result['recommendations']['amend_percentage']:.1f}%") + print( + f"Recommendation: {hearing_result['recommendations']['recommendation']}" + ) + print( + f"Support: {hearing_result['recommendations']['support_percentage']:.1f}%" + ) + print( + f"Oppose: {hearing_result['recommendations']['oppose_percentage']:.1f}%" + ) + print( + f"Amend: {hearing_result['recommendations']['amend_percentage']:.1f}%" + ) def demonstrate_parliamentary_debate(parliament): """Demonstrate parliamentary debate functionality.""" - + print("\nPARLIAMENTARY DEBATE DEMONSTRATION") print("=" * 60) - + # Get a real MEP as sponsor sponsor = list(parliament.meps.keys())[1] - + # Create a test bill bill = parliament.introduce_bill( title="European Green Deal Implementation Act", description="Legislation to implement the European Green Deal, including carbon neutrality targets, renewable energy investments, and sustainable development measures.", bill_type=VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, committee="Environment, Public Health and Food Safety", - sponsor=sponsor + sponsor=sponsor, ) - + print(f"Bill: {bill.title}") print(f"Description: {bill.description}") - + # Conduct parliamentary debate - print(f"\nCONDUCTING PARLIAMENTARY DEBATE...") - debate_result = parliament.conduct_parliamentary_debate(bill, max_speakers=10) - - print(f"Debate Participants: {len(debate_result['participants'])} MEPs") - print(f"Debate Analysis:") - print(f" Support: {debate_result['analysis']['support_count']} speakers ({debate_result['analysis']['support_percentage']:.1f}%)") - print(f" Oppose: {debate_result['analysis']['oppose_count']} speakers ({debate_result['analysis']['oppose_percentage']:.1f}%)") - print(f" Neutral: {debate_result['analysis']['neutral_count']} speakers ({debate_result['analysis']['neutral_percentage']:.1f}%)") + print("\nCONDUCTING PARLIAMENTARY DEBATE...") + debate_result = parliament.conduct_parliamentary_debate( + bill, max_speakers=10 + ) + + print( + f"Debate Participants: {len(debate_result['participants'])} MEPs" + ) + print("Debate Analysis:") + print( + f" Support: {debate_result['analysis']['support_count']} speakers ({debate_result['analysis']['support_percentage']:.1f}%)" + ) + print( + f" Oppose: {debate_result['analysis']['oppose_count']} speakers ({debate_result['analysis']['oppose_percentage']:.1f}%)" + ) + print( + f" Neutral: {debate_result['analysis']['neutral_count']} speakers ({debate_result['analysis']['neutral_percentage']:.1f}%)" + ) def demonstrate_democratic_voting(parliament): """Demonstrate democratic voting functionality.""" - + print("\nDEMOCRATIC VOTING DEMONSTRATION") print("=" * 60) - + # Get a real MEP as sponsor sponsor = list(parliament.meps.keys())[2] - + # Create a test bill bill = parliament.introduce_bill( title="European Social Rights and Labor Protection Act", description="Legislation to strengthen social rights, improve labor conditions, and ensure fair treatment of workers across the European Union.", bill_type=VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, committee="Employment and Social Affairs", - sponsor=sponsor + sponsor=sponsor, ) - + print(f"Bill: {bill.title}") print(f"Sponsor: {bill.sponsor}") - + # Conduct democratic vote - print(f"\nCONDUCTING DEMOCRATIC VOTE...") + print("\nCONDUCTING DEMOCRATIC VOTE...") vote_result = parliament.conduct_democratic_vote(bill) - + # Calculate percentages - total_votes = vote_result.votes_for + vote_result.votes_against + vote_result.abstentions - in_favor_percentage = (vote_result.votes_for / total_votes * 100) if total_votes > 0 else 0 - against_percentage = (vote_result.votes_against / total_votes * 100) if total_votes > 0 else 0 - abstentions_percentage = (vote_result.abstentions / total_votes * 100) if total_votes > 0 else 0 - - print(f"Vote Results:") + total_votes = ( + vote_result.votes_for + + vote_result.votes_against + + vote_result.abstentions + ) + in_favor_percentage = ( + (vote_result.votes_for / total_votes * 100) + if total_votes > 0 + else 0 + ) + against_percentage = ( + (vote_result.votes_against / total_votes * 100) + if total_votes > 0 + else 0 + ) + abstentions_percentage = ( + (vote_result.abstentions / total_votes * 100) + if total_votes > 0 + else 0 + ) + + print("Vote Results:") print(f" Total Votes: {total_votes}") - print(f" In Favor: {vote_result.votes_for} ({in_favor_percentage:.1f}%)") - print(f" Against: {vote_result.votes_against} ({against_percentage:.1f}%)") - print(f" Abstentions: {vote_result.abstentions} ({abstentions_percentage:.1f}%)") + print( + f" In Favor: {vote_result.votes_for} ({in_favor_percentage:.1f}%)" + ) + print( + f" Against: {vote_result.votes_against} ({against_percentage:.1f}%)" + ) + print( + f" Abstentions: {vote_result.abstentions} ({abstentions_percentage:.1f}%)" + ) print(f" Result: {vote_result.result.value}") - + # Show political group breakdown if available - if hasattr(vote_result, 'group_votes') and vote_result.group_votes: - print(f"\nPOLITICAL GROUP BREAKDOWN:") + if ( + hasattr(vote_result, "group_votes") + and vote_result.group_votes + ): + print("\nPOLITICAL GROUP BREAKDOWN:") for group, votes in vote_result.group_votes.items(): - print(f" {group}: {votes['in_favor']}/{votes['total']} in favor ({votes['percentage']:.1f}%)") + print( + f" {group}: {votes['in_favor']}/{votes['total']} in favor ({votes['percentage']:.1f}%)" + ) else: - print(f"\nIndividual votes recorded: {len(vote_result.individual_votes)} MEPs") + print( + f"\nIndividual votes recorded: {len(vote_result.individual_votes)} MEPs" + ) def demonstrate_complete_democratic_session(parliament): """Demonstrate a complete democratic parliamentary session.""" - + print("\nCOMPLETE DEMOCRATIC SESSION DEMONSTRATION") print("=" * 60) - + # Get a real MEP as sponsor sponsor = list(parliament.meps.keys())[3] - + # Run complete session session_result = parliament.run_democratic_session( bill_title="European Innovation and Technology Advancement Act", bill_description="Comprehensive legislation to promote innovation, support technology startups, and establish Europe as a global leader in digital transformation and technological advancement.", bill_type=VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, committee="Industry, Research and Energy", - sponsor=sponsor + sponsor=sponsor, ) - - print(f"Session Results:") + + print("Session Results:") print(f" Bill: {session_result['bill'].title}") - print(f" Committee Hearing: {session_result['hearing']['recommendations']['recommendation']}") - print(f" Debate Participants: {len(session_result['debate']['participants'])} MEPs") + print( + f" Committee Hearing: {session_result['hearing']['recommendations']['recommendation']}" + ) + print( + f" Debate Participants: {len(session_result['debate']['participants'])} MEPs" + ) print(f" Final Vote: {session_result['vote']['result']}") - print(f" Vote Margin: {session_result['vote']['in_favor_percentage']:.1f}% in favor") + print( + f" Vote Margin: {session_result['vote']['in_favor_percentage']:.1f}% in favor" + ) def demonstrate_political_analysis(parliament): """Demonstrate political analysis and voting prediction.""" - + print("\nPOLITICAL ANALYSIS DEMONSTRATION") print("=" * 60) - + # Get a real MEP as sponsor sponsor = list(parliament.meps.keys())[4] - + # Create a test bill bill = parliament.introduce_bill( title="European Climate Action and Sustainability Act", description="Comprehensive climate action legislation including carbon pricing, renewable energy targets, and sustainable development measures.", bill_type=VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, committee="Environment, Public Health and Food Safety", - sponsor=sponsor + sponsor=sponsor, ) - + print(f"Bill: {bill.title}") print(f"Sponsor: {bill.sponsor}") - + # Analyze political landscape analysis = parliament.analyze_political_landscape(bill) - - print(f"\nPOLITICAL LANDSCAPE ANALYSIS:") + + print("\nPOLITICAL LANDSCAPE ANALYSIS:") print(f" Overall Support: {analysis['overall_support']:.1f}%") print(f" Opposition: {analysis['opposition']:.1f}%") print(f" Uncertainty: {analysis['uncertainty']:.1f}%") - - print(f"\nPOLITICAL GROUP ANALYSIS:") - for group, data in analysis['group_analysis'].items(): - print(f" {group}: {data['support']:.1f}% support, {data['opposition']:.1f}% opposition") + + print("\nPOLITICAL GROUP ANALYSIS:") + for group, data in analysis["group_analysis"].items(): + print( + f" {group}: {data['support']:.1f}% support, {data['opposition']:.1f}% opposition" + ) def demonstrate_hierarchical_democratic_voting(parliament): """Demonstrate hierarchical democratic voting with political group boards.""" - + print("\nHIERARCHICAL DEMOCRATIC VOTING DEMONSTRATION") print("=" * 60) - + # Get a real MEP as sponsor sponsor = list(parliament.meps.keys())[5] - + # Create a test bill bill = parliament.introduce_bill( title="European Climate Action and Sustainability Act", description="Comprehensive climate action legislation including carbon pricing, renewable energy targets, and sustainable development measures.", bill_type=VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, committee="Environment, Public Health and Food Safety", - sponsor=sponsor + sponsor=sponsor, ) - + print(f"Bill: {bill.title}") print(f"Sponsor: {bill.sponsor}") - + # Conduct hierarchical vote - print(f"\nCONDUCTING HIERARCHICAL DEMOCRATIC VOTE...") - hierarchical_result = parliament.conduct_hierarchical_democratic_vote(bill) - - print(f"Hierarchical Vote Results:") + print("\nCONDUCTING HIERARCHICAL DEMOCRATIC VOTE...") + hierarchical_result = ( + parliament.conduct_hierarchical_democratic_vote(bill) + ) + + print("Hierarchical Vote Results:") print(f" Total Votes: {hierarchical_result['total_votes']}") - print(f" In Favor: {hierarchical_result['in_favor']} ({hierarchical_result['in_favor_percentage']:.1f}%)") - print(f" Against: {hierarchical_result['against']} ({hierarchical_result['against_percentage']:.1f}%)") + print( + f" In Favor: {hierarchical_result['in_favor']} ({hierarchical_result['in_favor_percentage']:.1f}%)" + ) + print( + f" Against: {hierarchical_result['against']} ({hierarchical_result['against_percentage']:.1f}%)" + ) print(f" Result: {hierarchical_result['result']}") - - print(f"\nPOLITICAL GROUP BOARD DECISIONS:") - for group, decision in hierarchical_result['group_decisions'].items(): - print(f" {group}: {decision['decision']} ({decision['confidence']:.1f}% confidence)") + + print("\nPOLITICAL GROUP BOARD DECISIONS:") + for group, decision in hierarchical_result[ + "group_decisions" + ].items(): + print( + f" {group}: {decision['decision']} ({decision['confidence']:.1f}% confidence)" + ) def demonstrate_complete_hierarchical_session(parliament): """Demonstrate a complete hierarchical democratic session.""" - + print("\nCOMPLETE HIERARCHICAL DEMOCRATIC SESSION DEMONSTRATION") print("=" * 60) - + # Get a real MEP as sponsor sponsor = list(parliament.meps.keys())[6] - + # Run complete hierarchical session session_result = parliament.run_hierarchical_democratic_session( bill_title="European Climate Action and Sustainability Act", bill_description="Comprehensive climate action legislation including carbon pricing, renewable energy targets, and sustainable development measures.", bill_type=VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, committee="Environment, Public Health and Food Safety", - sponsor=sponsor + sponsor=sponsor, ) - - print(f"Hierarchical Session Results:") + + print("Hierarchical Session Results:") print(f" Bill: {session_result['bill'].title}") - print(f" Committee Hearing: {session_result['hearing']['recommendations']['recommendation']}") - print(f" Debate Participants: {len(session_result['debate']['participants'])} MEPs") + print( + f" Committee Hearing: {session_result['hearing']['recommendations']['recommendation']}" + ) + print( + f" Debate Participants: {len(session_result['debate']['participants'])} MEPs" + ) print(f" Final Vote: {session_result['vote']['result']}") - print(f" Vote Margin: {session_result['vote']['in_favor_percentage']:.1f}% in favor") + print( + f" Vote Margin: {session_result['vote']['in_favor_percentage']:.1f}% in favor" + ) def demonstrate_wikipedia_personalities(parliament): """Demonstrate the Wikipedia personality system for realistic MEP behavior.""" - + print("\nWIKIPEDIA PERSONALITY SYSTEM DEMONSTRATION") print("=" * 60) - + # Check if Wikipedia personalities are available if not parliament.enable_wikipedia_personalities: print("Wikipedia personality system not available") - print("To enable: Install required dependencies and run Wikipedia scraper") + print( + "To enable: Install required dependencies and run Wikipedia scraper" + ) return - - print(f"Wikipedia personality system enabled") - print(f"Loaded {len(parliament.personality_profiles)} personality profiles") - + + print("Wikipedia personality system enabled") + print( + f"Loaded {len(parliament.personality_profiles)} personality profiles" + ) + # Show sample personality profiles - print(f"\nSAMPLE PERSONALITY PROFILES:") + print("\nSAMPLE PERSONALITY PROFILES:") print("-" * 40) - + sample_count = 0 for mep_name, profile in parliament.personality_profiles.items(): if sample_count >= 3: # Show only 3 samples break - + print(f"\n{mep_name}") - print(f" Wikipedia URL: {profile.wikipedia_url if profile.wikipedia_url else 'Not available'}") - print(f" Summary: {profile.summary[:200]}..." if profile.summary else "No summary available") - print(f" Political Views: {profile.political_views[:150]}..." if profile.political_views else "Based on party alignment") - print(f" Policy Focus: {profile.policy_focus[:150]}..." if profile.policy_focus else "General parliamentary work") - print(f" Achievements: {profile.achievements[:150]}..." if profile.achievements else "Parliamentary service") + print( + f" Wikipedia URL: {profile.wikipedia_url if profile.wikipedia_url else 'Not available'}" + ) + print( + f" Summary: {profile.summary[:200]}..." + if profile.summary + else "No summary available" + ) + print( + f" Political Views: {profile.political_views[:150]}..." + if profile.political_views + else "Based on party alignment" + ) + print( + f" Policy Focus: {profile.policy_focus[:150]}..." + if profile.policy_focus + else "General parliamentary work" + ) + print( + f" Achievements: {profile.achievements[:150]}..." + if profile.achievements + else "Parliamentary service" + ) print(f" Last Updated: {profile.last_updated}") - + sample_count += 1 - + # Demonstrate personality-driven voting - print(f"\nPERSONALITY-DRIVEN VOTING DEMONSTRATION:") + print("\nPERSONALITY-DRIVEN VOTING DEMONSTRATION:") print("-" * 50) - + # Create a test bill that would trigger different personality responses bill = parliament.introduce_bill( title="European Climate Action and Green Technology Investment Act", description="Comprehensive legislation to accelerate Europe's transition to renewable energy, including massive investments in green technology, carbon pricing mechanisms, and support for affected industries and workers.", bill_type=VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, committee="Environment", - sponsor="Climate Action Leader" + sponsor="Climate Action Leader", ) - + print(f"Bill: {bill.title}") print(f"Description: {bill.description}") - + # Show how different MEPs with Wikipedia personalities would respond - print(f"\nPERSONALITY-BASED RESPONSES:") + print("\nPERSONALITY-BASED RESPONSES:") print("-" * 40) - + sample_meps = list(parliament.personality_profiles.keys())[:3] - + for mep_name in sample_meps: mep = parliament.meps.get(mep_name) profile = parliament.personality_profiles.get(mep_name) - + if mep and profile: print(f"\n{mep_name} ({mep.political_group})") - + # Show personality influence if profile.political_views: - print(f" Political Views: {profile.political_views[:100]}...") - + print( + f" Political Views: {profile.political_views[:100]}..." + ) + if profile.policy_focus: - print(f" Policy Focus: {profile.policy_focus[:100]}...") - + print( + f" Policy Focus: {profile.policy_focus[:100]}..." + ) + # Predict voting behavior based on personality - if "environment" in profile.policy_focus.lower() or "climate" in profile.political_views.lower(): + if ( + "environment" in profile.policy_focus.lower() + or "climate" in profile.political_views.lower() + ): predicted_vote = "LIKELY SUPPORT" - reasoning = "Environmental policy focus and climate advocacy" - elif "economic" in profile.policy_focus.lower() or "business" in profile.political_views.lower(): + reasoning = ( + "Environmental policy focus and climate advocacy" + ) + elif ( + "economic" in profile.policy_focus.lower() + or "business" in profile.political_views.lower() + ): predicted_vote = "LIKELY OPPOSE" reasoning = "Economic concerns about investment costs" else: predicted_vote = "UNCERTAIN" - reasoning = "Mixed considerations based on party alignment" - + reasoning = ( + "Mixed considerations based on party alignment" + ) + print(f" Predicted Vote: {predicted_vote}") print(f" Reasoning: {reasoning}") - + # Demonstrate scraping functionality - print(f"\nWIKIPEDIA SCRAPING CAPABILITIES:") + print("\nWIKIPEDIA SCRAPING CAPABILITIES:") print("-" * 50) print("Can scrape Wikipedia data for all 717 MEPs") - print("Extracts political views, career history, and achievements") + print( + "Extracts political views, career history, and achievements" + ) print("Creates detailed personality profiles in JSON format") - print("Integrates real personality data into AI agent system prompts") + print( + "Integrates real personality data into AI agent system prompts" + ) print("Enables realistic, personality-driven voting behavior") print("Respectful API usage with configurable delays") - - print(f"\nTo scrape all MEP personalities:") + + print("\nTo scrape all MEP personalities:") print(" parliament.scrape_wikipedia_personalities(delay=1.0)") - print(" # This will create personality profiles for all 717 MEPs") + print( + " # This will create personality profiles for all 717 MEPs" + ) print(" # Profiles are saved in 'mep_personalities/' directory") def demonstrate_optimized_parliamentary_session(parliament): """Demonstrate cost-optimized parliamentary session.""" - + print("\nCOST-OPTIMIZED PARLIAMENTARY SESSION DEMONSTRATION") print("=" * 60) - + # Run optimized session with cost limit session_result = parliament.run_optimized_parliamentary_session( bill_title="European Digital Rights and Privacy Protection Act", bill_description="Comprehensive legislation to strengthen digital rights, enhance privacy protection, and establish clear guidelines for data handling across the European Union.", bill_type=VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, committee="Legal Affairs", - max_cost=25.0 # Max $25 for this session - ) - - print(f"Session Results:") - print(f" Bill: {session_result['session_summary']['bill_title']}") - print(f" Final Outcome: {session_result['session_summary']['final_outcome']}") - print(f" Total Cost: ${session_result['session_summary']['total_cost']:.2f}") - print(f" Budget Remaining: ${session_result['cost_stats']['budget_remaining']:.2f}") - + max_cost=25.0, # Max $25 for this session + ) + + print("Session Results:") + print( + f" Bill: {session_result['session_summary']['bill_title']}" + ) + print( + f" Final Outcome: {session_result['session_summary']['final_outcome']}" + ) + print( + f" Total Cost: ${session_result['session_summary']['total_cost']:.2f}" + ) + print( + f" Budget Remaining: ${session_result['cost_stats']['budget_remaining']:.2f}" + ) + # Show detailed cost statistics cost_stats = parliament.get_cost_statistics() - print(f"\nDETAILED COST STATISTICS:") + print("\nDETAILED COST STATISTICS:") print(f" Total Tokens Used: {cost_stats['total_tokens']:,}") print(f" Requests Made: {cost_stats['requests_made']}") print(f" Cache Hits: {cost_stats['cache_hits']}") print(f" Cache Hit Rate: {cost_stats['cache_hit_rate']:.1%}") - print(f" Loading Efficiency: {cost_stats['loading_efficiency']:.1%}") + print( + f" Loading Efficiency: {cost_stats['loading_efficiency']:.1%}" + ) print(f" Cache Size: {cost_stats['cache_size']} entries") - + return session_result def main(): """Main demonstration function.""" - + print("EUROSWARM PARLIAMENT - COST OPTIMIZED DEMONSTRATION") print("=" * 60) - print("This demonstration shows the EuroSwarm Parliament with cost optimization features:") + print( + "This demonstration shows the EuroSwarm Parliament with cost optimization features:" + ) print("• Lazy loading of MEP agents (only create when needed)") print("• Response caching (avoid repeated API calls)") print("• Batch processing (control memory and cost)") print("• Budget controls (hard limits on spending)") print("• Cost tracking (real-time monitoring)") - + # Initialize parliament with cost optimization parliament = demonstrate_parliament_initialization() - + # Demonstrate individual MEP interaction (will trigger lazy loading) demonstrate_individual_mep_interaction(parliament) - + # Demonstrate committee work with cost optimization demonstrate_committee_work(parliament) - + # Demonstrate parliamentary debate with cost optimization demonstrate_parliamentary_debate(parliament) - + # Demonstrate democratic voting with cost optimization demonstrate_democratic_voting(parliament) - + # Demonstrate political analysis with cost optimization demonstrate_political_analysis(parliament) - + # Demonstrate optimized parliamentary session demonstrate_optimized_parliamentary_session(parliament) - + # Show final cost statistics final_stats = parliament.get_cost_statistics() - print(f"\nFINAL COST STATISTICS:") + print("\nFINAL COST STATISTICS:") print(f"Total Cost: ${final_stats['total_cost']:.2f}") print(f"Budget Remaining: ${final_stats['budget_remaining']:.2f}") print(f"Cache Hit Rate: {final_stats['cache_hit_rate']:.1%}") - print(f"Loading Efficiency: {final_stats['loading_efficiency']:.1%}") - - print(f"\n✅ COST OPTIMIZATION DEMONSTRATION COMPLETED!") - print(f"✅ EuroSwarm Parliament now supports cost-effective large-scale simulations") - print(f"✅ Lazy loading: {final_stats['loaded_meps']}/{final_stats['total_meps']} MEPs loaded") + print( + f"Loading Efficiency: {final_stats['loading_efficiency']:.1%}" + ) + + print("\n✅ COST OPTIMIZATION DEMONSTRATION COMPLETED!") + print( + "✅ EuroSwarm Parliament now supports cost-effective large-scale simulations" + ) + print( + f"✅ Lazy loading: {final_stats['loaded_meps']}/{final_stats['total_meps']} MEPs loaded" + ) print(f"✅ Caching: {final_stats['cache_hit_rate']:.1%} hit rate") - print(f"✅ Budget control: ${final_stats['total_cost']:.2f} spent of ${final_stats['budget_remaining'] + final_stats['total_cost']:.2f} budget") + print( + f"✅ Budget control: ${final_stats['total_cost']:.2f} spent of ${final_stats['budget_remaining'] + final_stats['total_cost']:.2f} budget" + ) if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/examples/simulations/euroswarm_parliament/mass_agent_template.py b/examples/simulations/euroswarm_parliament/mass_agent_template.py index a6e29d8c..fc42e3f9 100644 --- a/examples/simulations/euroswarm_parliament/mass_agent_template.py +++ b/examples/simulations/euroswarm_parliament/mass_agent_template.py @@ -17,13 +17,10 @@ Key Features: import os import random import json -import time import hashlib -from typing import Dict, List, Optional, Union, Any, Set +from typing import Dict, List, Optional, Any from dataclasses import dataclass, field from enum import Enum -from datetime import datetime -from functools import lru_cache from swarms import Agent from swarms.structs.multi_agent_exec import run_agents_concurrently @@ -31,10 +28,6 @@ from swarms.structs.board_of_directors_swarm import ( BoardOfDirectorsSwarm, BoardMember, BoardMemberRole, - BoardDecisionType, - BoardSpec, - BoardOrder, - BoardDecision, enable_board_feature, ) from swarms.utils.loguru_logger import initialize_logger @@ -48,7 +41,7 @@ enable_board_feature() class AgentRole(str, Enum): """Enumeration of agent roles and specializations.""" - + WORKER = "worker" MANAGER = "manager" SPECIALIST = "specialist" @@ -61,7 +54,7 @@ class AgentRole(str, Enum): class AgentCategory(str, Enum): """Enumeration of agent categories for organization.""" - + TECHNICAL = "technical" CREATIVE = "creative" ANALYTICAL = "analytical" @@ -74,7 +67,7 @@ class AgentCategory(str, Enum): class AgentProfile: """ Represents a single agent in the mass agent system. - + Attributes: name: Unique name of the agent role: Primary role of the agent @@ -86,7 +79,7 @@ class AgentProfile: agent: The AI agent instance (lazy loaded) is_loaded: Whether the agent has been instantiated """ - + name: str role: AgentRole category: AgentCategory @@ -102,7 +95,7 @@ class AgentProfile: class AgentGroup: """ Represents a group of agents with similar roles or categories. - + Attributes: name: Name of the group category: Category of the group @@ -112,7 +105,7 @@ class AgentGroup: group_swarm: Board of Directors swarm for this group is_swarm_loaded: Whether the swarm has been instantiated """ - + name: str category: AgentCategory agents: List[str] = field(default_factory=list) @@ -125,28 +118,30 @@ class AgentGroup: @dataclass class CostTracker: """Track costs and usage for budget management.""" - + total_tokens_used: int = 0 total_cost_estimate: float = 0.0 budget_limit: float = 100.0 # Default $100 budget token_cost_per_1m: float = 0.15 # GPT-4o-mini cost requests_made: int = 0 cache_hits: int = 0 - + def add_tokens(self, tokens: int): """Add tokens used and calculate cost.""" self.total_tokens_used += tokens - self.total_cost_estimate = (self.total_tokens_used / 1_000_000) * self.token_cost_per_1m + self.total_cost_estimate = ( + self.total_tokens_used / 1_000_000 + ) * self.token_cost_per_1m self.requests_made += 1 - + def add_cache_hit(self): """Record a cache hit.""" self.cache_hits += 1 - + def check_budget(self) -> bool: """Check if within budget.""" return self.total_cost_estimate <= self.budget_limit - + def get_stats(self) -> Dict[str, Any]: """Get cost statistics.""" return { @@ -154,19 +149,22 @@ class CostTracker: "total_cost": self.total_cost_estimate, "requests_made": self.requests_made, "cache_hits": self.cache_hits, - "cache_hit_rate": self.cache_hits / max(1, self.requests_made + self.cache_hits), - "budget_remaining": max(0, self.budget_limit - self.total_cost_estimate) + "cache_hit_rate": self.cache_hits + / max(1, self.requests_made + self.cache_hits), + "budget_remaining": max( + 0, self.budget_limit - self.total_cost_estimate + ), } class MassAgentTemplate: """ Template for creating large-scale multi-agent systems with cost optimization. - + This class provides a framework for generating hundreds of agents on the fly, organizing them into groups, and managing their interactions with cost controls. """ - + def __init__( self, data_source: str = None, # Path to data file (CSV, JSON, XML, etc.) @@ -181,7 +179,7 @@ class MassAgentTemplate: ): """ Initialize the Mass Agent Template with cost optimization. - + Args: data_source: Path to data file containing agent information agent_count: Target number of agents to generate @@ -195,65 +193,78 @@ class MassAgentTemplate: """ self.data_source = data_source self.agent_count = agent_count - self.enable_hierarchical_organization = enable_hierarchical_organization + self.enable_hierarchical_organization = ( + enable_hierarchical_organization + ) self.enable_group_swarms = enable_group_swarms self.enable_lazy_loading = enable_lazy_loading self.enable_caching = enable_caching self.batch_size = batch_size self.verbose = verbose - + # Initialize cost tracking self.cost_tracker = CostTracker(budget_limit=budget_limit) - + # Initialize agent storage self.agents: Dict[str, AgentProfile] = {} self.groups: Dict[str, AgentGroup] = {} self.categories: Dict[AgentCategory, List[str]] = {} - + # Initialize caching self.response_cache: Dict[str, str] = {} - + # Load agent profiles (without creating agents) self._load_agent_profiles() - + if self.enable_hierarchical_organization: self._organize_agents() - + if self.verbose: - logger.info(f"Mass Agent Template initialized with {len(self.agents)} agent profiles") - logger.info(f"Lazy loading: {self.enable_lazy_loading}, Caching: {self.enable_caching}") - logger.info(f"Budget limit: ${budget_limit}, Batch size: {batch_size}") - + logger.info( + f"Mass Agent Template initialized with {len(self.agents)} agent profiles" + ) + logger.info( + f"Lazy loading: {self.enable_lazy_loading}, Caching: {self.enable_caching}" + ) + logger.info( + f"Budget limit: ${budget_limit}, Batch size: {batch_size}" + ) + def _load_agent_profiles(self) -> List[Dict[str, Any]]: """ Load agent profiles from the specified data source. - + This method loads agent data but doesn't create AI agents yet (lazy loading). - + Returns: List[Dict[str, Any]]: List of agent data dictionaries """ agent_data = [] - + if self.data_source and os.path.exists(self.data_source): # Load from file - customize based on your data format try: - if self.data_source.endswith('.json'): - with open(self.data_source, 'r', encoding='utf-8') as f: + if self.data_source.endswith(".json"): + with open( + self.data_source, "r", encoding="utf-8" + ) as f: agent_data = json.load(f) - elif self.data_source.endswith('.csv'): + elif self.data_source.endswith(".csv"): import pandas as pd + df = pd.read_csv(self.data_source) - agent_data = df.to_dict('records') + agent_data = df.to_dict("records") else: - logger.warning(f"Unsupported data format: {self.data_source}") + logger.warning( + f"Unsupported data format: {self.data_source}" + ) except Exception as e: logger.error(f"Error loading agent data: {e}") - + # If no data loaded, generate synthetic data if not agent_data: agent_data = self._generate_synthetic_data() - + # Create agent profiles (without instantiating agents) for data in agent_data: agent_profile = AgentProfile( @@ -265,68 +276,72 @@ class MassAgentTemplate: skills=data["skills"], experience_level=data["experience_level"], agent=None, # Will be created on demand - is_loaded=False + is_loaded=False, ) - + self.agents[data["name"]] = agent_profile - + return agent_data - + def _load_agent(self, agent_name: str) -> Optional[Agent]: """ Lazy load a single agent on demand. - + Args: agent_name: Name of the agent to load - + Returns: Optional[Agent]: Loaded agent or None if not found """ if agent_name not in self.agents: return None - + profile = self.agents[agent_name] - + # Check if already loaded if profile.is_loaded and profile.agent: return profile.agent - + # Create agent (no cost for creation, only for running) profile.agent = self._create_agent(profile) profile.is_loaded = True - + if self.verbose: logger.info(f"Loaded agent: {agent_name}") - + return profile.agent - - def _load_agents_batch(self, agent_names: List[str]) -> List[Agent]: + + def _load_agents_batch( + self, agent_names: List[str] + ) -> List[Agent]: """ Load multiple agents in a batch. - + Args: agent_names: List of agent names to load - + Returns: List[Agent]: List of loaded agents """ loaded_agents = [] - + for agent_name in agent_names: agent = self._load_agent(agent_name) if agent: loaded_agents.append(agent) - + return loaded_agents - - def _get_cache_key(self, task: str, agent_names: List[str]) -> str: + + def _get_cache_key( + self, task: str, agent_names: List[str] + ) -> str: """ Generate a cache key for a task and agent combination. - + Args: task: Task to execute agent_names: List of agent names - + Returns: str: Cache key """ @@ -334,32 +349,32 @@ class MassAgentTemplate: sorted_agents = sorted(agent_names) content = f"{task}:{':'.join(sorted_agents)}" return hashlib.md5(content.encode()).hexdigest() - + def _check_cache(self, cache_key: str) -> Optional[str]: """ Check if a response is cached. - + Args: cache_key: Cache key to check - + Returns: Optional[str]: Cached response or None """ if not self.enable_caching: return None - + cached_response = self.response_cache.get(cache_key) if cached_response: self.cost_tracker.add_cache_hit() if self.verbose: logger.info(f"Cache hit for key: {cache_key[:20]}...") - + return cached_response - + def _cache_response(self, cache_key: str, response: str): """ Cache a response. - + Args: cache_key: Cache key response: Response to cache @@ -367,101 +382,167 @@ class MassAgentTemplate: if self.enable_caching: self.response_cache[cache_key] = response if self.verbose: - logger.info(f"Cached response for key: {cache_key[:20]}...") - + logger.info( + f"Cached response for key: {cache_key[:20]}..." + ) + def _generate_synthetic_data(self) -> List[Dict[str, Any]]: """ Generate synthetic agent data for demonstration purposes. - + Returns: List[Dict[str, Any]]: List of synthetic agent data """ synthetic_data = [] - + # Define sample data for different agent types sample_agents = [ { "name": "Alex_Developer", "role": AgentRole.SPECIALIST, "category": AgentCategory.TECHNICAL, - "specialization": ["Python", "Machine Learning", "API Development"], - "personality_traits": ["analytical", "detail-oriented", "problem-solver"], - "skills": ["Python", "TensorFlow", "FastAPI", "Docker"], - "experience_level": "senior" + "specialization": [ + "Python", + "Machine Learning", + "API Development", + ], + "personality_traits": [ + "analytical", + "detail-oriented", + "problem-solver", + ], + "skills": [ + "Python", + "TensorFlow", + "FastAPI", + "Docker", + ], + "experience_level": "senior", }, { "name": "Sarah_Designer", "role": AgentRole.CREATOR, "category": AgentCategory.CREATIVE, - "specialization": ["UI/UX Design", "Visual Design", "Brand Identity"], - "personality_traits": ["creative", "user-focused", "aesthetic"], - "skills": ["Figma", "Adobe Creative Suite", "User Research", "Prototyping"], - "experience_level": "senior" + "specialization": [ + "UI/UX Design", + "Visual Design", + "Brand Identity", + ], + "personality_traits": [ + "creative", + "user-focused", + "aesthetic", + ], + "skills": [ + "Figma", + "Adobe Creative Suite", + "User Research", + "Prototyping", + ], + "experience_level": "senior", }, { "name": "Mike_Analyst", "role": AgentRole.ANALYST, "category": AgentCategory.ANALYTICAL, - "specialization": ["Data Analysis", "Business Intelligence", "Market Research"], - "personality_traits": ["data-driven", "curious", "insightful"], + "specialization": [ + "Data Analysis", + "Business Intelligence", + "Market Research", + ], + "personality_traits": [ + "data-driven", + "curious", + "insightful", + ], "skills": ["SQL", "Python", "Tableau", "Statistics"], - "experience_level": "expert" + "experience_level": "expert", }, { "name": "Lisa_Manager", "role": AgentRole.MANAGER, "category": AgentCategory.STRATEGIC, - "specialization": ["Project Management", "Team Leadership", "Strategic Planning"], - "personality_traits": ["organized", "leadership", "strategic"], - "skills": ["Agile", "Scrum", "Risk Management", "Stakeholder Communication"], - "experience_level": "senior" + "specialization": [ + "Project Management", + "Team Leadership", + "Strategic Planning", + ], + "personality_traits": [ + "organized", + "leadership", + "strategic", + ], + "skills": [ + "Agile", + "Scrum", + "Risk Management", + "Stakeholder Communication", + ], + "experience_level": "senior", }, { "name": "Tom_Coordinator", "role": AgentRole.COORDINATOR, "category": AgentCategory.OPERATIONAL, - "specialization": ["Process Optimization", "Workflow Management", "Resource Allocation"], - "personality_traits": ["efficient", "coordinated", "systematic"], - "skills": ["Process Mapping", "Automation", "Resource Planning", "Quality Assurance"], - "experience_level": "senior" - } + "specialization": [ + "Process Optimization", + "Workflow Management", + "Resource Allocation", + ], + "personality_traits": [ + "efficient", + "coordinated", + "systematic", + ], + "skills": [ + "Process Mapping", + "Automation", + "Resource Planning", + "Quality Assurance", + ], + "experience_level": "senior", + }, ] - + # Generate the specified number of agents for i in range(self.agent_count): # Use sample data as template and create variations template = random.choice(sample_agents) - + agent_data = { "name": f"{template['name']}_{i:04d}", "role": template["role"], "category": template["category"], "specialization": template["specialization"].copy(), - "personality_traits": template["personality_traits"].copy(), + "personality_traits": template[ + "personality_traits" + ].copy(), "skills": template["skills"].copy(), - "experience_level": template["experience_level"] + "experience_level": template["experience_level"], } - + # Add some randomization for variety if random.random() < 0.3: - agent_data["experience_level"] = random.choice(["junior", "senior", "expert"]) - + agent_data["experience_level"] = random.choice( + ["junior", "senior", "expert"] + ) + synthetic_data.append(agent_data) - + return synthetic_data - + def _create_agent(self, profile: AgentProfile) -> Agent: """ Create an AI agent for the given profile. - + Args: profile: Agent profile data - + Returns: Agent: AI agent instance """ system_prompt = self._generate_agent_system_prompt(profile) - + return Agent( agent_name=profile.name, system_prompt=system_prompt, @@ -469,14 +550,16 @@ class MassAgentTemplate: max_loops=3, verbose=self.verbose, ) - - def _generate_agent_system_prompt(self, profile: AgentProfile) -> str: + + def _generate_agent_system_prompt( + self, profile: AgentProfile + ) -> str: """ Generate a comprehensive system prompt for an agent. - + Args: profile: Agent profile data - + Returns: str: System prompt for the agent """ @@ -513,12 +596,12 @@ When working on tasks: Remember: You are part of a large multi-agent system. Your unique combination of role, skills, and personality makes you valuable to the team. """ - + return prompt - + def _get_role_responsibilities(self, role: AgentRole) -> str: """Get responsibilities for a specific role.""" - + responsibilities = { AgentRole.WORKER: """ - Execute assigned tasks efficiently and accurately @@ -526,126 +609,137 @@ Remember: You are part of a large multi-agent system. Your unique combination of - Report progress and any issues encountered - Maintain quality standards in all work - Collaborate with team members as needed""", - AgentRole.MANAGER: """ - Oversee team activities and coordinate efforts - Set priorities and allocate resources - Monitor progress and ensure deadlines are met - Provide guidance and support to team members - Make strategic decisions for the team""", - AgentRole.SPECIALIST: """ - Provide expert knowledge in specific domains - Solve complex technical problems - Mentor other agents in your area of expertise - Stay updated on latest developments in your field - Contribute specialized insights to projects""", - AgentRole.COORDINATOR: """ - Facilitate communication between different groups - Ensure smooth workflow and process optimization - Manage dependencies and resource allocation - Track project timelines and milestones - Resolve conflicts and bottlenecks""", - AgentRole.ANALYST: """ - Analyze data and extract meaningful insights - Identify patterns and trends - Provide evidence-based recommendations - Create reports and visualizations - Support decision-making with data""", - AgentRole.CREATOR: """ - Generate innovative ideas and solutions - Design and develop new content or products - Think creatively and outside the box - Prototype and iterate on concepts - Inspire and motivate other team members""", - AgentRole.VALIDATOR: """ - Review and validate work quality - Ensure compliance with standards and requirements - Provide constructive feedback - Identify potential issues and risks - Maintain quality assurance processes""", - AgentRole.EXECUTOR: """ - Implement plans and strategies - Execute tasks with precision and efficiency - Adapt to changing circumstances - Ensure successful completion of objectives -- Maintain focus on results and outcomes""" +- Maintain focus on results and outcomes""", } - - return responsibilities.get(role, "Execute tasks according to your role and expertise.") - + + return responsibilities.get( + role, + "Execute tasks according to your role and expertise.", + ) + def _organize_agents(self): """Organize agents into groups and categories.""" - + # Organize by category for agent_name, profile in self.agents.items(): category = profile.category if category not in self.categories: self.categories[category] = [] self.categories[category].append(agent_name) - + # Create groups for each category for category, agent_names in self.categories.items(): group_name = f"{category.value.capitalize()}_Group" - + # Select a leader (first agent in the category) leader = agent_names[0] if agent_names else None - + group = AgentGroup( name=group_name, category=category, agents=agent_names, leader=leader, - total_agents=len(agent_names) + total_agents=len(agent_names), ) - + self.groups[group_name] = group - + if self.verbose: - logger.info(f"Organized agents into {len(self.groups)} groups") - + logger.info( + f"Organized agents into {len(self.groups)} groups" + ) + def _create_group_swarms(self): """Create Board of Directors swarms for each group.""" - + for group_name, group in self.groups.items(): if not group.agents: continue - + # Create board members from group agents board_members = [] - + # Add group leader as chairman if group.leader and group.leader in self.agents: leader_profile = self.agents[group.leader] if leader_profile.agent: - board_members.append(BoardMember( - agent=leader_profile.agent, - role=BoardMemberRole.CHAIRMAN, - voting_weight=1.0, - expertise_areas=leader_profile.specialization - )) - + board_members.append( + BoardMember( + agent=leader_profile.agent, + role=BoardMemberRole.CHAIRMAN, + voting_weight=1.0, + expertise_areas=leader_profile.specialization, + ) + ) + # Add other agents as board members - for agent_name in group.agents[:5]: # Limit to 5 board members - if agent_name != group.leader and agent_name in self.agents: + for agent_name in group.agents[ + :5 + ]: # Limit to 5 board members + if ( + agent_name != group.leader + and agent_name in self.agents + ): profile = self.agents[agent_name] if profile.agent: - board_members.append(BoardMember( - agent=profile.agent, - role=BoardMemberRole.EXECUTIVE_DIRECTOR, - voting_weight=0.8, - expertise_areas=profile.specialization - )) - + board_members.append( + BoardMember( + agent=profile.agent, + role=BoardMemberRole.EXECUTIVE_DIRECTOR, + voting_weight=0.8, + expertise_areas=profile.specialization, + ) + ) + # Create Board of Directors swarm if board_members: - agents = [member.agent for member in board_members if member.agent is not None] - + agents = [ + member.agent + for member in board_members + if member.agent is not None + ] + group.group_swarm = BoardOfDirectorsSwarm( name=group_name, description=f"Specialized swarm for {group_name} with expertise in {group.category.value}", @@ -655,78 +749,94 @@ Remember: You are part of a large multi-agent system. Your unique combination of verbose=self.verbose, decision_threshold=0.6, enable_voting=True, - enable_consensus=True + enable_consensus=True, ) - + if self.verbose: - logger.info(f"Created {len([g for g in self.groups.values() if g.group_swarm])} group swarms") - + logger.info( + f"Created {len([g for g in self.groups.values() if g.group_swarm])} group swarms" + ) + def get_agent(self, agent_name: str) -> Optional[AgentProfile]: """ Get a specific agent by name. - + Args: agent_name: Name of the agent - + Returns: Optional[AgentProfile]: Agent profile if found, None otherwise """ return self.agents.get(agent_name) - + def get_group(self, group_name: str) -> Optional[AgentGroup]: """ Get a specific group by name. - + Args: group_name: Name of the group - + Returns: Optional[AgentGroup]: Group if found, None otherwise """ return self.groups.get(group_name) - - def get_agents_by_category(self, category: AgentCategory) -> List[str]: + + def get_agents_by_category( + self, category: AgentCategory + ) -> List[str]: """ Get all agents in a specific category. - + Args: category: Agent category - + Returns: List[str]: List of agent names in the category """ return self.categories.get(category, []) - + def get_agents_by_role(self, role: AgentRole) -> List[str]: """ Get all agents with a specific role. - + Args: role: Agent role - + Returns: List[str]: List of agent names with the role """ - return [name for name, profile in self.agents.items() if profile.role == role] - - def run_mass_task(self, task: str, agent_count: int = 10) -> Dict[str, Any]: + return [ + name + for name, profile in self.agents.items() + if profile.role == role + ] + + def run_mass_task( + self, task: str, agent_count: int = 10 + ) -> Dict[str, Any]: """ Run a task with multiple agents working in parallel with cost optimization. - + Args: task: Task to execute agent_count: Number of agents to use - + Returns: Dict[str, Any]: Results from the mass task execution """ # Check budget before starting if not self.cost_tracker.check_budget(): - return {"error": "Budget exceeded", "cost_stats": self.cost_tracker.get_stats()} - + return { + "error": "Budget exceeded", + "cost_stats": self.cost_tracker.get_stats(), + } + # Select random agents - selected_agent_names = random.sample(list(self.agents.keys()), min(agent_count, len(self.agents))) - + selected_agent_names = random.sample( + list(self.agents.keys()), + min(agent_count, len(self.agents)), + ) + # Check cache first cache_key = self._get_cache_key(task, selected_agent_names) cached_result = self._check_cache(cache_key) @@ -737,132 +847,160 @@ Remember: You are part of a large multi-agent system. Your unique combination of "results": cached_result, "total_agents": len(selected_agent_names), "cached": True, - "cost_stats": self.cost_tracker.get_stats() + "cost_stats": self.cost_tracker.get_stats(), } - + # Process in batches to control memory and cost all_results = [] total_processed = 0 - + for i in range(0, len(selected_agent_names), self.batch_size): - batch_names = selected_agent_names[i:i + self.batch_size] - + batch_names = selected_agent_names[ + i : i + self.batch_size + ] + # Check budget for this batch if not self.cost_tracker.check_budget(): - logger.warning(f"Budget exceeded after processing {total_processed} agents") - logger.warning(f"Current cost: ${self.cost_tracker.total_cost_estimate:.4f}, Budget: ${self.cost_tracker.budget_limit:.2f}") + logger.warning( + f"Budget exceeded after processing {total_processed} agents" + ) + logger.warning( + f"Current cost: ${self.cost_tracker.total_cost_estimate:.4f}, Budget: ${self.cost_tracker.budget_limit:.2f}" + ) break - + # Load agents for this batch batch_agents = self._load_agents_batch(batch_names) - + if not batch_agents: continue - + # Run batch try: - batch_results = run_agents_concurrently(batch_agents, task) + batch_results = run_agents_concurrently( + batch_agents, task + ) all_results.extend(batch_results) total_processed += len(batch_agents) - + # Estimate tokens used (more realistic approximation) # Include both input tokens (task) and output tokens (response) - task_tokens = len(task.split()) * 1.3 # ~1.3 tokens per word - response_tokens = len(batch_agents) * 200 # ~200 tokens per response + task_tokens = ( + len(task.split()) * 1.3 + ) # ~1.3 tokens per word + response_tokens = ( + len(batch_agents) * 200 + ) # ~200 tokens per response total_tokens = int(task_tokens + response_tokens) self.cost_tracker.add_tokens(total_tokens) - + if self.verbose: - logger.info(f"Processed batch {i//self.batch_size + 1}: {len(batch_agents)} agents") - logger.info(f"Current cost: ${self.cost_tracker.total_cost_estimate:.4f}, Budget remaining: ${self.cost_tracker.budget_limit - self.cost_tracker.total_cost_estimate:.2f}") - + logger.info( + f"Processed batch {i//self.batch_size + 1}: {len(batch_agents)} agents" + ) + logger.info( + f"Current cost: ${self.cost_tracker.total_cost_estimate:.4f}, Budget remaining: ${self.cost_tracker.budget_limit - self.cost_tracker.total_cost_estimate:.2f}" + ) + except Exception as e: logger.error(f"Error processing batch: {e}") continue - + # Cache the results if all_results: self._cache_response(cache_key, str(all_results)) - + return { "task": task, "agents_used": selected_agent_names[:total_processed], "results": all_results, "total_agents": total_processed, "cached": False, - "cost_stats": self.cost_tracker.get_stats() + "cost_stats": self.cost_tracker.get_stats(), } - - def run_mass_task_optimized(self, task: str, agent_count: int = 1000, - max_cost: float = 10.0) -> Dict[str, Any]: + + def run_mass_task_optimized( + self, + task: str, + agent_count: int = 1000, + max_cost: float = 10.0, + ) -> Dict[str, Any]: """ Run a task with cost-optimized mass execution for large-scale operations. - + Args: task: Task to execute agent_count: Target number of agents to use max_cost: Maximum cost for this task in dollars - + Returns: Dict[str, Any]: Results from the optimized mass task execution """ # Store original settings original_budget = self.cost_tracker.budget_limit original_batch_size = self.batch_size - + try: # Set temporary budget for this task (don't reduce if max_cost is higher) if max_cost < original_budget: self.cost_tracker.budget_limit = max_cost - + # Use smaller batches for better cost control - self.batch_size = min(25, self.batch_size) # Smaller batches for cost control - + self.batch_size = min( + 25, self.batch_size + ) # Smaller batches for cost control + result = self.run_mass_task(task, agent_count) - + return result - + finally: # Restore original settings self.cost_tracker.budget_limit = original_budget self.batch_size = original_batch_size - - def run_group_task(self, group_name: str, task: str) -> Dict[str, Any]: + + def run_group_task( + self, group_name: str, task: str + ) -> Dict[str, Any]: """ Run a task with a specific group using their Board of Directors swarm. - + Args: group_name: Name of the group task: Task to execute - + Returns: Dict[str, Any]: Results from the group task execution """ group = self.groups.get(group_name) if not group or not group.group_swarm: - return {"error": f"Group {group_name} not found or no swarm available"} - + return { + "error": f"Group {group_name} not found or no swarm available" + } + # Run task with group swarm result = group.group_swarm.run(task) - + return { "group": group_name, "task": task, "result": result, - "agents_involved": group.agents + "agents_involved": group.agents, } - + def get_system_stats(self) -> Dict[str, Any]: """ Get statistics about the mass agent system including cost tracking. - + Returns: Dict[str, Any]: System statistics """ stats = { "total_agents": len(self.agents), "total_groups": len(self.groups), - "loaded_agents": len([a for a in self.agents.values() if a.is_loaded]), + "loaded_agents": len( + [a for a in self.agents.values() if a.is_loaded] + ), "categories": {}, "roles": {}, "experience_levels": {}, @@ -871,35 +1009,41 @@ Remember: You are part of a large multi-agent system. Your unique combination of "lazy_loading": self.enable_lazy_loading, "caching": self.enable_caching, "batch_size": self.batch_size, - "budget_limit": self.cost_tracker.budget_limit - } + "budget_limit": self.cost_tracker.budget_limit, + }, } - + # Category breakdown for category in AgentCategory: - stats["categories"][category.value] = len(self.get_agents_by_category(category)) - + stats["categories"][category.value] = len( + self.get_agents_by_category(category) + ) + # Role breakdown for role in AgentRole: - stats["roles"][role.value] = len(self.get_agents_by_role(role)) - + stats["roles"][role.value] = len( + self.get_agents_by_role(role) + ) + # Experience level breakdown experience_counts = {} for profile in self.agents.values(): level = profile.experience_level - experience_counts[level] = experience_counts.get(level, 0) + 1 + experience_counts[level] = ( + experience_counts.get(level, 0) + 1 + ) stats["experience_levels"] = experience_counts - + return stats # Example usage and demonstration def demonstrate_mass_agent_template(): """Demonstrate the Mass Agent Template functionality with cost optimization.""" - + print("MASS AGENT TEMPLATE DEMONSTRATION (COST OPTIMIZED)") print("=" * 60) - + # Initialize the template with 1000 agents and cost optimization template = MassAgentTemplate( agent_count=1000, @@ -909,90 +1053,114 @@ def demonstrate_mass_agent_template(): enable_caching=True, batch_size=25, budget_limit=50.0, # $50 budget limit - verbose=True + verbose=True, ) - + # Show system statistics stats = template.get_system_stats() - - print(f"\nSYSTEM STATISTICS:") + + print("\nSYSTEM STATISTICS:") print(f"Total Agents: {stats['total_agents']}") - print(f"Loaded Agents: {stats['loaded_agents']} (lazy loading active)") + print( + f"Loaded Agents: {stats['loaded_agents']} (lazy loading active)" + ) print(f"Total Groups: {stats['total_groups']}") - - print(f"\nCOST OPTIMIZATION:") - cost_stats = stats['cost_stats'] - print(f"Budget Limit: ${cost_stats['budget_remaining'] + cost_stats['total_cost']:.2f}") + + print("\nCOST OPTIMIZATION:") + cost_stats = stats["cost_stats"] + print( + f"Budget Limit: ${cost_stats['budget_remaining'] + cost_stats['total_cost']:.2f}" + ) print(f"Budget Used: ${cost_stats['total_cost']:.2f}") print(f"Budget Remaining: ${cost_stats['budget_remaining']:.2f}") print(f"Cache Hit Rate: {cost_stats['cache_hit_rate']:.1%}") - - print(f"\nCATEGORY BREAKDOWN:") - for category, count in stats['categories'].items(): + + print("\nCATEGORY BREAKDOWN:") + for category, count in stats["categories"].items(): print(f" {category}: {count} agents") - - print(f"\nROLE BREAKDOWN:") - for role, count in stats['roles'].items(): + + print("\nROLE BREAKDOWN:") + for role, count in stats["roles"].items(): print(f" {role}: {count} agents") - - print(f"\nEXPERIENCE LEVEL BREAKDOWN:") - for level, count in stats['experience_levels'].items(): + + print("\nEXPERIENCE LEVEL BREAKDOWN:") + for level, count in stats["experience_levels"].items(): print(f" {level}: {count} agents") - + # Demonstrate cost-optimized mass task execution - print(f"\nCOST-OPTIMIZED MASS TASK DEMONSTRATION:") + print("\nCOST-OPTIMIZED MASS TASK DEMONSTRATION:") print("-" * 40) - + # Small task first (low cost) small_result = template.run_mass_task( "What is the most important skill for a software developer?", - agent_count=5 + agent_count=5, ) - - print(f"Small Task Results:") + + print("Small Task Results:") print(f" Agents Used: {len(small_result['agents_used'])}") print(f" Cached: {small_result.get('cached', False)}") print(f" Cost: ${small_result['cost_stats']['total_cost']:.2f}") - + # Large task to demonstrate full capability - print(f"\nLarge Task Demonstration (Full Capability):") + print("\nLarge Task Demonstration (Full Capability):") large_result = template.run_mass_task( "Analyze the benefits of cloud computing for small businesses", - agent_count=200 # Use more agents to show capability + agent_count=200, # Use more agents to show capability ) - + print(f" Agents Used: {len(large_result['agents_used'])}") print(f" Cached: {large_result.get('cached', False)}") print(f" Cost: ${large_result['cost_stats']['total_cost']:.2f}") - print(f" Budget Remaining: ${large_result['cost_stats']['budget_remaining']:.2f}") - + print( + f" Budget Remaining: ${large_result['cost_stats']['budget_remaining']:.2f}" + ) + # Show what happens with cost limits - print(f"\nCost-Limited Task Demonstration:") + print("\nCost-Limited Task Demonstration:") cost_limited_result = template.run_mass_task_optimized( "What are the key principles of agile development?", agent_count=100, - max_cost=2.0 # Show cost limiting in action + max_cost=2.0, # Show cost limiting in action ) - + print(f" Agents Used: {len(cost_limited_result['agents_used'])}") print(f" Cached: {cost_limited_result.get('cached', False)}") - print(f" Cost: ${cost_limited_result['cost_stats']['total_cost']:.2f}") - print(f" Budget Remaining: ${cost_limited_result['cost_stats']['budget_remaining']:.2f}") - + print( + f" Cost: ${cost_limited_result['cost_stats']['total_cost']:.2f}" + ) + print( + f" Budget Remaining: ${cost_limited_result['cost_stats']['budget_remaining']:.2f}" + ) + # Show final cost statistics final_stats = template.get_system_stats() - print(f"\nFINAL COST STATISTICS:") - print(f"Total Cost: ${final_stats['cost_stats']['total_cost']:.2f}") - print(f"Budget Remaining: ${final_stats['cost_stats']['budget_remaining']:.2f}") - print(f"Cache Hit Rate: {final_stats['cost_stats']['cache_hit_rate']:.1%}") - print(f"Total Requests: {final_stats['cost_stats']['requests_made']}") + print("\nFINAL COST STATISTICS:") + print( + f"Total Cost: ${final_stats['cost_stats']['total_cost']:.2f}" + ) + print( + f"Budget Remaining: ${final_stats['cost_stats']['budget_remaining']:.2f}" + ) + print( + f"Cache Hit Rate: {final_stats['cost_stats']['cache_hit_rate']:.1%}" + ) + print( + f"Total Requests: {final_stats['cost_stats']['requests_made']}" + ) print(f"Cache Hits: {final_stats['cost_stats']['cache_hits']}") - - print(f"\nDEMONSTRATION COMPLETED SUCCESSFULLY!") - print(f"✅ Cost optimization working: ${final_stats['cost_stats']['total_cost']:.2f} spent") - print(f"✅ Lazy loading working: {final_stats['loaded_agents']}/{final_stats['total_agents']} agents loaded") - print(f"✅ Caching working: {final_stats['cost_stats']['cache_hit_rate']:.1%} hit rate") + + print("\nDEMONSTRATION COMPLETED SUCCESSFULLY!") + print( + f"✅ Cost optimization working: ${final_stats['cost_stats']['total_cost']:.2f} spent" + ) + print( + f"✅ Lazy loading working: {final_stats['loaded_agents']}/{final_stats['total_agents']} agents loaded" + ) + print( + f"✅ Caching working: {final_stats['cost_stats']['cache_hit_rate']:.1%} hit rate" + ) if __name__ == "__main__": - demonstrate_mass_agent_template() \ No newline at end of file + demonstrate_mass_agent_template() diff --git a/examples/simulations/euroswarm_parliament/test_mass_agents.py b/examples/simulations/euroswarm_parliament/test_mass_agents.py index 54ec2223..c747b0a0 100644 --- a/examples/simulations/euroswarm_parliament/test_mass_agents.py +++ b/examples/simulations/euroswarm_parliament/test_mass_agents.py @@ -5,63 +5,76 @@ Test script to verify mass agent template can process more than 500 agents. from mass_agent_template import MassAgentTemplate + def test_mass_agents(): - print("Testing Mass Agent Template - Processing More Than 50 Agents") + print( + "Testing Mass Agent Template - Processing More Than 50 Agents" + ) print("=" * 60) - + # Initialize template with 200 agents template = MassAgentTemplate( agent_count=200, budget_limit=50.0, batch_size=25, - verbose=True + verbose=True, ) - + print(f"Initialized with {len(template.agents)} agents") print(f"Budget limit: ${template.cost_tracker.budget_limit}") - + # Test processing 100 agents - print(f"\nTesting with 100 agents...") + print("\nTesting with 100 agents...") result = template.run_mass_task( "What is the most important skill for your role?", - agent_count=100 + agent_count=100, ) - - print(f"Results:") + + print("Results:") print(f" Agents processed: {len(result['agents_used'])}") print(f" Cost: ${result['cost_stats']['total_cost']:.4f}") - print(f" Budget remaining: ${result['cost_stats']['budget_remaining']:.2f}") + print( + f" Budget remaining: ${result['cost_stats']['budget_remaining']:.2f}" + ) print(f" Cached: {result.get('cached', False)}") - + # Test processing 150 agents - print(f"\nTesting with 150 agents...") + print("\nTesting with 150 agents...") result2 = template.run_mass_task( - "Describe your approach to problem-solving", - agent_count=150 + "Describe your approach to problem-solving", agent_count=150 ) - - print(f"Results:") + + print("Results:") print(f" Agents processed: {len(result2['agents_used'])}") print(f" Cost: ${result2['cost_stats']['total_cost']:.4f}") - print(f" Budget remaining: ${result2['cost_stats']['budget_remaining']:.2f}") + print( + f" Budget remaining: ${result2['cost_stats']['budget_remaining']:.2f}" + ) print(f" Cached: {result2.get('cached', False)}") - + # Show final stats final_stats = template.get_system_stats() - print(f"\nFinal Statistics:") + print("\nFinal Statistics:") print(f" Total agents: {final_stats['total_agents']}") print(f" Loaded agents: {final_stats['loaded_agents']}") - print(f" Total cost: ${final_stats['cost_stats']['total_cost']:.4f}") - print(f" Budget remaining: ${final_stats['cost_stats']['budget_remaining']:.2f}") - + print( + f" Total cost: ${final_stats['cost_stats']['total_cost']:.4f}" + ) + print( + f" Budget remaining: ${final_stats['cost_stats']['budget_remaining']:.2f}" + ) + # Success criteria - total_processed = len(result['agents_used']) + len(result2['agents_used']) + total_processed = len(result["agents_used"]) + len( + result2["agents_used"] + ) print(f"\nTotal agents processed: {total_processed}") - + if total_processed > 50: print("✅ SUCCESS: Template processed more than 50 agents!") else: print("❌ FAILURE: Template still limited to 50 agents") + if __name__ == "__main__": - test_mass_agents() \ No newline at end of file + test_mass_agents() diff --git a/examples/simulations/euroswarm_parliament/wikipedia_personality_scraper.py b/examples/simulations/euroswarm_parliament/wikipedia_personality_scraper.py index 0ef7cda7..e7c555cf 100644 --- a/examples/simulations/euroswarm_parliament/wikipedia_personality_scraper.py +++ b/examples/simulations/euroswarm_parliament/wikipedia_personality_scraper.py @@ -14,14 +14,13 @@ from typing import Dict, List, Optional, Any from dataclasses import dataclass, asdict import requests from loguru import logger -import xml.etree.ElementTree as ET @dataclass class MEPPersonalityProfile: """ Comprehensive personality profile for an MEP based on Wikipedia data. - + Attributes: full_name: Full name of the MEP mep_id: Unique MEP identifier @@ -47,7 +46,7 @@ class MEPPersonalityProfile: social_media: Social media presence last_updated: When the profile was last updated """ - + full_name: str mep_id: str wikipedia_url: Optional[str] = None @@ -77,11 +76,15 @@ class WikipediaPersonalityScraper: """ Scraper for gathering Wikipedia personality data for MEPs. """ - - def __init__(self, output_dir: str = "mep_personalities", verbose: bool = True): + + def __init__( + self, + output_dir: str = "mep_personalities", + verbose: bool = True, + ): """ Initialize the Wikipedia personality scraper. - + Args: output_dir: Directory to store personality profiles verbose: Enable verbose logging @@ -89,61 +92,81 @@ class WikipediaPersonalityScraper: self.output_dir = output_dir self.verbose = verbose self.session = requests.Session() - self.session.headers.update({ - 'User-Agent': 'EuroSwarm Parliament Personality Scraper/1.0 (https://github.com/swarms-democracy)' - }) - + self.session.headers.update( + { + "User-Agent": "EuroSwarm Parliament Personality Scraper/1.0 (https://github.com/swarms-democracy)" + } + ) + # Create output directory os.makedirs(output_dir, exist_ok=True) - + if verbose: - logger.info(f"Wikipedia Personality Scraper initialized. Output directory: {output_dir}") + logger.info( + f"Wikipedia Personality Scraper initialized. Output directory: {output_dir}" + ) - def extract_mep_data_from_xml(self, xml_file: str = "EU.xml") -> List[Dict[str, str]]: + def extract_mep_data_from_xml( + self, xml_file: str = "EU.xml" + ) -> List[Dict[str, str]]: """ Extract MEP data from EU.xml file. - + Args: xml_file: Path to EU.xml file - + Returns: List of MEP data dictionaries """ meps = [] - + try: - with open(xml_file, 'r', encoding='utf-8') as f: + with open(xml_file, "r", encoding="utf-8") as f: content = f.read() - + # Use regex to extract MEP data - mep_pattern = r'\s*(.*?)\s*(.*?)\s*(.*?)\s*(.*?)\s*(.*?)\s*' + mep_pattern = r"\s*(.*?)\s*(.*?)\s*(.*?)\s*(.*?)\s*(.*?)\s*" mep_matches = re.findall(mep_pattern, content, re.DOTALL) - - for full_name, country, political_group, mep_id, national_party in mep_matches: - meps.append({ - 'full_name': full_name.strip(), - 'country': country.strip(), - 'political_group': political_group.strip(), - 'mep_id': mep_id.strip(), - 'national_party': national_party.strip() - }) - + + for ( + full_name, + country, + political_group, + mep_id, + national_party, + ) in mep_matches: + meps.append( + { + "full_name": full_name.strip(), + "country": country.strip(), + "political_group": political_group.strip(), + "mep_id": mep_id.strip(), + "national_party": national_party.strip(), + } + ) + if self.verbose: - logger.info(f"Extracted {len(meps)} MEPs from {xml_file}") - + logger.info( + f"Extracted {len(meps)} MEPs from {xml_file}" + ) + except Exception as e: - logger.error(f"Error extracting MEP data from {xml_file}: {e}") - + logger.error( + f"Error extracting MEP data from {xml_file}: {e}" + ) + return meps - def search_wikipedia_page(self, mep_name: str, country: str) -> Optional[str]: + def search_wikipedia_page( + self, mep_name: str, country: str + ) -> Optional[str]: """ Search for a Wikipedia page for an MEP. - + Args: mep_name: Full name of the MEP country: Country of the MEP - + Returns: Wikipedia page title if found, None otherwise """ @@ -151,48 +174,56 @@ class WikipediaPersonalityScraper: # Search for the MEP on Wikipedia search_url = "https://en.wikipedia.org/w/api.php" search_params = { - 'action': 'query', - 'format': 'json', - 'list': 'search', - 'srsearch': f'"{mep_name}" {country}', - 'srlimit': 5, - 'srnamespace': 0 + "action": "query", + "format": "json", + "list": "search", + "srsearch": f'"{mep_name}" {country}', + "srlimit": 5, + "srnamespace": 0, } - - response = self.session.get(search_url, params=search_params) + + response = self.session.get( + search_url, params=search_params + ) response.raise_for_status() - + data = response.json() - search_results = data.get('query', {}).get('search', []) - + search_results = data.get("query", {}).get("search", []) + if search_results: # Return the first result - return search_results[0]['title'] - + return search_results[0]["title"] + # Try alternative search without quotes - search_params['srsearch'] = f'{mep_name} {country}' - response = self.session.get(search_url, params=search_params) + search_params["srsearch"] = f"{mep_name} {country}" + response = self.session.get( + search_url, params=search_params + ) response.raise_for_status() - + data = response.json() - search_results = data.get('query', {}).get('search', []) - + search_results = data.get("query", {}).get("search", []) + if search_results: - return search_results[0]['title'] - + return search_results[0]["title"] + except Exception as e: if self.verbose: - logger.warning(f"Error searching Wikipedia for {mep_name}: {e}") - + logger.warning( + f"Error searching Wikipedia for {mep_name}: {e}" + ) + return None - def get_wikipedia_content(self, page_title: str) -> Optional[Dict[str, Any]]: + def get_wikipedia_content( + self, page_title: str + ) -> Optional[Dict[str, Any]]: """ Get Wikipedia content for a specific page. - + Args: page_title: Wikipedia page title - + Returns: Dictionary containing page content and metadata """ @@ -200,376 +231,451 @@ class WikipediaPersonalityScraper: # Get page content content_url = "https://en.wikipedia.org/w/api.php" content_params = { - 'action': 'query', - 'format': 'json', - 'titles': page_title, - 'prop': 'extracts|info|categories', - 'exintro': True, - 'explaintext': True, - 'inprop': 'url', - 'cllimit': 50 + "action": "query", + "format": "json", + "titles": page_title, + "prop": "extracts|info|categories", + "exintro": True, + "explaintext": True, + "inprop": "url", + "cllimit": 50, } - - response = self.session.get(content_url, params=content_params) + + response = self.session.get( + content_url, params=content_params + ) response.raise_for_status() - + data = response.json() - pages = data.get('query', {}).get('pages', {}) - + pages = data.get("query", {}).get("pages", {}) + if pages: page_id = list(pages.keys())[0] page_data = pages[page_id] - + return { - 'title': page_data.get('title', ''), - 'extract': page_data.get('extract', ''), - 'url': page_data.get('fullurl', ''), - 'categories': [cat['title'] for cat in page_data.get('categories', [])], - 'pageid': page_data.get('pageid', ''), - 'length': page_data.get('length', 0) + "title": page_data.get("title", ""), + "extract": page_data.get("extract", ""), + "url": page_data.get("fullurl", ""), + "categories": [ + cat["title"] + for cat in page_data.get("categories", []) + ], + "pageid": page_data.get("pageid", ""), + "length": page_data.get("length", 0), } - + except Exception as e: if self.verbose: - logger.warning(f"Error getting Wikipedia content for {page_title}: {e}") - + logger.warning( + f"Error getting Wikipedia content for {page_title}: {e}" + ) + return None - def parse_wikipedia_content(self, content: str, mep_name: str) -> Dict[str, str]: + def parse_wikipedia_content( + self, content: str, mep_name: str + ) -> Dict[str, str]: """ Parse Wikipedia content to extract structured personality information. - + Args: content: Raw Wikipedia content mep_name: Name of the MEP - + Returns: Dictionary of parsed personality information """ personality_data = { - 'summary': '', - 'early_life': '', - 'political_career': '', - 'political_views': '', - 'policy_focus': '', - 'achievements': '', - 'controversies': '', - 'personal_life': '', - 'education': '', - 'professional_background': '', - 'party_affiliations': '', - 'committee_experience': '', - 'voting_record': '', - 'public_statements': '', - 'interests': '', - 'languages': '', - 'awards': '', - 'publications': '', - 'social_media': '' + "summary": "", + "early_life": "", + "political_career": "", + "political_views": "", + "policy_focus": "", + "achievements": "", + "controversies": "", + "personal_life": "", + "education": "", + "professional_background": "", + "party_affiliations": "", + "committee_experience": "", + "voting_record": "", + "public_statements": "", + "interests": "", + "languages": "", + "awards": "", + "publications": "", + "social_media": "", } - + # Extract summary (first paragraph) - paragraphs = content.split('\n\n') + paragraphs = content.split("\n\n") if paragraphs: - personality_data['summary'] = paragraphs[0][:1000] # Limit summary length - + personality_data["summary"] = paragraphs[0][ + :1000 + ] # Limit summary length + # Look for specific sections content_lower = content.lower() - + # Early life and education early_life_patterns = [ - r'early life[^.]*\.', - r'born[^.]*\.', - r'childhood[^.]*\.', - r'grew up[^.]*\.', - r'education[^.]*\.' + r"early life[^.]*\.", + r"born[^.]*\.", + r"childhood[^.]*\.", + r"grew up[^.]*\.", + r"education[^.]*\.", ] - + for pattern in early_life_patterns: - matches = re.findall(pattern, content_lower, re.IGNORECASE) + matches = re.findall( + pattern, content_lower, re.IGNORECASE + ) if matches: - personality_data['early_life'] = ' '.join(matches[:3]) # Take first 3 matches + personality_data["early_life"] = " ".join( + matches[:3] + ) # Take first 3 matches break - + # Political career political_patterns = [ - r'political career[^.]*\.', - r'elected[^.]*\.', - r'parliament[^.]*\.', - r'minister[^.]*\.', - r'party[^.]*\.' + r"political career[^.]*\.", + r"elected[^.]*\.", + r"parliament[^.]*\.", + r"minister[^.]*\.", + r"party[^.]*\.", ] - + for pattern in political_patterns: - matches = re.findall(pattern, content_lower, re.IGNORECASE) + matches = re.findall( + pattern, content_lower, re.IGNORECASE + ) if matches: - personality_data['political_career'] = ' '.join(matches[:5]) # Take first 5 matches + personality_data["political_career"] = " ".join( + matches[:5] + ) # Take first 5 matches break - + # Political views views_patterns = [ - r'political views[^.]*\.', - r'positions[^.]*\.', - r'advocates[^.]*\.', - r'supports[^.]*\.', - r'opposes[^.]*\.' + r"political views[^.]*\.", + r"positions[^.]*\.", + r"advocates[^.]*\.", + r"supports[^.]*\.", + r"opposes[^.]*\.", ] - + for pattern in views_patterns: - matches = re.findall(pattern, content_lower, re.IGNORECASE) + matches = re.findall( + pattern, content_lower, re.IGNORECASE + ) if matches: - personality_data['political_views'] = ' '.join(matches[:3]) + personality_data["political_views"] = " ".join( + matches[:3] + ) break - + # Policy focus policy_patterns = [ - r'policy[^.]*\.', - r'focus[^.]*\.', - r'issues[^.]*\.', - r'legislation[^.]*\.' + r"policy[^.]*\.", + r"focus[^.]*\.", + r"issues[^.]*\.", + r"legislation[^.]*\.", ] - + for pattern in policy_patterns: - matches = re.findall(pattern, content_lower, re.IGNORECASE) + matches = re.findall( + pattern, content_lower, re.IGNORECASE + ) if matches: - personality_data['policy_focus'] = ' '.join(matches[:3]) + personality_data["policy_focus"] = " ".join( + matches[:3] + ) break - + # Achievements achievement_patterns = [ - r'achievements[^.]*\.', - r'accomplishments[^.]*\.', - r'success[^.]*\.', - r'won[^.]*\.', - r'received[^.]*\.' + r"achievements[^.]*\.", + r"accomplishments[^.]*\.", + r"success[^.]*\.", + r"won[^.]*\.", + r"received[^.]*\.", ] - + for pattern in achievement_patterns: - matches = re.findall(pattern, content_lower, re.IGNORECASE) + matches = re.findall( + pattern, content_lower, re.IGNORECASE + ) if matches: - personality_data['achievements'] = ' '.join(matches[:3]) + personality_data["achievements"] = " ".join( + matches[:3] + ) break - + return personality_data - def create_personality_profile(self, mep_data: Dict[str, str]) -> MEPPersonalityProfile: + def create_personality_profile( + self, mep_data: Dict[str, str] + ) -> MEPPersonalityProfile: """ Create a personality profile for an MEP. - + Args: mep_data: MEP data from XML file - + Returns: MEPPersonalityProfile object """ - mep_name = mep_data['full_name'] - country = mep_data['country'] - + mep_name = mep_data["full_name"] + country = mep_data["country"] + # Search for Wikipedia page page_title = self.search_wikipedia_page(mep_name, country) - + if page_title: # Get Wikipedia content wiki_content = self.get_wikipedia_content(page_title) - + if wiki_content: # Parse content - personality_data = self.parse_wikipedia_content(wiki_content['extract'], mep_name) - + personality_data = self.parse_wikipedia_content( + wiki_content["extract"], mep_name + ) + # Create profile profile = MEPPersonalityProfile( full_name=mep_name, - mep_id=mep_data['mep_id'], - wikipedia_url=wiki_content['url'], - summary=personality_data['summary'], - early_life=personality_data['early_life'], - political_career=personality_data['political_career'], - political_views=personality_data['political_views'], - policy_focus=personality_data['policy_focus'], - achievements=personality_data['achievements'], - controversies=personality_data['controversies'], - personal_life=personality_data['personal_life'], - education=personality_data['education'], - professional_background=personality_data['professional_background'], - party_affiliations=personality_data['party_affiliations'], - committee_experience=personality_data['committee_experience'], - voting_record=personality_data['voting_record'], - public_statements=personality_data['public_statements'], - interests=personality_data['interests'], - languages=personality_data['languages'], - awards=personality_data['awards'], - publications=personality_data['publications'], - social_media=personality_data['social_media'], - last_updated=time.strftime("%Y-%m-%d %H:%M:%S") + mep_id=mep_data["mep_id"], + wikipedia_url=wiki_content["url"], + summary=personality_data["summary"], + early_life=personality_data["early_life"], + political_career=personality_data[ + "political_career" + ], + political_views=personality_data[ + "political_views" + ], + policy_focus=personality_data["policy_focus"], + achievements=personality_data["achievements"], + controversies=personality_data["controversies"], + personal_life=personality_data["personal_life"], + education=personality_data["education"], + professional_background=personality_data[ + "professional_background" + ], + party_affiliations=personality_data[ + "party_affiliations" + ], + committee_experience=personality_data[ + "committee_experience" + ], + voting_record=personality_data["voting_record"], + public_statements=personality_data[ + "public_statements" + ], + interests=personality_data["interests"], + languages=personality_data["languages"], + awards=personality_data["awards"], + publications=personality_data["publications"], + social_media=personality_data["social_media"], + last_updated=time.strftime("%Y-%m-%d %H:%M:%S"), ) - + if self.verbose: - logger.info(f"Created personality profile for {mep_name} from Wikipedia") - + logger.info( + f"Created personality profile for {mep_name} from Wikipedia" + ) + return profile - + # Create minimal profile if no Wikipedia data found profile = MEPPersonalityProfile( full_name=mep_name, - mep_id=mep_data['mep_id'], + mep_id=mep_data["mep_id"], summary=f"{mep_name} is a Member of the European Parliament representing {country}.", political_career=f"Currently serving as MEP for {country}.", political_views=f"Member of {mep_data['political_group']} and {mep_data['national_party']}.", - last_updated=time.strftime("%Y-%m-%d %H:%M:%S") + last_updated=time.strftime("%Y-%m-%d %H:%M:%S"), ) - + if self.verbose: - logger.warning(f"No Wikipedia data found for {mep_name}, created minimal profile") - + logger.warning( + f"No Wikipedia data found for {mep_name}, created minimal profile" + ) + return profile - def save_personality_profile(self, profile: MEPPersonalityProfile) -> str: + def save_personality_profile( + self, profile: MEPPersonalityProfile + ) -> str: """ Save personality profile to JSON file. - + Args: profile: MEPPersonalityProfile object - + Returns: Path to saved file """ # Create safe filename - safe_name = re.sub(r'[^\w\s-]', '', profile.full_name).strip() - safe_name = re.sub(r'[-\s]+', '_', safe_name) + safe_name = re.sub(r"[^\w\s-]", "", profile.full_name).strip() + safe_name = re.sub(r"[-\s]+", "_", safe_name) filename = f"{safe_name}_{profile.mep_id}.json" filepath = os.path.join(self.output_dir, filename) - + # Convert to dictionary and save profile_dict = asdict(profile) - - with open(filepath, 'w', encoding='utf-8') as f: + + with open(filepath, "w", encoding="utf-8") as f: json.dump(profile_dict, f, indent=2, ensure_ascii=False) - + if self.verbose: logger.info(f"Saved personality profile: {filepath}") - + return filepath - def scrape_all_mep_personalities(self, xml_file: str = "EU.xml", delay: float = 1.0) -> Dict[str, str]: + def scrape_all_mep_personalities( + self, xml_file: str = "EU.xml", delay: float = 1.0 + ) -> Dict[str, str]: """ Scrape personality data for all MEPs. - + Args: xml_file: Path to EU.xml file delay: Delay between requests to be respectful to Wikipedia - + Returns: Dictionary mapping MEP names to their personality profile file paths """ meps = self.extract_mep_data_from_xml(xml_file) profile_files = {} - + if self.verbose: - logger.info(f"Starting personality scraping for {len(meps)} MEPs") - + logger.info( + f"Starting personality scraping for {len(meps)} MEPs" + ) + for i, mep_data in enumerate(meps, 1): - mep_name = mep_data['full_name'] - + mep_name = mep_data["full_name"] + if self.verbose: logger.info(f"Processing {i}/{len(meps)}: {mep_name}") - + try: # Create personality profile profile = self.create_personality_profile(mep_data) - + # Save profile filepath = self.save_personality_profile(profile) profile_files[mep_name] = filepath - + # Respectful delay time.sleep(delay) - + except Exception as e: logger.error(f"Error processing {mep_name}: {e}") continue - + if self.verbose: - logger.info(f"Completed personality scraping. {len(profile_files)} profiles created.") - + logger.info( + f"Completed personality scraping. {len(profile_files)} profiles created." + ) + return profile_files - def load_personality_profile(self, filepath: str) -> MEPPersonalityProfile: + def load_personality_profile( + self, filepath: str + ) -> MEPPersonalityProfile: """ Load personality profile from JSON file. - + Args: filepath: Path to personality profile JSON file - + Returns: MEPPersonalityProfile object """ - with open(filepath, 'r', encoding='utf-8') as f: + with open(filepath, "r", encoding="utf-8") as f: data = json.load(f) - + return MEPPersonalityProfile(**data) - def get_personality_summary(self, profile: MEPPersonalityProfile) -> str: + def get_personality_summary( + self, profile: MEPPersonalityProfile + ) -> str: """ Generate a personality summary for use in AI agent system prompts. - + Args: profile: MEPPersonalityProfile object - + Returns: Formatted personality summary """ summary_parts = [] - + if profile.summary: summary_parts.append(f"Background: {profile.summary}") - + if profile.political_career: - summary_parts.append(f"Political Career: {profile.political_career}") - + summary_parts.append( + f"Political Career: {profile.political_career}" + ) + if profile.political_views: - summary_parts.append(f"Political Views: {profile.political_views}") - + summary_parts.append( + f"Political Views: {profile.political_views}" + ) + if profile.policy_focus: - summary_parts.append(f"Policy Focus: {profile.policy_focus}") - + summary_parts.append( + f"Policy Focus: {profile.policy_focus}" + ) + if profile.achievements: - summary_parts.append(f"Notable Achievements: {profile.achievements}") - + summary_parts.append( + f"Notable Achievements: {profile.achievements}" + ) + if profile.education: summary_parts.append(f"Education: {profile.education}") - + if profile.professional_background: - summary_parts.append(f"Professional Background: {profile.professional_background}") - + summary_parts.append( + f"Professional Background: {profile.professional_background}" + ) + return "\n".join(summary_parts) def main(): """Main function to run the Wikipedia personality scraper.""" - + print("🏛️ WIKIPEDIA PERSONALITY SCRAPER FOR EUROSWARM PARLIAMENT") print("=" * 70) - + # Initialize scraper - scraper = WikipediaPersonalityScraper(output_dir="mep_personalities", verbose=True) - + scraper = WikipediaPersonalityScraper( + output_dir="mep_personalities", verbose=True + ) + # Scrape all MEP personalities profile_files = scraper.scrape_all_mep_personalities(delay=1.0) - - print(f"\n✅ Scraping completed!") + + print("\n✅ Scraping completed!") print(f"📁 Profiles saved to: {scraper.output_dir}") print(f"📊 Total profiles created: {len(profile_files)}") - + # Show sample profile if profile_files: sample_name = list(profile_files.keys())[0] sample_file = profile_files[sample_name] sample_profile = scraper.load_personality_profile(sample_file) - + print(f"\n📋 Sample Profile: {sample_name}") print("-" * 50) print(scraper.get_personality_summary(sample_profile)) if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/swarms/structs/__init__.py b/swarms/structs/__init__.py index 8da55bf3..288c0273 100644 --- a/swarms/structs/__init__.py +++ b/swarms/structs/__init__.py @@ -1,5 +1,6 @@ from swarms.structs.agent import Agent from swarms.structs.agent_builder import AgentsBuilder +from swarms.structs.agent_rearrange import AgentRearrange, rearrange from swarms.structs.auto_swarm_builder import AutoSwarmBuilder from swarms.structs.base_structure import BaseStructure from swarms.structs.base_swarm import BaseSwarm @@ -66,7 +67,6 @@ from swarms.structs.multi_agent_exec import ( run_single_agent, ) from swarms.structs.multi_agent_router import MultiAgentRouter -from swarms.structs.agent_rearrange import AgentRearrange, rearrange from swarms.structs.round_robin import RoundRobinSwarm from swarms.structs.sequential_workflow import SequentialWorkflow from swarms.structs.spreadsheet_swarm import SpreadSheetSwarm diff --git a/swarms/structs/agent.py b/swarms/structs/agent.py index 914c0adb..f9e04013 100644 --- a/swarms/structs/agent.py +++ b/swarms/structs/agent.py @@ -21,6 +21,13 @@ from typing import ( import toml import yaml +from litellm import model_list +from litellm.utils import ( + get_max_tokens, + supports_function_calling, + supports_parallel_function_calling, + supports_vision, +) from loguru import logger from pydantic import BaseModel @@ -45,7 +52,6 @@ from swarms.schemas.base_schemas import ( ChatMessageResponse, ) from swarms.schemas.conversation_schema import ConversationSchema -from swarms.schemas.llm_agent_schema import ModelConfigOrigin from swarms.schemas.mcp_schemas import ( MCPConnection, ) @@ -422,7 +428,6 @@ class Agent: mcp_config: Optional[MCPConnection] = None, top_p: Optional[float] = 0.90, conversation_schema: Optional[ConversationSchema] = None, - aditional_llm_config: Optional[ModelConfigOrigin] = None, llm_base_url: Optional[str] = None, llm_api_key: Optional[str] = None, rag_config: Optional[RAGConfig] = None, @@ -430,8 +435,8 @@ class Agent: output_raw_json_from_tool_call: bool = False, summarize_multiple_images: bool = False, tool_retry_attempts: int = 3, - speed_mode: str = None, reasoning_prompt_on: bool = True, + dynamic_context_window: bool = True, *args, **kwargs, ): @@ -562,7 +567,6 @@ class Agent: self.mcp_config = mcp_config self.top_p = top_p self.conversation_schema = conversation_schema - self.aditional_llm_config = aditional_llm_config self.llm_base_url = llm_base_url self.llm_api_key = llm_api_key self.rag_config = rag_config @@ -572,8 +576,8 @@ class Agent: ) self.summarize_multiple_images = summarize_multiple_images self.tool_retry_attempts = tool_retry_attempts - self.speed_mode = speed_mode self.reasoning_prompt_on = reasoning_prompt_on + self.dynamic_context_window = dynamic_context_window # Initialize the feedback self.feedback = [] @@ -676,17 +680,15 @@ class Agent: # Initialize the short term memory memory = Conversation( name=f"{self.agent_name}_conversation", + system_prompt=prompt, user=self.user_name, rules=self.rules, token_count=False, message_id_on=False, time_enabled=True, - ) - - # Add the system prompt to the conversation - memory.add( - role="system", - content=prompt, + dynamic_context_window=self.dynamic_context_window, + tokenizer_model_name=self.model_name, + context_length=self.context_length, ) return memory @@ -888,11 +890,7 @@ class Agent: Returns: bool: True if model supports vision and image is provided, False otherwise. """ - from litellm.utils import ( - supports_function_calling, - supports_parallel_function_calling, - supports_vision, - ) + # Only check vision support if an image is provided if img is not None: @@ -1294,8 +1292,6 @@ class Agent: self._handle_run_error(error) def __handle_run_error(self, error: any): - import traceback - if self.autosave is True: self.save() log_agent_data(self.to_dict()) @@ -1539,11 +1535,6 @@ class Agent: raise def reliability_check(self): - from litellm import model_list - from litellm.utils import ( - get_max_tokens, - supports_function_calling, - ) if self.system_prompt is None: logger.warning( diff --git a/swarms/structs/conversation.py b/swarms/structs/conversation.py index 97316aa3..de7c1de2 100644 --- a/swarms/structs/conversation.py +++ b/swarms/structs/conversation.py @@ -1,21 +1,21 @@ -import traceback import concurrent.futures import datetime +import inspect import json import os +import traceback import uuid from typing import ( TYPE_CHECKING, + Any, Dict, List, + Literal, Optional, Union, - Literal, - Any, ) import yaml -import inspect from swarms.utils.any_to_str import any_to_str from swarms.utils.litellm_tokenizer import count_tokens @@ -26,6 +26,18 @@ if TYPE_CHECKING: from loguru import logger +# Define available providers +providers = Literal[ + "mem0", + "in-memory", + "supabase", + "redis", + "sqlite", + "duckdb", + "pulsar", +] + + def generate_conversation_id(): """Generate a unique conversation ID.""" return str(uuid.uuid4()) @@ -50,18 +62,6 @@ def get_conversation_dir(): return conversation_dir -# Define available providers -providers = Literal[ - "mem0", - "in-memory", - "supabase", - "redis", - "sqlite", - "duckdb", - "pulsar", -] - - def _create_backend_conversation(backend: str, **kwargs): """ Create a backend conversation instance based on the specified backend type. @@ -183,9 +183,9 @@ class Conversation: name: str = "conversation-test", system_prompt: Optional[str] = None, time_enabled: bool = False, - autosave: bool = False, # Changed default to False + autosave: bool = False, save_filepath: str = None, - load_filepath: str = None, # New parameter to specify which file to load from + load_filepath: str = None, context_length: int = 8192, rules: str = None, custom_rules_prompt: str = None, @@ -211,6 +211,8 @@ class Conversation: redis_data_dir: Optional[str] = None, conversations_dir: Optional[str] = None, export_method: str = "json", + dynamic_context_window: bool = True, + caching: bool = True, *args, **kwargs, ): @@ -249,6 +251,8 @@ class Conversation: self.auto_persist = auto_persist self.redis_data_dir = redis_data_dir self.export_method = export_method + self.dynamic_context_window = dynamic_context_window + self.caching = caching if self.name is None: self.name = id @@ -933,7 +937,15 @@ class Conversation: # Fallback to in-memory implementation pass + elif self.dynamic_context_window is True: + return self.dynamic_auto_chunking() + + else: + return self._return_history_as_string_worker() + + def _return_history_as_string_worker(self): formatted_messages = [] + for message in self.conversation_history: formatted_messages.append( f"{message['role']}: {message['content']}" @@ -1778,20 +1790,38 @@ class Conversation: pass self.conversation_history = [] + def dynamic_auto_chunking(self): + all_tokens = self._return_history_as_string_worker() + + total_tokens = count_tokens( + all_tokens, self.tokenizer_model_name + ) + + if total_tokens > self.context_length: + # Get the difference between the count_tokens and the context_length + difference = total_tokens - self.context_length + + # Slice the first difference number of messages and contents from the beginning of the conversation history + new_history = all_tokens[difference:] + + return new_history -# # Example usage -# # conversation = Conversation() -# conversation = Conversation(token_count=True) + +# Example usage +# conversation = Conversation() +# conversation = Conversation(token_count=True, context_length=14) # conversation.add("user", "Hello, how are you?") # conversation.add("assistant", "I am doing well, thanks.") +# conversation.add("user", "What is the weather in Tokyo?") +# print(conversation.dynamic_auto_chunking()) # # conversation.add( # # "assistant", {"name": "tool_1", "output": "Hello, how are you?"} -# # ) -# # print(conversation.return_json()) +# ) +# print(conversation.return_json()) -# # # print(conversation.get_last_message_as_string()) +# # print(conversation.get_last_message_as_string()) # print(conversation.return_json()) -# # # conversation.add("assistant", "I am doing well, thanks.") -# # # # print(conversation.to_json()) -# # print(type(conversation.to_dict())) -# # print(conversation.to_yaml()) +# # conversation.add("assistant", "I am doing well, thanks.") +# # # print(conversation.to_json()) +# print(type(conversation.to_dict())) +# print(conversation.to_yaml()) From ba2a4d14718b8461151f0459706299a13b5fc442 Mon Sep 17 00:00:00 2001 From: harshalmore31 Date: Tue, 19 Aug 2025 22:28:25 +0530 Subject: [PATCH 07/19] updates and fixes ! --- agents_loader_example.py | 9 + examples/agent_loader_demo.py | 28 +++ examples/agents_loader_example.py | 175 ------------- .../multi_agent/simple_agent_loader_demo.py | 233 ------------------ 4 files changed, 37 insertions(+), 408 deletions(-) create mode 100644 agents_loader_example.py create mode 100644 examples/agent_loader_demo.py delete mode 100644 examples/agents_loader_example.py delete mode 100644 examples/multi_agent/simple_agent_loader_demo.py diff --git a/agents_loader_example.py b/agents_loader_example.py new file mode 100644 index 00000000..83fde8e6 --- /dev/null +++ b/agents_loader_example.py @@ -0,0 +1,9 @@ +from swarms.utils.agent_loader import load_agent_from_markdown + +# Load the Finance Advisor agent from markdown +agent = load_agent_from_markdown("Finance_advisor.md") + +# Use the agent to get financial advice +response = agent.run( + "I have $10,000 to invest. What's a good strategy for a beginner?" +) \ No newline at end of file diff --git a/examples/agent_loader_demo.py b/examples/agent_loader_demo.py new file mode 100644 index 00000000..bcc127fb --- /dev/null +++ b/examples/agent_loader_demo.py @@ -0,0 +1,28 @@ +from swarms.utils.agent_loader import load_agent_from_markdown + +# Example 1: Load a single agent +market_researcher = load_agent_from_markdown("market_researcher.md") + +# Example 2: Load multiple agents +from swarms.utils.agent_loader import load_agents_from_markdown + +agents = load_agents_from_markdown([ + "market_researcher.md", + "financial_analyst.md", + "risk_analyst.md" +]) + +# Example 3: Use agents in a workflow +from swarms.structs.sequential_workflow import SequentialWorkflow + +workflow = SequentialWorkflow( + agents=agents, + max_loops=1 +) + +task = """ +Analyze the AI healthcare market for a $50M investment opportunity. +Focus on market size, competition, financials, and risks. +""" + +result = workflow.run(task) \ No newline at end of file diff --git a/examples/agents_loader_example.py b/examples/agents_loader_example.py deleted file mode 100644 index f70e4435..00000000 --- a/examples/agents_loader_example.py +++ /dev/null @@ -1,175 +0,0 @@ -""" -Example demonstrating the AgentLoader for loading agents from markdown files. - -This example shows: -1. Loading a single agent from a markdown file -2. Loading multiple agents from markdown files -3. Using the convenience functions -4. Error handling and validation -""" - -import os -from swarms.utils.agent_loader import AgentLoader, load_agent_from_markdown, load_agents_from_markdown - -def main(): - # Initialize the loader - loader = AgentLoader() - - print("=== AgentLoader Demo ===") - - # Example 1: Create sample markdown files for testing - Claude Code format - - # Performance Engineer agent - performance_md = """--- -name: performance-engineer -description: Optimize application performance and identify bottlenecks -model_name: gpt-4 -temperature: 0.3 -max_loops: 2 -mcp_url: http://example.com/mcp ---- - -You are a Performance Engineer specializing in application optimization and scalability. - -Your role involves: -- Analyzing application architecture and identifying potential bottlenecks -- Implementing comprehensive monitoring and logging -- Conducting performance testing under various load conditions -- Optimizing critical paths and resource usage -- Documenting findings and providing actionable recommendations - -Expected output: -- Performance analysis reports with specific metrics -- Optimized code recommendations -- Infrastructure scaling suggestions -- Monitoring and alerting setup guidelines -""" - - # Data Analyst agent - data_analyst_md = """--- -name: data-analyst -description: Analyze data and provide business insights -model_name: gpt-4 -temperature: 0.2 -max_loops: 1 ---- - -You are a Data Analyst specializing in extracting insights from complex datasets. - -Your responsibilities include: -- Collecting and cleaning data from various sources -- Performing exploratory data analysis and statistical modeling -- Creating compelling visualizations and interactive dashboards -- Applying statistical methods and machine learning techniques -- Presenting findings and actionable business recommendations - -Focus on providing data-driven insights that support strategic decision making. -""" - - # Create sample markdown files - performance_file = "performance_engineer.md" - data_file = "data_analyst.md" - - with open(performance_file, 'w') as f: - f.write(performance_md) - - with open(data_file, 'w') as f: - f.write(data_analyst_md) - - try: - # Example 2: Load Performance Engineer agent - print("\\n1. Loading Performance Engineer agent (YAML frontmatter):") - perf_agent = loader.load_single_agent(performance_file) - print(f" Loaded agent: {perf_agent.agent_name}") - print(f" Model: {perf_agent.model_name}") - print(f" Temperature: {getattr(perf_agent, 'temperature', 'Not set')}") - print(f" Max loops: {perf_agent.max_loops}") - print(f" System prompt preview: {perf_agent.system_prompt[:100]}...") - - # Example 3: Load Data Analyst agent - print("\\n2. Loading Data Analyst agent:") - data_agent = loader.load_single_agent(data_file) - print(f" Loaded agent: {data_agent.agent_name}") - print(f" Temperature: {getattr(data_agent, 'temperature', 'Not set')}") - print(f" System prompt preview: {data_agent.system_prompt[:100]}...") - - # Example 4: Load single agent using convenience function - print("\\n3. Loading single agent using convenience function:") - agent2 = load_agent_from_markdown(performance_file) - print(f" Loaded agent: {agent2.agent_name}") - - # Example 5: Load multiple agents (from directory or list) - print("\\n4. Loading multiple agents:") - - # Create another sample file - Security Analyst - security_md = """--- -name: security-analyst -description: Analyze and improve system security -model_name: gpt-4 -temperature: 0.1 -max_loops: 3 ---- - -You are a Security Analyst specializing in cybersecurity assessment and protection. - -Your expertise includes: -- Conducting comprehensive security vulnerability assessments -- Performing detailed code security reviews and penetration testing -- Implementing robust infrastructure hardening measures -- Developing incident response and recovery procedures - -Key methodology: -1. Conduct thorough security audits across all system components -2. Identify and classify potential vulnerabilities and threats -3. Recommend and implement security improvements and controls -4. Develop comprehensive security policies and best practices -5. Monitor and respond to security incidents - -Provide detailed security reports with specific remediation steps and risk assessments. -""" - - security_file = "security_analyst.md" - with open(security_file, 'w') as f: - f.write(security_md) - - # Load multiple agents from list - agents = loader.load_multiple_agents([performance_file, data_file, security_file]) - print(f" Loaded {len(agents)} agents:") - for agent in agents: - temp_attr = getattr(agent, 'temperature', 'default') - print(f" - {agent.agent_name} (temp: {temp_attr})") - - # Example 6: Load agents from directory (current directory) - print("\\n5. Loading agents from current directory:") - current_dir_agents = load_agents_from_markdown(".") - print(f" Found {len(current_dir_agents)} agents in current directory") - - # Example 7: Demonstrate error handling - print("\\n6. Error handling demo:") - try: - loader.load_single_agent("nonexistent.md") - except FileNotFoundError as e: - print(f" Caught expected error: {e}") - - # Example 8: Test agent functionality - print("\\n7. Testing loaded agent functionality:") - test_agent = agents[0] - print(f" Agent: {test_agent.agent_name}") - print(f" Temperature: {getattr(test_agent, 'temperature', 'default')}") - print(f" Max loops: {test_agent.max_loops}") - print(f" Ready for task execution") - - except Exception as e: - print(f"Error during demo: {e}") - - finally: - # Cleanup sample files - for file in [performance_file, data_file, security_file]: - if os.path.exists(file): - os.remove(file) - print("\\n Cleaned up sample files") - - print("\\n=== Demo Complete ===") - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/examples/multi_agent/simple_agent_loader_demo.py b/examples/multi_agent/simple_agent_loader_demo.py deleted file mode 100644 index 481a5aea..00000000 --- a/examples/multi_agent/simple_agent_loader_demo.py +++ /dev/null @@ -1,233 +0,0 @@ -""" -Simple AgentLoader Demo - Claude Code Format -============================================= - -A comprehensive demonstration of the AgentLoader using the Claude Code -sub-agent YAML frontmatter format. - -This example shows: -1. Creating agents using Claude Code YAML frontmatter format -2. Loading agents from markdown files with YAML frontmatter -3. Using loaded agents in multi-agent workflows -4. Demonstrating different agent configurations -""" - -import os -import tempfile -from pathlib import Path -import sys - -# Add local swarms to path -sys.path.insert(0, str(Path(__file__).parent.parent.parent)) - -from swarms.structs.agent import Agent -from swarms.structs.sequential_workflow import SequentialWorkflow -from swarms.utils.agent_loader import AgentLoader, load_agents_from_markdown - -def create_markdown_agent_files(): - """Create markdown files demonstrating Claude Code YAML frontmatter format""" - - # Claude Code YAML frontmatter format - agent_files = { - "market_researcher.md": """--- -name: market-researcher -description: Expert in market analysis and competitive intelligence -model_name: gpt-4 -temperature: 0.2 -max_loops: 2 -mcp_url: http://example.com/market-data ---- - -You are a market research specialist with deep expertise in analyzing market dynamics and competitive landscapes. - -Your core responsibilities include: -- Conducting comprehensive market size and growth analysis -- Performing detailed competitive landscape assessments -- Analyzing consumer behavior patterns and preferences -- Identifying emerging industry trends and opportunities - -Methodology: -1. Gather comprehensive quantitative and qualitative market data -2. Analyze key market drivers, barriers, and success factors -3. Evaluate competitive positioning and market share dynamics -4. Assess market opportunities, threats, and entry strategies -5. Provide actionable insights with data-driven recommendations - -Always provide detailed market analysis reports with specific metrics, growth projections, and strategic recommendations for market entry or expansion. -""", - - "financial_analyst.md": """--- -name: financial-analyst -description: Specialist in financial modeling and investment analysis -model_name: gpt-4 -temperature: 0.1 -max_loops: 3 ---- - -You are a financial analysis expert specializing in investment evaluation and financial modeling. - -Your areas of expertise include: -- Financial statement analysis and ratio interpretation -- DCF modeling and valuation techniques (DCF, comparable company analysis, precedent transactions) -- Investment risk assessment and sensitivity analysis -- Cash flow projections and working capital analysis - -Analytical approach: -1. Conduct thorough financial statement analysis -2. Build comprehensive financial models with multiple scenarios -3. Perform detailed valuation using multiple methodologies -4. Assess financial risks and conduct sensitivity analysis -5. Generate investment recommendations with clear rationale - -Provide detailed financial reports with valuation models, risk assessments, and investment recommendations supported by quantitative analysis. -""", - - "industry_expert.md": """--- -name: industry-expert -description: Domain specialist with deep industry knowledge and regulatory expertise -model_name: gpt-4 -temperature: 0.3 -max_loops: 2 ---- - -You are an industry analysis expert with comprehensive knowledge of market structures, regulatory environments, and technology trends. - -Your specialization areas: -- Industry structure analysis and value chain mapping -- Regulatory environment assessment and compliance requirements -- Technology trends identification and disruption analysis -- Supply chain dynamics and operational considerations - -Research methodology: -1. Map industry structure, key players, and stakeholder relationships -2. Analyze current and emerging regulatory framework -3. Identify technology trends and potential market disruptions -4. Evaluate supply chain dynamics and operational requirements -5. Assess competitive positioning and strategic opportunities - -Generate comprehensive industry landscape reports with regulatory insights, technology trend analysis, and strategic recommendations for market positioning. -""" - "risk_analyst.md": """--- -name: risk-analyst -description: Specialist in investment risk assessment and mitigation strategies -model_name: gpt-4 -temperature: 0.15 -max_loops: 2 ---- - -You are a Risk Analyst specializing in comprehensive investment risk assessment and portfolio management. - -Your core competencies include: -- Conducting detailed investment risk evaluation and categorization -- Implementing sophisticated portfolio risk management strategies -- Ensuring regulatory compliance and conducting compliance assessments -- Performing advanced scenario analysis and stress testing methodologies - -Analytical framework: -1. Systematically identify and categorize all investment risks -2. Quantify risk exposure using advanced statistical methods and models -3. Develop comprehensive risk mitigation strategies and frameworks -4. Conduct rigorous scenario analysis and stress testing procedures -5. Provide actionable risk management recommendations with implementation roadmaps - -Deliver comprehensive risk assessment reports with quantitative analysis, compliance guidelines, and strategic risk management recommendations. -""" - } - - temp_files = [] - - # Create Claude Code format files - for filename, content in agent_files.items(): - temp_file = os.path.join(tempfile.gettempdir(), filename) - with open(temp_file, 'w', encoding='utf-8') as f: - f.write(content) - temp_files.append(temp_file) - - return temp_files - -def main(): - """Main execution function demonstrating AgentLoader with Claude Code format""" - - print("AgentLoader Demo - Claude Code YAML Frontmatter Format") - print("=" * 60) - - # Create markdown files demonstrating both formats - print("\n1. Creating markdown files...") - temp_files = create_markdown_agent_files() - - try: - # Load agents using AgentLoader - print("\n2. Loading agents using AgentLoader...") - agents = load_agents_from_markdown(temp_files) - - print(f" Successfully loaded {len(agents)} agents:") - for agent in agents: - temp_attr = getattr(agent, 'temperature', 'default') - max_loops = getattr(agent, 'max_loops', 1) - print(f" - {agent.agent_name} (temp: {temp_attr}, loops: {max_loops})") - - # Demonstrate individual agent configuration - print("\n3. Agent Configuration Details:") - for i, agent in enumerate(agents, 1): - print(f" Agent {i}: {agent.agent_name}") - print(f" Model: {getattr(agent, 'model_name', 'default')}") - print(f" System prompt preview: {agent.system_prompt[:100]}...") - print() - - # Create sequential workflow with loaded agents - print("4. Creating sequential workflow...") - research_workflow = SequentialWorkflow( - agents=agents, - max_loops=1, - ) - - # Define research task - task = """ - Analyze the AI-powered healthcare diagnostics market for a potential $50M investment. - - Focus on: - 1. Market size, growth projections, and key drivers - 2. Competitive landscape and major players - 3. Financial viability and investment attractiveness - 4. Industry dynamics and regulatory considerations - 5. Risk assessment and mitigation strategies - - Provide comprehensive strategic recommendations for market entry. - """ - - print("5. Executing research workflow...") - print("=" * 50) - - # Note: In a real scenario, this would execute the workflow - # For demo purposes, we'll show the task distribution - print(f"Task distributed to {len(agents)} specialized agents:") - for i, agent in enumerate(agents, 1): - print(f" Agent {i} ({agent.agent_name}): Ready to process") - - print(f"\nTask preview: {task[:150]}...") - print("\n[Demo mode - actual workflow execution would call LLM APIs]") - - print("\nDemo Summary:") - print("-" * 50) - print("✓ Successfully loaded agents using Claude Code YAML frontmatter format") - print("✓ Agents configured with different temperatures and max_loops from YAML") - print("✓ Multi-agent workflow created with specialized investment analysis agents") - print("✓ Workflow ready for comprehensive market analysis execution") - - except Exception as e: - print(f"Error during demo: {e}") - import traceback - traceback.print_exc() - - finally: - # Cleanup temporary files - print("\n6. Cleaning up temporary files...") - for temp_file in temp_files: - try: - os.remove(temp_file) - print(f" Removed: {os.path.basename(temp_file)}") - except OSError: - pass - -if __name__ == "__main__": - main() \ No newline at end of file From 4c5dd5a67f52a9009558e04e0711c5416f90887a Mon Sep 17 00:00:00 2001 From: harshalmore31 Date: Tue, 19 Aug 2025 22:29:11 +0530 Subject: [PATCH 08/19] cleanup ! --- agents_loader_example.py | 9 --------- 1 file changed, 9 deletions(-) delete mode 100644 agents_loader_example.py diff --git a/agents_loader_example.py b/agents_loader_example.py deleted file mode 100644 index 83fde8e6..00000000 --- a/agents_loader_example.py +++ /dev/null @@ -1,9 +0,0 @@ -from swarms.utils.agent_loader import load_agent_from_markdown - -# Load the Finance Advisor agent from markdown -agent = load_agent_from_markdown("Finance_advisor.md") - -# Use the agent to get financial advice -response = agent.run( - "I have $10,000 to invest. What's a good strategy for a beginner?" -) \ No newline at end of file From dbb085c371c3258008d1636280559ec28f1808ea Mon Sep 17 00:00:00 2001 From: Kye Gomez Date: Wed, 20 Aug 2025 08:29:52 -0700 Subject: [PATCH 09/19] [FEAT][SIM][BellLabs] [IMPROVEMENT][AGENT.PY][Show tool call output option] [FIX][Conversation.dynamic_context_window ISSUE] [examples] --- docs/swarms/structs/agent.md | 118 ++- example.py | 11 +- .../euroswarm_parliament_example.py | 642 +------------- examples/simulations/example_bell_labs.py | 39 + .../single_agent/dynamic_context_window.py | 29 + examples/tools/claude_as_a_tool.py | 206 +++++ pyproject.toml | 2 +- simulation_vote_example.py | 4 - swarms/sims/bell_labs.py | 815 ++++++++++++++++++ swarms/structs/agent.py | 143 ++- swarms/structs/conversation.py | 56 +- swarms/utils/litellm_wrapper.py | 40 +- 12 files changed, 1351 insertions(+), 754 deletions(-) create mode 100644 examples/simulations/example_bell_labs.py create mode 100644 examples/single_agent/dynamic_context_window.py create mode 100644 examples/tools/claude_as_a_tool.py create mode 100644 swarms/sims/bell_labs.py diff --git a/docs/swarms/structs/agent.md b/docs/swarms/structs/agent.md index 6b533ab4..862aa36c 100644 --- a/docs/swarms/structs/agent.md +++ b/docs/swarms/structs/agent.md @@ -89,7 +89,6 @@ graph TD | `callback` | Callable function to be called after each agent loop. | | `metadata` | Dictionary containing metadata for the agent. | | `callbacks` | List of callable functions to be called during execution. | -| `logger_handler` | Handler for logging messages. | | `search_algorithm` | Callable function for long-term memory retrieval. | | `logs_to_filename` | File path for logging agent activities. | | `evaluator` | Callable function for evaluating the agent's responses. | @@ -121,14 +120,12 @@ graph TD | `memory_chunk_size` | Integer representing the maximum size of memory chunks for long-term memory retrieval. | | `agent_ops_on` | Boolean indicating whether agent operations should be enabled. | | `return_step_meta` | Boolean indicating whether to return JSON of all steps and additional metadata. | -| `output_type` | Literal type indicating whether to output "string", "str", "list", "json", "dict", or "yaml". | | `time_created` | Float representing the time the agent was created. | | `tags` | Optional list of strings for tagging the agent. | | `use_cases` | Optional list of dictionaries describing use cases for the agent. | | `step_pool` | List of Step objects representing the agent's execution steps. | | `print_every_step` | Boolean indicating whether to print every step of execution. | | `agent_output` | ManySteps object containing the agent's output and metadata. | -| `executor_workers` | Integer representing the number of executor workers for concurrent operations. | | `data_memory` | Optional callable for data memory operations. | | `load_yaml_path` | String representing the path to a YAML file for loading configurations. | | `auto_generate_prompt` | Boolean indicating whether to automatically generate prompts. | @@ -137,17 +134,44 @@ graph TD | `artifacts_on` | Boolean indicating whether to save artifacts from agent execution | | `artifacts_output_path` | File path where artifacts should be saved | | `artifacts_file_extension` | File extension to use for saved artifacts | -| `device` | Device to run computations on ("cpu" or "gpu") | | `all_cores` | Boolean indicating whether to use all CPU cores | | `device_id` | ID of the GPU device to use if running on GPU | | `scheduled_run_date` | Optional datetime for scheduling future agent runs | - +| `do_not_use_cluster_ops` | Boolean indicating whether to avoid cluster operations | +| `all_gpus` | Boolean indicating whether to use all available GPUs | +| `model_name` | String representing the name of the model to use | +| `llm_args` | Dictionary containing additional arguments for the LLM | +| `load_state_path` | String representing the path to load state from | +| `role` | String representing the role of the agent (e.g., "worker") | +| `print_on` | Boolean indicating whether to print output | +| `tools_list_dictionary` | List of dictionaries representing tool schemas | +| `mcp_url` | String or MCPConnection representing the MCP server URL | +| `mcp_urls` | List of strings representing multiple MCP server URLs | +| `react_on` | Boolean indicating whether to enable ReAct reasoning | +| `safety_prompt_on` | Boolean indicating whether to enable safety prompts | +| `random_models_on` | Boolean indicating whether to randomly select models | +| `mcp_config` | MCPConnection object containing MCP configuration | +| `top_p` | Float representing the top-p sampling parameter | +| `conversation_schema` | ConversationSchema object for conversation formatting | +| `llm_base_url` | String representing the base URL for the LLM API | +| `llm_api_key` | String representing the API key for the LLM | +| `rag_config` | RAGConfig object containing RAG configuration | +| `tool_call_summary` | Boolean indicating whether to summarize tool calls | +| `output_raw_json_from_tool_call` | Boolean indicating whether to output raw JSON from tool calls | +| `summarize_multiple_images` | Boolean indicating whether to summarize multiple image outputs | +| `tool_retry_attempts` | Integer representing the number of retry attempts for tool execution | +| `reasoning_prompt_on` | Boolean indicating whether to enable reasoning prompts | +| `dynamic_context_window` | Boolean indicating whether to dynamically adjust context window | +| `created_at` | Float representing the timestamp when the agent was created | +| `workspace_dir` | String representing the workspace directory for the agent | +| `timeout` | Integer representing the timeout for operations in seconds | ## `Agent` Methods | Method | Description | Inputs | Usage Example | |--------|-------------|--------|----------------| -| `run(task, img=None, is_last=False, device="cpu", device_id=0, all_cores=True, *args, **kwargs)` | Runs the autonomous agent loop to complete the given task. | `task` (str): The task to be performed.
`img` (str, optional): Path to an image file.
`is_last` (bool): Whether this is the last task.
`device` (str): Device to run on ("cpu" or "gpu").
`device_id` (int): ID of the GPU to use.
`all_cores` (bool): Whether to use all CPU cores.
`*args`, `**kwargs`: Additional arguments. | `response = agent.run("Generate a report on financial performance.")` | +| `run(task, img=None, imgs=None, correct_answer=None, streaming_callback=None, *args, **kwargs)` | Runs the autonomous agent loop to complete the given task. | `task` (str): The task to be performed.
`img` (str, optional): Path to an image file.
`imgs` (List[str], optional): List of image paths.
`correct_answer` (str, optional): Expected correct answer for validation.
`streaming_callback` (Callable, optional): Callback for streaming tokens.
`*args`, `**kwargs`: Additional arguments. | `response = agent.run("Generate a report on financial performance.")` | +| `run_batched(tasks, imgs=None, *args, **kwargs)` | Runs multiple tasks concurrently in batch mode. | `tasks` (List[str]): List of tasks to run.
`imgs` (List[str], optional): List of images to process.
`*args`, `**kwargs`: Additional arguments. | `responses = agent.run_batched(["Task 1", "Task 2"])` | | `__call__(task, img=None, *args, **kwargs)` | Alternative way to call the `run` method. | Same as `run`. | `response = agent("Generate a report on financial performance.")` | | `parse_and_execute_tools(response, *args, **kwargs)` | Parses the agent's response and executes any tools mentioned in it. | `response` (str): The agent's response to be parsed.
`*args`, `**kwargs`: Additional arguments. | `agent.parse_and_execute_tools(response)` | | `add_memory(message)` | Adds a message to the agent's memory. | `message` (str): The message to add. | `agent.add_memory("Important information")` | @@ -155,6 +179,8 @@ graph TD | `run_concurrent(task, *args, **kwargs)` | Runs a task concurrently. | `task` (str): The task to run.
`*args`, `**kwargs`: Additional arguments. | `response = await agent.run_concurrent("Concurrent task")` | | `run_concurrent_tasks(tasks, *args, **kwargs)` | Runs multiple tasks concurrently. | `tasks` (List[str]): List of tasks to run.
`*args`, `**kwargs`: Additional arguments. | `responses = agent.run_concurrent_tasks(["Task 1", "Task 2"])` | | `bulk_run(inputs)` | Generates responses for multiple input sets. | `inputs` (List[Dict[str, Any]]): List of input dictionaries. | `responses = agent.bulk_run([{"task": "Task 1"}, {"task": "Task 2"}])` | +| `run_multiple_images(task, imgs, *args, **kwargs)` | Runs the agent with multiple images using concurrent processing. | `task` (str): The task to perform on each image.
`imgs` (List[str]): List of image paths or URLs.
`*args`, `**kwargs`: Additional arguments. | `outputs = agent.run_multiple_images("Describe image", ["img1.jpg", "img2.png"])` | +| `continuous_run_with_answer(task, img=None, correct_answer=None, max_attempts=10)` | Runs the agent until the correct answer is provided. | `task` (str): The task to perform.
`img` (str, optional): Image to process.
`correct_answer` (str): Expected answer.
`max_attempts` (int): Maximum attempts. | `response = agent.continuous_run_with_answer("Math problem", correct_answer="42")` | | `save()` | Saves the agent's history to a file. | None | `agent.save()` | | `load(file_path)` | Loads the agent's history from a file. | `file_path` (str): Path to the file. | `agent.load("agent_history.json")` | | `graceful_shutdown()` | Gracefully shuts down the system, saving the state. | None | `agent.graceful_shutdown()` | @@ -178,8 +204,6 @@ graph TD | `send_agent_message(agent_name, message, *args, **kwargs)` | Sends a message from the agent to a user. | `agent_name` (str): Name of the agent.
`message` (str): Message to send.
`*args`, `**kwargs`: Additional arguments. | `response = agent.send_agent_message("AgentX", "Task completed")` | | `add_tool(tool)` | Adds a tool to the agent's toolset. | `tool` (Callable): Tool to add. | `agent.add_tool(my_custom_tool)` | | `add_tools(tools)` | Adds multiple tools to the agent's toolset. | `tools` (List[Callable]): List of tools to add. | `agent.add_tools([tool1, tool2])` | -| `remove_tool(tool)` | Removes a tool from the agent's toolset. || Method | Description | Inputs | Usage Example | -|--------|-------------|--------|----------------| | `remove_tool(tool)` | Removes a tool from the agent's toolset. | `tool` (Callable): Tool to remove. | `agent.remove_tool(my_custom_tool)` | | `remove_tools(tools)` | Removes multiple tools from the agent's toolset. | `tools` (List[Callable]): List of tools to remove. | `agent.remove_tools([tool1, tool2])` | | `get_docs_from_doc_folders()` | Retrieves and processes documents from the specified folder. | None | `agent.get_docs_from_doc_folders()` | @@ -208,18 +232,30 @@ graph TD | `handle_sop_ops()` | Handles operations related to standard operating procedures. | None | `agent.handle_sop_ops()` | | `agent_output_type(responses)` | Processes and returns the agent's output based on the specified output type. | `responses` (list): List of responses. | `formatted_output = agent.agent_output_type(responses)` | | `check_if_no_prompt_then_autogenerate(task)` | Checks if a system prompt is not set and auto-generates one if needed. | `task` (str): The task to use for generating a prompt. | `agent.check_if_no_prompt_then_autogenerate("Analyze data")` | -| `check_if_no_prompt_then_autogenerate(task)` | Checks if auto_generate_prompt is enabled and generates a prompt by combining agent name, description and system prompt | `task` (str, optional): Task to use as fallback | `agent.check_if_no_prompt_then_autogenerate("Analyze data")` | | `handle_artifacts(response, output_path, extension)` | Handles saving artifacts from agent execution | `response` (str): Agent response
`output_path` (str): Output path
`extension` (str): File extension | `agent.handle_artifacts(response, "outputs/", ".txt")` | +| `showcase_config()` | Displays the agent's configuration in a formatted table. | None | `agent.showcase_config()` | +| `talk_to(agent, task, img=None, *args, **kwargs)` | Initiates a conversation with another agent. | `agent` (Any): Target agent.
`task` (str): Task to discuss.
`img` (str, optional): Image to share.
`*args`, `**kwargs`: Additional arguments. | `response = agent.talk_to(other_agent, "Let's collaborate")` | +| `talk_to_multiple_agents(agents, task, *args, **kwargs)` | Talks to multiple agents concurrently. | `agents` (List[Any]): List of target agents.
`task` (str): Task to discuss.
`*args`, `**kwargs`: Additional arguments. | `responses = agent.talk_to_multiple_agents([agent1, agent2], "Group discussion")` | +| `get_agent_role()` | Returns the role of the agent. | None | `role = agent.get_agent_role()` | +| `pretty_print(response, loop_count)` | Prints the response in a formatted panel. | `response` (str): Response to print.
`loop_count` (int): Current loop number. | `agent.pretty_print("Analysis complete", 1)` | +| `parse_llm_output(response)` | Parses and standardizes the output from the LLM. | `response` (Any): Response from the LLM. | `parsed_response = agent.parse_llm_output(llm_output)` | +| `sentiment_and_evaluator(response)` | Performs sentiment analysis and evaluation on the response. | `response` (str): Response to analyze. | `agent.sentiment_and_evaluator("Great response!")` | +| `output_cleaner_op(response)` | Applies output cleaning operations to the response. | `response` (str): Response to clean. | `cleaned_response = agent.output_cleaner_op(response)` | +| `mcp_tool_handling(response, current_loop)` | Handles MCP tool execution and responses. | `response` (Any): Response containing tool calls.
`current_loop` (int): Current loop number. | `agent.mcp_tool_handling(response, 1)` | +| `temp_llm_instance_for_tool_summary()` | Creates a temporary LLM instance for tool summaries. | None | `temp_llm = agent.temp_llm_instance_for_tool_summary()` | +| `execute_tools(response, loop_count)` | Executes tools based on the LLM response. | `response` (Any): Response containing tool calls.
`loop_count` (int): Current loop number. | `agent.execute_tools(response, 1)` | +| `list_output_types()` | Returns available output types. | None | `types = agent.list_output_types()` | +| `tool_execution_retry(response, loop_count)` | Executes tools with retry logic for handling failures. | `response` (Any): Response containing tool calls.
`loop_count` (int): Current loop number. | `agent.tool_execution_retry(response, 1)` | ## Updated Run Method -Update the run method documentation to include new parameters: +The run method has been updated with new parameters for enhanced functionality: | Method | Description | Inputs | Usage Example | |--------|-------------|--------|----------------| -| `run(task, img=None, is_last=False, device="cpu", device_id=0, all_cores=True, scheduled_run_date=None)` | Runs the agent with specified parameters | `task` (str): Task to run
`img` (str, optional): Image path
`is_last` (bool): If this is last task
`device` (str): Device to use
`device_id` (int): GPU ID
`all_cores` (bool): Use all CPU cores
`scheduled_run_date` (datetime, optional): Future run date | `agent.run("Analyze data", device="gpu", device_id=0)` | +| `run(task, img=None, imgs=None, correct_answer=None, streaming_callback=None, *args, **kwargs)` | Runs the agent with enhanced parameters | `task` (str): Task to run
`img` (str, optional): Single image path
`imgs` (List[str], optional): List of image paths
`correct_answer` (str, optional): Expected answer for validation
`streaming_callback` (Callable, optional): Callback for streaming tokens
`*args`, `**kwargs`: Additional arguments | `agent.run("Analyze data", imgs=["img1.jpg", "img2.png"])` | @@ -420,9 +456,35 @@ tasks = [ ] responses = agent.bulk_run(tasks) print(responses) + +# Run multiple tasks in batch mode (new method) +task_list = ["Analyze data", "Generate report", "Create summary"] +batch_responses = agent.run_batched(task_list) +print(f"Completed {len(batch_responses)} tasks in batch mode") ``` +### Batch Processing with `run_batched` + +The new `run_batched` method allows you to process multiple tasks efficiently: + +```python +# Process multiple tasks in batch +tasks = [ + "Analyze the financial data for Q1", + "Generate a summary report for stakeholders", + "Create recommendations for Q2 planning" +] + +# Run all tasks concurrently +batch_results = agent.run_batched(tasks) + +# Process results +for i, (task, result) in enumerate(zip(tasks, batch_results)): + print(f"Task {i+1}: {task}") + print(f"Result: {result}\n") +``` + ### Various other settings ```python @@ -611,6 +673,36 @@ print(type(str_to_dict(out))) ``` +## New Features and Parameters + +### Enhanced Run Method Parameters + +The `run` method now supports several new parameters for advanced functionality: + +- **`imgs`**: Process multiple images simultaneously instead of just one +- **`correct_answer`**: Validate responses against expected answers with automatic retries +- **`streaming_callback`**: Real-time token streaming for interactive applications + +### MCP (Model Context Protocol) Integration + +New parameters enable seamless MCP server integration: + +- **`mcp_url`**: Connect to a single MCP server +- **`mcp_urls`**: Connect to multiple MCP servers +- **`mcp_config`**: Advanced MCP configuration options + +### Advanced Reasoning and Safety + +- **`react_on`**: Enable ReAct reasoning for complex problem-solving +- **`safety_prompt_on`**: Add safety constraints to agent responses +- **`reasoning_prompt_on`**: Enable multi-loop reasoning for complex tasks + +### Performance and Resource Management + +- **`dynamic_context_window`**: Automatically adjust context window based on available tokens +- **`tool_retry_attempts`**: Configure retry behavior for tool execution +- **`summarize_multiple_images`**: Automatically summarize results from multiple image processing + ## Best Practices 1. Always provide a clear and concise `system_prompt` to guide the agent's behavior. @@ -627,5 +719,9 @@ print(type(str_to_dict(out))) 12. Configure `device` and `device_id` appropriately for optimal performance 13. Enable `rag_every_loop` when continuous context from long-term memory is needed 14. Use `scheduled_run_date` for automated task scheduling +15. Leverage `run_batched` for efficient processing of multiple related tasks +16. Use `mcp_url` or `mcp_urls` to extend agent capabilities with external tools +17. Enable `react_on` for complex reasoning tasks requiring step-by-step analysis +18. Configure `tool_retry_attempts` for robust tool execution in production environments By following these guidelines and leveraging the Swarm Agent's extensive features, you can create powerful, flexible, and efficient autonomous agents for a wide range of applications. \ No newline at end of file diff --git a/example.py b/example.py index 17398527..c9cc03d7 100644 --- a/example.py +++ b/example.py @@ -1,10 +1,5 @@ from swarms import Agent -import litellm - -litellm._turn_on_debug() # 👈 this is the 1-line change you need to make - - # Initialize the agent agent = Agent( agent_name="Quantitative-Trading-Agent", @@ -41,10 +36,8 @@ agent = Agent( model_name="claude-sonnet-4-20250514", dynamic_temperature_enabled=True, output_type="str-all-except-first", - max_loops="auto", - interactive=True, - no_reasoning_prompt=True, - streaming_on=True, + max_loops=1, + dynamic_context_window=True, ) out = agent.run( diff --git a/examples/simulations/euroswarm_parliament/euroswarm_parliament_example.py b/examples/simulations/euroswarm_parliament/euroswarm_parliament_example.py index b2ccf858..1ae8f7f5 100644 --- a/examples/simulations/euroswarm_parliament/euroswarm_parliament_example.py +++ b/examples/simulations/euroswarm_parliament/euroswarm_parliament_example.py @@ -1,661 +1,59 @@ """ -EuroSwarm Parliament - Example Script +EuroSwarm Parliament - Simple Example -This script demonstrates the comprehensive democratic functionality of the EuroSwarm Parliament, -including bill introduction, committee work, parliamentary debates, and democratic voting. +A basic demonstration of the EuroSwarm Parliament functionality. """ -# Import directly from the file -from euroswarm_parliament import ( - EuroSwarmParliament, - VoteType, -) +from euroswarm_parliament import EuroSwarmParliament, VoteType -def demonstrate_parliament_initialization(): - """Demonstrate parliament initialization and basic functionality with cost optimization.""" +def main(): + """Simple demonstration of EuroSwarm Parliament.""" - print( - "\nEUROSWARM PARLIAMENT INITIALIZATION DEMONSTRATION (COST OPTIMIZED)" - ) - print("=" * 60) + print("EUROSWARM PARLIAMENT - SIMPLE EXAMPLE") + print("=" * 50) - # Initialize the parliament with cost optimization + # Initialize the parliament parliament = EuroSwarmParliament( eu_data_file="EU.xml", - parliament_size=None, # Use all MEPs from EU.xml (717) enable_democratic_discussion=True, enable_committee_work=True, - enable_amendment_process=True, - enable_lazy_loading=True, # NEW: Lazy load MEP agents - enable_caching=True, # NEW: Enable response caching - batch_size=25, # NEW: Batch size for concurrent execution - budget_limit=100.0, # NEW: Budget limit in dollars verbose=True, ) print(f"Parliament initialized with {len(parliament.meps)} MEPs") - # Show parliament composition with cost stats - composition = parliament.get_parliament_composition() - - print("\nPARLIAMENT COMPOSITION:") - print(f"Total MEPs: {composition['total_meps']}") - print( - f"Loaded MEPs: {composition['loaded_meps']} (lazy loading active)" - ) - - print("\nCOST OPTIMIZATION:") - cost_stats = composition["cost_stats"] - print( - f"Budget Limit: ${cost_stats['budget_remaining'] + cost_stats['total_cost']:.2f}" - ) - print(f"Budget Used: ${cost_stats['total_cost']:.2f}") - print(f"Budget Remaining: ${cost_stats['budget_remaining']:.2f}") - print(f"Cache Hit Rate: {cost_stats['cache_hit_rate']:.1%}") - - print("\nPOLITICAL GROUP DISTRIBUTION:") - for group, data in composition["political_groups"].items(): - count = data["count"] - percentage = data["percentage"] - print(f" {group}: {count} MEPs ({percentage:.1f}%)") - - print("\nCOMMITTEE LEADERSHIP:") - for committee_name, committee_data in composition[ - "committees" - ].items(): - chair = committee_data["chair"] - if chair: - print(f" {committee_name}: {chair}") - - return parliament - - -def demonstrate_individual_mep_interaction(parliament): - """Demonstrate individual MEP interaction and personality.""" - - print("\nINDIVIDUAL MEP INTERACTION DEMONSTRATION") - print("=" * 60) - # Get a sample MEP sample_mep_name = list(parliament.meps.keys())[0] sample_mep = parliament.meps[sample_mep_name] - print(f"Sample MEP: {sample_mep.full_name}") + print(f"\nSample MEP: {sample_mep.full_name}") print(f"Country: {sample_mep.country}") print(f"Political Group: {sample_mep.political_group}") - print(f"National Party: {sample_mep.national_party}") - print(f"Committees: {', '.join(sample_mep.committees)}") - print(f"Expertise Areas: {', '.join(sample_mep.expertise_areas)}") - - # Test MEP agent interaction - if sample_mep.agent: - test_prompt = "What are your views on European integration and how do you approach cross-border cooperation?" - - print(f"\nMEP Response to: '{test_prompt}'") - print("-" * 50) - - try: - response = sample_mep.agent.run(test_prompt) - print( - response[:500] + "..." - if len(response) > 500 - else response - ) - except Exception as e: - print(f"Error getting MEP response: {e}") - -def demonstrate_committee_work(parliament): - """Demonstrate committee work and hearings.""" - - print("\nCOMMITTEE WORK DEMONSTRATION") - print("=" * 60) - - # Get a real MEP as sponsor - sponsor = list(parliament.meps.keys())[0] - - # Create a test bill + # Create a simple bill bill = parliament.introduce_bill( - title="European Digital Rights and Privacy Protection Act", - description="Comprehensive legislation to strengthen digital rights, enhance privacy protection, and establish clear guidelines for data handling across the European Union.", + title="European Digital Rights Act", + description="Basic legislation to protect digital rights across the EU.", bill_type=VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, committee="Legal Affairs", - sponsor=sponsor, + sponsor=sample_mep_name, ) - print(f"Bill: {bill.title}") + print(f"\nBill introduced: {bill.title}") print(f"Committee: {bill.committee}") - print(f"Sponsor: {bill.sponsor}") - - # Conduct committee hearing - print("\nCONDUCTING COMMITTEE HEARING...") - hearing_result = parliament.conduct_committee_hearing( - bill.committee, bill - ) - - print(f"Committee: {hearing_result['committee']}") - print(f"Participants: {len(hearing_result['participants'])} MEPs") - print( - f"Recommendation: {hearing_result['recommendations']['recommendation']}" - ) - print( - f"Support: {hearing_result['recommendations']['support_percentage']:.1f}%" - ) - print( - f"Oppose: {hearing_result['recommendations']['oppose_percentage']:.1f}%" - ) - print( - f"Amend: {hearing_result['recommendations']['amend_percentage']:.1f}%" - ) - - -def demonstrate_parliamentary_debate(parliament): - """Demonstrate parliamentary debate functionality.""" - - print("\nPARLIAMENTARY DEBATE DEMONSTRATION") - print("=" * 60) - - # Get a real MEP as sponsor - sponsor = list(parliament.meps.keys())[1] - - # Create a test bill - bill = parliament.introduce_bill( - title="European Green Deal Implementation Act", - description="Legislation to implement the European Green Deal, including carbon neutrality targets, renewable energy investments, and sustainable development measures.", - bill_type=VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, - committee="Environment, Public Health and Food Safety", - sponsor=sponsor, - ) - - print(f"Bill: {bill.title}") - print(f"Description: {bill.description}") - - # Conduct parliamentary debate - print("\nCONDUCTING PARLIAMENTARY DEBATE...") - debate_result = parliament.conduct_parliamentary_debate( - bill, max_speakers=10 - ) - - print( - f"Debate Participants: {len(debate_result['participants'])} MEPs" - ) - print("Debate Analysis:") - print( - f" Support: {debate_result['analysis']['support_count']} speakers ({debate_result['analysis']['support_percentage']:.1f}%)" - ) - print( - f" Oppose: {debate_result['analysis']['oppose_count']} speakers ({debate_result['analysis']['oppose_percentage']:.1f}%)" - ) - print( - f" Neutral: {debate_result['analysis']['neutral_count']} speakers ({debate_result['analysis']['neutral_percentage']:.1f}%)" - ) - -def demonstrate_democratic_voting(parliament): - """Demonstrate democratic voting functionality.""" - - print("\nDEMOCRATIC VOTING DEMONSTRATION") - print("=" * 60) - - # Get a real MEP as sponsor - sponsor = list(parliament.meps.keys())[2] - - # Create a test bill - bill = parliament.introduce_bill( - title="European Social Rights and Labor Protection Act", - description="Legislation to strengthen social rights, improve labor conditions, and ensure fair treatment of workers across the European Union.", - bill_type=VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, - committee="Employment and Social Affairs", - sponsor=sponsor, - ) - - print(f"Bill: {bill.title}") - print(f"Sponsor: {bill.sponsor}") - - # Conduct democratic vote - print("\nCONDUCTING DEMOCRATIC VOTE...") + # Conduct a simple vote + print("\nConducting democratic vote...") vote_result = parliament.conduct_democratic_vote(bill) - # Calculate percentages - total_votes = ( - vote_result.votes_for - + vote_result.votes_against - + vote_result.abstentions - ) - in_favor_percentage = ( - (vote_result.votes_for / total_votes * 100) - if total_votes > 0 - else 0 - ) - against_percentage = ( - (vote_result.votes_against / total_votes * 100) - if total_votes > 0 - else 0 - ) - abstentions_percentage = ( - (vote_result.abstentions / total_votes * 100) - if total_votes > 0 - else 0 - ) - print("Vote Results:") - print(f" Total Votes: {total_votes}") - print( - f" In Favor: {vote_result.votes_for} ({in_favor_percentage:.1f}%)" - ) - print( - f" Against: {vote_result.votes_against} ({against_percentage:.1f}%)" - ) - print( - f" Abstentions: {vote_result.abstentions} ({abstentions_percentage:.1f}%)" - ) + print(f" In Favor: {vote_result.votes_for}") + print(f" Against: {vote_result.votes_against}") + print(f" Abstentions: {vote_result.abstentions}") print(f" Result: {vote_result.result.value}") - # Show political group breakdown if available - if ( - hasattr(vote_result, "group_votes") - and vote_result.group_votes - ): - print("\nPOLITICAL GROUP BREAKDOWN:") - for group, votes in vote_result.group_votes.items(): - print( - f" {group}: {votes['in_favor']}/{votes['total']} in favor ({votes['percentage']:.1f}%)" - ) - else: - print( - f"\nIndividual votes recorded: {len(vote_result.individual_votes)} MEPs" - ) - - -def demonstrate_complete_democratic_session(parliament): - """Demonstrate a complete democratic parliamentary session.""" - - print("\nCOMPLETE DEMOCRATIC SESSION DEMONSTRATION") - print("=" * 60) - - # Get a real MEP as sponsor - sponsor = list(parliament.meps.keys())[3] - - # Run complete session - session_result = parliament.run_democratic_session( - bill_title="European Innovation and Technology Advancement Act", - bill_description="Comprehensive legislation to promote innovation, support technology startups, and establish Europe as a global leader in digital transformation and technological advancement.", - bill_type=VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, - committee="Industry, Research and Energy", - sponsor=sponsor, - ) - - print("Session Results:") - print(f" Bill: {session_result['bill'].title}") - print( - f" Committee Hearing: {session_result['hearing']['recommendations']['recommendation']}" - ) - print( - f" Debate Participants: {len(session_result['debate']['participants'])} MEPs" - ) - print(f" Final Vote: {session_result['vote']['result']}") - print( - f" Vote Margin: {session_result['vote']['in_favor_percentage']:.1f}% in favor" - ) - - -def demonstrate_political_analysis(parliament): - """Demonstrate political analysis and voting prediction.""" - - print("\nPOLITICAL ANALYSIS DEMONSTRATION") - print("=" * 60) - - # Get a real MEP as sponsor - sponsor = list(parliament.meps.keys())[4] - - # Create a test bill - bill = parliament.introduce_bill( - title="European Climate Action and Sustainability Act", - description="Comprehensive climate action legislation including carbon pricing, renewable energy targets, and sustainable development measures.", - bill_type=VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, - committee="Environment, Public Health and Food Safety", - sponsor=sponsor, - ) - - print(f"Bill: {bill.title}") - print(f"Sponsor: {bill.sponsor}") - - # Analyze political landscape - analysis = parliament.analyze_political_landscape(bill) - - print("\nPOLITICAL LANDSCAPE ANALYSIS:") - print(f" Overall Support: {analysis['overall_support']:.1f}%") - print(f" Opposition: {analysis['opposition']:.1f}%") - print(f" Uncertainty: {analysis['uncertainty']:.1f}%") - - print("\nPOLITICAL GROUP ANALYSIS:") - for group, data in analysis["group_analysis"].items(): - print( - f" {group}: {data['support']:.1f}% support, {data['opposition']:.1f}% opposition" - ) - - -def demonstrate_hierarchical_democratic_voting(parliament): - """Demonstrate hierarchical democratic voting with political group boards.""" - - print("\nHIERARCHICAL DEMOCRATIC VOTING DEMONSTRATION") - print("=" * 60) - - # Get a real MEP as sponsor - sponsor = list(parliament.meps.keys())[5] - - # Create a test bill - bill = parliament.introduce_bill( - title="European Climate Action and Sustainability Act", - description="Comprehensive climate action legislation including carbon pricing, renewable energy targets, and sustainable development measures.", - bill_type=VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, - committee="Environment, Public Health and Food Safety", - sponsor=sponsor, - ) - - print(f"Bill: {bill.title}") - print(f"Sponsor: {bill.sponsor}") - - # Conduct hierarchical vote - print("\nCONDUCTING HIERARCHICAL DEMOCRATIC VOTE...") - hierarchical_result = ( - parliament.conduct_hierarchical_democratic_vote(bill) - ) - - print("Hierarchical Vote Results:") - print(f" Total Votes: {hierarchical_result['total_votes']}") - print( - f" In Favor: {hierarchical_result['in_favor']} ({hierarchical_result['in_favor_percentage']:.1f}%)" - ) - print( - f" Against: {hierarchical_result['against']} ({hierarchical_result['against_percentage']:.1f}%)" - ) - print(f" Result: {hierarchical_result['result']}") - - print("\nPOLITICAL GROUP BOARD DECISIONS:") - for group, decision in hierarchical_result[ - "group_decisions" - ].items(): - print( - f" {group}: {decision['decision']} ({decision['confidence']:.1f}% confidence)" - ) - - -def demonstrate_complete_hierarchical_session(parliament): - """Demonstrate a complete hierarchical democratic session.""" - - print("\nCOMPLETE HIERARCHICAL DEMOCRATIC SESSION DEMONSTRATION") - print("=" * 60) - - # Get a real MEP as sponsor - sponsor = list(parliament.meps.keys())[6] - - # Run complete hierarchical session - session_result = parliament.run_hierarchical_democratic_session( - bill_title="European Climate Action and Sustainability Act", - bill_description="Comprehensive climate action legislation including carbon pricing, renewable energy targets, and sustainable development measures.", - bill_type=VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, - committee="Environment, Public Health and Food Safety", - sponsor=sponsor, - ) - - print("Hierarchical Session Results:") - print(f" Bill: {session_result['bill'].title}") - print( - f" Committee Hearing: {session_result['hearing']['recommendations']['recommendation']}" - ) - print( - f" Debate Participants: {len(session_result['debate']['participants'])} MEPs" - ) - print(f" Final Vote: {session_result['vote']['result']}") - print( - f" Vote Margin: {session_result['vote']['in_favor_percentage']:.1f}% in favor" - ) - - -def demonstrate_wikipedia_personalities(parliament): - """Demonstrate the Wikipedia personality system for realistic MEP behavior.""" - - print("\nWIKIPEDIA PERSONALITY SYSTEM DEMONSTRATION") - print("=" * 60) - - # Check if Wikipedia personalities are available - if not parliament.enable_wikipedia_personalities: - print("Wikipedia personality system not available") - print( - "To enable: Install required dependencies and run Wikipedia scraper" - ) - return - - print("Wikipedia personality system enabled") - print( - f"Loaded {len(parliament.personality_profiles)} personality profiles" - ) - - # Show sample personality profiles - print("\nSAMPLE PERSONALITY PROFILES:") - print("-" * 40) - - sample_count = 0 - for mep_name, profile in parliament.personality_profiles.items(): - if sample_count >= 3: # Show only 3 samples - break - - print(f"\n{mep_name}") - print( - f" Wikipedia URL: {profile.wikipedia_url if profile.wikipedia_url else 'Not available'}" - ) - print( - f" Summary: {profile.summary[:200]}..." - if profile.summary - else "No summary available" - ) - print( - f" Political Views: {profile.political_views[:150]}..." - if profile.political_views - else "Based on party alignment" - ) - print( - f" Policy Focus: {profile.policy_focus[:150]}..." - if profile.policy_focus - else "General parliamentary work" - ) - print( - f" Achievements: {profile.achievements[:150]}..." - if profile.achievements - else "Parliamentary service" - ) - print(f" Last Updated: {profile.last_updated}") - - sample_count += 1 - - # Demonstrate personality-driven voting - print("\nPERSONALITY-DRIVEN VOTING DEMONSTRATION:") - print("-" * 50) - - # Create a test bill that would trigger different personality responses - bill = parliament.introduce_bill( - title="European Climate Action and Green Technology Investment Act", - description="Comprehensive legislation to accelerate Europe's transition to renewable energy, including massive investments in green technology, carbon pricing mechanisms, and support for affected industries and workers.", - bill_type=VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, - committee="Environment", - sponsor="Climate Action Leader", - ) - - print(f"Bill: {bill.title}") - print(f"Description: {bill.description}") - - # Show how different MEPs with Wikipedia personalities would respond - print("\nPERSONALITY-BASED RESPONSES:") - print("-" * 40) - - sample_meps = list(parliament.personality_profiles.keys())[:3] - - for mep_name in sample_meps: - mep = parliament.meps.get(mep_name) - profile = parliament.personality_profiles.get(mep_name) - - if mep and profile: - print(f"\n{mep_name} ({mep.political_group})") - - # Show personality influence - if profile.political_views: - print( - f" Political Views: {profile.political_views[:100]}..." - ) - - if profile.policy_focus: - print( - f" Policy Focus: {profile.policy_focus[:100]}..." - ) - - # Predict voting behavior based on personality - if ( - "environment" in profile.policy_focus.lower() - or "climate" in profile.political_views.lower() - ): - predicted_vote = "LIKELY SUPPORT" - reasoning = ( - "Environmental policy focus and climate advocacy" - ) - elif ( - "economic" in profile.policy_focus.lower() - or "business" in profile.political_views.lower() - ): - predicted_vote = "LIKELY OPPOSE" - reasoning = "Economic concerns about investment costs" - else: - predicted_vote = "UNCERTAIN" - reasoning = ( - "Mixed considerations based on party alignment" - ) - - print(f" Predicted Vote: {predicted_vote}") - print(f" Reasoning: {reasoning}") - - # Demonstrate scraping functionality - print("\nWIKIPEDIA SCRAPING CAPABILITIES:") - print("-" * 50) - print("Can scrape Wikipedia data for all 717 MEPs") - print( - "Extracts political views, career history, and achievements" - ) - print("Creates detailed personality profiles in JSON format") - print( - "Integrates real personality data into AI agent system prompts" - ) - print("Enables realistic, personality-driven voting behavior") - print("Respectful API usage with configurable delays") - - print("\nTo scrape all MEP personalities:") - print(" parliament.scrape_wikipedia_personalities(delay=1.0)") - print( - " # This will create personality profiles for all 717 MEPs" - ) - print(" # Profiles are saved in 'mep_personalities/' directory") - - -def demonstrate_optimized_parliamentary_session(parliament): - """Demonstrate cost-optimized parliamentary session.""" - - print("\nCOST-OPTIMIZED PARLIAMENTARY SESSION DEMONSTRATION") - print("=" * 60) - - # Run optimized session with cost limit - session_result = parliament.run_optimized_parliamentary_session( - bill_title="European Digital Rights and Privacy Protection Act", - bill_description="Comprehensive legislation to strengthen digital rights, enhance privacy protection, and establish clear guidelines for data handling across the European Union.", - bill_type=VoteType.ORDINARY_LEGISLATIVE_PROCEDURE, - committee="Legal Affairs", - max_cost=25.0, # Max $25 for this session - ) - - print("Session Results:") - print( - f" Bill: {session_result['session_summary']['bill_title']}" - ) - print( - f" Final Outcome: {session_result['session_summary']['final_outcome']}" - ) - print( - f" Total Cost: ${session_result['session_summary']['total_cost']:.2f}" - ) - print( - f" Budget Remaining: ${session_result['cost_stats']['budget_remaining']:.2f}" - ) - - # Show detailed cost statistics - cost_stats = parliament.get_cost_statistics() - print("\nDETAILED COST STATISTICS:") - print(f" Total Tokens Used: {cost_stats['total_tokens']:,}") - print(f" Requests Made: {cost_stats['requests_made']}") - print(f" Cache Hits: {cost_stats['cache_hits']}") - print(f" Cache Hit Rate: {cost_stats['cache_hit_rate']:.1%}") - print( - f" Loading Efficiency: {cost_stats['loading_efficiency']:.1%}" - ) - print(f" Cache Size: {cost_stats['cache_size']} entries") - - return session_result - - -def main(): - """Main demonstration function.""" - - print("EUROSWARM PARLIAMENT - COST OPTIMIZED DEMONSTRATION") - print("=" * 60) - print( - "This demonstration shows the EuroSwarm Parliament with cost optimization features:" - ) - print("• Lazy loading of MEP agents (only create when needed)") - print("• Response caching (avoid repeated API calls)") - print("• Batch processing (control memory and cost)") - print("• Budget controls (hard limits on spending)") - print("• Cost tracking (real-time monitoring)") - - # Initialize parliament with cost optimization - parliament = demonstrate_parliament_initialization() - - # Demonstrate individual MEP interaction (will trigger lazy loading) - demonstrate_individual_mep_interaction(parliament) - - # Demonstrate committee work with cost optimization - demonstrate_committee_work(parliament) - - # Demonstrate parliamentary debate with cost optimization - demonstrate_parliamentary_debate(parliament) - - # Demonstrate democratic voting with cost optimization - demonstrate_democratic_voting(parliament) - - # Demonstrate political analysis with cost optimization - demonstrate_political_analysis(parliament) - - # Demonstrate optimized parliamentary session - demonstrate_optimized_parliamentary_session(parliament) - - # Show final cost statistics - final_stats = parliament.get_cost_statistics() - print("\nFINAL COST STATISTICS:") - print(f"Total Cost: ${final_stats['total_cost']:.2f}") - print(f"Budget Remaining: ${final_stats['budget_remaining']:.2f}") - print(f"Cache Hit Rate: {final_stats['cache_hit_rate']:.1%}") - print( - f"Loading Efficiency: {final_stats['loading_efficiency']:.1%}" - ) - - print("\n✅ COST OPTIMIZATION DEMONSTRATION COMPLETED!") - print( - "✅ EuroSwarm Parliament now supports cost-effective large-scale simulations" - ) - print( - f"✅ Lazy loading: {final_stats['loaded_meps']}/{final_stats['total_meps']} MEPs loaded" - ) - print(f"✅ Caching: {final_stats['cache_hit_rate']:.1%} hit rate") - print( - f"✅ Budget control: ${final_stats['total_cost']:.2f} spent of ${final_stats['budget_remaining'] + final_stats['total_cost']:.2f} budget" - ) + print("\n✅ Simple example completed!") if __name__ == "__main__": diff --git a/examples/simulations/example_bell_labs.py b/examples/simulations/example_bell_labs.py new file mode 100644 index 00000000..b3a2004a --- /dev/null +++ b/examples/simulations/example_bell_labs.py @@ -0,0 +1,39 @@ +""" +Bell Labs Research Simulation Example + +This example demonstrates how to use the BellLabsSwarm to simulate +collaborative research among famous physicists. +""" + +from swarms.sims.bell_labs import ( + run_bell_labs_research, +) + + +def main(): + """ + Run the Bell Labs research simulation. + + This example asks the research question: + "Why doesn't physics take a vacation? Why are the laws of physics consistent?" + """ + + research_question = """ + Why doesn't physics take a vacation? Why are the laws of physics consistent across time and space? + Explore the philosophical and scientific foundations for the uniformity and invariance of physical laws. + Consider both theoretical explanations and any empirical evidence or challenges to this consistency. + """ + + # Run the research simulation + results = run_bell_labs_research( + research_question=research_question, + max_loops=1, + model_name="claude-3-5-sonnet-20240620", + verbose=True, + ) + + print(results) + + +if __name__ == "__main__": + main() diff --git a/examples/single_agent/dynamic_context_window.py b/examples/single_agent/dynamic_context_window.py new file mode 100644 index 00000000..2a654d7f --- /dev/null +++ b/examples/single_agent/dynamic_context_window.py @@ -0,0 +1,29 @@ +from swarms import Agent + + +def main(): + """ + Run a quantitative trading agent to recommend top 3 gold ETFs. + """ + agent = Agent( + agent_name="Quantitative-Trading-Agent", + agent_description="Advanced quantitative trading and algorithmic analysis agent", + system_prompt=( + "You are an expert quantitative trading agent. " + "Recommend the best gold ETFs using your expertise in trading strategies, " + "risk management, and financial analysis. Be concise and precise." + ), + model_name="claude-sonnet-4-20250514", + dynamic_temperature_enabled=True, + max_loops=1, + dynamic_context_window=True, + ) + + out = agent.run( + task="What are the best top 3 etfs for gold coverage?" + ) + print(out) + + +if __name__ == "__main__": + main() diff --git a/examples/tools/claude_as_a_tool.py b/examples/tools/claude_as_a_tool.py new file mode 100644 index 00000000..3101c1db --- /dev/null +++ b/examples/tools/claude_as_a_tool.py @@ -0,0 +1,206 @@ +""" +Claude Code Agent Tool - Setup Guide + +This tool provides a Claude Code Agent that can: +- Generate code and applications from natural language descriptions +- Write files, execute shell commands, and manage Git repositories +- Perform web searches and file operations +- Handle complex development tasks with retry logic + +SETUP GUIDE: +1. Install dependencies: + pip install claude-code-sdk + npm install -g @anthropic-ai/claude-code + +2. Set environment variable: + export ANTHROPIC_API_KEY="your-api-key-here" + +3. Use the tool: + from claude_as_a_tool import developer_worker_agent + + result = developer_worker_agent( + task="Create a Python web scraper", + system_prompt="You are a helpful coding assistant" + ) + +REQUIRED: ANTHROPIC_API_KEY environment variable must be set +""" + +import asyncio +from typing import Any, Dict + +from claude_code_sdk import ClaudeCodeOptions, ClaudeSDKClient +from dotenv import load_dotenv +from tenacity import retry, stop_after_attempt, wait_exponential +from loguru import logger + +load_dotenv() + + +class ClaudeAppGenerator: + """ + Generates applications using Claude Code SDK based on specifications. + """ + + def __init__( + self, + name: str = "Developer Worker Agent", + description: str = "A developer worker agent that can generate code and write it to a file.", + retries: int = 3, + retry_delay: float = 2.0, + system_prompt: str = None, + debug_mode: bool = False, + max_steps: int = 40, + model: str = "claude-sonnet-4-20250514", + max_thinking_tokens: int = 1000, + ): + """ + Initialize the app generator. + + Args: + name: Name of the app + description: Description of the app + retries: Number of retries + retry_delay: Delay between retries + system_prompt: System prompt + debug_mode: Enable extra verbose logging for Claude outputs + max_steps: Maximum number of steps + model: Model to use + """ + self.name = name + self.description = description + self.retries = retries + self.retry_delay = retry_delay + self.system_prompt = system_prompt + self.model = model + self.debug_mode = debug_mode + self.max_steps = max_steps + self.max_thinking_tokens = max_thinking_tokens + + @retry( + stop=stop_after_attempt(3), + wait=wait_exponential(multiplier=1, min=4, max=15), + ) + async def generate_app_with_claude( + self, task: str + ) -> Dict[str, Any]: + """ + Generate app using Claude Code SDK with robust error handling and retry logic. + + Args: + task: Task to be completed + + Returns: + Dict containing generation results + """ + # Log the Claude SDK configuration + claude_options = ClaudeCodeOptions( + system_prompt=self.system_prompt, + max_turns=self.max_steps, # Sufficient for local app development and GitHub setup + allowed_tools=[ + "Read", + "Write", + "Bash", + "GitHub", + "Git", + "Grep", + "WebSearch", + ], + continue_conversation=True, # Start fresh each time + model=self.model, + max_thinking_tokens=self.max_thinking_tokens, + ) + + async with ClaudeSDKClient(options=claude_options) as client: + # Generate the application + await client.query(task) + + response_text = [] + message_count = 0 + + async for message in client.receive_response(): + message_count += 1 + + if hasattr(message, "content"): + for block in message.content: + if hasattr(block, "text"): + text_content = block.text + response_text.append(text_content) + logger.info(text_content) + + elif hasattr(block, "type"): + if self.debug_mode and hasattr( + block, "input" + ): + input_str = str(block.input) + if len(input_str) > 200: + input_str = ( + input_str[:200] + + "... (truncated)" + ) + print(f"Tool Input: {input_str}") + + elif type(message).__name__ == "ResultMessage": + result_text = str(message.result) + response_text.append(result_text) + + return response_text + + def run(self, task: str) -> Dict[str, Any]: + """ + Synchronous wrapper for app generation to work with ThreadPoolExecutor. + + Args: + spec: App specification + + Returns: + Dict containing generation results + """ + return asyncio.run(self.generate_app_with_claude(task)) + + +def developer_worker_agent(task: str, system_prompt: str) -> str: + """ + Developer Worker Agent + + This function instantiates a ClaudeAppGenerator agent, which is a highly capable developer assistant designed to automate software development tasks. + The agent leverages the Claude Code SDK to interpret natural language instructions and generate code, scripts, or even entire applications. + It can interact with files, execute shell commands, perform web searches, and utilize version control systems such as Git and GitHub. + The agent is robust, featuring retry logic, customizable system prompts, and debug modes for verbose output. + It is ideal for automating repetitive coding tasks, prototyping, and integrating with developer workflows. + + Capabilities: + - Generate code based on detailed task descriptions. + - Write generated code to files. + - Execute shell commands and scripts. + - Interact with Git and GitHub for version control operations. + - Perform web searches to gather information or code snippets. + - Provide detailed logs and debugging information if enabled. + - Handle errors gracefully with configurable retry logic. + + Args: + task (str): The development task or instruction for the agent to complete. + system_prompt (str): The system prompt to guide the agent's behavior and context. + + Returns: + str: The result of the agent's execution for the given task. + """ + claude_code_sdk = ClaudeAppGenerator(system_prompt=system_prompt) + return claude_code_sdk.run(task) + + +# agent = Agent( +# agent_name="Developer Worker Agent", +# agent_description="A developer worker agent that can generate code and write it to a file.", +# tools=[developer_worker_agent], +# system_prompt="You are a developer worker agent. You are given a task and you need to complete it.", +# ) + +# agent.run( +# task="Write a simple python script that prints 'Hello, World!'" +# ) + +# if __name__ == "__main__": +# task = "Write a simple python script that prints 'Hello, World!'" +# system_prompt = "You are a developer worker agent. You are given a task and you need to complete it." +# print(developer_worker_agent(task, system_prompt)) diff --git a/pyproject.toml b/pyproject.toml index 95f28547..9d57a8f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "swarms" -version = "8.0.5" +version = "8.1.1" description = "Swarms - TGSC" license = "MIT" authors = ["Kye Gomez "] diff --git a/simulation_vote_example.py b/simulation_vote_example.py index 377728c9..c39a3480 100644 --- a/simulation_vote_example.py +++ b/simulation_vote_example.py @@ -2,10 +2,6 @@ from swarms.sims.senator_assembly import SenatorAssembly def main(): - """ - Runs a simulation of a Senate vote on a bill proposing significant tax cuts for all Americans. - The bill is described in realistic legislative terms, and the simulation uses a concurrent voting model. - """ senator_simulation = SenatorAssembly( model_name="claude-sonnet-4-20250514" ) diff --git a/swarms/sims/bell_labs.py b/swarms/sims/bell_labs.py new file mode 100644 index 00000000..682e8bf1 --- /dev/null +++ b/swarms/sims/bell_labs.py @@ -0,0 +1,815 @@ +""" +Bell Labs Research Simulation with Physicist Agents + +This simulation creates specialized AI agents representing famous physicists +from the Bell Labs era, including Oppenheimer, von Neumann, Feynman, Einstein, +and others. The agents work together in a collaborative research environment +following a structured workflow: task -> Oppenheimer (planning) -> physicist discussion +-> code implementation -> results analysis -> repeat for n loops. +""" + +from functools import lru_cache +from typing import Any, Dict, List, Optional + +from loguru import logger + +from swarms.structs.agent import Agent +from swarms.structs.conversation import Conversation +from swarms.utils.history_output_formatter import ( + history_output_formatter, +) +# from examples.tools.claude_as_a_tool import developer_worker_agent + + +@lru_cache(maxsize=1) +def _create_physicist_agents( + model_name: str, random_model_name: bool = False +) -> List[Agent]: + """ + Create specialized agents for each physicist. + + Args: + model_name: Model to use for all agents + + Returns: + List of configured physicist agents + """ + physicists_data = { + "J. Robert Oppenheimer": { + "role": "Research Director & Theoretical Physicist", + "expertise": [ + "Nuclear physics", + "Quantum mechanics", + "Research coordination", + "Strategic planning", + "Team leadership", + ], + "background": "Director of the Manhattan Project, expert in quantum mechanics and nuclear physics", + "system_prompt": """You are J. Robert Oppenheimer, the brilliant theoretical physicist and research director. + + Your role is to: + 1. Analyze complex research questions and break them down into manageable components + 2. Create comprehensive research plans with clear objectives and methodologies + 3. Coordinate the research team and ensure effective collaboration + 4. Synthesize findings from different physicists into coherent conclusions + 5. Guide the research process with strategic insights and theoretical frameworks + + You excel at: + - Identifying the core theoretical challenges in any research question + - Designing experimental approaches that test fundamental principles + - Balancing theoretical rigor with practical implementation + - Fostering interdisciplinary collaboration between specialists + - Maintaining focus on the most promising research directions + + When creating research plans, be thorough, systematic, and consider multiple approaches. + Always emphasize the theoretical foundations and experimental validation of any proposed solution.""", + }, + "John von Neumann": { + "role": "Mathematical Physicist & Computer Scientist", + "expertise": [ + "Mathematical physics", + "Computer architecture", + "Game theory", + "Quantum mechanics", + "Numerical methods", + ], + "background": "Pioneer of computer science, game theory, and mathematical physics", + "system_prompt": """You are John von Neumann, the brilliant mathematical physicist and computer scientist. + + Your approach to research questions involves: + 1. Mathematical rigor and formal mathematical frameworks + 2. Computational and algorithmic solutions to complex problems + 3. Game theory and strategic analysis of research approaches + 4. Numerical methods and computational physics + 5. Bridging abstract theory with practical implementation + + You excel at: + - Formulating problems in precise mathematical terms + - Developing computational algorithms and numerical methods + - Applying game theory to optimize research strategies + - Creating mathematical models that capture complex phenomena + - Designing efficient computational approaches to physical problems + + When analyzing research questions, focus on mathematical foundations, computational feasibility, + and the development of rigorous theoretical frameworks that can be implemented and tested.""", + }, + "Richard Feynman": { + "role": "Theoretical Physicist & Problem Solver", + "expertise": [ + "Quantum electrodynamics", + "Particle physics", + "Problem-solving methodology", + "Intuitive physics", + "Experimental design", + ], + "background": "Nobel laureate in physics, known for intuitive problem-solving and quantum electrodynamics", + "system_prompt": """You are Richard Feynman, the brilliant theoretical physicist and master problem solver. + + Your research methodology involves: + 1. Intuitive understanding of complex physical phenomena + 2. Creative problem-solving approaches that cut through complexity + 3. Experimental design that tests fundamental principles + 4. Clear communication of complex ideas through analogies and examples + 5. Focus on the most essential aspects of any research question + + You excel at: + - Finding elegant solutions to seemingly intractable problems + - Designing experiments that reveal fundamental truths + - Communicating complex physics in accessible terms + - Identifying the core physics behind any phenomenon + - Developing intuitive models that capture essential behavior + + When approaching research questions, look for the simplest, most elegant solutions. + Focus on the fundamental physics and design experiments that test your understanding directly.""", + }, + "Albert Einstein": { + "role": "Theoretical Physicist & Conceptual Innovator", + "expertise": [ + "Relativity theory", + "Quantum mechanics", + "Conceptual physics", + "Thought experiments", + "Fundamental principles", + ], + "background": "Revolutionary physicist who developed relativity theory and influenced quantum mechanics", + "system_prompt": """You are Albert Einstein, the revolutionary theoretical physicist and conceptual innovator. + + Your research approach involves: + 1. Deep conceptual thinking about fundamental physical principles + 2. Thought experiments that reveal the essence of physical phenomena + 3. Questioning established assumptions and exploring new paradigms + 4. Focus on the most fundamental and universal aspects of physics + 5. Intuitive understanding of space, time, and the nature of reality + + You excel at: + - Identifying the conceptual foundations of any physical theory + - Developing thought experiments that challenge conventional wisdom + - Finding elegant mathematical descriptions of physical reality + - Questioning fundamental assumptions and exploring alternatives + - Developing unified theories that explain diverse phenomena + + When analyzing research questions, focus on the conceptual foundations and fundamental principles. + Look for elegant, unified explanations and be willing to challenge established paradigms.""", + }, + "Enrico Fermi": { + "role": "Experimental Physicist & Nuclear Scientist", + "expertise": [ + "Nuclear physics", + "Experimental physics", + "Neutron physics", + "Statistical physics", + "Practical applications", + ], + "background": "Nobel laureate known for nuclear physics, experimental work, and the first nuclear reactor", + "system_prompt": """You are Enrico Fermi, the brilliant experimental physicist and nuclear scientist. + + Your research methodology involves: + 1. Rigorous experimental design and execution + 2. Practical application of theoretical principles + 3. Statistical analysis and probability in physics + 4. Nuclear physics and particle interactions + 5. Bridging theory with experimental validation + + You excel at: + - Designing experiments that test theoretical predictions + - Applying statistical methods to physical problems + - Developing practical applications of fundamental physics + - Nuclear physics and particle physics experiments + - Creating experimental setups that reveal new phenomena + + When approaching research questions, focus on experimental design and practical implementation. + Emphasize the importance of experimental validation and statistical analysis in physics research.""", + }, + "Code-Implementer": { + "role": "Computational Physicist & Code Developer", + "expertise": [ + "Scientific computing", + "Physics simulations", + "Data analysis", + "Algorithm implementation", + "Numerical methods", + ], + "background": "Specialized in implementing computational solutions to physics problems", + "system_prompt": """You are a specialized computational physicist and code developer. + + Your responsibilities include: + 1. Implementing computational solutions to physics problems + 2. Developing simulations and numerical methods + 3. Analyzing data and presenting results clearly + 4. Testing theoretical predictions through computation + 5. Providing quantitative analysis of research findings + + You excel at: + - Writing clear, efficient scientific code + - Implementing numerical algorithms for physics problems + - Data analysis and visualization + - Computational optimization and performance + - Bridging theoretical physics with computational implementation + + When implementing solutions, focus on: + - Clear, well-documented code + - Efficient numerical algorithms + - Comprehensive testing and validation + - Clear presentation of results and analysis + - Quantitative assessment of theoretical predictions""", + }, + } + + agents = [] + for name, data in physicists_data.items(): + agent = Agent( + agent_name=name, + system_prompt=data["system_prompt"], + model_name=model_name, + random_model_name=random_model_name, + max_loops=1, + dynamic_temperature_enabled=True, + dynamic_context_window=True, + ) + agents.append(agent) + + return agents + + +class BellLabsSwarm: + """ + Bell Labs Research Simulation Swarm + + Simulates the collaborative research environment of Bell Labs with famous physicists + working together on complex research questions. The workflow follows: + + 1. Task is presented to the team + 2. Oppenheimer creates a research plan + 3. Physicists discuss and vote on approaches using majority voting + 4. Code implementation agent tests the theory + 5. Results are analyzed and fed back to the team + 6. Process repeats for n loops with iterative refinement + """ + + def __init__( + self, + name: str = "Bell Labs Research Team", + description: str = "A collaborative research environment simulating Bell Labs physicists", + max_loops: int = 1, + verbose: bool = True, + model_name: str = "gpt-4o-mini", + random_model_name: bool = False, + output_type: str = "str-all-except-first", + dynamic_context_window: bool = True, + **kwargs, + ): + """ + Initialize the Bell Labs Research Swarm. + + Args: + name: Name of the swarm + description: Description of the swarm's purpose + max_loops: Number of research iteration loops + verbose: Whether to enable verbose logging + model_name: Model to use for all agents + **kwargs: Additional arguments passed to BaseSwarm + """ + self.name = name + self.description = description + self.max_loops = max_loops + self.verbose = verbose + self.model_name = model_name + self.kwargs = kwargs + self.random_model_name = random_model_name + self.output_type = output_type + self.dynamic_context_window = dynamic_context_window + + self.conversation = Conversation( + dynamic_context_window=dynamic_context_window + ) + + # Create the physicist agents + self.agents = _create_physicist_agents( + model_name=model_name, random_model_name=random_model_name + ) + + # Set up specialized agents + self.oppenheimer = self._get_agent_by_name( + "J. Robert Oppenheimer" + ) + self.code_implementer = self._get_agent_by_name( + "Code-Implementer" + ) + + self.physicists = [ + agent + for agent in self.agents + if agent.agent_name != "J. Robert Oppenheimer" + and agent.agent_name != "Code-Implementer" + ] + + # # Find the code implementer agent + # code_implementer = self._get_agent_by_name("Code-Implementer") + # code_implementer.tools = [developer_worker_agent] + + logger.info( + f"Bell Labs Research Team initialized with {len(self.agents)} agents" + ) + + def _get_agent_by_name(self, name: str) -> Optional[Agent]: + """Get an agent by name.""" + for agent in self.agents: + if agent.agent_name == name: + return agent + return None + + def run( + self, task: str, img: Optional[str] = None + ) -> Dict[str, Any]: + """ + Run the Bell Labs research simulation. + + Args: + task: The research question or task to investigate + + Returns: + Dictionary containing the research results, process history, and full conversation + """ + logger.info(f"Starting Bell Labs research on: {task}") + + # Add initial task to conversation history + self.conversation.add( + "Research Coordinator", f"Initial Research Task: {task}" + ) + + # Oppenheimer + oppenheimer_plan = self.oppenheimer.run( + task=self.conversation.get_str(), img=img + ) + + self.conversation.add( + self.oppenheimer.agent_name, + f"Research Plan: {oppenheimer_plan}", + ) + + # Discussion + + # Physicists + physicist_discussion = self._conduct_physicist_discussion( + task, self.conversation.get_str() + ) + + # Add to conversation history + self.conversation.add( + "Group Discussion", physicist_discussion + ) + + # Now implement the solution + implementation_results = self._implement_and_test_solution( + history=self.conversation.get_str() + ) + + # Add to conversation history + self.conversation.add( + self.code_implementer.agent_name, implementation_results + ) + + return history_output_formatter( + conversation=self.conversation, type="str" + ) + + def _create_research_plan( + self, task: str, loop_number: int + ) -> str: + """ + Have Oppenheimer create a research plan. + + Args: + task: Research task + loop_number: Current loop number + + Returns: + Research plan from Oppenheimer + """ + prompt = f""" + Research Task: {task} + + Loop Number: {loop_number + 1} + + As J. Robert Oppenheimer, create a comprehensive research plan for this task. + + Your plan should include: + 1. Clear research objectives and hypotheses + 2. Theoretical framework and approach + 3. Specific research questions to investigate + 4. Methodology for testing and validation + 5. Expected outcomes and success criteria + 6. Timeline and milestones + 7. Resource requirements and team coordination + + Provide a detailed, actionable plan that the research team can follow. + """ + + plan = self.oppenheimer.run(prompt) + return plan + + def _conduct_physicist_discussion( + self, task: str, history: str + ) -> str: + """ + Conduct a natural discussion among physicists where they build on each other's ideas. + + Args: + task: Research task + history: Conversation history including Oppenheimer's plan + + Returns: + Results of the physicist discussion as a conversation transcript + """ + import random + + # Shuffle the physicists to create random discussion order + discussion_order = self.physicists.copy() + random.shuffle(discussion_order) + + discussion_transcript = [] + current_context = ( + f"{history}\n\nCurrent Research Task: {task}\n\n" + ) + + # Each physicist contributes to the discussion, building on previous contributions + for i, physicist in enumerate(discussion_order): + if i == 0: + # First physicist starts the discussion + discussion_prompt = f""" + {current_context} + + As {physicist.agent_name}, you are starting the group discussion about this research plan. + + Based on your expertise, provide your initial thoughts on: + + 1. What aspects of Oppenheimer's research plan do you find most promising? + 2. What theoretical challenges or concerns do you see? + 3. What specific approaches would you recommend based on your expertise? + 4. What questions or clarifications do you have for the team? + + Be specific and draw from your unique perspective and expertise. This will set the tone for the group discussion. + """ + else: + # Subsequent physicists build on the discussion + previous_contributions = "\n\n".join( + discussion_transcript + ) + discussion_prompt = f""" + {current_context} + + Previous Discussion: + {previous_contributions} + + As {physicist.agent_name}, continue the group discussion by building on your colleagues' ideas. + + Consider: + 1. How do your colleagues' perspectives relate to your expertise in {', '.join(physicist.expertise)}? + 2. What additional insights can you add to the discussion? + 3. How can you address any concerns or questions raised by others? + 4. What specific next steps would you recommend based on the discussion so far? + + Engage directly with your colleagues' ideas and contribute your unique perspective to move the research forward. + """ + + # Get the physicist's contribution + contribution = physicist.run(discussion_prompt) + + # Add to transcript with clear attribution + discussion_transcript.append( + f"{physicist.agent_name}: {contribution}" + ) + + # Update context for next iteration + current_context = ( + f"{history}\n\nCurrent Research Task: {task}\n\nGroup Discussion:\n" + + "\n\n".join(discussion_transcript) + ) + + # Create a summary of the discussion + summary_prompt = f""" + Research Task: {task} + + Complete Discussion Transcript: + {chr(10).join(discussion_transcript)} + + As a research coordinator, provide a concise summary of the key points from this group discussion: + + 1. Main areas of agreement among the physicists + 2. Key concerns or challenges identified + 3. Specific recommendations made by the team + 4. Next steps for moving forward with the research + + Focus on actionable insights and clear next steps that the team can implement. + """ + + # Use Oppenheimer to summarize the discussion + discussion_summary = self.oppenheimer.run(summary_prompt) + + # Return the full discussion transcript with summary + full_discussion = f"Group Discussion Transcript:\n\n{chr(10).join(discussion_transcript)}\n\n---\nDiscussion Summary:\n{discussion_summary}" + + return full_discussion + + def _implement_and_test_solution( + self, + history: str, + ) -> Dict[str, Any]: + """ + Implement and test the proposed solution. + + Args: + task: Research task + plan: Research plan + discussion_results: Results from physicist discussion + loop_number: Current loop number + + Returns: + Implementation and testing results + """ + implementation_prompt = f""" + {history} + + As the Code Implementer, your task is to: + + 1. Implement a computational solution based on the research plan + 2. Test the theoretical predictions through simulation or calculation + 3. Analyze the results and provide quantitative assessment + 4. Identify any discrepancies between theory and implementation + 5. Suggest improvements or next steps + + Provide: + - Clear description of your implementation approach + - Code or algorithm description + - Test results and analysis + - Comparison with theoretical predictions + - Recommendations for further investigation + + Focus on practical implementation and quantitative results. + """ + + implementation_results = self.code_implementer.run( + implementation_prompt + ) + + return implementation_results + + def _analyze_results( + self, implementation_results: Dict[str, Any], loop_number: int + ) -> str: + """ + Analyze the results and provide team review. + + Args: + implementation_results: Results from implementation phase + loop_number: Current loop number + + Returns: + Analysis and recommendations + """ + analysis_prompt = f""" + Implementation Results: {implementation_results} + + Loop Number: {loop_number + 1} + + As the research team, analyze these results and provide: + + 1. Assessment of whether the implementation supports the theoretical predictions + 2. Identification of any unexpected findings or discrepancies + 3. Evaluation of the methodology and approach + 4. Recommendations for the next research iteration + 5. Insights gained from this round of investigation + + Consider: + - What worked well in this approach? + - What challenges or limitations were encountered? + - How can the research be improved in the next iteration? + - What new questions or directions have emerged? + + Provide a comprehensive analysis that will guide the next research phase. + """ + + # Use team discussion for results analysis + analysis_results = self._conduct_team_analysis( + analysis_prompt + ) + return analysis_results + + def _conduct_team_analysis(self, analysis_prompt: str) -> str: + """ + Conduct a team analysis discussion using the same approach as physicist discussion. + + Args: + analysis_prompt: The prompt for the analysis + + Returns: + Results of the team analysis discussion + """ + import random + + # Shuffle the agents to create random discussion order + discussion_order = self.agents.copy() + random.shuffle(discussion_order) + + discussion_transcript = [] + current_context = analysis_prompt + + # Each agent contributes to the analysis, building on previous contributions + for i, agent in enumerate(discussion_order): + if i == 0: + # First agent starts the analysis + agent_prompt = f""" + {current_context} + + As {agent.agent_name}, you are starting the team analysis discussion. + + Based on your expertise and role, provide your initial analysis of the implementation results. + Focus on what you can contribute from your unique perspective. + """ + else: + # Subsequent agents build on the analysis + previous_contributions = "\n\n".join( + discussion_transcript + ) + agent_prompt = f""" + {current_context} + + Previous Analysis: + {previous_contributions} + + As {agent.agent_name}, continue the team analysis by building on your colleagues' insights. + + Consider: + 1. How do your colleagues' perspectives relate to your expertise? + 2. What additional insights can you add to the analysis? + 3. How can you address any concerns or questions raised by others? + 4. What specific recommendations would you make based on the analysis so far? + + Engage directly with your colleagues' ideas and contribute your unique perspective. + """ + + # Get the agent's contribution + contribution = agent.run(agent_prompt) + + # Add to transcript with clear attribution + discussion_transcript.append( + f"{agent.agent_name}: {contribution}" + ) + + # Update context for next iteration + current_context = ( + f"{analysis_prompt}\n\nTeam Analysis:\n" + + "\n\n".join(discussion_transcript) + ) + + # Create a summary of the analysis + summary_prompt = f""" + Analysis Prompt: {analysis_prompt} + + Complete Analysis Transcript: + {chr(10).join(discussion_transcript)} + + As a research coordinator, provide a concise summary of the key points from this team analysis: + + 1. Main findings and insights from the team + 2. Key recommendations made + 3. Areas of agreement and disagreement + 4. Next steps for the research + + Focus on actionable insights and clear next steps. + """ + + # Use Oppenheimer to summarize the analysis + analysis_summary = self.oppenheimer.run(summary_prompt) + + # Return the full analysis transcript with summary + full_analysis = f"Team Analysis Transcript:\n\n{chr(10).join(discussion_transcript)}\n\n---\nAnalysis Summary:\n{analysis_summary}" + + return full_analysis + + def _refine_task_for_next_iteration( + self, current_task: str, loop_results: Dict[str, Any] + ) -> str: + """ + Refine the task for the next research iteration. + + Args: + current_task: Current research task + loop_results: Results from the current loop + + Returns: + Refined task for next iteration + """ + refinement_prompt = f""" + Current Research Task: {current_task} + + Results from Current Loop: {loop_results} + + Based on the findings and analysis from this research loop, refine the research task for the next iteration. + + Consider: + - What new questions have emerged? + - What aspects need deeper investigation? + - What alternative approaches should be explored? + - What specific hypotheses should be tested? + + Provide a refined, focused research question that builds upon the current findings + and addresses the most important next steps identified by the team. + """ + + # Use Oppenheimer to refine the task + refined_task = self.oppenheimer.run(refinement_prompt) + + # Add task refinement to conversation history + self.conversation.add( + "J. Robert Oppenheimer", + f"Task Refined for Next Iteration: {refined_task}", + ) + + return refined_task + + def _generate_final_conclusion( + self, research_results: Dict[str, Any] + ) -> str: + """ + Generate a final conclusion summarizing all research findings. + + Args: + research_results: Complete research results from all loops + + Returns: + Final research conclusion + """ + conclusion_prompt = f""" + Complete Research Results: {research_results} + + As J. Robert Oppenheimer, provide a comprehensive final conclusion for this research project. + + Your conclusion should: + 1. Summarize the key findings from all research loops + 2. Identify the most significant discoveries or insights + 3. Evaluate the success of the research approach + 4. Highlight any limitations or areas for future investigation + 5. Provide a clear statement of what was accomplished + 6. Suggest next steps for continued research + + Synthesize the work of the entire team and provide a coherent narrative + of the research journey and its outcomes. + """ + + final_conclusion = self.oppenheimer.run(conclusion_prompt) + return final_conclusion + + +# Example usage function +def run_bell_labs_research( + research_question: str, + max_loops: int = 3, + model_name: str = "gpt-4o-mini", + verbose: bool = True, +) -> Dict[str, Any]: + """ + Run a Bell Labs research simulation. + + Args: + research_question: The research question to investigate + max_loops: Number of research iteration loops + model_name: Model to use for all agents + verbose: Whether to enable verbose logging + + Returns: + Complete research results and findings + """ + bell_labs = BellLabsSwarm( + max_loops=max_loops, verbose=verbose, model_name=model_name + ) + + results = bell_labs.run(research_question) + return results + + +# if __name__ == "__main__": +# # Example research question +# research_question = """ +# Investigate the feasibility of quantum computing for solving complex optimization problems. +# Consider both theoretical foundations and practical implementation challenges. +# """ + +# print("Starting Bell Labs Research Simulation...") +# print(f"Research Question: {research_question}") +# print("-" * 80) + +# results = run_bell_labs_research( +# research_question=research_question, +# max_loops=2, +# verbose=True +# ) + +# print("\n" + "=" * 80) +# print("RESEARCH SIMULATION COMPLETED") +# print("=" * 80) + +# print(f"\nFinal Conclusion:\n{results['final_conclusion']}") + +# print(f"\nResearch completed in {len(results['research_history'])} loops.") +# print("Check the results dictionary for complete research details.") diff --git a/swarms/structs/agent.py b/swarms/structs/agent.py index f9e04013..bad1fc95 100644 --- a/swarms/structs/agent.py +++ b/swarms/structs/agent.py @@ -437,6 +437,7 @@ class Agent: tool_retry_attempts: int = 3, reasoning_prompt_on: bool = True, dynamic_context_window: bool = True, + show_tool_execution_output: bool = True, *args, **kwargs, ): @@ -578,15 +579,17 @@ class Agent: self.tool_retry_attempts = tool_retry_attempts self.reasoning_prompt_on = reasoning_prompt_on self.dynamic_context_window = dynamic_context_window - - # Initialize the feedback - self.feedback = [] + self.show_tool_execution_output = show_tool_execution_output # self.init_handling() self.setup_config() + # Initialize the short memory self.short_memory = self.short_memory_init() + # Initialize the tools + self.tool_struct = self.setup_tools() + if exists(self.docs_folder): self.get_docs_from_doc_folders() @@ -610,8 +613,6 @@ class Agent: if self.react_on is True: self.system_prompt += REACT_SYS_PROMPT - # Run sequential operations after all concurrent tasks are done - # self.agent_output = self.agent_output_model() if self.autosave is True: log_agent_data(self.to_dict()) @@ -640,13 +641,14 @@ class Agent: verbose=self.verbose, ) - def tool_handling(self): - - self.tool_struct = BaseTool( + def setup_tools(self): + return BaseTool( tools=self.tools, verbose=self.verbose, ) + def tool_handling(self): + # Convert all the tools into a list of dictionaries self.tools_list_dictionary = ( convert_multiple_functions_to_openai_function_schema( @@ -693,26 +695,6 @@ class Agent: return memory - def agent_output_model(self): - # Many steps - id = agent_id() - - return ManySteps( - agent_id=id, - agent_name=self.agent_name, - # run_id=run_id, - task="", - max_loops=self.max_loops, - steps=self.short_memory.to_dict(), - full_history=self.short_memory.get_str(), - total_tokens=count_tokens( - text=self.short_memory.get_str() - ), - stopping_token=self.stopping_token, - interactive=self.interactive, - dynamic_temperature_enabled=self.dynamic_temperature_enabled, - ) - def llm_handling(self, *args, **kwargs): """Initialize the LiteLLM instance with combined configuration from all sources. @@ -729,9 +711,6 @@ class Agent: Returns: LiteLLM: The initialized LiteLLM instance """ - # Use cached instance if available - if self.llm is not None: - return self.llm if self.model_name is None: self.model_name = "gpt-4o-mini" @@ -754,6 +733,7 @@ class Agent: "max_tokens": self.max_tokens, "system_prompt": self.system_prompt, "stream": self.streaming_on, + "top_p": self.top_p, } # Initialize tools_list_dictionary, if applicable @@ -815,7 +795,7 @@ class Agent: return self.llm except AgentLLMInitializationError as e: logger.error( - f"AgentLLMInitializationError: Agent Name: {self.agent_name} Error in llm_handling: {e} Your current configuration is not supported. Please check the configuration and parameters." + f"AgentLLMInitializationError: Agent Name: {self.agent_name} Error in llm_handling: {e} Your current configuration is not supported. Please check the configuration and parameters. Traceback: {traceback.format_exc()}" ) return None @@ -878,6 +858,9 @@ class Agent: if self.preset_stopping_token is not None: self.stopping_token = "" + # Initialize the feedback + self.feedback = [] + def check_model_supports_utilities( self, img: Optional[str] = None ) -> bool: @@ -890,7 +873,6 @@ class Agent: Returns: bool: True if model supports vision and image is provided, False otherwise. """ - # Only check vision support if an image is provided if img is not None: @@ -1213,7 +1195,7 @@ class Agent: self.save() logger.error( - f"Attempt {attempt+1}/{self.retry_attempts}: Error generating response in loop {loop_count} for agent '{self.agent_name}': {str(e)} | " + f"Attempt {attempt+1}/{self.retry_attempts}: Error generating response in loop {loop_count} for agent '{self.agent_name}': {str(e)} | Traceback: {traceback.format_exc()}" ) attempt += 1 @@ -1291,7 +1273,7 @@ class Agent: except KeyboardInterrupt as error: self._handle_run_error(error) - def __handle_run_error(self, error: any): + def _handle_run_error(self, error: any): if self.autosave is True: self.save() log_agent_data(self.to_dict()) @@ -1313,11 +1295,6 @@ class Agent: raise error - def _handle_run_error(self, error: any): - # Handle error directly instead of using daemon thread - # to ensure proper exception propagation - self.__handle_run_error(error) - async def arun( self, task: Optional[str] = None, @@ -1514,26 +1491,6 @@ class Agent: except Exception as error: logger.info(f"Error running bulk run: {error}", "red") - async def arun_batched( - self, - tasks: List[str], - *args, - **kwargs, - ): - """Asynchronously runs a batch of tasks.""" - try: - # Create a list of coroutines for each task - coroutines = [ - self.arun(task=task, *args, **kwargs) - for task in tasks - ] - # Use asyncio.gather to run them concurrently - results = await asyncio.gather(*coroutines) - return results - except Exception as error: - logger.error(f"Error running batched tasks: {error}") - raise - def reliability_check(self): if self.system_prompt is None: @@ -1568,7 +1525,7 @@ class Agent: try: if self.max_tokens > get_max_tokens(self.model_name): logger.warning( - f"Max tokens is set to {self.max_tokens}, but the model '{self.model_name}' only supports {get_max_tokens(self.model_name)} tokens. Please set max tokens to {get_max_tokens(self.model_name)} or less." + f"Max tokens is set to {self.max_tokens}, but the model '{self.model_name}' may or may not support {get_max_tokens(self.model_name)} tokens. Please set max tokens to {get_max_tokens(self.model_name)} or less." ) except Exception: @@ -1576,7 +1533,7 @@ class Agent: if self.model_name not in model_list: logger.warning( - f"The model '{self.model_name}' is not supported. Please use a supported model, or override the model name with the 'llm' parameter, which should be a class with a 'run(task: str)' method or a '__call__' method." + f"The model '{self.model_name}' may not be supported. Please use a supported model, or override the model name with the 'llm' parameter, which should be a class with a 'run(task: str)' method or a '__call__' method." ) def save(self, file_path: str = None) -> None: @@ -1822,14 +1779,6 @@ class Agent: ) as executor: self.executor = executor - # # Reinitialize tool structure if needed - # if hasattr(self, 'tools') and (self.tools or getattr(self, 'list_base_models', None)): - # self.tool_struct = BaseTool( - # tools=self.tools, - # base_models=getattr(self, 'list_base_models', None), - # tool_system_prompt=self.tool_system_prompt - # ) - except Exception as e: logger.error(f"Error reinitializing components: {e}") raise @@ -2640,19 +2589,20 @@ class Agent: self.llm.stream = original_stream return streaming_response else: - # Non-streaming call + args = { + "task": task, + } + if img is not None: - out = self.llm.run( - task=task, img=img, *args, **kwargs - ) - else: - out = self.llm.run(task=task, *args, **kwargs) + args["img"] = img + + out = self.llm.run(**args, **kwargs) return out except AgentLLMError as e: logger.error( - f"Error calling LLM: {e}. Task: {task}, Args: {args}, Kwargs: {kwargs}" + f"Error calling LLM: {e}. Task: {task}, Args: {args}, Kwargs: {kwargs} Traceback: {traceback.format_exc()}" ) raise e @@ -2743,6 +2693,30 @@ class Agent: ) raise KeyboardInterrupt + def run_batched( + self, + tasks: List[str], + imgs: List[str] = None, + *args, + **kwargs, + ): + """ + Run a batch of tasks concurrently. + + Args: + tasks (List[str]): List of tasks to run. + imgs (List[str], optional): List of images to run. Defaults to None. + *args: Additional positional arguments to be passed to the execution method. + **kwargs: Additional keyword arguments to be passed to the execution method. + + Returns: + List[Any]: List of results from each task execution. + """ + return [ + self.run(task=task, imgs=imgs, *args, **kwargs) + for task in tasks + ] + def handle_artifacts( self, text: str, file_output_path: str, file_extension: str ) -> None: @@ -3081,10 +3055,17 @@ class Agent: ) if self.print_on is True: - self.pretty_print( - f"Tool Executed Successfully [{time.strftime('%H:%M:%S')}]", - loop_count, - ) + if self.show_tool_execution_output is True: + + self.pretty_print( + f"Tool Executed Successfully [{time.strftime('%H:%M:%S')}] \n\nTool Output: {format_data_structure(output)}", + loop_count, + ) + else: + self.pretty_print( + f"Tool Executed Successfully [{time.strftime('%H:%M:%S')}]", + loop_count, + ) # Now run the LLM again without tools - create a temporary LLM instance # instead of modifying the cached one diff --git a/swarms/structs/conversation.py b/swarms/structs/conversation.py index de7c1de2..5b0415bf 100644 --- a/swarms/structs/conversation.py +++ b/swarms/structs/conversation.py @@ -1790,21 +1790,63 @@ class Conversation: pass self.conversation_history = [] - def dynamic_auto_chunking(self): + def _dynamic_auto_chunking_worker(self): + """ + Dynamically chunk the conversation history to fit within the context length. + + Returns: + str: The chunked conversation history as a string that fits within context_length tokens. + """ all_tokens = self._return_history_as_string_worker() total_tokens = count_tokens( all_tokens, self.tokenizer_model_name ) - if total_tokens > self.context_length: - # Get the difference between the count_tokens and the context_length - difference = total_tokens - self.context_length + if total_tokens <= self.context_length: + return all_tokens + + # We need to remove characters from the beginning until we're under the limit + # Start by removing a percentage of characters and adjust iteratively + target_tokens = self.context_length + current_string = all_tokens + + # Binary search approach to find the right cutoff point + left, right = 0, len(all_tokens) + + while left < right: + mid = (left + right) // 2 + test_string = all_tokens[mid:] + + if not test_string: + break + + test_tokens = count_tokens( + test_string, self.tokenizer_model_name + ) + + if test_tokens <= target_tokens: + # We can remove more from the beginning + right = mid + current_string = test_string + else: + # We need to keep more from the beginning + left = mid + 1 + + return current_string - # Slice the first difference number of messages and contents from the beginning of the conversation history - new_history = all_tokens[difference:] + def dynamic_auto_chunking(self): + """ + Dynamically chunk the conversation history to fit within the context length. - return new_history + Returns: + str: The chunked conversation history as a string that fits within context_length tokens. + """ + try: + return self._dynamic_auto_chunking_worker() + except Exception as e: + logger.error(f"Dynamic auto chunking failed: {e}") + return self._return_history_as_string_worker() # Example usage diff --git a/swarms/utils/litellm_wrapper.py b/swarms/utils/litellm_wrapper.py index 1b7d3c60..a64753b4 100644 --- a/swarms/utils/litellm_wrapper.py +++ b/swarms/utils/litellm_wrapper.py @@ -176,6 +176,12 @@ class LiteLLM: litellm.drop_params = True + # Add system prompt if present + if self.system_prompt is not None: + self.messages.append( + {"role": "system", "content": self.system_prompt} + ) + # Store additional args and kwargs for use in run method self.init_args = args self.init_kwargs = kwargs @@ -231,8 +237,8 @@ class LiteLLM: def _prepare_messages( self, - task: str, - img: str = None, + task: Optional[str] = None, + img: Optional[str] = None, ): """ Prepare the messages for the given task. @@ -245,24 +251,14 @@ class LiteLLM: """ self.check_if_model_supports_vision(img=img) - # Initialize messages - messages = [] - - # Add system prompt if present - if self.system_prompt is not None: - messages.append( - {"role": "system", "content": self.system_prompt} - ) - # Handle vision case if img is not None: - messages = self.vision_processing( - task=task, image=img, messages=messages - ) - else: - messages.append({"role": "user", "content": task}) + self.vision_processing(task=task, image=img) - return messages + if task is not None: + self.messages.append({"role": "user", "content": task}) + + return self.messages def anthropic_vision_processing( self, task: str, image: str, messages: list @@ -546,12 +542,18 @@ class LiteLLM: 5. Default parameters """ try: - messages = self._prepare_messages(task=task, img=img) + + self.messages.append({"role": "user", "content": task}) + + if img is not None: + self.messages = self.vision_processing( + task=task, image=img + ) # Base completion parameters completion_params = { "model": self.model_name, - "messages": messages, + "messages": self.messages, "stream": self.stream, "max_tokens": self.max_tokens, "caching": self.caching, From aa1be4c065da6493b169cc2491a59ea280bd57e2 Mon Sep 17 00:00:00 2001 From: harshalmore31 Date: Wed, 20 Aug 2025 22:00:51 +0530 Subject: [PATCH 10/19] updates ! --- docs/swarms/utils/agent_loader.md | 218 ++++++++++-------- swarms/utils/__init__.py | 10 + swarms/utils/agent_loader.py | 370 ++++++++++++++++++++---------- 3 files changed, 380 insertions(+), 218 deletions(-) diff --git a/docs/swarms/utils/agent_loader.md b/docs/swarms/utils/agent_loader.md index b8f1e94f..91a43578 100644 --- a/docs/swarms/utils/agent_loader.md +++ b/docs/swarms/utils/agent_loader.md @@ -6,9 +6,10 @@ The `AgentLoader` is a powerful utility for creating Swarms agents from markdown The AgentLoader enables you to: - Load single agents from markdown files with YAML frontmatter -- Load multiple agents from directories or file lists +- Load multiple agents from directories or file lists with concurrent processing - Parse Claude Code sub-agent YAML frontmatter configurations - Extract system prompts from markdown content +- Utilize 100% CPU cores for high-performance batch loading - Provide comprehensive error handling and validation ## Installation @@ -56,42 +57,37 @@ the subagent should follow. ```python from swarms.utils import load_agent_from_markdown -# Load Claude Code format agent (YAML frontmatter) -agent = load_agent_from_markdown( - file_path="performance-engineer.md" # Uses YAML frontmatter format -) +# Load agent from markdown file +agent = load_agent_from_markdown("finance_advisor.md") -# The agent automatically gets configured with: -# - Name, description from frontmatter -# - Temperature, max_loops, model settings -# - System prompt from content after frontmatter - -response = agent.run("Analyze application performance issues") -print(response) +# Use the agent +response = agent.run( + "I have $10,000 to invest. What's a good strategy for a beginner?" +) ``` -### Loading Multiple Agents +### Loading Multiple Agents (Concurrent) ```python from swarms.utils import load_agents_from_markdown -# Load all agents from directory (YAML frontmatter format) -agents = load_agents_from_markdown( - file_paths="./agents_directory/" # Directory with Claude Code format files -) +# Load agents from list of files with concurrent processing +agents = load_agents_from_markdown([ + "market_researcher.md", + "financial_analyst.md", + "risk_analyst.md" +], concurrent=True) # Uses all CPU cores for faster loading -# Load agents from specific files -agents = load_agents_from_markdown( - file_paths=[ - "performance-engineer.md", # Claude Code YAML format - "financial-analyst.md", # Claude Code YAML format - "security-analyst.md" # Claude Code YAML format - ] +# Use agents in a workflow +from swarms.structs import SequentialWorkflow + +workflow = SequentialWorkflow( + agents=agents, + max_loops=1 ) -print(f"Loaded {len(agents)} agents") -for agent in agents: - print(f"- {agent.agent_name}: {getattr(agent, 'temperature', 'default temp')}") +task = "Analyze the AI healthcare market for a $50M investment." +result = workflow.run(task) ``` ## Class-Based Usage @@ -109,8 +105,12 @@ loader = AgentLoader() # Load single agent agent = loader.load_single_agent("path/to/agent.md") -# Load multiple agents -agents = loader.load_multiple_agents("./agents_directory/") +# Load multiple agents with concurrent processing +agents = loader.load_multiple_agents( + "./agents_directory/", + concurrent=True, # Enable concurrent processing + max_workers=8 # Optional: limit worker threads +) # Parse markdown file without creating agent config = loader.parse_markdown_file("path/to/agent.md") @@ -150,34 +150,39 @@ agent = load_agent_from_markdown( ## Complete Example -### Example: Claude Code Sub-Agent Format +### Example: Finance Advisor Agent -Create a file `performance-engineer.md`: +Create a file `finance_advisor.md`: ```markdown --- -name: performance-engineer -description: Optimize application performance and identify bottlenecks +name: FinanceAdvisor +description: Expert financial advisor for investment and budgeting guidance model_name: gpt-4 -temperature: 0.3 -max_loops: 2 -mcp_url: http://example.com/mcp +temperature: 0.7 +max_loops: 1 --- -You are a Performance Engineer specializing in application optimization and scalability. - -Your role involves analyzing system performance, identifying bottlenecks, and implementing -solutions to improve efficiency and user experience. - -Key responsibilities: -- Profile applications to identify performance issues -- Optimize database queries and caching strategies -- Implement load testing and monitoring solutions -- Recommend infrastructure improvements -- Provide actionable optimization recommendations - -Always provide specific, measurable recommendations with implementation details. -Focus on both immediate wins and long-term architectural improvements. +You are an expert financial advisor with deep knowledge in: +- Investment strategies and portfolio management +- Personal budgeting and financial planning +- Risk assessment and diversification +- Tax optimization strategies +- Retirement planning + +Your approach: +- Provide clear, actionable financial advice +- Consider individual risk tolerance and goals +- Explain complex concepts in simple terms +- Always emphasize the importance of diversification +- Include relevant disclaimers about financial advice + +When analyzing financial situations: +1. Assess current financial position +2. Identify short-term and long-term goals +3. Evaluate risk tolerance +4. Recommend appropriate strategies +5. Suggest specific action steps ``` ### Loading and Using the Agent @@ -185,25 +190,13 @@ Focus on both immediate wins and long-term architectural improvements. ```python from swarms.utils import load_agent_from_markdown -# Load Claude Code format agent (YAML frontmatter) -performance_agent = load_agent_from_markdown( - file_path="performance-engineer.md" -) - -print(f"Agent: {performance_agent.agent_name}") -print(f"Temperature: {getattr(performance_agent, 'temperature', 'default')}") -print(f"Max loops: {performance_agent.max_loops}") -print(f"System prompt preview: {performance_agent.system_prompt[:100]}...") +# Load the Finance Advisor agent +agent = load_agent_from_markdown("finance_advisor.md") -# Use the performance agent -task = """ -Analyze the performance of a web application that handles 10,000 concurrent users -but is experiencing slow response times averaging 3 seconds. The application uses -a PostgreSQL database and is deployed on AWS with 4 EC2 instances behind a load balancer. -""" - -# Note: Actual agent.run() would make API calls -print(f"\nTask for {performance_agent.agent_name}: {task[:100]}...") +# Use the agent for financial advice +response = agent.run( + "I have $10,000 to invest. What's a good strategy for a beginner?" +) ``` ## Error Handling @@ -229,6 +222,54 @@ except Exception as e: print(f"Error loading agents: {e}") ``` +## Concurrent Processing Features + +### Multi-Core Performance + +The AgentLoader utilizes **100% of CPU cores** for concurrent agent loading, providing significant performance improvements when processing multiple markdown files: + +```python +from swarms.utils import load_agents_from_markdown + +# Automatic concurrent processing for multiple files +agents = load_agents_from_markdown([ + "agent1.md", "agent2.md", "agent3.md", "agent4.md" +]) # concurrent=True by default + +# Manual control over concurrency +agents = load_agents_from_markdown( + "./agents_directory/", + concurrent=True, # Enable concurrent processing + max_workers=8 # Limit to 8 worker threads +) + +# Disable concurrency for debugging or single files +agents = load_agents_from_markdown( + ["single_agent.md"], + concurrent=False # Sequential processing +) +``` + +### Resource Management + +```python +# Default: Uses all CPU cores +agents = load_agents_from_markdown(files, concurrent=True) + +# Custom worker count for resource control +agents = load_agents_from_markdown( + files, + concurrent=True, + max_workers=4 # Limit to 4 threads +) + +# ThreadPoolExecutor automatically manages: +# - Thread lifecycle +# - Resource cleanup +# - Exception handling +# - Result collection +``` + ## Advanced Features ### Custom System Prompt Building @@ -249,33 +290,6 @@ print("Generated System Prompt:") print(config.system_prompt) ``` -### Batch Processing - -Process multiple agent files efficiently: - -```python -import os -from pathlib import Path -from swarms.utils import AgentLoader - -loader = AgentLoader() - -# Find all markdown files in a directory -agent_dir = Path("./agents") -md_files = list(agent_dir.glob("*.md")) - -# Load all agents -agents = [] -for file_path in md_files: - try: - agent = loader.load_single_agent(str(file_path)) - agents.append(agent) - print(f"✓ Loaded: {agent.agent_name}") - except Exception as e: - print(f"✗ Failed to load {file_path}: {e}") - -print(f"\nSuccessfully loaded {len(agents)} agents") -``` ## Integration with Swarms @@ -323,8 +337,13 @@ class AgentLoader: ### Convenience Functions ```python -def load_agent_from_markdown(file_path: str, model: Optional[LiteLLM] = None, **kwargs) -> Agent -def load_agents_from_markdown(file_paths: Union[str, List[str]], model: Optional[LiteLLM] = None, **kwargs) -> List[Agent] +def load_agent_from_markdown(file_path: str, **kwargs) -> Agent +def load_agents_from_markdown( + file_paths: Union[str, List[str]], + concurrent: bool = True, # Enable concurrent processing + max_workers: Optional[int] = None, # Max worker threads (defaults to CPU count) + **kwargs +) -> List[Agent] ``` ### Configuration Model @@ -347,9 +366,8 @@ class MarkdownAgentConfig(BaseModel): ## Examples Repository Find more examples in the Swarms repository: -- `examples/agents_loader_example.py` - Complete usage demonstration -- `test_agent_loader.py` - Test suite with validation examples -- `examples/single_agent/utils/markdown_agent.py` - Markdown agent utilities +- `agents_loader_example.py` - Simple usage example +- `examples/agent_loader_demo.py` - Multi-agent workflow example ## Support diff --git a/swarms/utils/__init__.py b/swarms/utils/__init__.py index 2eb63089..62711e03 100644 --- a/swarms/utils/__init__.py +++ b/swarms/utils/__init__.py @@ -22,6 +22,12 @@ from swarms.utils.history_output_formatter import ( from swarms.utils.check_all_model_max_tokens import ( check_all_model_max_tokens, ) +from swarms.utils.agent_loader import ( + AgentLoader, + MarkdownAgentConfig, + load_agent_from_markdown, + load_agents_from_markdown, +) __all__ = [ @@ -41,4 +47,8 @@ __all__ = [ "HistoryOutputType", "history_output_formatter", "check_all_model_max_tokens", + "AgentLoader", + "MarkdownAgentConfig", + "load_agent_from_markdown", + "load_agents_from_markdown", ] diff --git a/swarms/utils/agent_loader.py b/swarms/utils/agent_loader.py index e6dce69d..58a81928 100644 --- a/swarms/utils/agent_loader.py +++ b/swarms/utils/agent_loader.py @@ -1,22 +1,29 @@ import os -import re -import random import yaml from pathlib import Path from typing import Any, Dict, List, Optional, Union +from concurrent.futures import ( + ThreadPoolExecutor, + as_completed, + TimeoutError, +) from pydantic import BaseModel, Field, field_validator from loguru import logger from swarms.structs.agent import Agent +# Default model configuration +DEFAULT_MODEL = "gpt-4o" + class MarkdownAgentConfig(BaseModel): """Configuration model for agents loaded from Claude Code markdown files.""" + name: str description: str - model_name: Optional[str] = "gpt-4" + model_name: Optional[str] = "gpt-4o" temperature: Optional[float] = Field(default=0.1, ge=0.0, le=2.0) - mcp_url: Optional[str] = None + mcp_url: Optional[int] = None system_prompt: str max_loops: int = Field(default=1, ge=1) autosave: bool = False @@ -33,22 +40,25 @@ class MarkdownAgentConfig(BaseModel): artifacts_on: bool = False artifacts_file_extension: str = ".md" artifacts_output_path: str = "" + streaming_on: bool = False @field_validator("system_prompt") @classmethod def validate_system_prompt(cls, v): if not v or not isinstance(v, str) or len(v.strip()) == 0: - raise ValueError("System prompt must be a non-empty string") + raise ValueError( + "System prompt must be a non-empty string" + ) return v - + class AgentLoader: """ Loader for creating agents from markdown files using Claude Code sub-agent format. - + Supports both single markdown file and multiple markdown files. Uses YAML frontmatter format for agent configuration. - + Features: - Single markdown file loading - Multiple markdown files loading (batch processing) @@ -56,223 +66,335 @@ class AgentLoader: - Agent configuration extraction from YAML metadata - Error handling and validation """ - + def __init__(self): """ Initialize the AgentLoader. """ pass - + def parse_yaml_frontmatter(self, content: str) -> Dict[str, Any]: """ Parse YAML frontmatter from markdown content. - + Args: content: Markdown content with potential YAML frontmatter - + Returns: Dictionary with parsed YAML data and remaining content """ - lines = content.split('\n') - + lines = content.split("\n") + # Check if content starts with YAML frontmatter - if not lines[0].strip() == '---': + if not lines[0].strip() == "---": return {"frontmatter": {}, "content": content} - + # Find end of frontmatter end_marker = -1 for i, line in enumerate(lines[1:], 1): - if line.strip() == '---': + if line.strip() == "---": end_marker = i break - + if end_marker == -1: return {"frontmatter": {}, "content": content} - + # Extract frontmatter and content - frontmatter_text = '\n'.join(lines[1:end_marker]) - remaining_content = '\n'.join(lines[end_marker + 1:]).strip() - + frontmatter_text = "\n".join(lines[1:end_marker]) + remaining_content = "\n".join(lines[end_marker + 1 :]).strip() + try: frontmatter_data = yaml.safe_load(frontmatter_text) or {} except yaml.YAMLError as e: logger.warning(f"Failed to parse YAML frontmatter: {e}") return {"frontmatter": {}, "content": content} - - return {"frontmatter": frontmatter_data, "content": remaining_content} - - - - def parse_markdown_file(self, file_path: str) -> MarkdownAgentConfig: + return { + "frontmatter": frontmatter_data, + "content": remaining_content, + } + + def parse_markdown_file( + self, file_path: str + ) -> MarkdownAgentConfig: """ Parse a single markdown file to extract agent configuration. Uses Claude Code sub-agent YAML frontmatter format. - + Args: file_path: Path to markdown file - + Returns: MarkdownAgentConfig object with parsed configuration - + Raises: FileNotFoundError: If file doesn't exist ValueError: If parsing fails or no YAML frontmatter found """ if not os.path.exists(file_path): - raise FileNotFoundError(f"Markdown file {file_path} not found.") - + raise FileNotFoundError( + f"Markdown file {file_path} not found." + ) + try: - with open(file_path, 'r', encoding='utf-8') as file: + with open(file_path, "r", encoding="utf-8") as file: content = file.read() - + # Parse YAML frontmatter (Claude Code sub-agent format) yaml_result = self.parse_yaml_frontmatter(content) frontmatter = yaml_result["frontmatter"] remaining_content = yaml_result["content"] - + if not frontmatter: - raise ValueError(f"No YAML frontmatter found in {file_path}. File must use Claude Code sub-agent format with YAML frontmatter.") - + raise ValueError( + f"No YAML frontmatter found in {file_path}. File must use Claude Code sub-agent format with YAML frontmatter." + ) + # Use YAML frontmatter data config_data = { - 'name': frontmatter.get('name', Path(file_path).stem), - 'description': frontmatter.get('description', 'Agent loaded from markdown'), - 'model_name': frontmatter.get('model_name') or frontmatter.get('model', 'gpt-4'), - 'temperature': frontmatter.get('temperature', 0.1), - 'max_loops': frontmatter.get('max_loops', 1), - 'mcp_url': frontmatter.get('mcp_url'), - 'system_prompt': remaining_content.strip(), + "name": frontmatter.get("name", Path(file_path).stem), + "description": frontmatter.get( + "description", "Agent loaded from markdown" + ), + "model_name": frontmatter.get("model_name") + or frontmatter.get("model", DEFAULT_MODEL), + "temperature": frontmatter.get("temperature", 0.1), + "max_loops": frontmatter.get("max_loops", 1), + "mcp_url": frontmatter.get("mcp_url"), + "system_prompt": remaining_content.strip(), + "streaming_on": frontmatter.get( + "streaming_on", False + ), } - - # Generate random model if not specified - if not config_data['model_name'] or config_data['model_name'] == 'random': - models = ['gpt-4', 'gpt-4-turbo', 'claude-3-sonnet', 'claude-3-haiku'] - config_data['model_name'] = random.choice(models) - - logger.info(f"Successfully parsed markdown file: {file_path}") + + # Use default model if not specified + if not config_data["model_name"]: + config_data["model_name"] = DEFAULT_MODEL + + logger.info( + f"Successfully parsed markdown file: {file_path}" + ) return MarkdownAgentConfig(**config_data) - + except Exception as e: - logger.error(f"Error parsing markdown file {file_path}: {str(e)}") - raise ValueError(f"Error parsing markdown file {file_path}: {str(e)}") - - def load_agent_from_markdown(self, file_path: str, **kwargs) -> Agent: + logger.error( + f"Error parsing markdown file {file_path}: {str(e)}" + ) + raise ValueError( + f"Error parsing markdown file {file_path}: {str(e)}" + ) + + def load_agent_from_markdown( + self, file_path: str, **kwargs + ) -> Agent: """ Load a single agent from a markdown file. - + Args: file_path: Path to markdown file **kwargs: Additional arguments to override default configuration - + Returns: Configured Agent instance """ config = self.parse_markdown_file(file_path) - + # Override with any provided kwargs config_dict = config.model_dump() config_dict.update(kwargs) - - # Remove fields not needed for Agent creation - agent_fields = { - 'agent_name': config_dict['name'], - 'system_prompt': config_dict['system_prompt'], - 'model_name': config_dict.get('model_name', 'gpt-4'), - 'temperature': config_dict.get('temperature', 0.1), - 'max_loops': config_dict['max_loops'], - 'autosave': config_dict['autosave'], - 'dashboard': config_dict['dashboard'], - 'verbose': config_dict['verbose'], - 'dynamic_temperature_enabled': config_dict['dynamic_temperature_enabled'], - 'saved_state_path': config_dict['saved_state_path'], - 'user_name': config_dict['user_name'], - 'retry_attempts': config_dict['retry_attempts'], - 'context_length': config_dict['context_length'], - 'return_step_meta': config_dict['return_step_meta'], - 'output_type': config_dict['output_type'], - 'auto_generate_prompt': config_dict['auto_generate_prompt'], - 'artifacts_on': config_dict['artifacts_on'], - 'artifacts_file_extension': config_dict['artifacts_file_extension'], - 'artifacts_output_path': config_dict['artifacts_output_path'], + + # Map config fields to Agent parameters, handling special cases + field_mapping = { + "name": "agent_name", # name -> agent_name + "description": None, # not used by Agent + "mcp_url": None, # not used by Agent } - + + agent_fields = {} + for config_key, config_value in config_dict.items(): + # Handle special field mappings + if config_key in field_mapping: + agent_key = field_mapping[config_key] + if agent_key: # Only include if mapped to something + agent_fields[agent_key] = config_value + else: + # Direct mapping for most fields + agent_fields[config_key] = config_value + try: - logger.info(f"Creating agent '{config.name}' from {file_path}") + logger.info( + f"Creating agent '{config.name}' from {file_path}" + ) agent = Agent(**agent_fields) - logger.info(f"Successfully created agent '{config.name}' from {file_path}") + logger.info( + f"Successfully created agent '{config.name}' from {file_path}" + ) return agent except Exception as e: import traceback - logger.error(f"Error creating agent from {file_path}: {str(e)}") + + logger.error( + f"Error creating agent from {file_path}: {str(e)}" + ) logger.error(f"Traceback: {traceback.format_exc()}") - raise ValueError(f"Error creating agent from {file_path}: {str(e)}") - - def load_agents_from_markdown(self, file_paths: Union[str, List[str]], **kwargs) -> List[Agent]: + raise ValueError( + f"Error creating agent from {file_path}: {str(e)}" + ) + + def load_agents_from_markdown( + self, + file_paths: Union[str, List[str]], + concurrent: bool = True, + max_workers: Optional[int] = None, + max_file_size_mb: float = 10.0, + **kwargs, + ) -> List[Agent]: """ - Load multiple agents from markdown files. - + Load multiple agents from markdown files with optional concurrent processing. + Args: file_paths: Single file path, directory path, or list of file paths + concurrent: Whether to use concurrent processing for multiple files + max_workers: Maximum number of worker threads (defaults to CPU count) + max_file_size_mb: Maximum file size in MB to prevent memory issues **kwargs: Additional arguments to override default configuration - + Returns: List of configured Agent instances """ agents = [] paths_to_process = [] - + # Handle different input types if isinstance(file_paths, str): if os.path.isdir(file_paths): # Directory - find all .md files - md_files = list(Path(file_paths).glob('*.md')) + md_files = list(Path(file_paths).glob("*.md")) paths_to_process = [str(f) for f in md_files] elif os.path.isfile(file_paths): # Single file paths_to_process = [file_paths] else: - raise FileNotFoundError(f"Path {file_paths} not found.") + raise FileNotFoundError( + f"Path {file_paths} not found." + ) elif isinstance(file_paths, list): paths_to_process = file_paths else: - raise ValueError("file_paths must be a string or list of strings") - - # Process each file + raise ValueError( + "file_paths must be a string or list of strings" + ) + + # Validate file sizes to prevent memory issues for file_path in paths_to_process: try: - agent = self.load_agent_from_markdown(file_path, **kwargs) - agents.append(agent) - except Exception as e: - logger.warning(f"Skipping {file_path} due to error: {str(e)}") - continue - - logger.info(f"Successfully loaded {len(agents)} agents from markdown files") + file_size_mb = os.path.getsize(file_path) / ( + 1024 * 1024 + ) + if file_size_mb > max_file_size_mb: + logger.warning( + f"Skipping {file_path}: size {file_size_mb:.2f}MB exceeds limit {max_file_size_mb}MB" + ) + paths_to_process.remove(file_path) + except OSError: + logger.warning( + f"Could not check size of {file_path}, skipping validation" + ) + + # Adjust max_workers for I/O-bound operations + if max_workers is None and concurrent: + # For I/O-bound: use more threads than CPU count, but cap it + max_workers = min( + 20, len(paths_to_process), os.cpu_count() * 2 + ) + + # Use concurrent processing for multiple files if enabled + if concurrent and len(paths_to_process) > 1: + logger.info( + f"Loading {len(paths_to_process)} agents concurrently with {max_workers} workers..." + ) + + with ThreadPoolExecutor( + max_workers=max_workers + ) as executor: + # Submit all tasks + future_to_path = { + executor.submit( + self.load_agent_from_markdown, + file_path, + **kwargs, + ): file_path + for file_path in paths_to_process + } + + # Collect results as they complete with timeout + for future in as_completed( + future_to_path, timeout=300 + ): # 5 minute timeout + file_path = future_to_path[future] + try: + agent = future.result( + timeout=60 + ) # 1 minute per agent + agents.append(agent) + logger.info( + f"Successfully loaded agent from {file_path}" + ) + except TimeoutError: + logger.error(f"Timeout loading {file_path}") + continue + except Exception as e: + logger.error( + f"Failed to load {file_path}: {str(e)}" + ) + continue + else: + # Sequential processing for single file or when concurrent is disabled + logger.info( + f"Loading {len(paths_to_process)} agents sequentially..." + ) + for file_path in paths_to_process: + try: + agent = self.load_agent_from_markdown( + file_path, **kwargs + ) + agents.append(agent) + except Exception as e: + logger.warning( + f"Skipping {file_path} due to error: {str(e)}" + ) + continue + + logger.info( + f"Successfully loaded {len(agents)} agents from markdown files" + ) return agents - + def load_single_agent(self, file_path: str, **kwargs) -> Agent: """ Convenience method for loading a single agent. Uses Claude Code sub-agent YAML frontmatter format. - + Args: file_path: Path to markdown file with YAML frontmatter **kwargs: Additional configuration overrides - + Returns: Configured Agent instance """ return self.load_agent_from_markdown(file_path, **kwargs) - - def load_multiple_agents(self, file_paths: Union[str, List[str]], **kwargs) -> List[Agent]: + + def load_multiple_agents( + self, file_paths: Union[str, List[str]], **kwargs + ) -> List[Agent]: """ Convenience method for loading multiple agents. Uses Claude Code sub-agent YAML frontmatter format. - + Args: file_paths: Directory path or list of file paths with YAML frontmatter **kwargs: Additional configuration overrides - + Returns: List of configured Agent instances """ @@ -283,11 +405,11 @@ class AgentLoader: def load_agent_from_markdown(file_path: str, **kwargs) -> Agent: """ Load a single agent from a markdown file with Claude Code YAML frontmatter format. - + Args: file_path: Path to markdown file with YAML frontmatter **kwargs: Additional configuration overrides - + Returns: Configured Agent instance """ @@ -295,16 +417,28 @@ def load_agent_from_markdown(file_path: str, **kwargs) -> Agent: return loader.load_single_agent(file_path, **kwargs) -def load_agents_from_markdown(file_paths: Union[str, List[str]], **kwargs) -> List[Agent]: +def load_agents_from_markdown( + file_paths: Union[str, List[str]], + concurrent: bool = True, + max_file_size_mb: float = 10.0, + **kwargs, +) -> List[Agent]: """ Load multiple agents from markdown files with Claude Code YAML frontmatter format. - + Args: file_paths: Directory path or list of file paths with YAML frontmatter + concurrent: Whether to use concurrent processing for multiple files + max_file_size_mb: Maximum file size in MB to prevent memory issues **kwargs: Additional configuration overrides - + Returns: List of configured Agent instances """ loader = AgentLoader() - return loader.load_multiple_agents(file_paths, **kwargs) \ No newline at end of file + return loader.load_agents_from_markdown( + file_paths, + concurrent=concurrent, + max_file_size_mb=max_file_size_mb, + **kwargs, + ) From f43e6787fd675f835c990fe3a9c2884085aa3d77 Mon Sep 17 00:00:00 2001 From: Kye Gomez Date: Wed, 20 Aug 2025 10:09:28 -0700 Subject: [PATCH 11/19] dynamic auto chunking --- swarms/sims/bell_labs.py | 1 + swarms/utils/__init__.py | 24 +++++--- swarms/utils/dynamic_context_window.py | 85 ++++++++++++++++++++++++++ 3 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 swarms/utils/dynamic_context_window.py diff --git a/swarms/sims/bell_labs.py b/swarms/sims/bell_labs.py index 682e8bf1..e2025840 100644 --- a/swarms/sims/bell_labs.py +++ b/swarms/sims/bell_labs.py @@ -18,6 +18,7 @@ from swarms.structs.conversation import Conversation from swarms.utils.history_output_formatter import ( history_output_formatter, ) + # from examples.tools.claude_as_a_tool import developer_worker_agent diff --git a/swarms/utils/__init__.py b/swarms/utils/__init__.py index 2eb63089..4cf0abbc 100644 --- a/swarms/utils/__init__.py +++ b/swarms/utils/__init__.py @@ -1,27 +1,30 @@ +from swarms.utils.check_all_model_max_tokens import ( + check_all_model_max_tokens, +) from swarms.utils.data_to_text import ( csv_to_text, data_to_text, json_to_text, txt_to_text, ) +from swarms.utils.dynamic_context_window import ( + dynamic_auto_chunking, +) from swarms.utils.file_processing import ( + create_file_in_folder, load_json, sanitize_file_path, - zip_workspace, - create_file_in_folder, zip_folders, + zip_workspace, ) -from swarms.utils.parse_code import extract_code_from_markdown -from swarms.utils.pdf_to_text import pdf_to_text -from swarms.utils.try_except_wrapper import try_except_wrapper -from swarms.utils.litellm_tokenizer import count_tokens -from swarms.utils.output_types import HistoryOutputType from swarms.utils.history_output_formatter import ( history_output_formatter, ) -from swarms.utils.check_all_model_max_tokens import ( - check_all_model_max_tokens, -) +from swarms.utils.litellm_tokenizer import count_tokens +from swarms.utils.output_types import HistoryOutputType +from swarms.utils.parse_code import extract_code_from_markdown +from swarms.utils.pdf_to_text import pdf_to_text +from swarms.utils.try_except_wrapper import try_except_wrapper __all__ = [ @@ -41,4 +44,5 @@ __all__ = [ "HistoryOutputType", "history_output_formatter", "check_all_model_max_tokens", + "dynamic_auto_chunking", ] diff --git a/swarms/utils/dynamic_context_window.py b/swarms/utils/dynamic_context_window.py new file mode 100644 index 00000000..115bc4d6 --- /dev/null +++ b/swarms/utils/dynamic_context_window.py @@ -0,0 +1,85 @@ +import traceback + +from loguru import logger + +from swarms.utils.litellm_tokenizer import count_tokens +from typing import Optional + + +def dynamic_auto_chunking_( + content: str, + context_length: Optional[int] = 8192, + tokenizer_model_name: Optional[str] = "gpt-4.1", +): + """ + Dynamically chunk the conversation history to fit within the context length. + + Args: + content (str): The conversation history as a string. + context_length (int): The maximum number of tokens allowed. + tokenizer_model_name (str): The name of the tokenizer model to use. + + Returns: + str: The chunked conversation history as a string that fits within context_length tokens. + """ + total_tokens = count_tokens( + text=content, model=tokenizer_model_name + ) + + if total_tokens <= context_length: + return content + + # We need to remove characters from the beginning until we're under the limit + # Start by removing a percentage of characters and adjust iteratively + target_tokens = context_length + current_string = content + + # Binary search approach to find the right cutoff point + left, right = 0, len(content) + + while left < right: + mid = (left + right) // 2 + test_string = content[mid:] + + if not test_string: + break + + test_tokens = count_tokens( + text=test_string, model=tokenizer_model_name + ) + + if test_tokens <= target_tokens: + # We can remove more from the beginning + right = mid + current_string = test_string + else: + # We need to keep more from the beginning + left = mid + 1 + + return current_string + + +def dynamic_auto_chunking( + content: str, + context_length: Optional[int] = 8192, + tokenizer_model_name: Optional[str] = "gpt-4.1", +): + """ + Dynamically chunk the conversation history to fit within the context length. + + Args: + content (str): The conversation history as a string. + context_length (int): The maximum number of tokens allowed. + tokenizer_model_name (str): The name of the tokenizer model to use. + """ + try: + return dynamic_auto_chunking_( + content=content, + context_length=context_length, + tokenizer_model_name=tokenizer_model_name, + ) + except Exception as e: + logger.error( + f"Dynamic auto chunking failed: {e} Traceback: {traceback.format_exc()}" + ) + return content From b871b8e8a34cba1980bf9a301b80e2d70bb52094 Mon Sep 17 00:00:00 2001 From: harshalmore31 Date: Wed, 20 Aug 2025 22:57:32 +0530 Subject: [PATCH 12/19] examples & docs --- docs/mkdocs.yml | 1 + docs/swarms/utils/agent_loader.md | 79 ++++++++++++++++++- examples/agent_loader/agent_loader_demo.py | 7 ++ examples/agent_loader/finance_advisor.md | 28 +++++++ .../agent_loader/multi_agents_loader_demo.py | 22 ++++++ 5 files changed, 134 insertions(+), 3 deletions(-) create mode 100644 examples/agent_loader/agent_loader_demo.py create mode 100644 examples/agent_loader/finance_advisor.md create mode 100644 examples/agent_loader/multi_agents_loader_demo.py diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 5a687cca..ff2bc4cc 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -369,6 +369,7 @@ nav: - Utils: - Agent with Streaming: "examples/agent_stream.md" - Agent Output Types: "swarms/examples/agent_output_types.md" + - Agent Loader: "swarms/utils/agent_loader.md" - Gradio Chat Interface: "swarms/ui/main.md" - LLM Providers: - Overview: "swarms/examples/model_providers.md" diff --git a/docs/swarms/utils/agent_loader.md b/docs/swarms/utils/agent_loader.md index 91a43578..74319f75 100644 --- a/docs/swarms/utils/agent_loader.md +++ b/docs/swarms/utils/agent_loader.md @@ -164,6 +164,7 @@ max_loops: 1 --- You are an expert financial advisor with deep knowledge in: + - Investment strategies and portfolio management - Personal budgeting and financial planning - Risk assessment and diversification @@ -171,6 +172,7 @@ You are an expert financial advisor with deep knowledge in: - Retirement planning Your approach: + - Provide clear, actionable financial advice - Consider individual risk tolerance and goals - Explain complex concepts in simple terms @@ -178,6 +180,7 @@ Your approach: - Include relevant disclaimers about financial advice When analyzing financial situations: + 1. Assess current financial position 2. Identify short-term and long-term goals 3. Evaluate risk tolerance @@ -365,9 +368,79 @@ class MarkdownAgentConfig(BaseModel): ## Examples Repository -Find more examples in the Swarms repository: -- `agents_loader_example.py` - Simple usage example -- `examples/agent_loader_demo.py` - Multi-agent workflow example +Find complete working examples in the `examples/agent_loader/` directory: + +### Single Agent Example (`agent_loader_demo.py`) +```python +from swarms.utils import load_agent_from_markdown + +agent = load_agent_from_markdown("finance_advisor.md") + +agent.run( + task="Analyze the financial market trends for 2023." +) +``` + +### Multi-Agent Workflow Example (`multi_agents_loader_demo.py`) +```python +from swarms.utils import load_agents_from_markdown + +agents = load_agents_from_markdown([ + "market_researcher.md", + "financial_analyst.md", + "risk_analyst.md" +]) + +# Use agents in a workflow +from swarms.structs.sequential_workflow import SequentialWorkflow + +workflow = SequentialWorkflow( + agents=agents, + max_loops=1 +) + +task = """ +Analyze the AI healthcare market for a $50M investment opportunity. +Focus on market size, competition, financials, and risks. +""" + +result = workflow.run(task) +``` + +### Sample Agent Definition (`finance_advisor.md`) +```markdown +--- +name: FinanceAdvisor +description: Expert financial advisor for investment and budgeting guidance +model_name: gpt-4o +temperature: 0.7 +max_loops: 1 +--- + +You are an expert financial advisor with deep knowledge in: + +- Investment strategies and portfolio management +- Personal budgeting and financial planning +- Risk assessment and diversification +- Tax optimization strategies +- Retirement planning + +Your approach: + +- Provide clear, actionable financial advice +- Consider individual risk tolerance and goals +- Explain complex concepts in simple terms +- Always emphasize the importance of diversification +- Include relevant disclaimers about financial advice + +When analyzing financial situations: + +1. Assess current financial position +2. Identify short-term and long-term goals +3. Evaluate risk tolerance +4. Recommend appropriate strategies +5. Suggest specific action steps +``` ## Support diff --git a/examples/agent_loader/agent_loader_demo.py b/examples/agent_loader/agent_loader_demo.py new file mode 100644 index 00000000..1277f349 --- /dev/null +++ b/examples/agent_loader/agent_loader_demo.py @@ -0,0 +1,7 @@ +from swarms.utils import load_agent_from_markdown + +agent = load_agent_from_markdown("finance_advisor.md") + +agent.run( + task="Analyze the financial market trends for 2023." +) \ No newline at end of file diff --git a/examples/agent_loader/finance_advisor.md b/examples/agent_loader/finance_advisor.md new file mode 100644 index 00000000..62c32e51 --- /dev/null +++ b/examples/agent_loader/finance_advisor.md @@ -0,0 +1,28 @@ +--- +name: FinanceAdvisor +description: Expert financial advisor for investment and budgeting guidance +model_name: gpt-4o +temperature: 0.7 +max_loops: 1 +--- + +You are an expert financial advisor with deep knowledge in: +- Investment strategies and portfolio management +- Personal budgeting and financial planning +- Risk assessment and diversification +- Tax optimization strategies +- Retirement planning + +Your approach: +- Provide clear, actionable financial advice +- Consider individual risk tolerance and goals +- Explain complex concepts in simple terms +- Always emphasize the importance of diversification +- Include relevant disclaimers about financial advice + +When analyzing financial situations: +1. Assess current financial position +2. Identify short-term and long-term goals +3. Evaluate risk tolerance +4. Recommend appropriate strategies +5. Suggest specific action steps \ No newline at end of file diff --git a/examples/agent_loader/multi_agents_loader_demo.py b/examples/agent_loader/multi_agents_loader_demo.py new file mode 100644 index 00000000..80d93eb0 --- /dev/null +++ b/examples/agent_loader/multi_agents_loader_demo.py @@ -0,0 +1,22 @@ +from swarms.utils import load_agents_from_markdown + +agents = load_agents_from_markdown([ + "market_researcher.md", + "financial_analyst.md", + "risk_analyst.md" +]) + +# Example 3: Use agents in a workflow +from swarms.structs.sequential_workflow import SequentialWorkflow + +workflow = SequentialWorkflow( + agents=agents, + max_loops=1 +) + +task = """ +Analyze the AI healthcare market for a $50M investment opportunity. +Focus on market size, competition, financials, and risks. +""" + +result = workflow.run(task) \ No newline at end of file From e3c36187992b962a90ec86563fcaa1cfad21bdfe Mon Sep 17 00:00:00 2001 From: harshalmore31 Date: Wed, 20 Aug 2025 22:58:21 +0530 Subject: [PATCH 13/19] cleanup ! --- examples/agent_loader_demo.py | 28 ---------------------------- 1 file changed, 28 deletions(-) delete mode 100644 examples/agent_loader_demo.py diff --git a/examples/agent_loader_demo.py b/examples/agent_loader_demo.py deleted file mode 100644 index bcc127fb..00000000 --- a/examples/agent_loader_demo.py +++ /dev/null @@ -1,28 +0,0 @@ -from swarms.utils.agent_loader import load_agent_from_markdown - -# Example 1: Load a single agent -market_researcher = load_agent_from_markdown("market_researcher.md") - -# Example 2: Load multiple agents -from swarms.utils.agent_loader import load_agents_from_markdown - -agents = load_agents_from_markdown([ - "market_researcher.md", - "financial_analyst.md", - "risk_analyst.md" -]) - -# Example 3: Use agents in a workflow -from swarms.structs.sequential_workflow import SequentialWorkflow - -workflow = SequentialWorkflow( - agents=agents, - max_loops=1 -) - -task = """ -Analyze the AI healthcare market for a $50M investment opportunity. -Focus on market size, competition, financials, and risks. -""" - -result = workflow.run(task) \ No newline at end of file From 14dcc612dc3aee9e5e28f236eae17a0868e7e535 Mon Sep 17 00:00:00 2001 From: harshalmore31 Date: Wed, 20 Aug 2025 23:04:48 +0530 Subject: [PATCH 14/19] fixed --- swarms/utils/__init__.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/swarms/utils/__init__.py b/swarms/utils/__init__.py index 40a0cbb7..4d41cd2c 100644 --- a/swarms/utils/__init__.py +++ b/swarms/utils/__init__.py @@ -21,10 +21,6 @@ from swarms.utils.history_output_formatter import ( history_output_formatter, ) -from swarms.utils.check_all_model_max_tokens import ( - check_all_model_max_tokens, -) - from swarms.utils.agent_loader import ( AgentLoader, MarkdownAgentConfig, From 9f414509098c25934197746a12e4529db066df69 Mon Sep 17 00:00:00 2001 From: harshalmore31 Date: Wed, 20 Aug 2025 23:09:58 +0530 Subject: [PATCH 15/19] updates ! --- docs/swarms/utils/agent_loader.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/swarms/utils/agent_loader.md b/docs/swarms/utils/agent_loader.md index 74319f75..d8ba42db 100644 --- a/docs/swarms/utils/agent_loader.md +++ b/docs/swarms/utils/agent_loader.md @@ -5,6 +5,7 @@ The `AgentLoader` is a powerful utility for creating Swarms agents from markdown ## Overview The AgentLoader enables you to: + - Load single agents from markdown files with YAML frontmatter - Load multiple agents from directories or file lists with concurrent processing - Parse Claude Code sub-agent YAML frontmatter configurations @@ -43,6 +44,7 @@ the subagent should follow. ``` **Schema Fields:** + - `name` (required): Your sub-agent name - `description` (required): Description of when this subagent should be invoked - `model_name` (optional): Name of model (defaults to random selection if not provided) @@ -229,7 +231,7 @@ except Exception as e: ### Multi-Core Performance -The AgentLoader utilizes **100% of CPU cores** for concurrent agent loading, providing significant performance improvements when processing multiple markdown files: +The AgentLoader utilizes 100% of CPU cores for concurrent agent loading, providing significant performance improvements when processing multiple markdown files: ```python from swarms.utils import load_agents_from_markdown @@ -445,6 +447,7 @@ When analyzing financial situations: ## Support For questions and support: + - GitHub Issues: [https://github.com/kyegomez/swarms/issues](https://github.com/kyegomez/swarms/issues) - Documentation: [https://docs.swarms.world](https://docs.swarms.world) - Community: Join our Discord for real-time support \ No newline at end of file From ded6d86a23dc980f64ea3dbab4a9c5212ffdf92b Mon Sep 17 00:00:00 2001 From: harshalmore31 Date: Wed, 20 Aug 2025 23:18:21 +0530 Subject: [PATCH 16/19] fixes ! --- docs/mkdocs.yml | 2 +- swarms/utils/agent_loader.py | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index ff2bc4cc..29db2115 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -326,6 +326,7 @@ nav: - Overview: "swarms_tools/overview.md" - BaseTool Reference: "swarms/tools/base_tool.md" - MCP Client Utils: "swarms/tools/mcp_client_call.md" + - Agent Loader: "swarms/utils/agent_loader.md" - Vertical Tools: - Finance: "swarms_tools/finance.md" @@ -369,7 +370,6 @@ nav: - Utils: - Agent with Streaming: "examples/agent_stream.md" - Agent Output Types: "swarms/examples/agent_output_types.md" - - Agent Loader: "swarms/utils/agent_loader.md" - Gradio Chat Interface: "swarms/ui/main.md" - LLM Providers: - Overview: "swarms/examples/model_providers.md" diff --git a/swarms/utils/agent_loader.py b/swarms/utils/agent_loader.py index 58a81928..ee3b0e89 100644 --- a/swarms/utils/agent_loader.py +++ b/swarms/utils/agent_loader.py @@ -10,7 +10,7 @@ from concurrent.futures import ( from pydantic import BaseModel, Field, field_validator from loguru import logger -from swarms.structs.agent import Agent +# Lazy import to avoid circular dependency # Default model configuration DEFAULT_MODEL = "gpt-4o" @@ -186,7 +186,7 @@ class AgentLoader: def load_agent_from_markdown( self, file_path: str, **kwargs - ) -> Agent: + ) -> "Agent": """ Load a single agent from a markdown file. @@ -222,6 +222,9 @@ class AgentLoader: agent_fields[config_key] = config_value try: + # Lazy import to avoid circular dependency + from swarms.structs.agent import Agent + logger.info( f"Creating agent '{config.name}' from {file_path}" ) @@ -248,7 +251,7 @@ class AgentLoader: max_workers: Optional[int] = None, max_file_size_mb: float = 10.0, **kwargs, - ) -> List[Agent]: + ) -> List["Agent"]: """ Load multiple agents from markdown files with optional concurrent processing. @@ -370,7 +373,7 @@ class AgentLoader: ) return agents - def load_single_agent(self, file_path: str, **kwargs) -> Agent: + def load_single_agent(self, file_path: str, **kwargs) -> "Agent": """ Convenience method for loading a single agent. Uses Claude Code sub-agent YAML frontmatter format. @@ -386,7 +389,7 @@ class AgentLoader: def load_multiple_agents( self, file_paths: Union[str, List[str]], **kwargs - ) -> List[Agent]: + ) -> List["Agent"]: """ Convenience method for loading multiple agents. Uses Claude Code sub-agent YAML frontmatter format. @@ -402,7 +405,7 @@ class AgentLoader: # Convenience functions -def load_agent_from_markdown(file_path: str, **kwargs) -> Agent: +def load_agent_from_markdown(file_path: str, **kwargs) -> "Agent": """ Load a single agent from a markdown file with Claude Code YAML frontmatter format. @@ -422,7 +425,7 @@ def load_agents_from_markdown( concurrent: bool = True, max_file_size_mb: float = 10.0, **kwargs, -) -> List[Agent]: +) -> List["Agent"]: """ Load multiple agents from markdown files with Claude Code YAML frontmatter format. From d59f2f702d5e38b129a8983bc16d97b8d99b15de Mon Sep 17 00:00:00 2001 From: Kye Gomez Date: Wed, 20 Aug 2025 10:48:30 -0700 Subject: [PATCH 17/19] [DOCS][Update ecosystem] --- docs/swarms/ecosystem.md | 167 ++++++++++----------------------------- 1 file changed, 40 insertions(+), 127 deletions(-) diff --git a/docs/swarms/ecosystem.md b/docs/swarms/ecosystem.md index 9a0a1ccc..2116c981 100644 --- a/docs/swarms/ecosystem.md +++ b/docs/swarms/ecosystem.md @@ -1,149 +1,62 @@ -# Swarms Ecosystem +# Swarms Infrastructure Stack -*The Complete Enterprise-Grade Multi-Agent AI Platform* +**We're Building the Operating System for the Agent Economy** ---- - -## **Join the Future of AI Development** - -**We're Building the Operating System for the Agent Economy** - The Swarms ecosystem represents the most comprehensive, production-ready multi-agent AI platform available today. From our flagship Python framework to high-performance Rust implementations and client libraries spanning every major programming language, we provide enterprise-grade tools that power the next generation of intelligent applications. - ---- - -## **Complete Product Portfolio** - -| **Product** | **Technology** | **Status** | **Repository** | **Documentation** | -|-------------|---------------|------------|----------------|-------------------| -| **Swarms Python Framework** | Python | **Production** | [swarms](https://github.com/kyegomez/swarms) | [Docs](https://docs.swarms.world/en/latest/swarms/install/install/) | -| **Swarms Rust Framework** | Rust | **Production** | [swarms-rs](https://github.com/The-Swarm-Corporation/swarms-rs) | [Docs](https://docs.swarms.world/en/latest/swarms_rs/overview/) | -| **Python API Client** | Python | **Production** | [swarms-sdk](https://github.com/The-Swarm-Corporation/swarms-sdk) | [Docs](https://docs.swarms.world/en/latest/swarms_cloud/python_client/) | -| **TypeScript/Node.js Client** | TypeScript | **Production** | [swarms-ts](https://github.com/The-Swarm-Corporation/swarms-ts) | *Coming Soon* | -| **Go Client** | Go | **Production** | [swarms-client-go](https://github.com/The-Swarm-Corporation/swarms-client-go) | *Coming Soon* | -| **Java Client** | Java | **Production** | [swarms-java](https://github.com/The-Swarm-Corporation/swarms-java) | *Coming Soon* | -| **Kotlin Client** | Kotlin | **Q2 2025** | *In Development* | *Coming Soon* | -| **Ruby Client** | Ruby | **Q2 2025** | *In Development* | *Coming Soon* | -| **Rust Client** | Rust | **Q2 2025** | *In Development* | *Coming Soon* | -| **C#/.NET Client** | C# | **Q3 2025** | *In Development* | *Coming Soon* | +The Swarms ecosystem represents the most comprehensive, production-ready multi-agent AI platform available today. From our flagship Python framework to high-performance Rust implementations and client libraries spanning every major programming language, we provide enterprise-grade tools that power the next generation of agentic applications. --- -## **Why Choose the Swarms Ecosystem?** - -### **Enterprise-Grade Architecture** - -- **Production Ready**: Battle-tested in enterprise environments with 99.9%+ uptime - -- **Scalable Infrastructure**: Handle millions of agent interactions with automatic scaling - -- **Security First**: End-to-end encryption, API key management, and enterprise compliance +## **Product Portfolio by Language & API** -- **Observability**: Comprehensive logging, monitoring, and debugging capabilities +### 🐍 **Python** -### **Developer Experience** - -- **Multiple Language Support**: Native clients for every major programming language - -- **Unified API**: Consistent interface across all platforms and languages - -- **Rich Documentation**: Comprehensive guides, tutorials, and API references - -- **Active Community**: 24/7 support through Discord, GitHub, and direct channels - -### **Performance & Reliability** - -- **High Throughput**: Process thousands of concurrent agent requests - -- **Low Latency**: Optimized for real-time applications and user experiences - -- **Fault Tolerance**: Automatic retries, circuit breakers, and graceful degradation - -- **Multi-Cloud**: Deploy on AWS, GCP, Azure, or on-premises infrastructure +| **Product** | **Description** | **Status** | **Repository** | **Documentation** | +|------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|-------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------| +| **Swarms Python Framework** | The core multi-agent orchestration framework for Python. Enables building, managing, and scaling complex agentic systems with robust abstractions, workflows, and integrations. | **Production** | [swarms](https://github.com/kyegomez/swarms) | [Docs](https://docs.swarms.world/en/latest/swarms/install/install/) | +| **Python API Client** | Official Python SDK for interacting with Swarms Cloud and remote agent infrastructure. Simplifies API calls, authentication, and integration into Python applications. | **Production** | [swarms-sdk](https://github.com/The-Swarm-Corporation/swarms-sdk) | [Docs](https://docs.swarms.world/en/latest/swarms_cloud/python_client/) | +| **Swarms Tools** | A comprehensive library of prebuilt tools for various domains, including finance, social media, data processing, and more. Accelerates agent development by providing ready-to-use capabilities and integrations. | **Production** | [swarms-tools](https://github.com/The-Swarm-Corporation/swarms-tools) | *Coming Soon* | +| **Swarms Memory** | A robust library of memory structures and data loaders for Retrieval-Augmented Generation (RAG) processing. Provides advanced memory management, vector stores, and integration with agentic workflows. | **Production** | [swarms-memory](https://github.com/The-Swarm-Corporation/swarms-memory) | *Coming Soon* | --- -## **Join Our Growing Community** - -### **Connect With Developers Worldwide** +### 🦀 **Rust** -| **Platform** | **Purpose** | **Join Link** | **Benefits** | -|--------------|-------------|---------------|--------------| -| **Discord Community** | Real-time support & discussions | [Join Discord](https://discord.gg/EamjgSaEQf) | • 24/7 developer support
• Weekly community events
• Direct access to core team
• Beta feature previews | -| **Twitter/X** | Latest updates & announcements | [Follow @swarms_corp](https://x.com/swarms_corp) | • Breaking news & updates
• Community highlights
• Technical insights
• Industry partnerships | -| **LinkedIn** | Professional network & updates | [The Swarm Corporation](https://www.linkedin.com/company/the-swarm-corporation) | • Professional networking
• Career opportunities
• Enterprise partnerships
• Industry insights | -| **YouTube** | Tutorials & technical content | [Swarms Channel](https://www.youtube.com/channel/UC9yXyitkbU_WSy7bd_41SqQ) | • In-depth tutorials
• Live coding sessions
• Architecture deep dives
• Community showcases | +| **Product** | **Description** | **Status** | **Repository** | **Documentation** | +|----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|-------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------| +| **Swarms Rust Framework** | High-performance, memory-safe multi-agent orchestration framework written in Rust. Designed for demanding production environments and seamless integration with Rust-based systems. | **Production** | [swarms-rs](https://github.com/The-Swarm-Corporation/swarms-rs) | [Docs](https://docs.swarms.world/en/latest/swarms_rs/overview/) | +| **Rust Client** | Official Rust client library for connecting to Swarms Cloud and orchestrating agents from Rust applications. Provides idiomatic Rust APIs for agent management and communication. | **Q2 2025** | *In Development* | *Coming Soon* | --- -## **Contribute to the Ecosystem** +### 🌐 **API Clients (Multi-Language)** -### **How You Can Make an Impact** - -| **Contribution Area** | **Skills Needed** | **Impact Level** | **Getting Started** | -|-----------------------|-------------------|------------------|---------------------| -| **Core Framework Development** | Python, Rust, Systems Design | **High Impact** | [Contributing Guide](https://docs.swarms.world/en/latest/contributors/main/) | -| **Client Library Development** | Various Languages (Go, Java, TS, etc.) | **High Impact** | [Client Development](https://github.com/The-Swarm-Corporation) | -| **Documentation & Tutorials** | Technical Writing, Examples | **High Impact** | [Docs Contributing](https://docs.swarms.world/en/latest/contributors/docs/) | -| **Testing & Quality Assurance** | Testing Frameworks, QA | **Medium Impact** | [Testing Guide](https://docs.swarms.world/en/latest/swarms/framework/test/) | -| **UI/UX & Design** | Design, Frontend Development | **Medium Impact** | [Design Contributions](https://github.com/The-Swarm-Corporation/swarms/issues) | -| **Bug Reports & Feature Requests** | User Experience, Testing | **Easy Start** | [Report Issues](https://github.com/The-Swarm-Corporation/swarms/issues) | - ---- - -## **We're Hiring Top Talent** - -### **Join the Team Building the Future Of The World Economy** - -**Ready to work on cutting-edge agent technology that's shaping the future?** We're actively recruiting exceptional engineers, researchers, and technical leaders to join our mission of building the operating system for the agent economy. - -| **Why Join Swarms?** | **What We Offer** | -|-----------------------|-------------------| -| **Cutting-Edge Technology** | Work on the most powerful multi-agent systems, distributed computing, and enterprise-scale infrastructure | -| **Global Impact** | Your code will power agent applications used by Fortune 500 companies and millions of developers | -| **World-Class Team** | Collaborate with top engineers, researchers, and industry experts from Google, OpenAI, and more | -| **Fast Growth** | Join a rapidly scaling company with massive market opportunity and venture backing | - -### **Open Positions** - -| **Position** | **Role Description** | -|-------------------------------|----------------------------------------------------------| -| **Senior Rust Engineers** | Building high-performance agent infrastructure | -| **Python Framework Engineers**| Expanding our core multi-agent capabilities | -| **DevOps/Platform Engineers** | Scaling cloud infrastructure for millions of agents | -| **Technical Writers** | Creating world-class developer documentation | -| **Solutions Engineers** | Helping enterprises adopt multi-agent AI | - -**Ready to Build the Future?** **[Apply Now at swarms.ai/hiring](https://swarms.ai/hiring)** +| **Language/Platform** | **Description** | **Status** | **Repository** | **Documentation** | +|----------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------|-------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------| +| **TypeScript/Node.js** | Official TypeScript/Node.js SDK for Swarms Cloud. Enables seamless integration of agentic workflows into JavaScript and TypeScript applications, both server-side and in the browser. | **Production** | [swarms-ts](https://github.com/The-Swarm-Corporation/swarms-ts) | *Coming Soon* | +| **Go** | Go client library for Swarms Cloud, providing Go developers with native APIs to manage, orchestrate, and interact with agents in distributed systems and microservices. | **Production** | [swarms-client-go](https://github.com/The-Swarm-Corporation/swarms-client-go) | *Coming Soon* | +| **Java** | Java SDK for Swarms Cloud, allowing enterprise Java applications to leverage multi-agent orchestration and integrate agentic capabilities into JVM-based systems. | **Production** | [swarms-java](https://github.com/The-Swarm-Corporation/swarms-java) | *Coming Soon* | +| **Kotlin** | Native Kotlin client for Swarms Cloud, designed for modern JVM and Android applications seeking to embed agentic intelligence and orchestration. | **Q2 2025** | *In Development* | *Coming Soon* | +| **Ruby** | Ruby SDK for Swarms Cloud, enabling Ruby and Rails developers to easily connect, manage, and orchestrate agents within their applications. | **Q2 2025** | *In Development* | *Coming Soon* | +| **C#/.NET** | Official C#/.NET client library for Swarms Cloud, providing .NET developers with tools to integrate agentic workflows into desktop, web, and cloud applications. | **Q3 2025** | *In Development* | *Coming Soon* | --- ---- - -## **Get Started Today** - -### **Quick Start Guide** - -| **Step** | **Action** | **Time Required** | -|----------|------------|-------------------| -| **1** | [Install Swarms Python Framework](https://docs.swarms.world/en/latest/swarms/install/install/) | 5 minutes | -| **2** | [Run Your First Agent](https://docs.swarms.world/en/latest/swarms/examples/basic_agent/) | 10 minutes | -| **3** | [Try Multi-Agent Workflows](https://docs.swarms.world/en/latest/swarms/examples/sequential_example/) | 15 minutes | -| **4** | [Join Our Discord Community](https://discord.gg/EamjgSaEQf) | 2 minutes | -| **5** | [Explore Enterprise Features](https://docs.swarms.world/en/latest/swarms_cloud/swarms_api/) | 20 minutes | - ---- - -## **Enterprise Support & Partnerships** - -### **Ready to Scale with Swarms?** +## **Why Choose the Swarms Ecosystem?** -| **Contact Type** | **Best For** | **Response Time** | **Contact Information** | -|------------------|--------------|-------------------|-------------------------| -| **Technical Support** | Development questions, troubleshooting | < 24 hours | [Book Support Call](https://cal.com/swarms/swarms-technical-support) | -| **Enterprise Sales** | Custom deployments, enterprise licensing | < 4 hours | [kye@swarms.world](mailto:kye@swarms.world) | -| **Partnerships** | Integration partnerships, technology alliances | < 48 hours | [kye@swarms.world](mailto:kye@swarms.world) | -| **Investor Relations** | Investment opportunities, funding updates | By appointment | [kye@swarms.world](mailto:kye@swarms.world) | +| **Feature** | **Description** | +|----------------------------|------------------------------------------------------------------------------------------------------| +| **Production Ready** | Battle-tested in enterprise environments with 99.9%+ uptime | +| **Scalable Infrastructure** | Handle millions of agent interactions with automatic scaling | +| **Security First** | End-to-end encryption, API key management, and enterprise compliance | +| **Observability** | Comprehensive logging, monitoring, and debugging capabilities | +| **Multiple Language Support** | Native clients for every major programming language | +| **Unified API** | Consistent interface across all platforms and languages | +| **Rich Documentation** | Comprehensive guides, tutorials, and API references | +| **Active Community** | 24/7 support through Discord, GitHub, and direct channels | +| **High Throughput** | Process thousands of concurrent agent requests | +| **Low Latency** | Optimized for real-time applications and user experiences | +| **Fault Tolerance** | Automatic retries, circuit breakers, and graceful degradation | +| **Multi-Cloud** | Deploy on AWS, GCP, Azure, or on-premises infrastructure | --- - -**Ready to build the future of AI? Start with Swarms today and join thousands of developers creating the next generation of intelligent applications.** From d487534e9283559f6acdbb73800114764afee0c7 Mon Sep 17 00:00:00 2001 From: harshalmore31 Date: Wed, 20 Aug 2025 23:29:57 +0530 Subject: [PATCH 18/19] fixed ! --- docs/mkdocs.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index 29db2115..abb07aeb 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -320,13 +320,15 @@ nav: - Storage: - AgentRegistry: "swarms/structs/agent_registry.md" + - Agent Loader: + - Agent Loader: "swarms/utils/agent_loader.md" + - Communication Structure: "swarms/structs/conversation.md" - Tools: - Overview: "swarms_tools/overview.md" - BaseTool Reference: "swarms/tools/base_tool.md" - MCP Client Utils: "swarms/tools/mcp_client_call.md" - - Agent Loader: "swarms/utils/agent_loader.md" - Vertical Tools: - Finance: "swarms_tools/finance.md" From ea55624d9fa1794bcb6eb5a3249586f71e2fd153 Mon Sep 17 00:00:00 2001 From: harshalmore31 Date: Wed, 20 Aug 2025 23:32:33 +0530 Subject: [PATCH 19/19] updates --- docs/swarms/utils/agent_loader.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/swarms/utils/agent_loader.md b/docs/swarms/utils/agent_loader.md index d8ba42db..dc749156 100644 --- a/docs/swarms/utils/agent_loader.md +++ b/docs/swarms/utils/agent_loader.md @@ -1,4 +1,4 @@ -# AgentLoader - Load Agents from Markdown Files +# AgentLoader Documentation The `AgentLoader` is a powerful utility for creating Swarms agents from markdown files using the Claude Code sub-agent format. It supports both single and multiple markdown file loading, providing a flexible way to define and deploy agents using YAML frontmatter configuration.