diff --git a/new_features_examples/rearrange_test.py b/rearrange_test.py similarity index 98% rename from new_features_examples/rearrange_test.py rename to rearrange_test.py index ddfd7670..d85e435a 100644 --- a/new_features_examples/rearrange_test.py +++ b/rearrange_test.py @@ -95,12 +95,14 @@ flow = "BossAgent -> ExpenseAnalyzer -> SummaryGenerator" # Using AgentRearrange class to manage the swarm agent_system = AgentRearrange( + name="pe-swarm", + description="ss", agents=agents, flow=flow, return_json=False, output_type="final", max_loops=1, - docs=["SECURITY.md"], + # docs=["SECURITY.md"], ) # Input task for the swarm diff --git a/swarms/structs/agents_available.py b/swarms/structs/agents_available.py index f676877d..5651f9b0 100644 --- a/swarms/structs/agents_available.py +++ b/swarms/structs/agents_available.py @@ -1,109 +1,87 @@ -import os -from typing import List, Any from swarms.structs.agent import Agent -from loguru import logger -import uuid - -WORKSPACE_DIR = os.getenv("WORKSPACE_DIR") -uuid_for_log = str(uuid.uuid4()) -logger.add( - os.path.join( - WORKSPACE_DIR, - "agents_available", - f"agents-available-{uuid_for_log}.log", - ), - level="INFO", - colorize=True, - backtrace=True, - diagnose=True, -) - - -def get_agent_name(agent: Any) -> str: - """Helper function to safely get agent name - - Args: - agent (Any): The agent object to get name from - - Returns: - str: The agent's name if found, 'Unknown' otherwise - """ - if isinstance(agent, Agent) and hasattr(agent, "agent_name"): - return agent.agent_name - return "Unknown" - - -def get_agent_description(agent: Any) -> str: - """Helper function to get agent description or system prompt preview - - Args: - agent (Any): The agent object - - Returns: - str: Description or first 100 chars of system prompt - """ - if not isinstance(agent, Agent): - return "N/A" - - if hasattr(agent, "description") and agent.description: - return agent.description - - if hasattr(agent, "system_prompt") and agent.system_prompt: - return f"{agent.system_prompt[:150]}..." - - return "N/A" +from typing import List def showcase_available_agents( + agents: List[Agent], name: str = None, description: str = None, - agents: List[Agent] = [], - update_agents_on: bool = False, + format: str = "XML", ) -> str: """ - Generate a formatted string showcasing all available agents and their descriptions. + Format the available agents in either XML or Table format. Args: - agents (List[Agent]): List of Agent objects to showcase. - update_agents_on (bool, optional): If True, updates each agent's system prompt with - the showcase information. Defaults to False. + agents (List[Agent]): A list of agents to represent + name (str, optional): Name of the swarm + description (str, optional): Description of the swarm + format (str, optional): Output format ("XML" or "Table"). Defaults to "XML" Returns: - str: Formatted string containing agent information, including names, descriptions - and IDs for all available agents. + str: Formatted string containing agent information """ - logger.info(f"Showcasing {len(agents)} available agents") - - formatted_agents = [] - header = f"\n####### Agents available in the swarm: {name} ############\n" - header += f"{description}\n" - row_format = "{:<5} | {:<20} | {:<50}" - header_row = row_format.format("ID", "Agent Name", "Description") - separator = "-" * 80 - - formatted_agents.append(header) - formatted_agents.append(separator) - formatted_agents.append(header_row) - formatted_agents.append(separator) - - for idx, agent in enumerate(agents): - if not isinstance(agent, Agent): - logger.warning( - f"Skipping non-Agent object: {type(agent)}" - ) - continue - agent_name = get_agent_name(agent) - description = ( - get_agent_description(agent)[:100] + "..." - if len(get_agent_description(agent)) > 100 - else get_agent_description(agent) + def truncate(text: str, max_length: int = 130) -> str: + return ( + f"{text[:max_length]}..." + if len(text) > max_length + else text ) - formatted_agents.append( - row_format.format(idx + 1, agent_name, description) + output = [] + + if format.upper() == "TABLE": + output.append("\n| ID | Agent Name | Description |") + output.append("|-----|------------|-------------|") + for idx, agent in enumerate(agents): + if isinstance(agent, Agent): + agent_name = getattr(agent, "agent_name", str(agent)) + description = getattr( + agent, + "description", + getattr( + agent, "system_prompt", "Unknown description" + ), + ) + desc = truncate(description, 50) + output.append( + f"| {idx + 1} | {agent_name} | {desc} |" + ) + else: + output.append( + f"| {idx + 1} | {agent} | Unknown description |" + ) + return "\n".join(output) + + # Default XML format + output.append("") + if name: + output.append(f" {name}") + if description: + output.append( + f" {truncate(description)}" ) + for idx, agent in enumerate(agents): + output.append(f" ") + if isinstance(agent, Agent): + agent_name = getattr(agent, "agent_name", str(agent)) + description = getattr( + agent, + "description", + getattr( + agent, "system_prompt", "Unknown description" + ), + ) + output.append(f" {agent_name}") + output.append( + f" {truncate(description)}" + ) + else: + output.append(f" {agent}") + output.append( + " Unknown description" + ) + output.append(" ") + output.append("") - showcase = "\n".join(formatted_agents) - - return showcase + return "\n".join(output) diff --git a/swarms/structs/rearrange.py b/swarms/structs/rearrange.py index a61efbee..801861b0 100644 --- a/swarms/structs/rearrange.py +++ b/swarms/structs/rearrange.py @@ -1,5 +1,5 @@ -import traceback import asyncio +import traceback import uuid from concurrent.futures import ThreadPoolExecutor from datetime import datetime @@ -13,10 +13,10 @@ from swarms.structs.agent import Agent from swarms.structs.agents_available import showcase_available_agents from swarms.structs.base_swarm import BaseSwarm from swarms.utils.add_docs_to_agents import handle_input_docs +from swarms.utils.loguru_logger import initialize_logger from swarms.utils.wrapper_clusterop import ( exec_callable_with_clusterops, ) -from swarms.utils.loguru_logger import initialize_logger logger = initialize_logger(log_folder="rearrange") @@ -153,33 +153,6 @@ class AgentRearrange(BaseSwarm): self.all_cores = all_cores self.all_gpus = all_gpus self.no_use_clusterops = no_use_clusterops - self.swarm_history = { - agent.agent_name: [] for agent in agents - } - - self.id = uuid.uuid4().hex if id is None else id - - # Output schema - self.input_config = AgentRearrangeInput( - swarm_id=self.id, - name=self.name, - description=self.description, - flow=self.flow, - max_loops=self.max_loops, - output_type=self.output_type, - ) - - # Output schema - self.output_schema = AgentRearrangeOutput( - Input=self.input_config, - outputs=[], - ) - - # Run the reliability checks to validate the swarm - # self.handle_input_docs() - - # Show the agents whose in the swarm - # self.showcase_agents() def showcase_agents(self): # Get formatted agent info once @@ -187,12 +160,34 @@ class AgentRearrange(BaseSwarm): name=self.name, description=self.description, agents=self.agents, + format="Table", ) - # Update all agents in one pass using values() - for agent in self.agents.values(): - if isinstance(agent, Agent): - agent.system_prompt += agents_available + return agents_available + + def rearrange_prompt_prep(self) -> str: + """Prepares a formatted prompt describing the swarm configuration. + + Returns: + str: A formatted string containing the swarm's name, description, + flow pattern, and participating agents. + """ + agents_available = self.showcase_agents() + prompt = f""" + ===== Swarm Configuration ===== + + Name: {self.name} + Description: {self.description} + + ===== Execution Flow ===== + {self.flow} + + ===== Participating Agents ===== + {agents_available} + + =========================== + """ + return prompt def set_custom_flow(self, flow: str): self.flow = flow @@ -325,6 +320,7 @@ class AgentRearrange(BaseSwarm): current_task = task all_responses = [] response_dict = {} + previous_agent = None logger.info( f"Starting task execution with {len(tasks)} steps" @@ -349,12 +345,19 @@ class AgentRearrange(BaseSwarm): f"Starting loop {loop_count + 1}/{self.max_loops}" ) - for task in tasks: + for task_idx, task in enumerate(tasks): is_last = task == tasks[-1] agent_names = [ name.strip() for name in task.split(",") ] + # Prepare prompt with previous agent info + prompt_prefix = "" + if previous_agent and task_idx > 0: + prompt_prefix = f"Previous agent {previous_agent} output: {current_task}\n" + elif task_idx == 0: + prompt_prefix = "Initial task: " + if len(agent_names) > 1: # Parallel processing logger.info( @@ -370,12 +373,14 @@ class AgentRearrange(BaseSwarm): ): current_task = ( self.custom_human_in_the_loop( - current_task + prompt_prefix + + str(current_task) ) ) else: current_task = input( - "Enter your response:" + prompt_prefix + + "Enter your response: " ) results.append(current_task) response_dict[agent_name] = ( @@ -383,13 +388,13 @@ class AgentRearrange(BaseSwarm): ) else: agent = self.agents[agent_name] - current_task = ( - str(current_task) + task_with_context = ( + prompt_prefix + str(current_task) if current_task - else "" + else prompt_prefix ) result = agent.run( - task=current_task, + task=task_with_context, img=img, is_last=is_last, *args, @@ -407,6 +412,7 @@ class AgentRearrange(BaseSwarm): current_task = "; ".join(results) all_responses.extend(results) + previous_agent = ",".join(agent_names) else: # Sequential processing @@ -422,23 +428,25 @@ class AgentRearrange(BaseSwarm): ): current_task = ( self.custom_human_in_the_loop( - current_task + prompt_prefix + + str(current_task) ) ) else: current_task = input( - "Enter the next task: " + prompt_prefix + + "Enter the next task: " ) response_dict[agent_name] = current_task else: agent = self.agents[agent_name] - current_task = ( - str(current_task) + task_with_context = ( + prompt_prefix + str(current_task) if current_task - else "" + else prompt_prefix ) current_task = agent.run( - task=current_task, + task=task_with_context, img=img, is_last=is_last, *args, @@ -454,6 +462,7 @@ class AgentRearrange(BaseSwarm): ) all_responses.append(current_task) + previous_agent = agent_name loop_count += 1 diff --git a/swarms/utils/update_agent_system_prompts.py b/swarms/utils/update_agent_system_prompts.py new file mode 100644 index 00000000..e6f82426 --- /dev/null +++ b/swarms/utils/update_agent_system_prompts.py @@ -0,0 +1,53 @@ +import concurrent.futures +from typing import List, Union +from swarms.structs.agent import Agent + + +def update_system_prompts( + agents: List[Union[Agent, str]], + prompt: str, +) -> List[Agent]: + """ + Update system prompts for a list of agents concurrently. + + Args: + agents: List of Agent objects or strings to update + prompt: The prompt text to append to each agent's system prompt + + Returns: + List of updated Agent objects + """ + if not agents: + return agents + + def update_agent_prompt(agent: Union[Agent, str]) -> Agent: + # Convert string to Agent if needed + if isinstance(agent, str): + agent = Agent( + agent_name=agent, + system_prompt=prompt, # Initialize with the provided prompt + ) + else: + # Preserve existing prompt and append new one + existing_prompt = ( + agent.system_prompt if agent.system_prompt else "" + ) + agent.system_prompt = existing_prompt + "\n" + prompt + return agent + + # Use ThreadPoolExecutor for concurrent execution + max_workers = min(len(agents), 4) # Reasonable thread count + with concurrent.futures.ThreadPoolExecutor( + max_workers=max_workers + ) as executor: + futures = [] + for agent in agents: + future = executor.submit(update_agent_prompt, agent) + futures.append(future) + + # Collect results as they complete + updated_agents = [] + for future in concurrent.futures.as_completed(futures): + updated_agents.append(future.result()) + + return updated_agents