diff --git a/README.md b/README.md index bf98c42e..916cc67f 100644 --- a/README.md +++ b/README.md @@ -609,7 +609,7 @@ You can now easily plug this custom Griptape agent into the **Swarms Framework** ### What is a Swarm? -A swarm, in the context of multi-agent systems, refers to a group of more than two agents working collaboratively to achieve a common goal. These agents can be software entities, such as llms that interact with each other to perform complex tasks. The concept of a swarm is inspired by natural systems like ant colonies or bird flocks, where simple individual behaviors lead to complex group dynamics and problem-solving capabilities. +A swarm refers to a group of more than two agents working collaboratively to achieve a common goal. These agents can be software entities, such as llms that interact with each other to perform complex tasks. The concept of a swarm is inspired by natural systems like ant colonies or bird flocks, where simple individual behaviors lead to complex group dynamics and problem-solving capabilities. ### How Swarm Architectures Facilitate Communication diff --git a/agents.yaml b/agents.yaml index 37a6d638..ade464a4 100644 --- a/agents.yaml +++ b/agents.yaml @@ -1,9 +1,9 @@ agents: - agent_name: "Delaware-C-Corp-Tax-Deduction-Agent" - model: - model_name: "gpt-4o-mini" - temperature: 0.1 - max_tokens: 2500 + # model: + # model_name: "gpt-4o-mini" + # temperature: 0.1 + # max_tokens: 2500 system_prompt: | You are a highly specialized financial analysis agent focused on Delaware C Corps tax deductions. Your task is to provide expert advice on optimizing tax strategies for Delaware C Corps, ensuring compliance with all relevant tax laws and regulations. You should be well-versed in Delaware state tax codes and federal tax laws affecting C Corps. Your responses should include detailed explanations of tax deductions available to Delaware C Corps, including but not limited to: - Research and Development (R&D) tax credits @@ -26,10 +26,10 @@ agents: task: "What are the most effective tax deduction strategies for a Delaware C Corp in the technology industry?" - agent_name: "Delaware-C-Corp-Tax-Optimization-Agent" - model: - model_name: "gpt-4o-mini" - temperature: 0.2 - max_tokens: 2000 + # model: + # model_name: "gpt-4o-mini" + # temperature: 0.2 + # max_tokens: 2000 system_prompt: | You are a highly specialized financial analysis agent focused on Delaware C Corps tax optimization. Your task is to provide expert advice on optimizing tax strategies for Delaware C Corps, ensuring compliance with all relevant tax laws and regulations. You should be well-versed in Delaware state tax codes and federal tax laws affecting C Corps. Your responses should include detailed explanations of tax optimization strategies available to Delaware C Corps, including but not limited to: - Entity structure optimization diff --git a/agents_multi_agent.yaml b/agents_multi_agent.yaml new file mode 100644 index 00000000..5d622ecf --- /dev/null +++ b/agents_multi_agent.yaml @@ -0,0 +1,53 @@ +agents: + - agent_name: "Delaware-C-Corp-Tax-Deduction-Agent" + system_prompt: | + You are a highly specialized financial analysis agent focused on Delaware C Corps tax deductions. Your task is to provide expert advice on optimizing tax strategies for Delaware C Corps, ensuring compliance with all relevant tax laws and regulations. You should be well-versed in Delaware state tax codes and federal tax laws affecting C Corps. Your responses should include detailed explanations of tax deductions available to Delaware C Corps, including but not limited to: + - Research and Development (R&D) tax credits + - Depreciation and amortization + - Business expense deductions + - Charitable contributions + - State-specific tax incentives + - Federal tax deductions applicable to C Corps + max_loops: 1 + autosave: true + dashboard: false + verbose: true + dynamic_temperature_enabled: true + saved_state_path: "delaware_c_corp_tax_deduction_agent.json" + user_name: "swarms_corp" + retry_attempts: 1 + context_length: 250000 + return_step_meta: false + output_type: "str" # Can be "json" or any other format + task: "What are the most effective tax deduction strategies for a Delaware C Corp in the technology industry?" + + - agent_name: "Delaware-C-Corp-Tax-Optimization-Agent" + system_prompt: | + You are a highly specialized financial analysis agent focused on Delaware C Corps tax optimization. Your task is to provide expert advice on optimizing tax strategies for Delaware C Corps, ensuring compliance with all relevant tax laws and regulations. You should be well-versed in Delaware state tax codes and federal tax laws affecting C Corps. Your responses should include detailed explanations of tax optimization strategies available to Delaware C Corps, including but not limited to: + - Entity structure optimization + - Income shifting strategies + - Loss utilization and carryovers + - Tax-efficient supply chain management + - State-specific tax planning + - Federal tax planning applicable to C Corps + max_loops: 2 + autosave: true + dashboard: false + verbose: true + dynamic_temperature_enabled: false + saved_state_path: "delaware_c_corp_tax_optimization_agent.json" + user_name: "tax_optimization_user" + retry_attempts: 3 + context_length: 200000 + return_step_meta: true + output_type: "str" + task: "How can a Delaware C Corp in the finance industry optimize its tax strategy for maximum savings?" + + + +swarm_architecture: + name: "MySwarm" + description: "A swarm for collaborative task solving" + max_loops: 5 + swarm_type: "ConcurrentWorkflow" + task: "How can we trademark concepts as a delaware C CORP for free" diff --git a/docs/swarms/agents/create_agents_yaml.md b/docs/swarms/agents/create_agents_yaml.md index ce124ca6..41327656 100644 --- a/docs/swarms/agents/create_agents_yaml.md +++ b/docs/swarms/agents/create_agents_yaml.md @@ -59,10 +59,10 @@ The function relies on a YAML file for defining agents and tasks. Below is an ex ```yaml agents: - agent_name: "Financial-Analysis-Agent" - model: - model_name: "gpt-4o-mini" - temperature: 0.1 - max_tokens: 2000 + # model: + # model_name: "gpt-4o-mini" + # temperature: 0.1 + # max_tokens: 2000 system_prompt: "Your full system prompt here" max_loops: 1 autosave: true @@ -78,10 +78,10 @@ agents: task: "How can I establish a ROTH IRA to buy stocks and get a tax break?" - agent_name: "Stock-Analysis-Agent" - model: - model_name: "gpt-4o-mini" - temperature: 0.2 - max_tokens: 1500 + # model: + # model_name: "gpt-4o-mini" + # temperature: 0.2 + # max_tokens: 1500 system_prompt: "Your full system prompt here" max_loops: 2 autosave: true @@ -112,44 +112,45 @@ agents: --- -# Example: Creating Agents and Running Tasks +### Full Code Example -### Example 1: Creating and Returning Agents ```python -from swarms import create_agents_from_yaml +import os -yaml_file = 'agents_config.yaml' -agents = create_agents_from_yaml(yaml_file, return_type="agents") +from dotenv import load_dotenv +from loguru import logger +from swarm_models import OpenAIChat # any model from swarm_models -for agent in agents: - print(f"Agent {agent.agent_name} created.") -``` +from swarms.agents.create_agents_from_yaml import ( + create_agents_from_yaml, +) -### Example 2: Creating Agents and Returning Task Results -```python -from swarms import create_agents_from_yaml +# Load environment variables +load_dotenv() -yaml_file = 'agents_config.yaml' -task_results = create_agents_from_yaml(yaml_file, return_type="tasks") +# Path to your YAML file +yaml_file = "agents.yaml" -for result in task_results: - print(f"Agent {result['agent_name']} executed task '{result['task']}': {result['output']}") -``` +# Get the OpenAI API key from the environment variable +api_key = os.getenv("OPENAI_API_KEY") -### Example 3: Returning Both Agents and Task Results -```python -from swarms import create_agents_from_yaml +# Create an instance of the OpenAIChat class +model = OpenAIChat( + openai_api_key=api_key, model_name="gpt-4o-mini", temperature=0.1 +) + + +try: + # Create agents and run tasks (using 'both' to return agents and task results) + task_results = create_agents_from_yaml( + model=model, yaml_file=yaml_file, return_type="tasks" + ) -yaml_file = 'agents_config.yaml' -agents, task_results = create_agents_from_yaml(yaml_file, return_type="both") + logger.info(f"Results from agents: {task_results}") +except Exception as e: + logger.error(f"An error occurred: {e}") -# Handling agents -for agent in agents: - print(f"Agent {agent.agent_name} created.") -# Handling task results -for result in task_results: - print(f"Agent {result['agent_name']} executed task '{result['task']}': {result['output']}") ``` --- diff --git a/docs/swarms/concept/swarm_architectures.md b/docs/swarms/concept/swarm_architectures.md index 8a6f1ca5..54b5d767 100644 --- a/docs/swarms/concept/swarm_architectures.md +++ b/docs/swarms/concept/swarm_architectures.md @@ -1,7 +1,7 @@ # Swarm Architectures ### What is a Swarm? -A swarm, in the context of multi-agent systems, refers to a group of more than two agents working collaboratively to achieve a common goal. These agents can be software entities, such as llms that interact with each other to perform complex tasks. The concept of a swarm is inspired by natural systems like ant colonies or bird flocks, where simple individual behaviors lead to complex group dynamics and problem-solving capabilities. +A swarm refers to a group of more than two agents working collaboratively to achieve a common goal. These agents can be software entities, such as llms that interact with each other to perform complex tasks. The concept of a swarm is inspired by natural systems like ant colonies or bird flocks, where simple individual behaviors lead to complex group dynamics and problem-solving capabilities. ### How Swarm Architectures Facilitate Communication diff --git a/docs/swarms/install/quickstart.md b/docs/swarms/install/quickstart.md index c5832ebf..ce71f5ed 100644 --- a/docs/swarms/install/quickstart.md +++ b/docs/swarms/install/quickstart.md @@ -133,26 +133,40 @@ Now, create the main Python script that will use the `create_agents_from_yaml` f ### `main.py`: ```python import os -from loguru import logger + from dotenv import load_dotenv -from swarms import create_agents_from_yaml +from loguru import logger +from swarm_models import OpenAIChat + +from swarms.agents.create_agents_from_yaml import ( + create_agents_from_yaml, +) # Load environment variables load_dotenv() # Path to your YAML file -yaml_file = 'agents_config.yaml' +yaml_file = "agents.yaml" + +# Get the OpenAI API key from the environment variable +api_key = os.getenv("OPENAI_API_KEY") + +# Create an instance of the OpenAIChat class +model = OpenAIChat( + openai_api_key=api_key, model_name="gpt-4o-mini", temperature=0.1 +) + try: # Create agents and run tasks (using 'both' to return agents and task results) - agents, task_results = create_agents_from_yaml(yaml_file, return_type="both") - - # Print the results of the tasks - for result in task_results: - print(f"Agent: {result['agent_name']} | Task: {result['task']} | Output: {result.get('output', 'Error encountered')}") + task_results = create_agents_from_yaml( + model=model, yaml_file=yaml_file, return_type="tasks" + ) + logger.info(f"Results from agents: {task_results}") except Exception as e: logger.error(f"An error occurred: {e}") + ``` ### Example Run: diff --git a/examples/agents/agents_from_yaml_example.py b/examples/agents/agents_from_yaml_example.py index 696267e6..154390b5 100644 --- a/examples/agents/agents_from_yaml_example.py +++ b/examples/agents/agents_from_yaml_example.py @@ -1,5 +1,9 @@ -from loguru import logger +import os + from dotenv import load_dotenv +from loguru import logger +from swarm_models import OpenAIChat + from swarms.agents.create_agents_from_yaml import ( create_agents_from_yaml, ) @@ -10,10 +14,19 @@ load_dotenv() # Path to your YAML file yaml_file = "agents.yaml" +# Get the OpenAI API key from the environment variable +api_key = os.getenv("OPENAI_API_KEY") + +# Create an instance of the OpenAIChat class +model = OpenAIChat( + openai_api_key=api_key, model_name="gpt-4o-mini", temperature=0.1 +) + + try: # Create agents and run tasks (using 'both' to return agents and task results) task_results = create_agents_from_yaml( - yaml_file, return_type="tasks" + model=model, yaml_file=yaml_file, return_type="tasks" ) logger.info(f"Results from agents: {task_results}") diff --git a/mm_agents_example.py b/mm_agents_example.py new file mode 100644 index 00000000..653bc4c6 --- /dev/null +++ b/mm_agents_example.py @@ -0,0 +1,37 @@ +import os + +from dotenv import load_dotenv +from loguru import logger +from swarm_models import OpenAIChat + +from swarms.agents.create_agents_from_yaml import ( + create_agents_from_yaml, +) + +# Load environment variables +load_dotenv() + +# Path to your YAML file +yaml_file = "agents_multi_agent.yaml" + + +# Get the OpenAI API key from the environment variable +api_key = os.getenv("GROQ_API_KEY") + +# Model +model = OpenAIChat( + openai_api_base="https://api.groq.com/openai/v1", + openai_api_key=api_key, + model_name="llama-3.1-70b-versatile", + temperature=0.1, +) + +try: + # Create agents and run tasks (using 'both' to return agents and task results) + task_results = create_agents_from_yaml( + model=model, yaml_file=yaml_file, return_type="run_swarm" + ) + + logger.info(f"Results from agents: {task_results}") +except Exception as e: + logger.error(f"An error occurred: {e}") diff --git a/pyproject.toml b/pyproject.toml index 36116427..bf739d10 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "swarms" -version = "5.8.4" +version = "5.8.5" description = "Swarms - Pytorch" license = "MIT" authors = ["Kye Gomez "] diff --git a/rearrange_example_blackstone.py b/rearrange_example_blackstone.py new file mode 100644 index 00000000..3ed207c2 --- /dev/null +++ b/rearrange_example_blackstone.py @@ -0,0 +1,205 @@ +import os +from swarms import Agent, AgentRearrange +from swarm_models import OpenAIChat + +# model = Anthropic(anthropic_api_key=os.getenv("ANTHROPIC_API_KEY")) +company = "TGSC" +# Get the OpenAI API key from the environment variable +api_key = os.getenv("GROQ_API_KEY") + +# Model +model = OpenAIChat( + openai_api_base="https://api.groq.com/openai/v1", + openai_api_key=api_key, + model_name="llama-3.1-70b-versatile", + temperature=0.1, +) + + +# Initialize the Managing Director agent +managing_director = Agent( + agent_name="Managing-Director", + system_prompt=f""" + As the Managing Director at Blackstone, your role is to oversee the entire investment analysis process for potential acquisitions. + Your responsibilities include: + 1. Setting the overall strategy and direction for the analysis + 2. Coordinating the efforts of the various team members and ensuring a comprehensive evaluation + 3. Reviewing the findings and recommendations from each team member + 4. Making the final decision on whether to proceed with the acquisition + + For the current potential acquisition of {company}, direct the tasks for the team to thoroughly analyze all aspects of the company, including its financials, industry position, technology, market potential, and regulatory compliance. Provide guidance and feedback as needed to ensure a rigorous and unbiased assessment. + """, + llm=model, + max_loops=1, + dashboard=False, + streaming_on=True, + verbose=True, + stopping_token="", + state_save_file_type="json", + saved_state_path="managing-director.json", +) + +# Initialize the Vice President of Finance +vp_finance = Agent( + agent_name="VP-Finance", + system_prompt=f""" + As the Vice President of Finance at Blackstone, your role is to lead the financial analysis of potential acquisitions. + For the current potential acquisition of {company}, your tasks include: + 1. Conducting a thorough review of {company}' financial statements, including income statements, balance sheets, and cash flow statements + 2. Analyzing key financial metrics such as revenue growth, profitability margins, liquidity ratios, and debt levels + 3. Assessing the company's historical financial performance and projecting future performance based on assumptions and market conditions + 4. Identifying any financial risks or red flags that could impact the acquisition decision + 5. Providing a detailed report on your findings and recommendations to the Managing Director + + Be sure to consider factors such as the sustainability of {company}' business model, the strength of its customer base, and its ability to generate consistent cash flows. Your analysis should be data-driven, objective, and aligned with Blackstone's investment criteria. + """, + llm=model, + max_loops=1, + dashboard=False, + streaming_on=True, + verbose=True, + stopping_token="", + state_save_file_type="json", + saved_state_path="vp-finance.json", +) + +# Initialize the Industry Analyst +industry_analyst = Agent( + agent_name="Industry-Analyst", + system_prompt=f""" + As the Industry Analyst at Blackstone, your role is to provide in-depth research and analysis on the industries and markets relevant to potential acquisitions. + For the current potential acquisition of {company}, your tasks include: + 1. Conducting a comprehensive analysis of the industrial robotics and automation solutions industry, including market size, growth rates, key trends, and future prospects + 2. Identifying the major players in the industry and assessing their market share, competitive strengths and weaknesses, and strategic positioning + 3. Evaluating {company}' competitive position within the industry, including its market share, differentiation, and competitive advantages + 4. Analyzing the key drivers and restraints for the industry, such as technological advancements, labor costs, regulatory changes, and economic conditions + 5. Identifying potential risks and opportunities for {company} based on the industry analysis, such as disruptive technologies, emerging markets, or shifts in customer preferences + + Your analysis should provide a clear and objective assessment of the attractiveness and future potential of the industrial robotics industry, as well as {company}' positioning within it. Consider both short-term and long-term factors, and provide evidence-based insights to inform the investment decision. + """, + llm=model, + max_loops=1, + dashboard=False, + streaming_on=True, + verbose=True, + stopping_token="", + state_save_file_type="json", + saved_state_path="industry-analyst.json", +) + +# Initialize the Technology Expert +tech_expert = Agent( + agent_name="Tech-Expert", + system_prompt=f""" + As the Technology Expert at Blackstone, your role is to assess the technological capabilities, competitive advantages, and potential risks of companies being considered for acquisition. + For the current potential acquisition of {company}, your tasks include: + 1. Conducting a deep dive into {company}' proprietary technologies, including its robotics platforms, automation software, and AI capabilities + 2. Assessing the uniqueness, scalability, and defensibility of {company}' technology stack and intellectual property + 3. Comparing {company}' technologies to those of its competitors and identifying any key differentiators or technology gaps + 4. Evaluating {company}' research and development capabilities, including its innovation pipeline, engineering talent, and R&D investments + 5. Identifying any potential technology risks or disruptive threats that could impact {company}' long-term competitiveness, such as emerging technologies or expiring patents + + Your analysis should provide a comprehensive assessment of {company}' technological strengths and weaknesses, as well as the sustainability of its competitive advantages. Consider both the current state of its technology and its future potential in light of industry trends and advancements. + """, + llm=model, + max_loops=1, + dashboard=False, + streaming_on=True, + verbose=True, + stopping_token="", + state_save_file_type="json", + saved_state_path="tech-expert.json", +) + +# Initialize the Market Researcher +market_researcher = Agent( + agent_name="Market-Researcher", + system_prompt=f""" + As the Market Researcher at Blackstone, your role is to analyze the target company's customer base, market share, and growth potential to assess the commercial viability and attractiveness of the potential acquisition. + For the current potential acquisition of {company}, your tasks include: + 1. Analyzing {company}' current customer base, including customer segmentation, concentration risk, and retention rates + 2. Assessing {company}' market share within its target markets and identifying key factors driving its market position + 3. Conducting a detailed market sizing and segmentation analysis for the industrial robotics and automation markets, including identifying high-growth segments and emerging opportunities + 4. Evaluating the demand drivers and sales cycles for {company}' products and services, and identifying any potential risks or limitations to adoption + 5. Developing financial projections and estimates for {company}' revenue growth potential based on the market analysis and assumptions around market share and penetration + + Your analysis should provide a data-driven assessment of the market opportunity for {company} and the feasibility of achieving our investment return targets. Consider both bottom-up and top-down market perspectives, and identify any key sensitivities or assumptions in your projections. + """, + llm=model, + max_loops=1, + dashboard=False, + streaming_on=True, + verbose=True, + stopping_token="", + state_save_file_type="json", + saved_state_path="market-researcher.json", +) + +# Initialize the Regulatory Specialist +regulatory_specialist = Agent( + agent_name="Regulatory-Specialist", + system_prompt=f""" + As the Regulatory Specialist at Blackstone, your role is to identify and assess any regulatory risks, compliance requirements, and potential legal liabilities associated with potential acquisitions. + For the current potential acquisition of {company}, your tasks include: + 1. Identifying all relevant regulatory bodies and laws that govern the operations of {company}, including industry-specific regulations, labor laws, and environmental regulations + 2. Reviewing {company}' current compliance policies, procedures, and track record to identify any potential gaps or areas of non-compliance + 3. Assessing the potential impact of any pending or proposed changes to relevant regulations that could affect {company}' business or create additional compliance burdens + 4. Evaluating the potential legal liabilities and risks associated with {company}' products, services, and operations, including product liability, intellectual property, and customer contracts + 5. Providing recommendations on any regulatory or legal due diligence steps that should be taken as part of the acquisition process, as well as any post-acquisition integration considerations + + Your analysis should provide a comprehensive assessment of the regulatory and legal landscape surrounding {company}, and identify any material risks or potential deal-breakers. Consider both the current state and future outlook, and provide practical recommendations to mitigate identified risks. + """, + llm=model, + max_loops=1, + dashboard=False, + streaming_on=True, + verbose=True, + stopping_token="", + state_save_file_type="json", + saved_state_path="regulatory-specialist.json", +) + +# Create a list of agents +agents = [ + managing_director, + vp_finance, + industry_analyst, + tech_expert, + market_researcher, + regulatory_specialist, +] + +# Define multiple flow patterns +flows = [ + "Industry-Analyst -> Tech-Expert -> Market-Researcher -> Regulatory-Specialist -> Managing-Director -> VP-Finance", + "Managing-Director -> VP-Finance -> Industry-Analyst -> Tech-Expert -> Market-Researcher -> Regulatory-Specialist", + "Tech-Expert -> Market-Researcher -> Regulatory-Specialist -> Industry-Analyst -> Managing-Director -> VP-Finance", +] + +# Create instances of AgentRearrange for each flow pattern +blackstone_acquisition_analysis = AgentRearrange( + name="Blackstone-Acquisition-Analysis", + description="A system for analyzing potential acquisitions", + agents=agents, + flow=flows[0], +) + +blackstone_investment_strategy = AgentRearrange( + name="Blackstone-Investment-Strategy", + description="A system for evaluating investment opportunities", + agents=agents, + flow=flows[1], +) + +blackstone_market_analysis = AgentRearrange( + name="Blackstone-Market-Analysis", + description="A system for analyzing market trends and opportunities", + agents=agents, + flow=flows[2], +) + +# Example of running each system +# output = blackstone_acquisition_analysis.run( +# f"Analyze the potential acquisition of {company}, a leading manufacturer of industrial robots and automation solutions." +# ) +# print(output) diff --git a/swarm_arange.py b/swarm_arange.py new file mode 100644 index 00000000..87b8fc22 --- /dev/null +++ b/swarm_arange.py @@ -0,0 +1,363 @@ +import threading +import time +import uuid +from typing import Any, Callable, Dict, List, Optional + +from swarms.utils.loguru_logger import logger + + +def swarm_id(): + return uuid.uuid4().hex + + +class SwarmArrangeInput: + id: str = uuid.uuid4().hex + time_stamp: str = time.strftime("%Y-%m-%d %H:%M:%S") + name: str + description: str + swarms: List[Callable] = [] + output_type: str + flow: str = "" + + +class SwarmArrangeOutput: + input_config: SwarmArrangeInput = None + + +class SwarmRearrange: + """ + A class representing a swarm of swarms for rearranging tasks. + + Attributes: + swarms (dict): A dictionary of swarms, where the key is the swarm's name and the value is the swarm object. + flow (str): The flow pattern of the tasks. + + Methods: + __init__(swarms: List[swarm] = None, flow: str = None): Initializes the SwarmRearrange object. + add_swarm(swarm: swarm): Adds an swarm to the swarm. + remove_swarm(swarm_name: str): Removes an swarm from the swarm. + add_swarms(swarms: List[swarm]): Adds multiple swarms to the swarm. + validate_flow(): Validates the flow pattern. + run(task): Runs the swarm to rearrange the tasks. + """ + + def __init__( + self, + id: str = swarm_id(), + name: str = "SwarmRearrange", + description: str = "A swarm of swarms for rearranging tasks.", + swarms: List[Any] = [], + flow: str = None, + max_loops: int = 1, + verbose: bool = True, + human_in_the_loop: bool = False, + custom_human_in_the_loop: Optional[ + Callable[[str], str] + ] = None, + return_json: bool = False, + *args, + **kwargs, + ): + """ + Initializes the SwarmRearrange object. + + Args: + swarms (List[swarm], optional): A list of swarm objects. Defaults to None. + flow (str, optional): The flow pattern of the tasks. Defaults to None. + """ + self.id = id + self.name = name + self.description = description + self.swarms = {swarm.name: swarm for swarm in swarms} + self.flow = flow if flow is not None else "" + self.verbose = verbose + self.max_loops = max_loops if max_loops > 0 else 1 + self.human_in_the_loop = human_in_the_loop + self.custom_human_in_the_loop = custom_human_in_the_loop + self.return_json = return_json + self.swarm_history = {swarm.name: [] for swarm in swarms} + self.lock = threading.Lock() + self.id = uuid.uuid4().hex if id is None else id + + # Run the relianility checks + self.reliability_checks() + + # # Output schema + # self.input_config = SwarmRearrangeInput( + # swarm_id=self.id, + # name=self.name, + # description=self.description, + # flow=self.flow, + # max_loops=self.max_loops, + # ) + + # # Output schema + # self.output_schema = SwarmRearrangeOutput( + # Input=self.input_config, + # outputs=[], + # ) + + def reliability_checks(self): + logger.info("Running reliability checks.") + if self.swarms is None: + raise ValueError("No swarms found in the swarm.") + + if self.flow is None: + raise ValueError("No flow found in the swarm.") + + if self.max_loops is None: + raise ValueError("No max_loops found in the swarm.") + + logger.info( + "SwarmRearrange initialized with swarms: {}".format( + list(self.swarms.keys()) + ) + ) + + # Verbose is True + if self.verbose is True: + logger.add("swarm_rearrange.log") + + def set_custom_flow(self, flow: str): + self.flow = flow + logger.info(f"Custom flow set: {flow}") + + def add_swarm(self, swarm: Any): + """ + Adds an swarm to the swarm. + + Args: + swarm (swarm): The swarm to be added. + """ + logger.info(f"Adding swarm {swarm.name} to the swarm.") + self.swarms[swarm.name] = swarm + + def track_history( + self, + swarm_name: str, + result: str, + ): + self.swarm_history[swarm_name].append(result) + + def remove_swarm(self, swarm_name: str): + """ + Removes an swarm from the swarm. + + Args: + swarm_name (str): The name of the swarm to be removed. + """ + del self.swarms[swarm_name] + + def add_swarms(self, swarms: List[Any]): + """ + Adds multiple swarms to the swarm. + + Args: + swarms (List[swarm]): A list of swarm objects. + """ + for swarm in swarms: + self.swarms[swarm.name] = swarm + + def validate_flow(self): + """ + Validates the flow pattern. + + Raises: + ValueError: If the flow pattern is incorrectly formatted or contains duplicate swarm names. + + Returns: + bool: True if the flow pattern is valid. + """ + if "->" not in self.flow: + raise ValueError( + "Flow must include '->' to denote the direction of the task." + ) + + swarms_in_flow = [] + + # Arrow + tasks = self.flow.split("->") + + # For the task in tasks + for task in tasks: + swarm_names = [name.strip() for name in task.split(",")] + + # Loop over the swarm names + for swarm_name in swarm_names: + if ( + swarm_name not in self.swarms + and swarm_name != "H" + ): + raise ValueError( + f"swarm '{swarm_name}' is not registered." + ) + swarms_in_flow.append(swarm_name) + + # If the length of the swarms does not equal the length of the swarms in flow + if len(set(swarms_in_flow)) != len(swarms_in_flow): + raise ValueError( + "Duplicate swarm names in the flow are not allowed." + ) + + print("Flow is valid.") + return True + + def run( + self, + task: str = None, + img: str = None, + custom_tasks: Optional[Dict[str, str]] = None, + *args, + **kwargs, + ): + """ + Runs the swarm to rearrange the tasks. + + Args: + task: The initial task to be processed. + img: An optional image input. + custom_tasks: A dictionary of custom tasks for specific swarms. + + Returns: + str: The final processed task. + """ + try: + if not self.validate_flow(): + return "Invalid flow configuration." + + tasks = self.flow.split("->") + current_task = task + + # Check if custom_tasks is a dictionary and not empty + if isinstance(custom_tasks, dict) and custom_tasks: + c_swarm_name, c_task = next( + iter(custom_tasks.items()) + ) + + # Find the position of the custom swarm in the tasks list + if c_swarm_name in tasks: + position = tasks.index(c_swarm_name) + + # If there is a previous swarm, merge its task with the custom tasks + if position > 0: + tasks[position - 1] += "->" + c_task + else: + # If there is no previous swarm, just insert the custom tasks + tasks.insert(position, c_task) + + # Set the loop counter + loop_count = 0 + while loop_count < self.max_loops: + for task in tasks: + swarm_names = [ + name.strip() for name in task.split(",") + ] + if len(swarm_names) > 1: + # Parallel processing + logger.info( + f"Running swarms in parallel: {swarm_names}" + ) + results = [] + for swarm_name in swarm_names: + if swarm_name == "H": + # Human in the loop intervention + if ( + self.human_in_the_loop + and self.custom_human_in_the_loop + ): + current_task = ( + self.custom_human_in_the_loop( + current_task + ) + ) + else: + current_task = input( + "Enter your response: " + ) + else: + swarm = self.swarms[swarm_name] + result = swarm.run( + current_task, img, *args, **kwargs + ) + logger.info( + f"Swarm {swarm_name} returned result of type: {type(result)}" + ) + if isinstance(result, bool): + logger.warning( + f"Swarm {swarm_name} returned a boolean value: {result}" + ) + result = str( + result + ) # Convert boolean to string + results.append(result) + + current_task = "; ".join( + str(r) for r in results if r is not None + ) + else: + # Sequential processing + logger.info( + f"Running swarms sequentially: {swarm_names}" + ) + swarm_name = swarm_names[0] + if swarm_name == "H": + # Human-in-the-loop intervention + if ( + self.human_in_the_loop + and self.custom_human_in_the_loop + ): + current_task = ( + self.custom_human_in_the_loop( + current_task + ) + ) + else: + current_task = input( + "Enter the next task: " + ) + else: + swarm = self.swarms[swarm_name] + result = swarm.run( + current_task, img, *args, **kwargs + ) + logger.info( + f"Swarm {swarm_name} returned result of type: {type(result)}" + ) + if isinstance(result, bool): + logger.warning( + f"Swarm {swarm_name} returned a boolean value: {result}" + ) + result = str( + result + ) # Convert boolean to string + current_task = ( + result + if result is not None + else current_task + ) + loop_count += 1 + + return current_task + + except Exception as e: + logger.error(f"An error occurred: {e}") + return str(e) + + +def swarm_arrange( + name: str = "SwarmArrange-01", + description: str = "Combine multiple swarms and execute them sequentially", + swarms: List[Callable] = None, + output_type: str = "json", + flow: str = None, + task: str = None, + *args, + **kwargs, +): + return SwarmRearrange( + name, + description, + swarms, + output_type, + flow, + ).run(task, *args, **kwargs) diff --git a/swarm_arange_demo.py b/swarm_arange_demo.py new file mode 100644 index 00000000..94b72bc8 --- /dev/null +++ b/swarm_arange_demo.py @@ -0,0 +1,21 @@ +from swarm_arange import SwarmRearrange +from rearrange_example_blackstone import ( + blackstone_acquisition_analysis, + blackstone_investment_strategy, + blackstone_market_analysis, +) + +swarm_arrange = SwarmRearrange( + swarms=[ + blackstone_acquisition_analysis, + blackstone_investment_strategy, + blackstone_market_analysis, + ], + flow=f"{blackstone_acquisition_analysis.name} -> {blackstone_investment_strategy.name} -> {blackstone_market_analysis.name}", +) + +print( + swarm_arrange.run( + "Analyze swarms, 150k revenue with 45m+ agents build, with 1.4m downloads since march 2024" + ) +) diff --git a/swarm_router.py b/swarm_router_doc_summarizer_agent.py similarity index 100% rename from swarm_router.py rename to swarm_router_doc_summarizer_agent.py diff --git a/swarms/agents/create_agents_from_yaml.py b/swarms/agents/create_agents_from_yaml.py index b4db8bbf..679d1550 100644 --- a/swarms/agents/create_agents_from_yaml.py +++ b/swarms/agents/create_agents_from_yaml.py @@ -1,46 +1,65 @@ import os - import yaml from dotenv import load_dotenv from loguru import logger -from swarm_models import OpenAIChat - +from typing import Callable, List, Union, Tuple, Dict, Any from swarms.structs.agent import Agent +from swarms.structs.swarm_router import SwarmRouter load_dotenv() -# Function to create and optionally run agents from a YAML file def create_agents_from_yaml( - yaml_file: str, return_type: str = "agents", *args, **kwargs -): + model: Callable = None, + yaml_file: str = "agents.yaml", + return_type: str = "auto", + *args, + **kwargs, +) -> Union[ + SwarmRouter, + Agent, + List[Agent], + Tuple[Union[SwarmRouter, Agent], List[Agent]], + List[Dict[str, Any]], +]: """ - Create agents based on configurations defined in a YAML file. - If a 'task' is provided in the YAML, the agent will execute the task after creation. + Create agents and/or SwarmRouter based on configurations defined in a YAML file. + + This function dynamically creates agents and a SwarmRouter (if specified) based on the + configuration in the YAML file. It adapts its behavior based on the presence of a + swarm architecture and the number of agents defined. Args: - yaml_file (str): Path to the YAML file containing agent configurations. - return_type (str): Determines the return value. "agents" to return agent list, - "tasks" to return task results, "both" to return both agents and tasks. - *args: Additional positional arguments for agent customization. - **kwargs: Additional keyword arguments for agent customization. + model (Callable): The language model to be used by the agents. + yaml_file (str): Path to the YAML file containing agent and swarm configurations. + return_type (str): Determines the return value. Options are: + "auto" (default): Automatically determine the most appropriate return type. + "swarm": Return SwarmRouter if present, otherwise a single agent or list of agents. + "agents": Return a list of agents (or a single agent if only one is defined). + "both": Return both SwarmRouter (or single agent) and list of agents. + "tasks": Return task results if any tasks were executed. + "run_swarm": Run the swarm and return its output. + *args: Additional positional arguments for agent or SwarmRouter customization. + **kwargs: Additional keyword arguments for agent or SwarmRouter customization. Returns: - List[Agent] or List[Task Results] or Tuple(List[Agent], List[Task Results]) + Union[SwarmRouter, Agent, List[Agent], Tuple[Union[SwarmRouter, Agent], List[Agent]], List[Dict[str, Any]]]: + The return type depends on the 'return_type' argument and the configuration in the YAML file. + + Raises: + FileNotFoundError: If the specified YAML file is not found. + ValueError: If the YAML configuration is invalid or if an invalid return_type is specified. """ logger.info(f"Checking if the YAML file {yaml_file} exists...") - # Check if the YAML file exists if not os.path.exists(yaml_file): logger.error(f"YAML file {yaml_file} not found.") raise FileNotFoundError(f"YAML file {yaml_file} not found.") - # Load the YAML configuration logger.info(f"Loading YAML file {yaml_file}") with open(yaml_file, "r") as file: config = yaml.safe_load(file) - # Ensure agents key exists if "agents" not in config: logger.error( "The YAML configuration does not contain 'agents'." @@ -49,39 +68,13 @@ def create_agents_from_yaml( "The YAML configuration does not contain 'agents'." ) - # List to store created agents and task results agents = [] task_results = [] - # Iterate over each agent configuration and create agents + # Create agents for agent_config in config["agents"]: logger.info(f"Creating agent: {agent_config['agent_name']}") - # # Get the OpenAI API key from environment or YAML config - # api_key = ( - # os.getenv("OPENAI_API_KEY") - # ) - - # Create an instance of OpenAIChat model - # model = OpenAIChat( - # openai_api_key=api_key, - # model_name=agent_config["model"]["model_name"], - # temperature=agent_config["model"]["temperature"], - # max_tokens=agent_config["model"]["max_tokens"], - # *args, - # **kwargs, # Pass any additional arguments to the model - # ) - # Model - api_key = os.getenv("GROQ_API_KEY") - - model = OpenAIChat( - openai_api_base="https://api.groq.com/openai/v1", - openai_api_key=api_key, - model_name="llama-3.1-70b-versatile", - temperature=0.1, - ) - - # Ensure the system prompt is provided if "system_prompt" not in agent_config: logger.error( f"System prompt is missing for agent: {agent_config['agent_name']}" @@ -90,7 +83,6 @@ def create_agents_from_yaml( f"System prompt is missing for agent: {agent_config['agent_name']}" ) - # Initialize the agent using the configuration agent = Agent( agent_name=agent_config["agent_name"], system_prompt=agent_config["system_prompt"], @@ -111,7 +103,7 @@ def create_agents_from_yaml( ), output_type=agent_config.get("output_type", "str"), *args, - **kwargs, # Pass any additional arguments to the agent + **kwargs, ) logger.info( @@ -119,43 +111,85 @@ def create_agents_from_yaml( ) agents.append(agent) - # Check if a task is provided, and if so, run the agent - task = agent_config.get("task") - if task: - logger.info( - f"Running task '{task}' with agent {agent_config['agent_name']}" - ) + # Create SwarmRouter if swarm_architecture is present + swarm_router = None + if "swarm_architecture" in config: + swarm_config = config["swarm_architecture"] + swarm_router = SwarmRouter( + name=swarm_config["name"], + description=swarm_config["description"], + max_loops=swarm_config["max_loops"], + agents=agents, + swarm_type=swarm_config["swarm_type"], + task=swarm_config.get("task"), + *args, + **kwargs, + ) + logger.info( + f"SwarmRouter '{swarm_config['name']}' created successfully." + ) + + # Define function to run SwarmRouter + def run_swarm_router( + task: str = ( + swarm_config.get("task") + if "swarm_architecture" in config + else None + ), + ): + if swarm_router: try: - output = agent.run(task) + output = swarm_router.run(task) + print(output) logger.info( - f"Output for agent {agent_config['agent_name']}: {output}" - ) - task_results.append( - { - "agent_name": agent_config["agent_name"], - "task": task, - "output": output, - } + f"Output for SwarmRouter '{swarm_config['name']}': {output}" ) + return output except Exception as e: logger.error( - f"Error running task for agent {agent_config['agent_name']}: {e}" - ) - task_results.append( - { - "agent_name": agent_config["agent_name"], - "task": task, - "error": str(e), - } + f"Error running task for SwarmRouter '{swarm_config['name']}': {e}" ) - - # Return results based on the `return_type` - if return_type == "agents": - return agents + raise e + else: + logger.error("SwarmRouter not created.") + raise ValueError("SwarmRouter not created.") + + # Handle return types + if return_type == "auto": + if swarm_router: + return swarm_router + elif len(agents) == 1: + return agents[0] + else: + return agents + elif return_type == "swarm": + return ( + swarm_router + if swarm_router + else (agents[0] if len(agents) == 1 else agents) + ) + elif return_type == "agents": + return agents[0] if len(agents) == 1 else agents + elif return_type == "both": + return ( + swarm_router + if swarm_router + else agents[0] if len(agents) == 1 else agents + ), agents elif return_type == "tasks": + if not task_results: + logger.warning( + "No tasks were executed. Returning empty list." + ) return task_results - elif return_type == "both": - return agents, task_results + elif return_type == "run_swarm": + if swarm_router: + return run_swarm_router() + else: + logger.error("Cannot run swarm: SwarmRouter not created.") + raise ValueError( + "Cannot run swarm: SwarmRouter not created." + ) else: logger.error(f"Invalid return_type: {return_type}") raise ValueError(f"Invalid return_type: {return_type}") diff --git a/swarms/prompts/prompt.py b/swarms/prompts/prompt.py index 62fa3e22..da1a0324 100644 --- a/swarms/prompts/prompt.py +++ b/swarms/prompts/prompt.py @@ -164,7 +164,7 @@ class Prompt(BaseModel): Returns: str: The current prompt content. """ - logger.debug(f"Returning prompt {self.id} as a string.") + # logger.debug(f"Returning prompt {self.id} as a string.") self.log_telemetry() return self.content diff --git a/swarms/structs/agent.py b/swarms/structs/agent.py index f5cea2cd..be02c704 100644 --- a/swarms/structs/agent.py +++ b/swarms/structs/agent.py @@ -523,15 +523,6 @@ class Agent: # Telemetry Processor to log agent data threading.Thread(target=self.log_agent_data).start() - if load_yaml_path is not None: - from swarms.agents.create_agents_from_yaml import ( - create_agents_from_yaml, - ) - - create_agents_from_yaml( - load_yaml_path, return_type="tasks" - ) - def set_system_prompt(self, system_prompt: str): """Set the system prompt""" self.system_prompt = system_prompt diff --git a/swarms/structs/swarm_arrange.py b/swarms/structs/swarm_arrange.py new file mode 100644 index 00000000..66559a61 --- /dev/null +++ b/swarms/structs/swarm_arrange.py @@ -0,0 +1,336 @@ +from typing import Callable, List, Dict, Any +from swarms.structs.base_swarm import BaseSwarm +from loguru import logger + +import time +import uuid + + +class SwarmArrangeInput: + id: str = uuid.uuid4().hex + time_stamp: str = time.strftime("%Y-%m-%d %H:%M:%S") + name: str + description: str + swarms: List[Callable] = [] + output_type: str + flow: str = "" + + +class SwarmArrangeOutput: + input_config: SwarmArrangeInput = None + + +class SwarmArrange: + """ + A class for arranging and executing multiple swarms sequentially. + + Attributes: + name (str): The name of the SwarmArrange instance. + description (str): A description of the SwarmArrange instance. + swarms (List[Callable]): A list of swarms to be arranged and executed. + output_type (str): The type of output expected from the SwarmArrange instance. + flow (str): The flow pattern of the swarms to be executed. + """ + + def __init__( + self, + name: str = "SwarmArrange-01", + description: str = "Combine multiple swarms and execute them sequentially", + swarms: List[Any] = [], + output_type: str = "json", + flow: str = None, + ): + """ + Initializes the SwarmArrange instance. + + Args: + name (str, optional): The name of the SwarmArrange instance. Defaults to "SwarmArrange-01". + description (str, optional): A description of the SwarmArrange instance. Defaults to "Combine multiple swarms and execute them sequentially". + swarms (List[Callable], optional): A list of swarms to be arranged and executed. Defaults to None. + output_type (str, optional): The type of output expected from the SwarmArrange instance. Defaults to "json". + flow (str, optional): The flow pattern of the swarms to be executed. Defaults to None. + + Raises: + ValueError: If the name or description is None. + """ + if not name: + raise ValueError("Name cannot be None") + if not description: + raise ValueError("Description cannot be None") + self.name = name + self.description = description + self.swarms = swarms + self.output_type = output_type + self.flow = flow + + self.reliability_check() + + # self.log = SwarmArrangeInput( + # name=name, + # description=description, + # swarms=swarms, + # output_type=output_type, + # flow=flow, + # ) + + def reliability_check(self): + """ + Performs a reliability check on the SwarmArrange instance. + + This method checks if the swarms provided are valid and logs the results. + """ + logger.info( + f"Initializing the SwarmArrange with name: {self.name} and description: {self.description}" + ) + + if self.swarms is None: + logger.warning( + "No swarms detected. Please input a callable with a .run(task: str) method for reliable operation." + ) + else: + logger.info( + "SwarmArrange initialized with swarms. Proceeding with reliability check." + ) + + # Additional logging for reliability check + logger.info( + "Checking if all swarms are callable or instances of BaseSwarm." + ) + for swarm in self.swarms: + if not callable(swarm) and not isinstance( + swarm, BaseSwarm + ): + logger.error( + f"Swarm {swarm} is not a callable or an instance of BaseSwarm. This may cause reliability issues." + ) + return False + logger.info("All swarms are valid. SwarmArrange is reliable.") + return True + + def set_custom_flow(self, flow: str): + """ + Sets a custom flow pattern for the SwarmArrange instance. + + Args: + flow (str): The custom flow pattern to be set. + """ + self.flow = flow + logger.info(f"Custom flow set: {flow}") + + def add_swarm(self, swarm: Callable): + """ + Adds an swarm to the SwarmArrange instance. + + Args: + swarm (swarm): The swarm to be added. + """ + logger.info(f"Adding swarm {swarm.name} to the swarm.") + self.swarms[swarm.name] = swarm + + def track_history( + self, + swarm_name: str, + result: str, + ): + """ + Tracks the history of a swarm's execution. + + Args: + swarm_name (str): The name of the swarm. + result (str): The result of the swarm's execution. + """ + self.swarm_history[swarm_name].append(result) + + def remove_swarm(self, swarm_name: str): + """ + Removes an swarm from the SwarmArrange instance. + + Args: + swarm_name (str): The name of the swarm to be removed. + """ + del self.swarms[swarm_name] + + def add_swarms(self, swarms: List[Callable]): + """ + Adds multiple swarms to the SwarmArrange instance. + + Args: + swarms (List[swarm]): A list of swarm objects. + """ + self.swarms.extend(swarms) + + def validate_flow(self): + """ + Validates the flow pattern of the SwarmArrange instance. + + Raises: + ValueError: If the flow pattern is incorrectly formatted or contains duplicate swarm names. + + Returns: + bool: True if the flow pattern is valid. + """ + if "->" not in self.flow: + raise ValueError( + "Flow must include '->' to denote the direction of the task." + ) + + swarms_in_flow = [] + + # Split the flow into tasks + tasks = self.flow.split("->") + + # For each task in the tasks + for task in tasks: + swarm_names = [name.strip() for name in task.split(",")] + + # Loop over the swarm names + for swarm_name in swarm_names: + if ( + swarm_name not in self.swarms + and swarm_name != "H" + ): + raise ValueError( + f"swarm '{swarm_name}' is not registered." + ) + swarms_in_flow.append(swarm_name) + + # Check for duplicate swarm names in the flow + if len(set(swarms_in_flow)) != len(swarms_in_flow): + raise ValueError( + "Duplicate swarm names in the flow are not allowed." + ) + + logger.info("Flow is valid.") + return True + + def run( + self, + task: str = None, + img: str = None, + custom_tasks: Dict[str, str] = None, + *args, + **kwargs, + ): + """ + Runs the SwarmArrange instance to rearrange and execute the swarms. + + Args: + task (str, optional): The initial task to be processed. Defaults to None. + img (str, optional): The image to be processed. Defaults to None. + custom_tasks (Dict[str, str], optional): Custom tasks to be executed. Defaults to None. + + Returns: + str: The final processed task. + """ + try: + if not self.validate_flow(): + return "Invalid flow configuration." + + tasks = self.flow.split("->") + current_task = task + + # If custom_tasks have the swarms name and tasks then combine them + if custom_tasks is not None: + c_swarm_name, c_task = next( + iter(custom_tasks.items()) + ) + + # Find the position of the custom swarm in the tasks list + position = tasks.index(c_swarm_name) + + # If there is a previous swarm merge its task with the custom tasks + if position > 0: + tasks[position - 1] += "->" + c_task + else: + # If there is no previous swarm just insert the custom tasks + tasks.insert(position, c_task) + + # Set the loop counter + loop_count = 0 + while loop_count < self.max_loops: + for task in tasks: + is_last = task == tasks[-1] + swarm_names = [ + name.strip() for name in task.split(",") + ] + if len(swarm_names) > 1: + # Parallel processing + logger.info( + f"Running swarms in parallel: {swarm_names}" + ) + results = [] + for swarm_name in swarm_names: + if swarm_name == "H": + # Human in the loop intervention + if ( + self.human_in_the_loop + and self.custom_human_in_the_loop + ): + current_task = ( + self.custom_human_in_the_loop( + current_task + ) + ) + else: + current_task = input( + "Enter your response:" + ) + else: + swarm = self.swarms[swarm_name] + result = swarm.run( + current_task, + # img, + # is_last, + *args, + **kwargs, + ) + results.append(result) + self.output_schema.outputs.append( + swarm.swarm_output + ) + + current_task = "; ".join(results) + else: + # Sequential processing + logger.info( + f"Running swarms sequentially: {swarm_names}" + ) + swarm_name = swarm_names[0] + if swarm_name == "H": + # Human-in-the-loop intervention + if ( + self.human_in_the_loop + and self.custom_human_in_the_loop + ): + current_task = ( + self.custom_human_in_the_loop( + current_task + ) + ) + else: + current_task = input( + "Enter the next task: " + ) + else: + swarm = self.swarms[swarm_name] + current_task = swarm.run( + current_task, + # img, + # is_last, + *args, + **kwargs, + ) + self.output_schema.outputs.append( + swarm.swarm_output + ) + loop_count += 1 + + # return current_task + if self.return_json: + return self.output_schema.model_dump_json(indent=4) + else: + return current_task + + except Exception as e: + logger.error(f"An error occurred: {e}") + return e