Fix SequentialWorkflow to allow initialization without agents

pull/1212/head
Steve-Dusty 1 month ago
parent c201979f85
commit 3f4c6c258e

@ -41,13 +41,13 @@ graph TD
### `__init__(self, agents: List[Union[Agent, Callable]] = None, max_loops: int = 1, team_awareness: bool = False, *args, **kwargs)` ### `__init__(self, agents: List[Union[Agent, Callable]] = None, max_loops: int = 1, team_awareness: bool = False, *args, **kwargs)`
The constructor initializes the `SequentialWorkflow` object. The constructor initializes the `SequentialWorkflow` object. Agents can be provided during initialization or the workflow can be initialized empty and configured later.
- **Parameters:** - **Parameters:**
- `id` (`str`, optional): Unique identifier for the workflow. Defaults to `"sequential_workflow"`. - `id` (`str`, optional): Unique identifier for the workflow. Defaults to `"sequential_workflow"`.
- `name` (`str`, optional): Name of the workflow. Defaults to `"SequentialWorkflow"`. - `name` (`str`, optional): Name of the workflow. Defaults to `"SequentialWorkflow"`.
- `description` (`str`, optional): Description of the workflow. Defaults to a standard description. - `description` (`str`, optional): Description of the workflow. Defaults to a standard description.
- `agents` (`List[Union[Agent, Callable]]`, optional): The list of agents or callables to execute in sequence. - `agents` (`List[Union[Agent, Callable]]`, optional): The list of agents or callables to execute in sequence. Can be `None` during initialization but must be set before calling any run methods. Defaults to `None`.
- `max_loops` (`int`, optional): The maximum number of loops to execute the workflow. Defaults to `1`. - `max_loops` (`int`, optional): The maximum number of loops to execute the workflow. Defaults to `1`.
- `output_type` (`OutputType`, optional): Output format for the workflow. Defaults to `"dict"`. - `output_type` (`OutputType`, optional): Output format for the workflow. Defaults to `"dict"`.
- `shared_memory_system` (`callable`, optional): Callable for shared memory management. Defaults to `None`. - `shared_memory_system` (`callable`, optional): Callable for shared memory management. Defaults to `None`.
@ -56,6 +56,8 @@ The constructor initializes the `SequentialWorkflow` object.
- `*args`: Variable length argument list. - `*args`: Variable length argument list.
- `**kwargs`: Arbitrary keyword arguments. - `**kwargs`: Arbitrary keyword arguments.
- **Note:** When initialized without agents, the workflow's `agent_rearrange` attribute will be `None` and `flow` will be an empty string. You must provide agents before running the workflow.
### `run(self, task: str, img: Optional[str] = None, imgs: Optional[List[str]] = None, *args, **kwargs) -> str` ### `run(self, task: str, img: Optional[str] = None, imgs: Optional[List[str]] = None, *args, **kwargs) -> str`
Runs the specified task through the agents in the dynamically constructed flow. Runs the specified task through the agents in the dynamically constructed flow.
@ -70,6 +72,9 @@ Runs the specified task through the agents in the dynamically constructed flow.
- **Returns:** - **Returns:**
- The final result after processing through all agents. - The final result after processing through all agents.
- **Raises:**
- `ValueError`: If agents list is None or empty when attempting to run.
### `run_batched(self, tasks: List[str]) -> List[str]` ### `run_batched(self, tasks: List[str]) -> List[str]`
Executes a batch of tasks through the agents in the dynamically constructed flow. Executes a batch of tasks through the agents in the dynamically constructed flow.
@ -80,6 +85,9 @@ Executes a batch of tasks through the agents in the dynamically constructed flow
- **Returns:** - **Returns:**
- `List[str]`: A list of final results after processing through all agents. - `List[str]`: A list of final results after processing through all agents.
- **Raises:**
- `ValueError`: If agents list is None or empty when attempting to run.
### `async run_async(self, task: str) -> str` ### `async run_async(self, task: str) -> str`
Executes the specified task through the agents asynchronously. Executes the specified task through the agents asynchronously.
@ -90,6 +98,9 @@ Executes the specified task through the agents asynchronously.
- **Returns:** - **Returns:**
- `str`: The final result after processing through all agents. - `str`: The final result after processing through all agents.
- **Raises:**
- `ValueError`: If agents list is None or empty when attempting to run.
### `async run_concurrent(self, tasks: List[str]) -> List[str]` ### `async run_concurrent(self, tasks: List[str]) -> List[str]`
Executes a batch of tasks through the agents concurrently. Executes a batch of tasks through the agents concurrently.
@ -100,6 +111,9 @@ Executes a batch of tasks through the agents concurrently.
- **Returns:** - **Returns:**
- `List[str]`: A list of final results after processing through all agents. - `List[str]`: A list of final results after processing through all agents.
- **Raises:**
- `ValueError`: If agents list is None or empty when attempting to run.
## Usage Examples ## Usage Examples
### Basic Sequential Workflow ### Basic Sequential Workflow
@ -303,7 +317,7 @@ print(result)
| Parameter | Description | Default | | Parameter | Description | Default |
|-----------|-------------|---------| |-----------|-------------|---------|
| `agents` | List of agents to execute in sequence | Required | | `agents` | List of agents to execute in sequence | None (optional at init, required before run) |
| `name` | Name of the workflow | "SequentialWorkflow" | | `name` | Name of the workflow | "SequentialWorkflow" |
| `description` | Description of workflow purpose | Standard description | | `description` | Description of workflow purpose | Standard description |
| `max_loops` | Number of times to execute workflow | 1 | | `max_loops` | Number of times to execute workflow | 1 |
@ -317,6 +331,29 @@ print(result)
4. **System Prompts**: Write comprehensive system prompts that explain the agent's expertise and responsibilities. 4. **System Prompts**: Write comprehensive system prompts that explain the agent's expertise and responsibilities.
5. **Task Clarity**: Provide clear, specific tasks when calling `run()`. 5. **Task Clarity**: Provide clear, specific tasks when calling `run()`.
## Initialization Behavior
The `SequentialWorkflow` can be initialized with or without agents:
### With Agents (Standard)
```python
workflow = SequentialWorkflow(agents=[agent1, agent2, agent3])
# Ready to run immediately
workflow.run("Task to execute")
```
### Without Agents (Deferred Configuration)
```python
# Create an empty workflow
workflow = SequentialWorkflow()
# Configure later (you'll need to reinitialize with agents)
workflow = SequentialWorkflow(agents=[agent1, agent2])
workflow.run("Task to execute")
```
**Note**: When initialized without agents, `agent_rearrange` will be `None` and `flow` will be an empty string. You must provide agents before calling any execution methods.
## Logging and Error Handling ## Logging and Error Handling
The `run` method includes comprehensive logging to track workflow execution: The `run` method includes comprehensive logging to track workflow execution:
@ -325,6 +362,19 @@ The `run` method includes comprehensive logging to track workflow execution:
2023-05-08 10:30:15.456 | INFO | Sequential Workflow Name: SequentialWorkflow is ready to run. 2023-05-08 10:30:15.456 | INFO | Sequential Workflow Name: SequentialWorkflow is ready to run.
``` ```
### Error Handling
All execution methods (`run`, `run_batched`, `run_async`, `run_concurrent`) validate that agents are configured before execution:
```python
workflow = SequentialWorkflow() # No agents
try:
workflow.run("Some task")
except ValueError as e:
print(e) # "Agents list cannot be None or empty. Add agents before running the workflow."
```
All errors during execution are logged and re-raised for proper error handling. All errors during execution are logged and re-raised for proper error handling.
## Accessing Workflow Information ## Accessing Workflow Information

