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)`
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:**
- `id` (`str`, optional): Unique identifier for the workflow. Defaults to `"sequential_workflow"`.
- `name` (`str`, optional): Name of the workflow. Defaults to `"SequentialWorkflow"`.
- `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`.
- `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`.
@ -56,6 +56,8 @@ The constructor initializes the `SequentialWorkflow` object.
- `*args`: Variable length argument list.
- `**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`
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:**
- 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]`
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:**
- `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`
Executes the specified task through the agents asynchronously.
@ -90,6 +98,9 @@ Executes the specified task through the agents asynchronously.
- **Returns:**
- `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]`
Executes a batch of tasks through the agents concurrently.
@ -100,6 +111,9 @@ Executes a batch of tasks through the agents concurrently.
- **Returns:**
- `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
### Basic Sequential Workflow
@ -303,7 +317,7 @@ print(result)
| 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" |
| `description` | Description of workflow purpose | Standard description |
| `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.
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
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.
```
### 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.
## Accessing Workflow Information

@ -80,18 +80,23 @@ class SequentialWorkflow:
self.multi_agent_collab_prompt = multi_agent_collab_prompt
self.team_awareness = team_awareness
self.reliability_check()
self.flow = self.sequential_flow()
self.agent_rearrange = AgentRearrange(
name=self.name,
description=self.description,
agents=self.agents,
flow=self.flow,
max_loops=self.max_loops,
output_type=self.output_type,
team_awareness=self.team_awareness,
)
# Only validate and initialize if agents are provided
if self.agents is not None and len(self.agents) > 0:
self.reliability_check()
self.flow = self.sequential_flow()
self.agent_rearrange = AgentRearrange(
name=self.name,
description=self.description,
agents=self.agents,
flow=self.flow,
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):
"""
@ -180,9 +185,15 @@ class SequentialWorkflow:
str: The final result after processing through all agents.
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.
"""
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:
# prompt = f"{MULTI_AGENT_COLLAB_PROMPT}\n\n{task}"
return self.agent_rearrange.run(
@ -221,9 +232,15 @@ class SequentialWorkflow:
List[str]: A list of final results after processing through all agents.
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.
"""
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(
isinstance(task, str) for task in tasks
):
@ -250,9 +267,15 @@ class SequentialWorkflow:
str: The final result after processing through all agents.
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.
"""
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):
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.
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.
"""
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(
isinstance(task, str) for task in tasks
):

@ -7,15 +7,12 @@ from swarms import Agent, SequentialWorkflow
def test_sequential_workflow_initialization():
workflow = SequentialWorkflow()
assert isinstance(workflow, SequentialWorkflow)
assert len(workflow.tasks) == 0
assert workflow.agents is None
assert workflow.max_loops == 1
assert workflow.autosave is False
assert (
workflow.saved_state_filepath
== "sequential_workflow_state.json"
)
assert workflow.restore_state_filepath is None
assert workflow.dashboard is False
assert workflow.flow == ""
assert workflow.agent_rearrange is None
assert workflow.name == "SequentialWorkflow"
assert workflow.id == "sequential_workflow"
def test_sequential_workflow_initialization_with_agents():
@ -83,6 +80,11 @@ def test_sequential_workflow_multi_agent_execution():
result = workflow.run(
"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
# 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():
"""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(
ValueError, match="Agents list cannot be None or empty"
):
SequentialWorkflow(agents=None)
workflow_none.run("test task")
with pytest.raises(
ValueError, match="Agents list cannot be None or empty"
):
SequentialWorkflow(agents=[])
workflow_empty.run("test task")
# Test with zero max_loops
with pytest.raises(ValueError, match="max_loops cannot be 0"):

Loading…
Cancel
Save