@ -80,18 +80,23 @@ class SequentialWorkflow:
self.multi_agent_collab_prompt = multi_agent_collab_prompt self.multi_agent_collab_prompt = multi_agent_collab_prompt
self.team_awareness = team_awareness self.team_awareness = team_awareness
self.reliability_check() # Only validate and initialize if agents are provided
self.flow = self.sequential_flow() if self.agents is not None and len(self.agents) > 0:
self.reliability_check()
self.agent_rearrange = AgentRearrange( self.flow = self.sequential_flow()
name=self.name,
description=self.description, self.agent_rearrange = AgentRearrange(
agents=self.agents, name=self.name,
flow=self.flow, description=self.description,
max_loops=self.max_loops, agents=self.agents,
output_type=self.output_type, flow=self.flow,
team_awareness=self.team_awareness, max_loops=self.max_loops,
) output_type=self.output_type,
team_awareness=self.team_awareness,
)
else:
self.flow = ""
self.agent_rearrange = None
def reliability_check(self): def reliability_check(self):
""" """
@ -180,9 +185,15 @@ class SequentialWorkflow:
str: The final result after processing through all agents. str: The final result after processing through all agents.
Raises: Raises:
ValueError: If the task is None or empty. ValueError: If the task is None or empty, or if no agents are configured.
Exception: If any error occurs during task execution. Exception: If any error occurs during task execution.
""" """
if self.agents is None or len(self.agents) == 0:
raise ValueError("Agents list cannot be None or empty. Add agents before running the workflow.")
if self.agent_rearrange is None:
raise ValueError("Workflow not properly initialized. AgentRearrange is None.")
try: try:
# prompt = f"{MULTI_AGENT_COLLAB_PROMPT}\n\n{task}" # prompt = f"{MULTI_AGENT_COLLAB_PROMPT}\n\n{task}"
return self.agent_rearrange.run( return self.agent_rearrange.run(
@ -221,9 +232,15 @@ class SequentialWorkflow:
List[str]: A list of final results after processing through all agents. List[str]: A list of final results after processing through all agents.
Raises: Raises:
ValueError: If tasks is None, empty, or contains non-string elements. ValueError: If tasks is None, empty, contains non-string elements, or if no agents are configured.
Exception: If any error occurs during task execution. Exception: If any error occurs during task execution.
""" """
if self.agents is None or len(self.agents) == 0:
raise ValueError("Agents list cannot be None or empty. Add agents before running the workflow.")
if self.agent_rearrange is None:
raise ValueError("Workflow not properly initialized. AgentRearrange is None.")
if not tasks or not all( if not tasks or not all(
isinstance(task, str) for task in tasks isinstance(task, str) for task in tasks
): ):
@ -250,9 +267,15 @@ class SequentialWorkflow:
str: The final result after processing through all agents. str: The final result after processing through all agents.
Raises: Raises:
ValueError: If task is None or not a string. ValueError: If task is None, not a string, or if no agents are configured.
Exception: If any error occurs during task execution. Exception: If any error occurs during task execution.
""" """
if self.agents is None or len(self.agents) == 0:
raise ValueError("Agents list cannot be None or empty. Add agents before running the workflow.")
if self.agent_rearrange is None:
raise ValueError("Workflow not properly initialized. AgentRearrange is None.")
if not task or not isinstance(task, str): if not task or not isinstance(task, str):
raise ValueError("Task must be a non-empty string") raise ValueError("Task must be a non-empty string")
@ -275,9 +298,15 @@ class SequentialWorkflow:
List[str]: A list of final results after processing through all agents. List[str]: A list of final results after processing through all agents.
Raises: Raises:
ValueError: If tasks is None, empty, or contains non-string elements. ValueError: If tasks is None, empty, contains non-string elements, or if no agents are configured.
Exception: If any error occurs during task execution. Exception: If any error occurs during task execution.
""" """
if self.agents is None or len(self.agents) == 0:
raise ValueError("Agents list cannot be None or empty. Add agents before running the workflow.")
if self.agent_rearrange is None:
raise ValueError("Workflow not properly initialized. AgentRearrange is None.")
if not tasks or not all( if not tasks or not all(
isinstance(task, str) for task in tasks isinstance(task, str) for task in tasks
): ):

@ -7,15 +7,12 @@ from swarms import Agent, SequentialWorkflow
def test_sequential_workflow_initialization(): def test_sequential_workflow_initialization():
workflow = SequentialWorkflow() workflow = SequentialWorkflow()
assert isinstance(workflow, SequentialWorkflow) assert isinstance(workflow, SequentialWorkflow)
assert len(workflow.tasks) == 0 assert workflow.agents is None
assert workflow.max_loops == 1 assert workflow.max_loops == 1
assert workflow.autosave is False assert workflow.flow == ""
assert ( assert workflow.agent_rearrange is None
workflow.saved_state_filepath assert workflow.name == "SequentialWorkflow"
== "sequential_workflow_state.json" assert workflow.id == "sequential_workflow"
)
assert workflow.restore_state_filepath is None
assert workflow.dashboard is False
def test_sequential_workflow_initialization_with_agents(): def test_sequential_workflow_initialization_with_agents():
@ -83,6 +80,11 @@ def test_sequential_workflow_multi_agent_execution():
result = workflow.run( result = workflow.run(
"Analyze the impact of renewable energy on climate change" "Analyze the impact of renewable energy on climate change"
) )
print("\n" + "="*80)
print("WORKFLOW RESULT:")
print("="*80)
print(result)
print("="*80 + "\n")
assert result is not None assert result is not None
# SequentialWorkflow may return different types based on output_type, just ensure it's not None # SequentialWorkflow may return different types based on output_type, just ensure it's not None
@ -232,16 +234,24 @@ def test_sequential_workflow_with_multi_agent_collaboration():
def test_sequential_workflow_error_handling(): def test_sequential_workflow_error_handling():
"""Test SequentialWorkflow error handling""" """Test SequentialWorkflow error handling"""
# Test with invalid agents list # Test that initialization with None agents is allowed
workflow_none = SequentialWorkflow(agents=None)
assert workflow_none.agents is None
# Test that initialization with empty agents is allowed
workflow_empty = SequentialWorkflow(agents=[])
assert workflow_empty.agents == []
# Test that running with no agents raises error
with pytest.raises( with pytest.raises(
ValueError, match="Agents list cannot be None or empty" ValueError, match="Agents list cannot be None or empty"
): ):
SequentialWorkflow(agents=None) workflow_none.run("test task")
with pytest.raises( with pytest.raises(
ValueError, match="Agents list cannot be None or empty" ValueError, match="Agents list cannot be None or empty"
): ):
SequentialWorkflow(agents=[]) workflow_empty.run("test task")
# Test with zero max_loops # Test with zero max_loops
with pytest.raises(ValueError, match="max_loops cannot be 0"): with pytest.raises(ValueError, match="max_loops cannot be 0"):

Loading…
Cancel
Save