commit
ac81c795df
@ -0,0 +1,12 @@
|
|||||||
|
# this is a config file for the github action labeler
|
||||||
|
|
||||||
|
# Add 'label1' to any changes within 'example' folder or any subfolders
|
||||||
|
example_change:
|
||||||
|
- example/**
|
||||||
|
|
||||||
|
# Add 'label2' to any file changes within 'example2' folder
|
||||||
|
example2_change: example2/*
|
||||||
|
|
||||||
|
# Add label3 to any change to .txt files within the entire repository. Quotation marks are required for the leading asterisk
|
||||||
|
text_files:
|
||||||
|
- '**/*.txt'
|
@ -1,93 +0,0 @@
|
|||||||
Create multi-page long and explicit professional pytorch-like documentation for the swarms code below follow the outline for the swarms library, provide many examples and teach the user about the code, provide examples for every function, make the documentation 10,000 words, provide many usage examples and note this is markdown docs, create the documentation for the code to document.
|
|
||||||
|
|
||||||
Now make the professional documentation for this code, provide the architecture and how the class works and why it works that way, it's purpose, provide args, their types, 3 ways of usage examples, in examples use from shapeless import x
|
|
||||||
|
|
||||||
BE VERY EXPLICIT AND THOROUGH, MAKE IT DEEP AND USEFUL
|
|
||||||
|
|
||||||
########
|
|
||||||
Step 1: Understand the purpose and functionality of the module or framework
|
|
||||||
|
|
||||||
Read and analyze the description provided in the documentation to understand the purpose and functionality of the module or framework.
|
|
||||||
Identify the key features, parameters, and operations performed by the module or framework.
|
|
||||||
Step 2: Provide an overview and introduction
|
|
||||||
|
|
||||||
Start the documentation by providing a brief overview and introduction to the module or framework.
|
|
||||||
Explain the importance and relevance of the module or framework in the context of the problem it solves.
|
|
||||||
Highlight any key concepts or terminology that will be used throughout the documentation.
|
|
||||||
Step 3: Provide a class or function definition
|
|
||||||
|
|
||||||
Provide the class or function definition for the module or framework.
|
|
||||||
Include the parameters that need to be passed to the class or function and provide a brief description of each parameter.
|
|
||||||
Specify the data types and default values for each parameter.
|
|
||||||
Step 4: Explain the functionality and usage
|
|
||||||
|
|
||||||
Provide a detailed explanation of how the module or framework works and what it does.
|
|
||||||
Describe the steps involved in using the module or framework, including any specific requirements or considerations.
|
|
||||||
Provide code examples to demonstrate the usage of the module or framework.
|
|
||||||
Explain the expected inputs and outputs for each operation or function.
|
|
||||||
Step 5: Provide additional information and tips
|
|
||||||
|
|
||||||
Provide any additional information or tips that may be useful for using the module or framework effectively.
|
|
||||||
Address any common issues or challenges that developers may encounter and provide recommendations or workarounds.
|
|
||||||
Step 6: Include references and resources
|
|
||||||
|
|
||||||
Include references to any external resources or research papers that provide further information or background on the module or framework.
|
|
||||||
Provide links to relevant documentation or websites for further exploration.
|
|
||||||
Example Template for the given documentation:
|
|
||||||
|
|
||||||
# Module/Function Name: MultiheadAttention
|
|
||||||
|
|
||||||
class torch.nn.MultiheadAttention(embed_dim, num_heads, dropout=0.0, bias=True, add_bias_kv=False, add_zero_attn=False, kdim=None, vdim=None, batch_first=False, device=None, dtype=None):
|
|
||||||
"""
|
|
||||||
Creates a multi-head attention module for joint information representation from the different subspaces.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
- embed_dim (int): Total dimension of the model.
|
|
||||||
- num_heads (int): Number of parallel attention heads. The embed_dim will be split across num_heads.
|
|
||||||
- dropout (float): Dropout probability on attn_output_weights. Default: 0.0 (no dropout).
|
|
||||||
- bias (bool): If specified, adds bias to input/output projection layers. Default: True.
|
|
||||||
- add_bias_kv (bool): If specified, adds bias to the key and value sequences at dim=0. Default: False.
|
|
||||||
- add_zero_attn (bool): If specified, adds a new batch of zeros to the key and value sequences at dim=1. Default: False.
|
|
||||||
- kdim (int): Total number of features for keys. Default: None (uses kdim=embed_dim).
|
|
||||||
- vdim (int): Total number of features for values. Default: None (uses vdim=embed_dim).
|
|
||||||
- batch_first (bool): If True, the input and output tensors are provided as (batch, seq, feature). Default: False.
|
|
||||||
- device (torch.device): If specified, the tensors will be moved to the specified device.
|
|
||||||
- dtype (torch.dtype): If specified, the tensors will have the specified dtype.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def forward(query, key, value, key_padding_mask=None, need_weights=True, attn_mask=None, average_attn_weights=True, is_causal=False):
|
|
||||||
"""
|
|
||||||
Forward pass of the multi-head attention module.
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
- query (Tensor): Query embeddings of shape (L, E_q) for unbatched input, (L, N, E_q) when batch_first=False, or (N, L, E_q) when batch_first=True.
|
|
||||||
- key (Tensor): Key embeddings of shape (S, E_k) for unbatched input, (S, N, E_k) when batch_first=False, or (N, S, E_k) when batch_first=True.
|
|
||||||
- value (Tensor): Value embeddings of shape (S, E_v) for unbatched input, (S, N, E_v) when batch_first=False, or (N, S, E_v) when batch_first=True.
|
|
||||||
- key_padding_mask (Optional[Tensor]): If specified, a mask indicating elements to be ignored in key for attention computation.
|
|
||||||
- need_weights (bool): If specified, returns attention weights in addition to attention outputs. Default: True.
|
|
||||||
- attn_mask (Optional[Tensor]): If specified, a mask preventing attention to certain positions.
|
|
||||||
- average_attn_weights (bool): If true, returns averaged attention weights per head. Otherwise, returns attention weights separately per head. Note that this flag only has an effect when need_weights=True. Default: True.
|
|
||||||
- is_causal (bool): If specified, applies a causal mask as the attention mask. Default: False.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Tuple[Tensor, Optional[Tensor]]:
|
|
||||||
- attn_output (Tensor): Attention outputs of shape (L, E) for unbatched input, (L, N, E) when batch_first=False, or (N, L, E) when batch_first=True.
|
|
||||||
- attn_output_weights (Optional[Tensor]): Attention weights of shape (L, S) when unbatched or (N, L, S) when batched. Optional, only returned when need_weights=True.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Implementation of the forward pass of the attention module goes here
|
|
||||||
|
|
||||||
return attn_output, attn_output_weights
|
|
||||||
|
|
||||||
|
|
||||||
# Usage example:
|
|
||||||
|
|
||||||
multihead_attn = nn.MultiheadAttention(embed_dim, num_heads)
|
|
||||||
attn_output, attn_output_weights = multihead_attn(query, key, value)
|
|
||||||
Note:
|
|
||||||
|
|
||||||
The above template includes the class or function definition, parameters, description, and usage example.
|
|
||||||
To replicate the documentation for any other module or framework, follow the same structure and provide the specific details for that module or framework.
|
|
||||||
|
|
||||||
|
|
||||||
############# CODE TO DOCUMENT, DOCUMENT THE
|
|
@ -0,0 +1,261 @@
|
|||||||
|
# `Dalle3` Documentation
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
|
||||||
|
1. [Introduction](#introduction)
|
||||||
|
2. [Installation](#installation)
|
||||||
|
3. [Quick Start](#quick-start)
|
||||||
|
4. [Dalle3 Class](#dalle3-class)
|
||||||
|
- [Attributes](#attributes)
|
||||||
|
- [Methods](#methods)
|
||||||
|
5. [Usage Examples](#usage-examples)
|
||||||
|
6. [Error Handling](#error-handling)
|
||||||
|
7. [Advanced Usage](#advanced-usage)
|
||||||
|
8. [References](#references)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Introduction<a name="introduction"></a>
|
||||||
|
|
||||||
|
The Dalle3 library is a Python module that provides an easy-to-use interface for generating images from text descriptions using the DALL·E 3 model by OpenAI. DALL·E 3 is a powerful language model capable of converting textual prompts into images. This documentation will guide you through the installation, setup, and usage of the Dalle3 library.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Installation<a name="installation"></a>
|
||||||
|
|
||||||
|
To use the Dalle3 model, you must first install swarms:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install swarms
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Quick Start<a name="quick-start"></a>
|
||||||
|
|
||||||
|
Let's get started with a quick example of using the Dalle3 library to generate an image from a text prompt:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from swarms.models.dalle3 import Dalle3
|
||||||
|
|
||||||
|
# Create an instance of the Dalle3 class
|
||||||
|
dalle = Dalle3()
|
||||||
|
|
||||||
|
# Define a text prompt
|
||||||
|
task = "A painting of a dog"
|
||||||
|
|
||||||
|
# Generate an image from the text prompt
|
||||||
|
image_url = dalle3(task)
|
||||||
|
|
||||||
|
# Print the generated image URL
|
||||||
|
print(image_url)
|
||||||
|
```
|
||||||
|
|
||||||
|
This example demonstrates the basic usage of the Dalle3 library to convert a text prompt into an image. The generated image URL will be printed to the console.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Dalle3 Class<a name="dalle3-class"></a>
|
||||||
|
|
||||||
|
The Dalle3 library provides a `Dalle3` class that allows you to interact with the DALL·E 3 model. This class has several attributes and methods for generating images from text prompts.
|
||||||
|
|
||||||
|
### Attributes<a name="attributes"></a>
|
||||||
|
|
||||||
|
- `model` (str): The name of the DALL·E 3 model. Default: "dall-e-3".
|
||||||
|
- `img` (str): The image URL generated by the Dalle3 API.
|
||||||
|
- `size` (str): The size of the generated image. Default: "1024x1024".
|
||||||
|
- `max_retries` (int): The maximum number of API request retries. Default: 3.
|
||||||
|
- `quality` (str): The quality of the generated image. Default: "standard".
|
||||||
|
- `n` (int): The number of variations to create. Default: 4.
|
||||||
|
|
||||||
|
### Methods<a name="methods"></a>
|
||||||
|
|
||||||
|
#### `__call__(self, task: str) -> Dalle3`
|
||||||
|
|
||||||
|
This method makes a call to the Dalle3 API and returns the image URL generated from the provided text prompt.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- `task` (str): The text prompt to be converted to an image.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- `Dalle3`: An instance of the Dalle3 class with the image URL generated by the Dalle3 API.
|
||||||
|
|
||||||
|
#### `create_variations(self, img: str)`
|
||||||
|
|
||||||
|
This method creates variations of an image using the Dalle3 API.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
- `img` (str): The image to be used for the API request.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
- `img` (str): The image URL of the generated variations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Usage Examples<a name="usage-examples"></a>
|
||||||
|
|
||||||
|
### Example 1: Basic Image Generation
|
||||||
|
|
||||||
|
```python
|
||||||
|
from swarms.models.dalle3 import Dalle3
|
||||||
|
|
||||||
|
# Create an instance of the Dalle3 class
|
||||||
|
dalle3 = Dalle3()
|
||||||
|
|
||||||
|
# Define a text prompt
|
||||||
|
task = "A painting of a dog"
|
||||||
|
|
||||||
|
# Generate an image from the text prompt
|
||||||
|
image_url = dalle3(task)
|
||||||
|
|
||||||
|
# Print the generated image URL
|
||||||
|
print(image_url)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 2: Creating Image Variations
|
||||||
|
|
||||||
|
```python
|
||||||
|
from swarms.models.dalle3 import Dalle3
|
||||||
|
|
||||||
|
# Create an instance of the Dalle3 class
|
||||||
|
dalle3 = Dalle3()
|
||||||
|
|
||||||
|
# Define the URL of an existing image
|
||||||
|
img_url = "https://images.unsplash.com/photo-1694734479898-6ac4633158ac?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D
|
||||||
|
|
||||||
|
# Create variations of the image
|
||||||
|
variations_url = dalle3.create_variations(img_url)
|
||||||
|
|
||||||
|
# Print the URLs of the generated variations
|
||||||
|
print(variations_url)
|
||||||
|
```
|
||||||
|
|
||||||
|
Certainly! Here are additional examples that cover various edge cases and methods of the `Dalle3` class in the Dalle3 library:
|
||||||
|
|
||||||
|
### Example 3: Customizing Image Size
|
||||||
|
|
||||||
|
You can customize the size of the generated image by specifying the `size` parameter when creating an instance of the `Dalle3` class. Here's how to generate a smaller image:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from swarms.models.dalle3 import Dalle3
|
||||||
|
|
||||||
|
# Create an instance of the Dalle3 class with a custom image size
|
||||||
|
dalle3 = Dalle3(size="512x512")
|
||||||
|
|
||||||
|
# Define a text prompt
|
||||||
|
task = "A small painting of a cat"
|
||||||
|
|
||||||
|
# Generate a smaller image from the text prompt
|
||||||
|
image_url = dalle3(task)
|
||||||
|
|
||||||
|
# Print the generated image URL
|
||||||
|
print(image_url)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 4: Adjusting Retry Limit
|
||||||
|
|
||||||
|
You can adjust the maximum number of API request retries using the `max_retries` parameter. Here's how to increase the retry limit:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from swarms.models.dalle3 import Dalle3
|
||||||
|
|
||||||
|
# Create an instance of the Dalle3 class with a higher retry limit
|
||||||
|
dalle3 = Dalle3(max_retries=5)
|
||||||
|
|
||||||
|
# Define a text prompt
|
||||||
|
task = "An image of a landscape"
|
||||||
|
|
||||||
|
# Generate an image with a higher retry limit
|
||||||
|
image_url = dalle3(task)
|
||||||
|
|
||||||
|
# Print the generated image URL
|
||||||
|
print(image_url)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 5: Generating Image Variations
|
||||||
|
|
||||||
|
To create variations of an existing image, you can use the `create_variations` method. Here's an example:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from swarms.models.dalle3 import Dalle3
|
||||||
|
|
||||||
|
# Create an instance of the Dalle3 class
|
||||||
|
dalle3 = Dalle3()
|
||||||
|
|
||||||
|
# Define the URL of an existing image
|
||||||
|
img_url = "https://images.unsplash.com/photo-1677290043066-12eccd944004?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||||
|
|
||||||
|
# Create variations of the image
|
||||||
|
variations_url = dalle3.create_variations(img_url)
|
||||||
|
|
||||||
|
# Print the URLs of the generated variations
|
||||||
|
print(variations_url)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 6: Handling API Errors
|
||||||
|
|
||||||
|
The Dalle3 library provides error handling for API-related issues. Here's how to handle and display API errors:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from swarms.models.dalle3 import Dalle3
|
||||||
|
|
||||||
|
# Create an instance of the Dalle3 class
|
||||||
|
dalle3 = Dalle3()
|
||||||
|
|
||||||
|
# Define a text prompt
|
||||||
|
task = "Invalid prompt that may cause an API error"
|
||||||
|
|
||||||
|
try:
|
||||||
|
# Attempt to generate an image with an invalid prompt
|
||||||
|
image_url = dalle3(task)
|
||||||
|
print(image_url)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error occurred: {str(e)}")
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example 7: Customizing Image Quality
|
||||||
|
|
||||||
|
You can customize the quality of the generated image by specifying the `quality` parameter. Here's how to generate a high-quality image:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from swarms.models.dalle3 import Dalle3
|
||||||
|
|
||||||
|
# Create an instance of the Dalle3 class with high quality
|
||||||
|
dalle3 = Dalle3(quality="high")
|
||||||
|
|
||||||
|
# Define a text prompt
|
||||||
|
task = "A high-quality image of a sunset"
|
||||||
|
|
||||||
|
# Generate a high-quality image from the text prompt
|
||||||
|
image_url = dalle3(task)
|
||||||
|
|
||||||
|
# Print the generated image URL
|
||||||
|
print(image_url)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Error Handling<a name="error-handling"></a>
|
||||||
|
|
||||||
|
The Dalle3 library provides error handling for API-related issues. If an error occurs during API communication, the library will handle it and provide detailed error messages. Make sure to handle exceptions appropriately in your code.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Advanced Usage<a name="advanced-usage"></a>
|
||||||
|
|
||||||
|
For advanced usage and customization of the Dalle3 library, you can explore the attributes and methods of the `Dalle3` class. Adjusting parameters such as `size`, `max_retries`, and `quality` allows you to fine-tune the image generation process to your specific needs.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## References<a name="references"></a>
|
||||||
|
|
||||||
|
For more information about the DALL·E 3 model and the Dalle3 library, you can refer to the official OpenAI documentation and resources.
|
||||||
|
|
||||||
|
- [OpenAI API Documentation](https://beta.openai.com/docs/)
|
||||||
|
- [DALL·E 3 Model Information](https://openai.com/research/dall-e-3)
|
||||||
|
- [Dalle3 GitHub Repository](https://github.com/openai/dall-e-3)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
This concludes the documentation for the Dalle3 library. You can now use the library to generate images from text prompts and explore its advanced features for various applications.
|
@ -0,0 +1,123 @@
|
|||||||
|
# DistilWhisperModel Documentation
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The `DistilWhisperModel` is a Python class designed to handle English speech recognition tasks. It leverages the capabilities of the Whisper model, which is fine-tuned for speech-to-text processes. It is designed for both synchronous and asynchronous transcription of audio inputs, offering flexibility for real-time applications or batch processing.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Before you can use `DistilWhisperModel`, ensure you have the required libraries installed:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pip3 install --upgrade swarms
|
||||||
|
```
|
||||||
|
|
||||||
|
## Initialization
|
||||||
|
|
||||||
|
The `DistilWhisperModel` class is initialized with the following parameters:
|
||||||
|
|
||||||
|
| Parameter | Type | Description | Default |
|
||||||
|
|-----------|------|-------------|---------|
|
||||||
|
| `model_id` | `str` | The identifier for the pre-trained Whisper model | `"distil-whisper/distil-large-v2"` |
|
||||||
|
|
||||||
|
Example of initialization:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from swarms.models import DistilWhisperModel
|
||||||
|
|
||||||
|
# Initialize with default model
|
||||||
|
model_wrapper = DistilWhisperModel()
|
||||||
|
|
||||||
|
# Initialize with a specific model ID
|
||||||
|
model_wrapper = DistilWhisperModel(model_id='distil-whisper/distil-large-v2')
|
||||||
|
```
|
||||||
|
|
||||||
|
## Attributes
|
||||||
|
|
||||||
|
After initialization, the `DistilWhisperModel` has several attributes:
|
||||||
|
|
||||||
|
| Attribute | Type | Description |
|
||||||
|
|-----------|------|-------------|
|
||||||
|
| `device` | `str` | The device used for computation (`"cuda:0"` for GPU or `"cpu"`). |
|
||||||
|
| `torch_dtype` | `torch.dtype` | The data type used for the Torch tensors. |
|
||||||
|
| `model_id` | `str` | The model identifier string. |
|
||||||
|
| `model` | `torch.nn.Module` | The actual Whisper model loaded from the identifier. |
|
||||||
|
| `processor` | `transformers.AutoProcessor` | The processor for handling input data. |
|
||||||
|
|
||||||
|
## Methods
|
||||||
|
|
||||||
|
### `transcribe`
|
||||||
|
|
||||||
|
Transcribes audio input synchronously.
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
| Argument | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `inputs` | `Union[str, dict]` | File path or audio data dictionary. |
|
||||||
|
|
||||||
|
**Returns**: `str` - The transcribed text.
|
||||||
|
|
||||||
|
**Usage Example**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Synchronous transcription
|
||||||
|
transcription = model_wrapper.transcribe('path/to/audio.mp3')
|
||||||
|
print(transcription)
|
||||||
|
```
|
||||||
|
|
||||||
|
### `async_transcribe`
|
||||||
|
|
||||||
|
Transcribes audio input asynchronously.
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
| Argument | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `inputs` | `Union[str, dict]` | File path or audio data dictionary. |
|
||||||
|
|
||||||
|
**Returns**: `Coroutine` - A coroutine that when awaited, returns the transcribed text.
|
||||||
|
|
||||||
|
**Usage Example**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
# Asynchronous transcription
|
||||||
|
transcription = asyncio.run(model_wrapper.async_transcribe('path/to/audio.mp3'))
|
||||||
|
print(transcription)
|
||||||
|
```
|
||||||
|
|
||||||
|
### `real_time_transcribe`
|
||||||
|
|
||||||
|
Simulates real-time transcription of an audio file.
|
||||||
|
|
||||||
|
**Arguments**:
|
||||||
|
|
||||||
|
| Argument | Type | Description |
|
||||||
|
|----------|------|-------------|
|
||||||
|
| `audio_file_path` | `str` | Path to the audio file. |
|
||||||
|
| `chunk_duration` | `int` | Duration of audio chunks in seconds. |
|
||||||
|
|
||||||
|
**Usage Example**:
|
||||||
|
|
||||||
|
```python
|
||||||
|
# Real-time transcription simulation
|
||||||
|
model_wrapper.real_time_transcribe('path/to/audio.mp3', chunk_duration=5)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Handling
|
||||||
|
|
||||||
|
The `DistilWhisperModel` class incorporates error handling for file not found errors and generic exceptions during the transcription process. If a non-recoverable exception is raised, it is printed to the console in red to indicate failure.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
The `DistilWhisperModel` offers a convenient interface to the powerful Whisper model for speech recognition. Its design supports both batch and real-time transcription, catering to different application needs. The class's error handling and retry logic make it robust for real-world applications.
|
||||||
|
|
||||||
|
## Additional Notes
|
||||||
|
|
||||||
|
- Ensure you have appropriate permissions to read audio files when using file paths.
|
||||||
|
- Transcription quality depends on the audio quality and the Whisper model's performance on your dataset.
|
||||||
|
- Adjust `chunk_duration` according to the processing power of your system for real-time transcription.
|
||||||
|
|
||||||
|
For a full list of models supported by `transformers.AutoModelForSpeechSeq2Seq`, visit the [Hugging Face Model Hub](https://huggingface.co/models).
|
@ -0,0 +1,251 @@
|
|||||||
|
# `GPT4Vision` Documentation
|
||||||
|
|
||||||
|
## Table of Contents
|
||||||
|
- [Overview](#overview)
|
||||||
|
- [Installation](#installation)
|
||||||
|
- [Initialization](#initialization)
|
||||||
|
- [Methods](#methods)
|
||||||
|
- [process_img](#process_img)
|
||||||
|
- [__call__](#__call__)
|
||||||
|
- [run](#run)
|
||||||
|
- [arun](#arun)
|
||||||
|
- [Configuration Options](#configuration-options)
|
||||||
|
- [Usage Examples](#usage-examples)
|
||||||
|
- [Additional Tips](#additional-tips)
|
||||||
|
- [References and Resources](#references-and-resources)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
The GPT4Vision Model API is designed to provide an easy-to-use interface for interacting with the OpenAI GPT-4 Vision model. This model can generate textual descriptions for images and answer questions related to visual content. Whether you want to describe images or perform other vision-related tasks, GPT4Vision makes it simple and efficient.
|
||||||
|
|
||||||
|
The library offers a straightforward way to send images and tasks to the GPT-4 Vision model and retrieve the generated responses. It handles API communication, authentication, and retries, making it a powerful tool for developers working with computer vision and natural language processing tasks.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
To use the GPT4Vision Model API, you need to install the required dependencies and configure your environment. Follow these steps to get started:
|
||||||
|
|
||||||
|
1. Install the required Python package:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip3 install --upgrade swarms
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Make sure you have an OpenAI API key. You can obtain one by signing up on the [OpenAI platform](https://beta.openai.com/signup/).
|
||||||
|
|
||||||
|
3. Set your OpenAI API key as an environment variable. You can do this in your code or your environment configuration. Alternatively, you can provide the API key directly when initializing the `GPT4Vision` class.
|
||||||
|
|
||||||
|
## Initialization
|
||||||
|
|
||||||
|
To start using the GPT4Vision Model API, you need to create an instance of the `GPT4Vision` class. You can customize its behavior by providing various configuration options, but it also comes with sensible defaults.
|
||||||
|
|
||||||
|
Here's how you can initialize the `GPT4Vision` class:
|
||||||
|
|
||||||
|
```python
|
||||||
|
from swarms.models.gpt4v import GPT4Vision
|
||||||
|
|
||||||
|
gpt4vision = GPT4Vision(
|
||||||
|
api_key="Your Key"
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
The above code initializes the `GPT4Vision` class with default settings. You can adjust these settings as needed.
|
||||||
|
|
||||||
|
## Methods
|
||||||
|
|
||||||
|
### `process_img`
|
||||||
|
|
||||||
|
The `process_img` method is used to preprocess an image before sending it to the GPT-4 Vision model. It takes the image path as input and returns the processed image in a format suitable for API requests.
|
||||||
|
|
||||||
|
```python
|
||||||
|
processed_img = gpt4vision.process_img(img_path)
|
||||||
|
```
|
||||||
|
|
||||||
|
- `img_path` (str): The file path or URL of the image to be processed.
|
||||||
|
|
||||||
|
### `__call__`
|
||||||
|
|
||||||
|
The `__call__` method is the main method for interacting with the GPT-4 Vision model. It sends the image and tasks to the model and returns the generated response.
|
||||||
|
|
||||||
|
```python
|
||||||
|
response = gpt4vision(img, tasks)
|
||||||
|
```
|
||||||
|
|
||||||
|
- `img` (Union[str, List[str]]): Either a single image URL or a list of image URLs to be used for the API request.
|
||||||
|
- `tasks` (List[str]): A list of tasks or questions related to the image(s).
|
||||||
|
|
||||||
|
This method returns a `GPT4VisionResponse` object, which contains the generated answer.
|
||||||
|
|
||||||
|
### `run`
|
||||||
|
|
||||||
|
The `run` method is an alternative way to interact with the GPT-4 Vision model. It takes a single task and image URL as input and returns the generated response.
|
||||||
|
|
||||||
|
```python
|
||||||
|
response = gpt4vision.run(task, img)
|
||||||
|
```
|
||||||
|
|
||||||
|
- `task` (str): The task or question related to the image.
|
||||||
|
- `img` (str): The image URL to be used for the API request.
|
||||||
|
|
||||||
|
This method simplifies interactions when dealing with a single task and image.
|
||||||
|
|
||||||
|
### `arun`
|
||||||
|
|
||||||
|
The `arun` method is an asynchronous version of the `run` method. It allows for asynchronous processing of API requests, which can be useful in certain scenarios.
|
||||||
|
|
||||||
|
```python
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
response = await gpt4vision.arun(task, img)
|
||||||
|
print(response)
|
||||||
|
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
loop.run_until_complete(main())
|
||||||
|
```
|
||||||
|
|
||||||
|
- `task` (str): The task or question related to the image.
|
||||||
|
- `img` (str): The image URL to be used for the API request.
|
||||||
|
|
||||||
|
## Configuration Options
|
||||||
|
|
||||||
|
The `GPT4Vision` class provides several configuration options that allow you to customize its behavior:
|
||||||
|
|
||||||
|
- `max_retries` (int): The maximum number of retries to make to the API. Default: 3
|
||||||
|
- `backoff_factor` (float): The backoff factor to use for exponential backoff. Default: 2.0
|
||||||
|
- `timeout_seconds` (int): The timeout in seconds for the API request. Default: 10
|
||||||
|
- `api_key` (str): The API key to use for the API request. Default: None (set via environment variable)
|
||||||
|
- `quality` (str): The quality of the image to generate. Options: 'low' or 'high'. Default: 'low'
|
||||||
|
- `max_tokens` (int): The maximum number of tokens to use for the API request. Default: 200
|
||||||
|
|
||||||
|
## Usage Examples
|
||||||
|
|
||||||
|
### Example 1: Generating Image Descriptions
|
||||||
|
|
||||||
|
```python
|
||||||
|
gpt4vision = GPT4Vision()
|
||||||
|
img = "https://example.com/image.jpg"
|
||||||
|
tasks = ["Describe this image."]
|
||||||
|
response = gpt4vision(img, tasks)
|
||||||
|
print(response.answer)
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, we create an instance of `GPT4Vision`, provide an image URL, and ask the model to describe the image. The response contains the generated description.
|
||||||
|
|
||||||
|
### Example 2: Custom Configuration
|
||||||
|
|
||||||
|
```python
|
||||||
|
custom_config = {
|
||||||
|
"max_retries": 5,
|
||||||
|
"timeout_seconds": 20,
|
||||||
|
"quality": "high",
|
||||||
|
"max_tokens": 300,
|
||||||
|
}
|
||||||
|
gpt4vision = GPT4Vision(**custom_config)
|
||||||
|
img = "https://example.com/another_image.jpg"
|
||||||
|
tasks = ["What objects can you identify in this image?"]
|
||||||
|
response = gpt4vision(img, tasks)
|
||||||
|
print(response.answer)
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, we create an instance of `GPT4Vision` with custom configuration options. We set a higher timeout, request high-quality images, and allow more tokens in the response.
|
||||||
|
|
||||||
|
### Example 3: Using the `run` Method
|
||||||
|
|
||||||
|
```python
|
||||||
|
gpt4vision = GPT4Vision()
|
||||||
|
img = "https://example.com/image.jpg"
|
||||||
|
task = "Describe this image in detail."
|
||||||
|
response = gpt4vision.run(task, img)
|
||||||
|
print(response)
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, we use the `run` method to simplify the interaction by providing a single task and image URL.
|
||||||
|
|
||||||
|
# Model Usage and Image Understanding
|
||||||
|
|
||||||
|
The GPT-4 Vision model processes images in a unique way, allowing it to answer questions about both or each of the images independently. Here's an overview:
|
||||||
|
|
||||||
|
| Purpose | Description |
|
||||||
|
| --------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| Image Understanding | The model is shown two copies of the same image and can answer questions about both or each of the images independently. |
|
||||||
|
|
||||||
|
# Image Detail Control
|
||||||
|
|
||||||
|
You have control over how the model processes the image and generates textual understanding by using the `detail` parameter, which has two options: `low` and `high`.
|
||||||
|
|
||||||
|
| Detail | Description |
|
||||||
|
| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
|
| low | Disables the "high-res" model. The model receives a low-res 512 x 512 version of the image and represents the image with a budget of 65 tokens. Ideal for use cases not requiring high detail. |
|
||||||
|
| high | Enables "high-res" mode. The model first sees the low-res image and then creates detailed crops of input images as 512px squares based on the input image size. Uses a total of 129 tokens. |
|
||||||
|
|
||||||
|
# Managing Images
|
||||||
|
|
||||||
|
To use the Chat Completions API effectively, you must manage the images you pass to the model. Here are some key considerations:
|
||||||
|
|
||||||
|
| Management Aspect | Description |
|
||||||
|
| ------------------------- | ------------------------------------------------------------------------------------------------- |
|
||||||
|
| Image Reuse | To pass the same image multiple times, include the image with each API request. |
|
||||||
|
| Image Size Optimization | Improve latency by downsizing images to meet the expected size requirements. |
|
||||||
|
| Image Deletion | After processing, images are deleted from OpenAI servers and not retained. No data is used for training. |
|
||||||
|
|
||||||
|
# Limitations
|
||||||
|
|
||||||
|
While GPT-4 with Vision is powerful, it has some limitations:
|
||||||
|
|
||||||
|
| Limitation | Description |
|
||||||
|
| -------------------------------------------- | --------------------------------------------------------------------------------------------------- |
|
||||||
|
| Medical Images | Not suitable for interpreting specialized medical images like CT scans. |
|
||||||
|
| Non-English Text | May not perform optimally when handling non-Latin alphabets, such as Japanese or Korean. |
|
||||||
|
| Large Text in Images | Enlarge text within images for readability, but avoid cropping important details. |
|
||||||
|
| Rotated or Upside-Down Text/Images | May misinterpret rotated or upside-down text or images. |
|
||||||
|
| Complex Visual Elements | May struggle to understand complex graphs or text with varying colors or styles. |
|
||||||
|
| Spatial Reasoning | Struggles with tasks requiring precise spatial localization, such as identifying chess positions. |
|
||||||
|
| Accuracy | May generate incorrect descriptions or captions in certain scenarios. |
|
||||||
|
| Panoramic and Fisheye Images | Struggles with panoramic and fisheye images. |
|
||||||
|
|
||||||
|
# Calculating Costs
|
||||||
|
|
||||||
|
Image inputs are metered and charged in tokens. The token cost depends on the image size and detail option.
|
||||||
|
|
||||||
|
| Example | Token Cost |
|
||||||
|
| --------------------------------------------- | ----------- |
|
||||||
|
| 1024 x 1024 square image in detail: high mode | 765 tokens |
|
||||||
|
| 2048 x 4096 image in detail: high mode | 1105 tokens |
|
||||||
|
| 4096 x 8192 image in detail: low mode | 85 tokens |
|
||||||
|
|
||||||
|
# FAQ
|
||||||
|
|
||||||
|
Here are some frequently asked questions about GPT-4 with Vision:
|
||||||
|
|
||||||
|
| Question | Answer |
|
||||||
|
| -------------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
||||||
|
| Fine-Tuning Image Capabilities | No, fine-tuning the image capabilities of GPT-4 is not supported at this time. |
|
||||||
|
| Generating Images | GPT-4 is used for understanding images, not generating them. |
|
||||||
|
| Supported Image File Types | Supported image file types include PNG (.png), JPEG (.jpeg and .jpg), WEBP (.webp), and non-animated GIF (.gif). |
|
||||||
|
| Image Size Limitations | Image uploads are restricted to 20MB per image. |
|
||||||
|
| Image Deletion | Uploaded images are automatically deleted after processing by the model. |
|
||||||
|
| Learning More | For more details about GPT-4 with Vision, refer to the GPT-4 with Vision system card. |
|
||||||
|
| CAPTCHA Submission | CAPTCHAs are blocked for safety reasons. |
|
||||||
|
| Rate Limits | Image processing counts toward your tokens per minute (TPM) limit. Refer to the calculating costs section for details. |
|
||||||
|
| Image Metadata | The model does not receive image metadata. |
|
||||||
|
| Handling Unclear Images | If an image is unclear, the model will do its best to interpret it, but results may be less accurate. |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Additional Tips
|
||||||
|
|
||||||
|
- Make sure to handle potential exceptions and errors when making API requests. The library includes retries and error handling, but it's essential to handle exceptions gracefully in your code.
|
||||||
|
- Experiment with different configuration options to optimize the trade-off between response quality and response time based on your specific requirements.
|
||||||
|
|
||||||
|
## References and Resources
|
||||||
|
|
||||||
|
- [OpenAI Platform](https://beta.openai.com/signup/): Sign up for an OpenAI API key.
|
||||||
|
- [OpenAI API Documentation](https://platform.openai.com/docs/api-reference/chat/create): Official API documentation for the GPT-4 Vision model.
|
||||||
|
|
||||||
|
Now you have a comprehensive understanding of the GPT4Vision Model API, its configuration options, and how to use it for various computer vision and natural language processing tasks. Start experimenting and integrating it into your projects to leverage the power of GPT-4 Vision for image-related tasks.
|
||||||
|
|
||||||
|
# Conclusion
|
||||||
|
|
||||||
|
With GPT-4 Vision, you have a powerful tool for understanding and generating textual descriptions for images. By considering its capabilities, limitations, and cost calculations, you can effectively leverage this model for various image-related tasks.
|
@ -0,0 +1,614 @@
|
|||||||
|
# `SequentialWorkflow` Documentation
|
||||||
|
|
||||||
|
The **SequentialWorkflow** class is a Python module designed to facilitate the execution of a sequence of tasks in a sequential manner. It is a part of the `swarms.structs` package and is particularly useful for orchestrating the execution of various callable objects, such as functions or models, in a predefined order. This documentation will provide an in-depth understanding of the **SequentialWorkflow** class, including its purpose, architecture, usage, and examples.
|
||||||
|
|
||||||
|
## Purpose and Relevance
|
||||||
|
|
||||||
|
The **SequentialWorkflow** class is essential for managing and executing a series of tasks or processes, where each task may depend on the outcome of the previous one. It is commonly used in various application scenarios, including but not limited to:
|
||||||
|
|
||||||
|
1. **Natural Language Processing (NLP) Workflows:** In NLP workflows, multiple language models are employed sequentially to process and generate text. Each model may depend on the results of the previous one, making sequential execution crucial.
|
||||||
|
|
||||||
|
2. **Data Analysis Pipelines:** Data analysis often involves a series of tasks such as data preprocessing, transformation, and modeling steps. These tasks must be performed sequentially to ensure data consistency and accuracy.
|
||||||
|
|
||||||
|
3. **Task Automation:** In task automation scenarios, there is a need to execute a series of automated tasks in a specific order. Sequential execution ensures that each task is performed in a predefined sequence, maintaining the workflow's integrity.
|
||||||
|
|
||||||
|
By providing a structured approach to managing these tasks, the **SequentialWorkflow** class helps developers streamline their workflow execution and improve code maintainability.
|
||||||
|
|
||||||
|
## Key Concepts and Terminology
|
||||||
|
|
||||||
|
Before delving into the details of the **SequentialWorkflow** class, let's define some key concepts and terminology that will be used throughout the documentation:
|
||||||
|
|
||||||
|
### Task
|
||||||
|
|
||||||
|
A **task** refers to a specific unit of work that needs to be executed as part of the workflow. Each task is associated with a description and can be implemented as a callable object, such as a function or a model.
|
||||||
|
|
||||||
|
### Flow
|
||||||
|
|
||||||
|
A **flow** represents a callable object that can be a task within the **SequentialWorkflow**. Flows encapsulate the logic and functionality of a particular task. Flows can be functions, models, or any callable object that can be executed.
|
||||||
|
|
||||||
|
### Sequential Execution
|
||||||
|
|
||||||
|
Sequential execution refers to the process of running tasks one after the other in a predefined order. In a **SequentialWorkflow**, tasks are executed sequentially, meaning that each task starts only after the previous one has completed.
|
||||||
|
|
||||||
|
### Workflow
|
||||||
|
|
||||||
|
A **workflow** is a predefined sequence of tasks that need to be executed in a specific order. It represents the overall process or pipeline that the **SequentialWorkflow** manages.
|
||||||
|
|
||||||
|
### Dashboard (Optional)
|
||||||
|
|
||||||
|
A **dashboard** is an optional feature of the **SequentialWorkflow** that provides real-time monitoring and visualization of the workflow's progress. It displays information such as the current task being executed, task results, and other relevant metadata.
|
||||||
|
|
||||||
|
### Max Loops
|
||||||
|
|
||||||
|
The **maximum number of times** the entire workflow can be run. This parameter allows developers to control how many times the workflow is executed.
|
||||||
|
|
||||||
|
### Autosaving
|
||||||
|
|
||||||
|
**Autosaving** is a feature that allows the **SequentialWorkflow** to automatically save its state to a file at specified intervals. This feature helps in resuming a workflow from where it left off, even after interruptions.
|
||||||
|
|
||||||
|
Now that we have a clear understanding of the key concepts and terminology, let's explore the architecture and usage of the **SequentialWorkflow** class in more detail.
|
||||||
|
|
||||||
|
## Architecture of SequentialWorkflow
|
||||||
|
|
||||||
|
The architecture of the **SequentialWorkflow** class is designed to provide a structured and flexible way to define, manage, and execute a sequence of tasks. It comprises the following core components:
|
||||||
|
|
||||||
|
1. **Task**: The **Task** class represents an individual unit of work within the workflow. Each task has a description, which serves as a human-readable identifier for the task. Tasks can be implemented as callable objects, allowing for great flexibility in defining their functionality.
|
||||||
|
|
||||||
|
2. **Workflow**: The **SequentialWorkflow** class itself represents the workflow. It manages a list of tasks in the order they should be executed. Workflows can be run sequentially or asynchronously, depending on the use case.
|
||||||
|
|
||||||
|
3. **Task Execution**: Task execution is the process of running each task in the workflow. Tasks are executed one after another in the order they were added to the workflow. Task results can be passed as inputs to subsequent tasks.
|
||||||
|
|
||||||
|
4. **Dashboard (Optional)**: The **SequentialWorkflow** optionally includes a dashboard feature. The dashboard provides a visual interface for monitoring the progress of the workflow. It displays information about the current task, task results, and other relevant metadata.
|
||||||
|
|
||||||
|
5. **State Management**: The **SequentialWorkflow** supports state management, allowing developers to save and load the state of the workflow to and from JSON files. This feature is valuable for resuming workflows after interruptions or for sharing workflow configurations.
|
||||||
|
|
||||||
|
## Usage of SequentialWorkflow
|
||||||
|
|
||||||
|
The **SequentialWorkflow** class is versatile and can be employed in a wide range of applications. Its usage typically involves the following steps:
|
||||||
|
|
||||||
|
1. **Initialization**: Begin by initializing any callable objects or flows that will serve as tasks in the workflow. These callable objects can include functions, models, or any other Python objects that can be executed.
|
||||||
|
|
||||||
|
2. **Workflow Creation**: Create an instance of the **SequentialWorkflow** class. Specify the maximum number of loops the workflow should run and whether a dashboard should be displayed.
|
||||||
|
|
||||||
|
3. **Task Addition**: Add tasks to the workflow using the `add` method. Each task should be described using a human-readable description, and the associated flow (callable object) should be provided. Additional arguments and keyword arguments can be passed to the task.
|
||||||
|
|
||||||
|
4. **Task Execution**: Execute the workflow using the `run` method. The tasks within the workflow will be executed sequentially, with task results passed as inputs to subsequent tasks.
|
||||||
|
|
||||||
|
5. **Accessing Results**: After running the workflow, you can access the results of each task using the `get_task_results` method or by directly accessing the `result` attribute of each task.
|
||||||
|
|
||||||
|
6. **Optional Features**: Optionally, you can enable features such as autosaving of the workflow state and utilize the dashboard for real-time monitoring.
|
||||||
|
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Before using the Sequential Workflow library, you need to install it. You can install it via pip:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip3 install --upgrade swarms
|
||||||
|
```
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
Let's begin with a quick example to demonstrate how to create and run a Sequential Workflow. In this example, we'll create a workflow that generates a 10,000-word blog on "health and wellness" using an AI model and then summarizes the generated content.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from swarms.models import OpenAIChat
|
||||||
|
from swarms.structs import Flow
|
||||||
|
from swarms.structs.sequential_workflow import SequentialWorkflow
|
||||||
|
|
||||||
|
# Initialize the language model flow (e.g., GPT-3)
|
||||||
|
llm = OpenAIChat(
|
||||||
|
openai_api_key="YOUR_API_KEY",
|
||||||
|
temperature=0.5,
|
||||||
|
max_tokens=3000,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize flows for individual tasks
|
||||||
|
flow1 = Flow(llm=llm, max_loops=1, dashboard=False)
|
||||||
|
flow2 = Flow(llm=llm, max_loops=1, dashboard=False)
|
||||||
|
|
||||||
|
# Create the Sequential Workflow
|
||||||
|
workflow = SequentialWorkflow(max_loops=1)
|
||||||
|
|
||||||
|
# Add tasks to the workflow
|
||||||
|
workflow.add("Generate a 10,000 word blog on health and wellness.", flow1)
|
||||||
|
workflow.add("Summarize the generated blog", flow2)
|
||||||
|
|
||||||
|
# Run the workflow
|
||||||
|
workflow.run()
|
||||||
|
|
||||||
|
# Output the results
|
||||||
|
for task in workflow.tasks:
|
||||||
|
print(f"Task: {task.description}, Result: {task.result}")
|
||||||
|
```
|
||||||
|
|
||||||
|
This quick example demonstrates the basic usage of the Sequential Workflow. It creates two tasks and executes them sequentially.
|
||||||
|
|
||||||
|
## Class: `Task`
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
The `Task` class represents an individual task in the workflow. A task is essentially a callable object, such as a function or a class, that can be executed sequentially. Tasks can have arguments and keyword arguments.
|
||||||
|
|
||||||
|
### Class Definition
|
||||||
|
|
||||||
|
```python
|
||||||
|
class Task:
|
||||||
|
def __init__(self, description: str, flow: Union[Callable, Flow], args: List[Any] = [], kwargs: Dict[str, Any] = {}, result: Any = None, history: List[Any] = [])
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
- `description` (str): A description of the task.
|
||||||
|
- `flow` (Union[Callable, Flow]): The callable object representing the task. It can be a function, class, or a `Flow` instance.
|
||||||
|
- `args` (List[Any]): A list of positional arguments to pass to the task when executed. Default is an empty list.
|
||||||
|
- `kwargs` (Dict[str, Any]): A dictionary of keyword arguments to pass to the task when executed. Default is an empty dictionary.
|
||||||
|
- `result` (Any): The result of the task's execution. Default is `None`.
|
||||||
|
- `history` (List[Any]): A list to store the historical results of the task. Default is an empty list.
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
|
||||||
|
#### `execute()`
|
||||||
|
|
||||||
|
Execute the task.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def execute(self):
|
||||||
|
```
|
||||||
|
|
||||||
|
This method executes the task and updates the `result` and `history` attributes of the task. It checks if the task is a `Flow` instance and if the 'task' argument is needed.
|
||||||
|
|
||||||
|
## Class: `SequentialWorkflow`
|
||||||
|
|
||||||
|
### Description
|
||||||
|
|
||||||
|
The `SequentialWorkflow` class is responsible for managing a sequence of tasks and executing them in a sequential order. It provides methods for adding tasks, running the workflow, and managing the state of the tasks.
|
||||||
|
|
||||||
|
### Class Definition
|
||||||
|
|
||||||
|
```python
|
||||||
|
class SequentialWorkflow:
|
||||||
|
def __init__(self, max_loops: int = 1, autosave: bool = False, saved_state_filepath: Optional[str] = "sequential_workflow_state.json", restore_state_filepath: Optional[str] = None, dashboard: bool = False, tasks: List[Task] = [])
|
||||||
|
```
|
||||||
|
|
||||||
|
### Parameters
|
||||||
|
|
||||||
|
- `max_loops` (int): The maximum number of times to run the workflow sequentially. Default is `1`.
|
||||||
|
- `autosave` (bool): Whether to enable autosaving of the workflow state. Default is `False`.
|
||||||
|
- `saved_state_filepath` (Optional[str]): The file path to save the workflow state when autosave is enabled. Default is `"sequential_workflow_state.json"`.
|
||||||
|
- `restore_state_filepath` (Optional[str]): The file path to restore the workflow state when initializing. Default is `None`.
|
||||||
|
- `dashboard` (bool): Whether to display a dashboard with workflow information. Default is `False`.
|
||||||
|
- `tasks` (List[Task]): A list of `Task` instances representing the tasks in the workflow. Default is an empty list.
|
||||||
|
|
||||||
|
### Methods
|
||||||
|
|
||||||
|
#### `add(task: str, flow: Union[Callable, Flow], *args, **kwargs)`
|
||||||
|
|
||||||
|
Add a task to the workflow.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def add(self, task: str, flow: Union[Callable, Flow], *args, **kwargs) -> None:
|
||||||
|
```
|
||||||
|
|
||||||
|
This method adds a new task to the workflow. You can provide a description of the task, the callable object (function, class, or `Flow` instance), and any additional positional or keyword arguments required for the task.
|
||||||
|
|
||||||
|
#### `reset_workflow()`
|
||||||
|
|
||||||
|
Reset the workflow by clearing the results of each task.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def reset_workflow(self) -> None:
|
||||||
|
```
|
||||||
|
|
||||||
|
This method clears the results of each task in the workflow, allowing you to start fresh without reinitializing the workflow.
|
||||||
|
|
||||||
|
#### `get_task_results()`
|
||||||
|
|
||||||
|
Get the results of each task in the workflow.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def get_task_results(self) -> Dict[str, Any]:
|
||||||
|
```
|
||||||
|
|
||||||
|
This method returns a dictionary containing the results of each task in the workflow, where the keys are task descriptions, and the values are the corresponding results.
|
||||||
|
|
||||||
|
#### `remove_task(task_description: str)`
|
||||||
|
|
||||||
|
Remove a task from the workflow.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def remove_task(self, task_description: str) -> None:
|
||||||
|
```
|
||||||
|
|
||||||
|
This method removes a specific task from the workflow based on its description.
|
||||||
|
|
||||||
|
#### `update_task(task_description: str, **updates)`
|
||||||
|
|
||||||
|
Update the arguments of a task in the workflow.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def update_task(self, task_description: str, **updates) -> None:
|
||||||
|
```
|
||||||
|
|
||||||
|
This method allows you to update the arguments and keyword arguments of a task in the workflow. You specify the task's description and provide the updates as keyword arguments.
|
||||||
|
|
||||||
|
#### `save_workflow_state(filepath: Optional[str] = "sequential_workflow_state.json", **kwargs)`
|
||||||
|
|
||||||
|
Save the workflow state to a JSON file.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def save_workflow_state(self, filepath: Optional[str] = "sequential_workflow_state.json", **kwargs) -> None:
|
||||||
|
```
|
||||||
|
|
||||||
|
This method saves the current state of the workflow, including the results and history of each task, to a JSON file. You can specify the file path for saving the state.
|
||||||
|
|
||||||
|
#### `load_workflow_state(filepath: str = None, **kwargs)`
|
||||||
|
|
||||||
|
Load the workflow state from a JSON file and restore the workflow state.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def load_workflow_state(self, filepath: str = None, **kwargs) -> None:
|
||||||
|
```
|
||||||
|
|
||||||
|
This method loads a previously saved workflow state from a JSON file
|
||||||
|
|
||||||
|
and restores the state, allowing you to continue the workflow from where it was saved. You can specify the file path for loading the state.
|
||||||
|
|
||||||
|
#### `run()`
|
||||||
|
|
||||||
|
Run the workflow sequentially.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def run(self) -> None:
|
||||||
|
```
|
||||||
|
|
||||||
|
This method executes the tasks in the workflow sequentially. It checks if a task is a `Flow` instance and handles the flow of data between tasks accordingly.
|
||||||
|
|
||||||
|
#### `arun()`
|
||||||
|
|
||||||
|
Asynchronously run the workflow.
|
||||||
|
|
||||||
|
```python
|
||||||
|
async def arun(self) -> None:
|
||||||
|
```
|
||||||
|
|
||||||
|
This method asynchronously executes the tasks in the workflow sequentially. It's suitable for use cases where asynchronous execution is required. It also handles data flow between tasks.
|
||||||
|
|
||||||
|
#### `workflow_bootup(**kwargs)`
|
||||||
|
|
||||||
|
Display a bootup message for the workflow.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def workflow_bootup(self, **kwargs) -> None:
|
||||||
|
```
|
||||||
|
|
||||||
|
This method displays a bootup message when the workflow is initialized. You can customize the message by providing additional keyword arguments.
|
||||||
|
|
||||||
|
#### `workflow_dashboard(**kwargs)`
|
||||||
|
|
||||||
|
Display a dashboard for the workflow.
|
||||||
|
|
||||||
|
```python
|
||||||
|
def workflow_dashboard(self, **kwargs) -> None:
|
||||||
|
```
|
||||||
|
|
||||||
|
This method displays a dashboard with information about the workflow, such as the number of tasks, maximum loops, and autosave settings. You can customize the dashboard by providing additional keyword arguments.
|
||||||
|
|
||||||
|
## Examples
|
||||||
|
|
||||||
|
Let's explore some examples to illustrate how to use the Sequential Workflow library effectively.
|
||||||
|
|
||||||
|
Sure, I'll recreate the usage examples section for each method and use case using the provided foundation. Here are the examples:
|
||||||
|
|
||||||
|
### Example 1: Adding Tasks to a Sequential Workflow
|
||||||
|
|
||||||
|
In this example, we'll create a Sequential Workflow and add tasks to it.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from swarms.models import OpenAIChat
|
||||||
|
from swarms.structs import Flow
|
||||||
|
from swarms.structs.sequential_workflow import SequentialWorkflow
|
||||||
|
|
||||||
|
# Example usage
|
||||||
|
api_key = (
|
||||||
|
"" # Your actual API key here
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize the language flow
|
||||||
|
llm = OpenAIChat(
|
||||||
|
openai_api_key=api_key,
|
||||||
|
temperature=0.5,
|
||||||
|
max_tokens=3000,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize Flows for individual tasks
|
||||||
|
flow1 = Flow(llm=llm, max_loops=1, dashboard=False)
|
||||||
|
flow2 = Flow(llm=llm, max_loops=1, dashboard=False)
|
||||||
|
|
||||||
|
# Create the Sequential Workflow
|
||||||
|
workflow = SequentialWorkflow(max_loops=1)
|
||||||
|
|
||||||
|
# Add tasks to the workflow
|
||||||
|
workflow.add("Generate a 10,000 word blog on health and wellness.", flow1)
|
||||||
|
workflow.add("Summarize the generated blog", flow2)
|
||||||
|
|
||||||
|
# Output the list of tasks in the workflow
|
||||||
|
print("Tasks in the workflow:")
|
||||||
|
for task in workflow.tasks:
|
||||||
|
print(f"Task: {task.description}")
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, we create a Sequential Workflow and add two tasks to it.
|
||||||
|
|
||||||
|
### Example 2: Resetting a Sequential Workflow
|
||||||
|
|
||||||
|
In this example, we'll create a Sequential Workflow, add tasks to it, and then reset it.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from swarms.models import OpenAIChat
|
||||||
|
from swarms.structs import Flow
|
||||||
|
from swarms.structs.sequential_workflow import SequentialWorkflow
|
||||||
|
|
||||||
|
# Example usage
|
||||||
|
api_key = (
|
||||||
|
"" # Your actual API key here
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize the language flow
|
||||||
|
llm = OpenAIChat(
|
||||||
|
openai_api_key=api_key,
|
||||||
|
temperature=0.5,
|
||||||
|
max_tokens=3000,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize Flows for individual tasks
|
||||||
|
flow1 = Flow(llm=llm, max_loops=1, dashboard=False)
|
||||||
|
flow2 = Flow(llm=llm, max_loops=1, dashboard=False)
|
||||||
|
|
||||||
|
# Create the Sequential Workflow
|
||||||
|
workflow = SequentialWorkflow(max_loops=1)
|
||||||
|
|
||||||
|
# Add tasks to the workflow
|
||||||
|
workflow.add("Generate a 10,000 word blog on health and wellness.", flow1)
|
||||||
|
workflow.add("Summarize the generated blog", flow2)
|
||||||
|
|
||||||
|
# Reset the workflow
|
||||||
|
workflow.reset_workflow()
|
||||||
|
|
||||||
|
# Output the list of tasks in the workflow after resetting
|
||||||
|
print("Tasks in the workflow after resetting:")
|
||||||
|
for task in workflow.tasks:
|
||||||
|
print(f"Task: {task.description}")
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, we create a Sequential Workflow, add two tasks to it, and then reset the workflow, clearing all task results.
|
||||||
|
|
||||||
|
### Example 3: Getting Task Results from a Sequential Workflow
|
||||||
|
|
||||||
|
In this example, we'll create a Sequential Workflow, add tasks to it, run the workflow, and then retrieve the results of each task.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from swarms.models import OpenAIChat
|
||||||
|
from swarms.structs import Flow
|
||||||
|
from swarms.structs.sequential_workflow import SequentialWorkflow
|
||||||
|
|
||||||
|
# Example usage
|
||||||
|
api_key = (
|
||||||
|
"" # Your actual API key here
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize the language flow
|
||||||
|
llm = OpenAIChat(
|
||||||
|
openai_api_key=api_key,
|
||||||
|
temperature=0.5,
|
||||||
|
max_tokens=3000,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize Flows for individual tasks
|
||||||
|
flow1 = Flow(llm=llm, max_loops=1, dashboard=False)
|
||||||
|
flow2 = Flow(llm=llm, max_loops=1, dashboard=False)
|
||||||
|
|
||||||
|
# Create the Sequential Workflow
|
||||||
|
workflow = SequentialWorkflow(max_loops=1)
|
||||||
|
|
||||||
|
# Add tasks to the workflow
|
||||||
|
workflow.add("Generate a 10,000 word blog on health and wellness.", flow1)
|
||||||
|
workflow.add("Summarize the generated blog", flow2)
|
||||||
|
|
||||||
|
# Run the workflow
|
||||||
|
workflow.run()
|
||||||
|
|
||||||
|
# Get and display the results of each task in the workflow
|
||||||
|
results = workflow.get_task_results()
|
||||||
|
for task_description, result in results.items():
|
||||||
|
print(f"Task: {task_description}, Result: {result}")
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, we create a Sequential Workflow, add two tasks to it, run the workflow, and then retrieve and display the results of each task.
|
||||||
|
|
||||||
|
### Example 4: Removing a Task from a Sequential Workflow
|
||||||
|
|
||||||
|
In this example, we'll create a Sequential Workflow, add tasks to it, and then remove a specific task from the workflow.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from swarms.models import OpenAIChat
|
||||||
|
from swarms.structs import Flow
|
||||||
|
from swarms.structs.sequential_workflow import SequentialWorkflow
|
||||||
|
|
||||||
|
# Example usage
|
||||||
|
api_key = (
|
||||||
|
"" # Your actual API key here
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize the language flow
|
||||||
|
llm = OpenAIChat(
|
||||||
|
openai_api_key=api_key,
|
||||||
|
temperature=0.5,
|
||||||
|
max_tokens=3000,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize Flows for individual tasks
|
||||||
|
flow1 = Flow(llm=llm, max_loops=1, dashboard=False)
|
||||||
|
flow2 = Flow(llm=llm, max_loops=1, dashboard=False)
|
||||||
|
|
||||||
|
# Create the Sequential Workflow
|
||||||
|
workflow = SequentialWorkflow(max_loops=1)
|
||||||
|
|
||||||
|
# Add tasks to the workflow
|
||||||
|
workflow.add("Generate a 10,000 word blog on health and wellness.", flow1)
|
||||||
|
workflow.add("Summarize the generated blog", flow2)
|
||||||
|
|
||||||
|
# Remove a specific task from the workflow
|
||||||
|
workflow.remove_task("Generate a 10,000 word blog on health and wellness.")
|
||||||
|
|
||||||
|
# Output the list of tasks in the workflow after removal
|
||||||
|
print("Tasks in the workflow after removing a task:")
|
||||||
|
for task in workflow.tasks:
|
||||||
|
print(f"Task: {task.description}")
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, we create a Sequential Workflow, add two tasks to it, and then remove a specific task from the workflow.
|
||||||
|
|
||||||
|
### Example 5: Updating Task Arguments in a Sequential Workflow
|
||||||
|
|
||||||
|
In this example, we'll create a Sequential Workflow, add tasks to it, and then update the arguments of a specific task in the workflow.
|
||||||
|
|
||||||
|
```python
|
||||||
|
from swarms.models import OpenAIChat
|
||||||
|
from swarms.structs import Flow
|
||||||
|
from swarms.structs.sequential_workflow import SequentialWorkflow
|
||||||
|
|
||||||
|
# Example usage
|
||||||
|
api_key = (
|
||||||
|
"" # Your actual API key here
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize the language flow
|
||||||
|
llm = OpenAIChat(
|
||||||
|
openai_api_key=api_key,
|
||||||
|
temperature=0.5,
|
||||||
|
max_tokens=3000,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize Flows for individual tasks
|
||||||
|
flow1 = Flow(llm=llm, max_loops=1, dashboard=False)
|
||||||
|
flow2 = Flow(llm=llm, max_loops=1, dashboard=False)
|
||||||
|
|
||||||
|
# Create the Sequential Workflow
|
||||||
|
workflow = SequentialWorkflow(max_loops=1)
|
||||||
|
|
||||||
|
# Add tasks to the workflow
|
||||||
|
workflow.add("Generate a 10,000 word blog on health and wellness.", flow1)
|
||||||
|
workflow.add("Summarize the generated blog", flow2)
|
||||||
|
|
||||||
|
# Update the arguments of a specific task in the workflow
|
||||||
|
workflow.update_task("Generate a 10,000 word blog on health and wellness.", max_loops=2)
|
||||||
|
|
||||||
|
# Output the list of tasks in the workflow after updating task arguments
|
||||||
|
print("Tasks in the workflow after updating task arguments:")
|
||||||
|
for task in workflow.tasks:
|
||||||
|
print(f"Task: {task.description}, Arguments: {
|
||||||
|
|
||||||
|
task.arguments}")
|
||||||
|
```
|
||||||
|
|
||||||
|
In this example, we create a Sequential Workflow, add two tasks to it, and then update the arguments of a specific task in the workflow.
|
||||||
|
|
||||||
|
These examples demonstrate various operations and use cases for working with a Sequential Workflow.
|
||||||
|
|
||||||
|
# Why `SequentialWorkflow`?
|
||||||
|
|
||||||
|
## Enhancing Autonomous Agent Development
|
||||||
|
|
||||||
|
The development of autonomous agents, whether they are conversational AI, robotic systems, or any other AI-driven application, often involves complex workflows that require a sequence of tasks to be executed in a specific order. Managing and orchestrating these tasks efficiently is crucial for building reliable and effective agents. The Sequential Workflow module serves as a valuable tool for AI engineers in achieving this goal.
|
||||||
|
|
||||||
|
## Reliability and Coordination
|
||||||
|
|
||||||
|
One of the primary challenges in autonomous agent development is ensuring that tasks are executed in the correct sequence and that the results of one task can be used as inputs for subsequent tasks. The Sequential Workflow module simplifies this process by allowing AI engineers to define and manage workflows in a structured and organized manner.
|
||||||
|
|
||||||
|
By using the Sequential Workflow module, AI engineers can achieve the following benefits:
|
||||||
|
|
||||||
|
### 1. Improved Reliability
|
||||||
|
|
||||||
|
Reliability is a critical aspect of autonomous agents. The ability to handle errors gracefully and recover from failures is essential for building robust systems. The Sequential Workflow module offers a systematic approach to task execution, making it easier to handle errors, retry failed tasks, and ensure that the agent continues to operate smoothly.
|
||||||
|
|
||||||
|
### 2. Task Coordination
|
||||||
|
|
||||||
|
Coordinating tasks in the correct order is essential for achieving the desired outcome. The Sequential Workflow module enforces task sequencing, ensuring that each task is executed only when its dependencies are satisfied. This eliminates the risk of executing tasks out of order, which can lead to incorrect results.
|
||||||
|
|
||||||
|
### 3. Code Organization
|
||||||
|
|
||||||
|
Managing complex workflows can become challenging without proper organization. The Sequential Workflow module encourages AI engineers to structure their code in a modular and maintainable way. Each task can be encapsulated as a separate unit, making it easier to understand, modify, and extend the agent's behavior.
|
||||||
|
|
||||||
|
### 4. Workflow Visualization
|
||||||
|
|
||||||
|
Visualization is a powerful tool for understanding and debugging workflows. The Sequential Workflow module can be extended to include a visualization dashboard, allowing AI engineers to monitor the progress of tasks, track results, and identify bottlenecks or performance issues.
|
||||||
|
|
||||||
|
## TODO: Future Features
|
||||||
|
|
||||||
|
While the Sequential Workflow module offers significant advantages, there are opportunities for further enhancement. Here is a list of potential features and improvements that can be added to make it even more versatile and adaptable for various AI engineering tasks:
|
||||||
|
|
||||||
|
### 1. Asynchronous Support
|
||||||
|
|
||||||
|
Adding support for asynchronous task execution can improve the efficiency of workflows, especially when dealing with tasks that involve waiting for external events or resources.
|
||||||
|
|
||||||
|
### 2. Context Managers
|
||||||
|
|
||||||
|
Introducing context manager support for tasks can simplify resource management, such as opening and closing files, database connections, or network connections within a task's context.
|
||||||
|
|
||||||
|
### 3. Workflow History
|
||||||
|
|
||||||
|
Maintaining a detailed history of workflow execution, including timestamps, task durations, and input/output data, can facilitate debugging and performance analysis.
|
||||||
|
|
||||||
|
### 4. Parallel Processing
|
||||||
|
|
||||||
|
Enhancing the module to support parallel processing with a pool of workers can significantly speed up the execution of tasks, especially for computationally intensive workflows.
|
||||||
|
|
||||||
|
### 5. Error Handling Strategies
|
||||||
|
|
||||||
|
Providing built-in error handling strategies, such as retries, fallbacks, and custom error handling functions, can make the module more robust in handling unexpected failures.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
The Sequential Workflow module is a valuable tool for AI engineers working on autonomous agents and complex AI-driven applications. It offers a structured and reliable approach to defining and executing workflows, ensuring that tasks are performed in the correct sequence. By using this module, AI engineers can enhance the reliability, coordination, and maintainability of their agents.
|
||||||
|
|
||||||
|
As the field of AI continues to evolve, the demand for efficient workflow management tools will only increase. The Sequential Workflow module is a step towards meeting these demands and empowering AI engineers to create more reliable and capable autonomous agents. With future enhancements and features, it has the potential to become an indispensable asset in the AI engineer's toolkit.
|
||||||
|
|
||||||
|
In summary, the Sequential Workflow module provides a foundation for orchestrating complex tasks and workflows, enabling AI engineers to focus on designing intelligent agents that can perform tasks with precision and reliability.
|
||||||
|
|
||||||
|
|
||||||
|
## Frequently Asked Questions (FAQs)
|
||||||
|
|
||||||
|
### Q1: What is the difference between a task and a flow in Sequential Workflows?
|
||||||
|
|
||||||
|
**A1:** In Sequential Workflows, a **task** refers to a specific unit of work that needs to be executed. It can be implemented as a callable object, such as a Python function, and is the fundamental building block of a workflow.
|
||||||
|
|
||||||
|
A **flow**, on the other hand, is an encapsulation of a task within the workflow. Flows define the order in which tasks are executed and can be thought of as task containers. They allow you to specify dependencies, error handling, and other workflow-related configurations.
|
||||||
|
|
||||||
|
### Q2: Can I run tasks in parallel within a Sequential Workflow?
|
||||||
|
|
||||||
|
**A2:** Yes, you can run tasks in parallel within a Sequential Workflow by using parallel execution techniques. This advanced feature allows you to execute multiple tasks concurrently, improving performance and efficiency. You can explore this feature further in the guide's section on "Parallel Execution."
|
||||||
|
|
||||||
|
### Q3: How do I handle errors within Sequential Workflows?
|
||||||
|
|
||||||
|
**A3:** Error handling within Sequential Workflows can be implemented by adding error-handling logic within your task functions. You can catch exceptions and handle errors gracefully, ensuring that your workflow can recover from unexpected scenarios. The guide also covers more advanced error handling strategies, such as retrying failed tasks and handling specific error types.
|
||||||
|
|
||||||
|
### Q4: What are some real-world use cases for Sequential Workflows?
|
||||||
|
|
||||||
|
**A4:** Sequential Workflows can be applied to a wide range of real-world use cases, including:
|
||||||
|
|
||||||
|
- **Data ETL (Extract, Transform, Load) Processes:** Automating data pipelines that involve data extraction, transformation, and loading into databases or data warehouses.
|
||||||
|
|
||||||
|
- **Batch Processing:** Running batch jobs that process large volumes of data or perform data analysis.
|
||||||
|
|
||||||
|
- **Automation of DevOps Tasks:** Streamlining DevOps processes such as deployment, provisioning, and monitoring.
|
||||||
|
|
||||||
|
- **Cross-system Integrations:** Automating interactions between different systems, services, or APIs.
|
||||||
|
|
||||||
|
- **Report Generation:** Generating reports and documents automatically based on data inputs.
|
||||||
|
|
||||||
|
- **Workflow Orchestration:** Orchestrating complex workflows involving multiple steps and dependencies.
|
||||||
|
|
||||||
|
- **Resource Provisioning:** Automatically provisioning and managing cloud resources.
|
||||||
|
|
||||||
|
These are just a few examples, and Sequential Workflows can be tailored to various automation needs across industries.
|
@ -1,24 +1,39 @@
|
|||||||
from swarms.models import OpenAIChat
|
from swarms.models import OpenAIChat
|
||||||
from swarms import Worker
|
from swarms.structs import Flow
|
||||||
from swarms.prompts import PRODUCT_AGENT_PROMPT
|
|
||||||
|
|
||||||
api_key = ""
|
api_key = ""
|
||||||
|
|
||||||
|
# Initialize the language model, this model can be swapped out with Anthropic, ETC, Huggingface Models like Mistral, ETC
|
||||||
llm = OpenAIChat(
|
llm = OpenAIChat(
|
||||||
|
# model_name="gpt-4"
|
||||||
openai_api_key=api_key,
|
openai_api_key=api_key,
|
||||||
temperature=0.5,
|
temperature=0.5,
|
||||||
|
# max_tokens=100,
|
||||||
)
|
)
|
||||||
|
|
||||||
node = Worker(
|
|
||||||
|
## Initialize the workflow
|
||||||
|
flow = Flow(
|
||||||
llm=llm,
|
llm=llm,
|
||||||
ai_name="Optimus Prime",
|
max_loops=5,
|
||||||
openai_api_key=api_key,
|
dashboard=True,
|
||||||
ai_role=PRODUCT_AGENT_PROMPT,
|
# tools = [search_api, slack, ]
|
||||||
external_tools=None,
|
# stopping_condition=None, # You can define a stopping condition as needed.
|
||||||
human_in_the_loop=False,
|
# loop_interval=1,
|
||||||
temperature=0.5,
|
# retry_attempts=3,
|
||||||
|
# retry_interval=1,
|
||||||
|
# interactive=False, # Set to 'True' for interactive mode.
|
||||||
|
# dynamic_temperature=False, # Set to 'True' for dynamic temperature handling.
|
||||||
)
|
)
|
||||||
|
|
||||||
task = "Locate 5 trending topics on healthy living, locate a website like NYTimes, and then generate an image of people doing those topics."
|
# out = flow.load_state("flow_state.json")
|
||||||
response = node.run(task)
|
# temp = flow.dynamic_temperature()
|
||||||
print(response)
|
# filter = flow.add_response_filter("Trump")
|
||||||
|
out = flow.run(
|
||||||
|
"Generate a 10,000 word blog on mental clarity and the benefits of meditation."
|
||||||
|
)
|
||||||
|
# out = flow.validate_response(out)
|
||||||
|
# out = flow.analyze_feedback(out)
|
||||||
|
# out = flow.print_history_and_memory()
|
||||||
|
# # out = flow.save_state("flow_state.json")
|
||||||
|
# print(out)
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
from swarms.models import OpenAIChat
|
|
||||||
from swarms.structs import Flow
|
|
||||||
|
|
||||||
api_key = ""
|
|
||||||
|
|
||||||
# Initialize the language model, this model can be swapped out with Anthropic, ETC, Huggingface Models like Mistral, ETC
|
|
||||||
llm = OpenAIChat(
|
|
||||||
openai_api_key=api_key,
|
|
||||||
temperature=0.5,
|
|
||||||
max_tokens=3000,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Initialize the flow
|
|
||||||
flow = Flow(
|
|
||||||
llm=llm,
|
|
||||||
max_loops=5,
|
|
||||||
dashboard=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
flow = Flow(
|
|
||||||
llm=llm,
|
|
||||||
max_loops=5,
|
|
||||||
dashboard=True,
|
|
||||||
# stopping_condition=None, # You can define a stopping condition as needed.
|
|
||||||
# loop_interval=1,
|
|
||||||
# retry_attempts=3,
|
|
||||||
# retry_interval=1,
|
|
||||||
# interactive=False, # Set to 'True' for interactive mode.
|
|
||||||
# dynamic_temperature=False, # Set to 'True' for dynamic temperature handling.
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
out = flow.run("Generate a 10,000 word blog on health and wellness.")
|
|
||||||
|
|
||||||
print(out)
|
|
@ -1,16 +0,0 @@
|
|||||||
from swarms.swarms import GodMode
|
|
||||||
from swarms.models import OpenAIChat
|
|
||||||
|
|
||||||
api_key = ""
|
|
||||||
|
|
||||||
llm = OpenAIChat(openai_api_key=api_key)
|
|
||||||
|
|
||||||
|
|
||||||
llms = [llm, llm, llm]
|
|
||||||
|
|
||||||
god_mode = GodMode(llms)
|
|
||||||
|
|
||||||
task = "Generate a 10,000 word blog on health and wellness."
|
|
||||||
|
|
||||||
out = god_mode.run(task)
|
|
||||||
god_mode.print_responses(task)
|
|
@ -1,109 +1,49 @@
|
|||||||
# from swarms.structs import Flow
|
from swarms import OpenAI, Flow
|
||||||
# from swarms.models import OpenAIChat
|
from swarms.swarms.groupchat import GroupChatManager, GroupChat
|
||||||
# from swarms.swarms.groupchat import GroupChat
|
|
||||||
# from swarms.agents import SimpleAgent
|
|
||||||
|
|
||||||
# api_key = ""
|
|
||||||
|
|
||||||
# llm = OpenAIChat(
|
api_key = ""
|
||||||
# openai_api_key=api_key,
|
|
||||||
# )
|
|
||||||
|
|
||||||
# agent1 = SimpleAgent("Captain Price", Flow(llm=llm, max_loops=4))
|
llm = OpenAI(
|
||||||
# agent2 = SimpleAgent("John Mactavis", Flow(llm=llm, max_loops=4))
|
|
||||||
|
|
||||||
# # Create a groupchat with the 2 agents
|
|
||||||
# chat = GroupChat([agent1, agent2])
|
|
||||||
|
|
||||||
# # Assign duties to the agents
|
|
||||||
# chat.assign_duty(agent1.name, "Buy the groceries")
|
|
||||||
# chat.assign_duty(agent2.name, "Clean the house")
|
|
||||||
|
|
||||||
# # Initate a chat
|
|
||||||
# response = chat.run("Captain Price", "Hello, how are you John?")
|
|
||||||
# print(response)
|
|
||||||
|
|
||||||
|
|
||||||
from swarms.models import OpenAIChat
|
|
||||||
from swarms.structs import Flow
|
|
||||||
import random
|
|
||||||
|
|
||||||
api_key = "" # Your API Key here
|
|
||||||
|
|
||||||
|
|
||||||
class GroupChat:
|
|
||||||
"""
|
|
||||||
GroupChat class that facilitates agent-to-agent communication using multiple instances of the Flow class.
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, agents: list):
|
|
||||||
self.agents = {f"agent_{i}": agent for i, agent in enumerate(agents)}
|
|
||||||
self.message_log = []
|
|
||||||
|
|
||||||
def add_agent(self, agent: Flow):
|
|
||||||
agent_id = f"agent_{len(self.agents)}"
|
|
||||||
self.agents[agent_id] = agent
|
|
||||||
|
|
||||||
def remove_agent(self, agent_id: str):
|
|
||||||
if agent_id in self.agents:
|
|
||||||
del self.agents[agent_id]
|
|
||||||
|
|
||||||
def send_message(self, sender_id: str, recipient_id: str, message: str):
|
|
||||||
if sender_id not in self.agents or recipient_id not in self.agents:
|
|
||||||
raise ValueError("Invalid sender or recipient ID.")
|
|
||||||
formatted_message = f"{sender_id} to {recipient_id}: {message}"
|
|
||||||
self.message_log.append(formatted_message)
|
|
||||||
recipient_agent = self.agents[recipient_id]
|
|
||||||
recipient_agent.run(message)
|
|
||||||
|
|
||||||
def broadcast_message(self, sender_id: str, message: str):
|
|
||||||
for agent_id, agent in self.agents.items():
|
|
||||||
if agent_id != sender_id:
|
|
||||||
self.send_message(sender_id, agent_id, message)
|
|
||||||
|
|
||||||
def get_message_log(self):
|
|
||||||
return self.message_log
|
|
||||||
|
|
||||||
|
|
||||||
class EnhancedGroupChatV2(GroupChat):
|
|
||||||
def __init__(self, agents: list):
|
|
||||||
super().__init__(agents)
|
|
||||||
|
|
||||||
def multi_round_conversation(self, rounds: int = 5):
|
|
||||||
"""
|
|
||||||
Initiate a multi-round conversation between agents.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
rounds (int): The number of rounds of conversation.
|
|
||||||
"""
|
|
||||||
for _ in range(rounds):
|
|
||||||
# Randomly select a sender and recipient agent for the conversation
|
|
||||||
sender_id = random.choice(list(self.agents.keys()))
|
|
||||||
recipient_id = random.choice(list(self.agents.keys()))
|
|
||||||
while recipient_id == sender_id: # Ensure the recipient is not the sender
|
|
||||||
recipient_id = random.choice(list(self.agents.keys()))
|
|
||||||
|
|
||||||
# Generate a message (for simplicity, a generic message is used)
|
|
||||||
message = f"Hello {recipient_id}, how are you today?"
|
|
||||||
self.send_message(sender_id, recipient_id, message)
|
|
||||||
|
|
||||||
|
|
||||||
# Sample usage with EnhancedGroupChatV2
|
|
||||||
# Initialize the language model
|
|
||||||
llm = OpenAIChat(
|
|
||||||
openai_api_key=api_key,
|
openai_api_key=api_key,
|
||||||
temperature=0.5,
|
temperature=0.5,
|
||||||
max_tokens=3000,
|
max_tokens=3000,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Initialize two Flow agents
|
# Initialize the flow
|
||||||
agent1 = Flow(llm=llm, max_loops=5, dashboard=True)
|
flow1 = Flow(
|
||||||
agent2 = Flow(llm=llm, max_loops=5, dashboard=True)
|
llm=llm,
|
||||||
|
max_loops=1,
|
||||||
|
system_message="YOU ARE SILLY, YOU OFFER NOTHING OF VALUE",
|
||||||
|
name="silly",
|
||||||
|
dashboard=True,
|
||||||
|
)
|
||||||
|
flow2 = Flow(
|
||||||
|
llm=llm,
|
||||||
|
max_loops=1,
|
||||||
|
system_message="YOU ARE VERY SMART AND ANSWER RIDDLES",
|
||||||
|
name="detective",
|
||||||
|
dashboard=True,
|
||||||
|
)
|
||||||
|
flow3 = Flow(
|
||||||
|
llm=llm,
|
||||||
|
max_loops=1,
|
||||||
|
system_message="YOU MAKE RIDDLES",
|
||||||
|
name="riddler",
|
||||||
|
dashboard=True,
|
||||||
|
)
|
||||||
|
manager = Flow(
|
||||||
|
llm=llm,
|
||||||
|
max_loops=1,
|
||||||
|
system_message="YOU ARE A GROUP CHAT MANAGER",
|
||||||
|
name="manager",
|
||||||
|
dashboard=True,
|
||||||
|
)
|
||||||
|
|
||||||
# Create an enhanced group chat with the two agents
|
|
||||||
enhanced_group_chat_v2 = EnhancedGroupChatV2(agents=[agent1, agent2])
|
|
||||||
|
|
||||||
# Simulate multi-round agent to agent communication
|
# Example usage:
|
||||||
enhanced_group_chat_v2.multi_round_conversation(rounds=5)
|
agents = [flow1, flow2, flow3]
|
||||||
|
|
||||||
enhanced_group_chat_v2.get_message_log() # Get the conversation log
|
group_chat = GroupChat(agents=agents, messages=[], max_round=10)
|
||||||
|
chat_manager = GroupChatManager(groupchat=group_chat, selector=manager)
|
||||||
|
chat_history = chat_manager("Write me a riddle")
|
||||||
|
@ -0,0 +1,30 @@
|
|||||||
|
from swarms.structs import Flow
|
||||||
|
from swarms.models import Idefics
|
||||||
|
|
||||||
|
# Multi Modality Auto Agent
|
||||||
|
llm = Idefics(max_length=2000)
|
||||||
|
|
||||||
|
task = "User: What is in this image? https://upload.wikimedia.org/wikipedia/commons/8/86/Id%C3%A9fix.JPG"
|
||||||
|
|
||||||
|
## Initialize the workflow
|
||||||
|
flow = Flow(
|
||||||
|
llm=llm,
|
||||||
|
max_loops=2,
|
||||||
|
dashboard=True,
|
||||||
|
# stopping_condition=None, # You can define a stopping condition as needed.
|
||||||
|
# loop_interval=1,
|
||||||
|
# retry_attempts=3,
|
||||||
|
# retry_interval=1,
|
||||||
|
# interactive=False, # Set to 'True' for interactive mode.
|
||||||
|
# dynamic_temperature=False, # Set to 'True' for dynamic temperature handling.
|
||||||
|
)
|
||||||
|
|
||||||
|
# out = flow.load_state("flow_state.json")
|
||||||
|
# temp = flow.dynamic_temperature()
|
||||||
|
# filter = flow.add_response_filter("Trump")
|
||||||
|
out = flow.run(task)
|
||||||
|
# out = flow.validate_response(out)
|
||||||
|
# out = flow.analyze_feedback(out)
|
||||||
|
# out = flow.print_history_and_memory()
|
||||||
|
# # out = flow.save_state("flow_state.json")
|
||||||
|
# print(out)
|
@ -0,0 +1,6 @@
|
|||||||
|
from swarms.models.dalle3 import Dalle3
|
||||||
|
|
||||||
|
model = Dalle3()
|
||||||
|
|
||||||
|
task = "A painting of a dog"
|
||||||
|
img = model(task)
|
@ -0,0 +1,7 @@
|
|||||||
|
from swarms.models.gpt4v import GPT4Vision
|
||||||
|
|
||||||
|
gpt4vision = GPT4Vision(api_key="")
|
||||||
|
task = "What is the following image about?"
|
||||||
|
img = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png"
|
||||||
|
|
||||||
|
answer = gpt4vision.run(task, img)
|
@ -1,56 +0,0 @@
|
|||||||
from swarms.models import OpenAIChat # Replace with your actual OpenAIChat import
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
api_key = "" # Your OpenAI API key here
|
|
||||||
agent = MultiTempAgent(api_key)
|
|
||||||
|
|
||||||
prompt = "Write a blog post about health and wellness"
|
|
||||||
final_output = agent.run(prompt)
|
|
||||||
|
|
||||||
print("Final chosen output:")
|
|
||||||
print(final_output)
|
|
||||||
|
|
||||||
|
|
||||||
class MultiTempAgent:
|
|
||||||
def __init__(self, api_key, default_temp=0.5, alt_temps=[0.2, 0.7, 0.9]):
|
|
||||||
self.api_key = api_key
|
|
||||||
self.default_temp = default_temp
|
|
||||||
self.alt_temps = alt_temps
|
|
||||||
|
|
||||||
def ask_user_feedback(self, text):
|
|
||||||
print(f"Generated text: {text}")
|
|
||||||
feedback = input("Are you satisfied with this output? (yes/no): ")
|
|
||||||
return feedback.lower() == "yes"
|
|
||||||
|
|
||||||
def present_options_to_user(self, outputs):
|
|
||||||
print("Alternative outputs:")
|
|
||||||
for temp, output in outputs.items():
|
|
||||||
print(f"Temperature {temp}: {output}")
|
|
||||||
chosen_temp = float(input("Choose the temperature of the output you like: "))
|
|
||||||
return outputs.get(chosen_temp, "Invalid temperature chosen.")
|
|
||||||
|
|
||||||
def run(self, prompt):
|
|
||||||
try:
|
|
||||||
llm = OpenAIChat(openai_api_key=self.api_key, temperature=self.default_temp)
|
|
||||||
initial_output = llm(prompt) # Using llm as a callable
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error generating initial output: {e}")
|
|
||||||
initial_output = None
|
|
||||||
|
|
||||||
user_satisfied = self.ask_user_feedback(initial_output)
|
|
||||||
|
|
||||||
if user_satisfied:
|
|
||||||
return initial_output
|
|
||||||
else:
|
|
||||||
outputs = {}
|
|
||||||
for temp in self.alt_temps:
|
|
||||||
try:
|
|
||||||
llm = OpenAIChat(
|
|
||||||
openai_api_key=self.api_key, temperature=temp
|
|
||||||
) # Re-initializing
|
|
||||||
outputs[temp] = llm(prompt) # Using llm as a callable
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error generating text at temperature {temp}: {e}")
|
|
||||||
outputs[temp] = None
|
|
||||||
chosen_output = self.present_options_to_user(outputs)
|
|
||||||
return chosen_output
|
|
@ -0,0 +1,35 @@
|
|||||||
|
from swarms.models import OpenAIChat
|
||||||
|
from swarms.structs import Flow
|
||||||
|
|
||||||
|
api_key = ""
|
||||||
|
|
||||||
|
# Initialize the language model, this model can be swapped out with Anthropic, ETC, Huggingface Models like Mistral, ETC
|
||||||
|
llm = OpenAIChat(
|
||||||
|
# model_name="gpt-4"
|
||||||
|
openai_api_key=api_key,
|
||||||
|
temperature=0.5,
|
||||||
|
# max_tokens=100,
|
||||||
|
)
|
||||||
|
|
||||||
|
## Initialize the workflow
|
||||||
|
flow = Flow(
|
||||||
|
llm=llm,
|
||||||
|
max_loops=2,
|
||||||
|
dashboard=True,
|
||||||
|
# stopping_condition=None, # You can define a stopping condition as needed.
|
||||||
|
# loop_interval=1,
|
||||||
|
# retry_attempts=3,
|
||||||
|
# retry_interval=1,
|
||||||
|
# interactive=False, # Set to 'True' for interactive mode.
|
||||||
|
# dynamic_temperature=False, # Set to 'True' for dynamic temperature handling.
|
||||||
|
)
|
||||||
|
|
||||||
|
# out = flow.load_state("flow_state.json")
|
||||||
|
# temp = flow.dynamic_temperature()
|
||||||
|
# filter = flow.add_response_filter("Trump")
|
||||||
|
out = flow.run("Generate a 10,000 word blog on health and wellness.")
|
||||||
|
# out = flow.validate_response(out)
|
||||||
|
# out = flow.analyze_feedback(out)
|
||||||
|
# out = flow.print_history_and_memory()
|
||||||
|
# # out = flow.save_state("flow_state.json")
|
||||||
|
# print(out)
|
@ -0,0 +1,31 @@
|
|||||||
|
from swarms.models import OpenAIChat
|
||||||
|
from swarms.structs import Flow
|
||||||
|
from swarms.structs.sequential_workflow import SequentialWorkflow
|
||||||
|
|
||||||
|
# Example usage
|
||||||
|
llm = OpenAIChat(
|
||||||
|
temperature=0.5,
|
||||||
|
max_tokens=3000,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize the Flow with the language flow
|
||||||
|
flow1 = Flow(llm=llm, max_loops=1, dashboard=False)
|
||||||
|
|
||||||
|
# Create another Flow for a different task
|
||||||
|
flow2 = Flow(llm=llm, max_loops=1, dashboard=False)
|
||||||
|
|
||||||
|
# Create the workflow
|
||||||
|
workflow = SequentialWorkflow(max_loops=1)
|
||||||
|
|
||||||
|
# Add tasks to the workflow
|
||||||
|
workflow.add("Generate a 10,000 word blog on health and wellness.", flow1)
|
||||||
|
|
||||||
|
# Suppose the next task takes the output of the first task as input
|
||||||
|
workflow.add("Summarize the generated blog", flow2)
|
||||||
|
|
||||||
|
# Run the workflow
|
||||||
|
workflow.run()
|
||||||
|
|
||||||
|
# Output the results
|
||||||
|
for task in workflow.tasks:
|
||||||
|
print(f"Task: {task.description}, Result: {task.result}")
|
@ -1,39 +1,16 @@
|
|||||||
|
from swarms.swarms import GodMode
|
||||||
from swarms.models import OpenAIChat
|
from swarms.models import OpenAIChat
|
||||||
|
|
||||||
from swarms.swarms import GodMode
|
api_key = ""
|
||||||
from swarms.workers.worker import Worker
|
|
||||||
|
llm = OpenAIChat(openai_api_key=api_key)
|
||||||
|
|
||||||
llm = OpenAIChat(model_name="gpt-4", openai_api_key="api-key", temperature=0.5)
|
|
||||||
|
|
||||||
worker1 = Worker(
|
llms = [llm, llm, llm]
|
||||||
llm=llm,
|
|
||||||
ai_name="Bumble Bee",
|
|
||||||
ai_role="Worker in a swarm",
|
|
||||||
external_tools=None,
|
|
||||||
human_in_the_loop=False,
|
|
||||||
temperature=0.5,
|
|
||||||
)
|
|
||||||
worker2 = Worker(
|
|
||||||
llm=llm,
|
|
||||||
ai_name="Optimus Prime",
|
|
||||||
ai_role="Worker in a swarm",
|
|
||||||
external_tools=None,
|
|
||||||
human_in_the_loop=False,
|
|
||||||
temperature=0.5,
|
|
||||||
)
|
|
||||||
worker3 = Worker(
|
|
||||||
llm=llm,
|
|
||||||
ai_name="Megatron",
|
|
||||||
ai_role="Worker in a swarm",
|
|
||||||
external_tools=None,
|
|
||||||
human_in_the_loop=False,
|
|
||||||
temperature=0.5,
|
|
||||||
)
|
|
||||||
# Usage
|
|
||||||
agents = [worker1, worker2, worker3]
|
|
||||||
|
|
||||||
god_mode = GodMode(agents)
|
god_mode = GodMode(llms)
|
||||||
|
|
||||||
task = "What are the biggest risks facing humanity?"
|
task = "Generate a 10,000 word blog on health and wellness."
|
||||||
|
|
||||||
|
out = god_mode.run(task)
|
||||||
god_mode.print_responses(task)
|
god_mode.print_responses(task)
|
||||||
|
@ -1,61 +1,49 @@
|
|||||||
from swarms.models import OpenAIChat
|
from swarms import OpenAI, Flow
|
||||||
from swarms.swarms import GroupChat, GroupChatManager
|
from swarms.swarms.groupchat import GroupChatManager, GroupChat
|
||||||
from swarms.workers import Worker
|
|
||||||
|
|
||||||
llm = OpenAIChat(model_name="gpt-4", openai_api_key="api-key", temperature=0.5)
|
|
||||||
|
|
||||||
node = Worker(
|
api_key = ""
|
||||||
llm=llm,
|
|
||||||
ai_name="Optimus Prime",
|
llm = OpenAI(
|
||||||
ai_role="Worker in a swarm",
|
openai_api_key=api_key,
|
||||||
external_tools=None,
|
|
||||||
human_in_the_loop=False,
|
|
||||||
temperature=0.5,
|
temperature=0.5,
|
||||||
|
max_tokens=3000,
|
||||||
)
|
)
|
||||||
|
|
||||||
node2 = Worker(
|
# Initialize the flow
|
||||||
|
flow1 = Flow(
|
||||||
llm=llm,
|
llm=llm,
|
||||||
ai_name="Optimus Prime",
|
max_loops=1,
|
||||||
ai_role="Worker in a swarm",
|
system_message="YOU ARE SILLY, YOU OFFER NOTHING OF VALUE",
|
||||||
external_tools=None,
|
name="silly",
|
||||||
human_in_the_loop=False,
|
dashboard=True,
|
||||||
temperature=0.5,
|
|
||||||
)
|
)
|
||||||
|
flow2 = Flow(
|
||||||
node3 = Worker(
|
|
||||||
llm=llm,
|
llm=llm,
|
||||||
ai_name="Optimus Prime",
|
max_loops=1,
|
||||||
ai_role="Worker in a swarm",
|
system_message="YOU ARE VERY SMART AND ANSWER RIDDLES",
|
||||||
external_tools=None,
|
name="detective",
|
||||||
human_in_the_loop=False,
|
dashboard=True,
|
||||||
temperature=0.5,
|
|
||||||
)
|
)
|
||||||
|
flow3 = Flow(
|
||||||
nodes = [node, node2, node3]
|
llm=llm,
|
||||||
|
max_loops=1,
|
||||||
messages = [
|
system_message="YOU MAKE RIDDLES",
|
||||||
{
|
name="riddler",
|
||||||
"role": "system",
|
dashboard=True,
|
||||||
"context": "Create an a small feedforward in pytorch",
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
group = GroupChat(
|
|
||||||
workers=nodes,
|
|
||||||
messages=messages,
|
|
||||||
max_rounds=3,
|
|
||||||
)
|
)
|
||||||
|
manager = Flow(
|
||||||
|
llm=llm,
|
||||||
manager = GroupChatManager(
|
max_loops=1,
|
||||||
groupchat=group,
|
system_message="YOU ARE A GROUP CHAT MANAGER",
|
||||||
max_consecutive_auto_reply=3,
|
name="manager",
|
||||||
|
dashboard=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
output = group.run(
|
|
||||||
messages,
|
|
||||||
sender=node,
|
|
||||||
config=group,
|
|
||||||
)
|
|
||||||
|
|
||||||
print(output)
|
# Example usage:
|
||||||
|
agents = [flow1, flow2, flow3]
|
||||||
|
|
||||||
|
group_chat = GroupChat(agents=agents, messages=[], max_round=10)
|
||||||
|
chat_manager = GroupChatManager(groupchat=group_chat, selector=manager)
|
||||||
|
chat_history = chat_manager("Write me a riddle")
|
||||||
|
@ -0,0 +1,35 @@
|
|||||||
|
from swarms.models import OpenAIChat
|
||||||
|
from swarms.structs import Flow
|
||||||
|
from swarms.structs.sequential_workflow import SequentialWorkflow
|
||||||
|
|
||||||
|
# Example usage
|
||||||
|
api_key = "" # Your actual API key here
|
||||||
|
|
||||||
|
# Initialize the language flow
|
||||||
|
llm = OpenAIChat(
|
||||||
|
openai_api_key=api_key,
|
||||||
|
temperature=0.5,
|
||||||
|
max_tokens=3000,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Initialize the Flow with the language flow
|
||||||
|
flow1 = Flow(llm=llm, max_loops=1, dashboard=False)
|
||||||
|
|
||||||
|
# Create another Flow for a different task
|
||||||
|
flow2 = Flow(llm=llm, max_loops=1, dashboard=False)
|
||||||
|
|
||||||
|
# Create the workflow
|
||||||
|
workflow = SequentialWorkflow(max_loops=1)
|
||||||
|
|
||||||
|
# Add tasks to the workflow
|
||||||
|
workflow.add("Generate a 10,000 word blog on health and wellness.", flow1)
|
||||||
|
|
||||||
|
# Suppose the next task takes the output of the first task as input
|
||||||
|
workflow.add("Summarize the generated blog", flow2)
|
||||||
|
|
||||||
|
# Run the workflow
|
||||||
|
workflow.run()
|
||||||
|
|
||||||
|
# Output the results
|
||||||
|
for task in workflow.tasks:
|
||||||
|
print(f"Task: {task.description}, Result: {task.result}")
|
@ -0,0 +1,4 @@
|
|||||||
|
"""
|
||||||
|
Companion agents converse with the user about the agent the user wants to create then creates the agent with the desired attributes and traits and tools and configurations
|
||||||
|
|
||||||
|
"""
|
@ -0,0 +1,124 @@
|
|||||||
|
"""
|
||||||
|
Omni Chunker is a chunker that chunks all files into select chunks of size x strings
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
--------------
|
||||||
|
from swarms.chunkers.omni_chunker import OmniChunker
|
||||||
|
|
||||||
|
# Example
|
||||||
|
pdf = "swarmdeck.pdf"
|
||||||
|
chunker = OmniChunker(chunk_size=1000, beautify=True)
|
||||||
|
chunks = chunker(pdf)
|
||||||
|
print(chunks)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import List, Optional, Callable
|
||||||
|
from termcolor import colored
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class OmniChunker:
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
chunk_size: int = 1000
|
||||||
|
beautify: bool = False
|
||||||
|
use_tokenizer: bool = False
|
||||||
|
tokenizer: Optional[Callable[[str], List[str]]] = None
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def __call__(self, file_path: str) -> List[str]:
|
||||||
|
"""
|
||||||
|
Chunk the given file into parts of size `chunk_size`.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
file_path (str): The path to the file to chunk.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[str]: A list of string chunks from the file.
|
||||||
|
"""
|
||||||
|
if not os.path.isfile(file_path):
|
||||||
|
print(colored("The file does not exist.", "red"))
|
||||||
|
return []
|
||||||
|
|
||||||
|
file_extension = os.path.splitext(file_path)[1]
|
||||||
|
try:
|
||||||
|
with open(file_path, "rb") as file:
|
||||||
|
content = file.read()
|
||||||
|
# Decode content based on MIME type or file extension
|
||||||
|
decoded_content = self.decode_content(content, file_extension)
|
||||||
|
chunks = self.chunk_content(decoded_content)
|
||||||
|
return chunks
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(colored(f"Error reading file: {e}", "red"))
|
||||||
|
return []
|
||||||
|
|
||||||
|
def decode_content(self, content: bytes, file_extension: str) -> str:
|
||||||
|
"""
|
||||||
|
Decode the content of the file based on its MIME type or file extension.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
content (bytes): The content of the file.
|
||||||
|
file_extension (str): The file extension of the file.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: The decoded content of the file.
|
||||||
|
"""
|
||||||
|
# Add logic to handle different file types based on the extension
|
||||||
|
# For simplicity, this example assumes text files encoded in utf-8
|
||||||
|
try:
|
||||||
|
return content.decode("utf-8")
|
||||||
|
except UnicodeDecodeError as e:
|
||||||
|
print(
|
||||||
|
colored(
|
||||||
|
f"Could not decode file with extension {file_extension}: {e}",
|
||||||
|
"yellow",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return ""
|
||||||
|
|
||||||
|
def chunk_content(self, content: str) -> List[str]:
|
||||||
|
"""
|
||||||
|
Split the content into chunks of size `chunk_size`.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
content (str): The content to chunk.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
List[str]: The list of chunks.
|
||||||
|
"""
|
||||||
|
return [
|
||||||
|
content[i : i + self.chunk_size]
|
||||||
|
for i in range(0, len(content), self.chunk_size)
|
||||||
|
]
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"OmniChunker(chunk_size={self.chunk_size}, beautify={self.beautify})"
|
||||||
|
|
||||||
|
def metrics(self):
|
||||||
|
return {
|
||||||
|
"chunk_size": self.chunk_size,
|
||||||
|
"beautify": self.beautify,
|
||||||
|
}
|
||||||
|
|
||||||
|
def print_dashboard(self):
|
||||||
|
print(
|
||||||
|
colored(
|
||||||
|
f"""
|
||||||
|
Omni Chunker
|
||||||
|
------------
|
||||||
|
{self.metrics()}
|
||||||
|
""",
|
||||||
|
"cyan",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
@ -0,0 +1,172 @@
|
|||||||
|
import logging
|
||||||
|
import os
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from io import BytesIO
|
||||||
|
|
||||||
|
import openai
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from openai import OpenAI
|
||||||
|
from PIL import Image
|
||||||
|
from pydantic import validator
|
||||||
|
from termcolor import colored
|
||||||
|
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
# api_key = os.getenv("OPENAI_API_KEY")
|
||||||
|
|
||||||
|
# Configure Logging
|
||||||
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Dalle3:
|
||||||
|
"""
|
||||||
|
Dalle3 model class
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
-----------
|
||||||
|
image_url: str
|
||||||
|
The image url generated by the Dalle3 API
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
--------
|
||||||
|
__call__(self, task: str) -> Dalle3:
|
||||||
|
Makes a call to the Dalle3 API and returns the image url
|
||||||
|
|
||||||
|
Example:
|
||||||
|
--------
|
||||||
|
>>> dalle3 = Dalle3()
|
||||||
|
>>> task = "A painting of a dog"
|
||||||
|
>>> image_url = dalle3(task)
|
||||||
|
>>> print(image_url)
|
||||||
|
https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
model: str = "dall-e-3"
|
||||||
|
img: str = None
|
||||||
|
size: str = "1024x1024"
|
||||||
|
max_retries: int = 3
|
||||||
|
quality: str = "standard"
|
||||||
|
api_key: str = None
|
||||||
|
n: int = 4
|
||||||
|
client = OpenAI(
|
||||||
|
api_key=api_key,
|
||||||
|
max_retries=max_retries,
|
||||||
|
)
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
"""Config class for the Dalle3 model"""
|
||||||
|
|
||||||
|
arbitrary_types_allowed = True
|
||||||
|
|
||||||
|
@validator("max_retries", "time_seconds")
|
||||||
|
def must_be_positive(cls, value):
|
||||||
|
if value <= 0:
|
||||||
|
raise ValueError("Must be positive")
|
||||||
|
return value
|
||||||
|
|
||||||
|
def read_img(self, img: str):
|
||||||
|
"""Read the image using pil"""
|
||||||
|
img = Image.open(img)
|
||||||
|
return img
|
||||||
|
|
||||||
|
def set_width_height(self, img: str, width: int, height: int):
|
||||||
|
"""Set the width and height of the image"""
|
||||||
|
img = self.read_img(img)
|
||||||
|
img = img.resize((width, height))
|
||||||
|
return img
|
||||||
|
|
||||||
|
def convert_to_bytesio(self, img: str, format: str = "PNG"):
|
||||||
|
"""Convert the image to an bytes io object"""
|
||||||
|
byte_stream = BytesIO()
|
||||||
|
img.save(byte_stream, format=format)
|
||||||
|
byte_array = byte_stream.getvalue()
|
||||||
|
return byte_array
|
||||||
|
|
||||||
|
# @lru_cache(maxsize=32)
|
||||||
|
def __call__(self, task: str):
|
||||||
|
"""
|
||||||
|
Text to image conversion using the Dalle3 API
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
-----------
|
||||||
|
task: str
|
||||||
|
The task to be converted to an image
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
--------
|
||||||
|
Dalle3:
|
||||||
|
An instance of the Dalle3 class with the image url generated by the Dalle3 API
|
||||||
|
|
||||||
|
Example:
|
||||||
|
--------
|
||||||
|
>>> dalle3 = Dalle3()
|
||||||
|
>>> task = "A painting of a dog"
|
||||||
|
>>> image_url = dalle3(task)
|
||||||
|
>>> print(image_url)
|
||||||
|
https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# Making a call to the the Dalle3 API
|
||||||
|
response = self.client.images.generate(
|
||||||
|
model=self.model,
|
||||||
|
prompt=task,
|
||||||
|
size=self.size,
|
||||||
|
quality=self.quality,
|
||||||
|
n=self.n,
|
||||||
|
)
|
||||||
|
# Extracting the image url from the response
|
||||||
|
img = response.data[0].url
|
||||||
|
return img
|
||||||
|
except openai.OpenAIError as error:
|
||||||
|
# Handling exceptions and printing the errors details
|
||||||
|
print(
|
||||||
|
colored(
|
||||||
|
f"Error running Dalle3: {error} try optimizing your api key and or try again",
|
||||||
|
"red",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
raise error
|
||||||
|
|
||||||
|
def create_variations(self, img: str):
|
||||||
|
"""
|
||||||
|
Create variations of an image using the Dalle3 API
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
-----------
|
||||||
|
img: str
|
||||||
|
The image to be used for the API request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
--------
|
||||||
|
img: str
|
||||||
|
The image url generated by the Dalle3 API
|
||||||
|
|
||||||
|
Example:
|
||||||
|
--------
|
||||||
|
>>> dalle3 = Dalle3()
|
||||||
|
>>> img = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png"
|
||||||
|
>>> img = dalle3.create_variations(img)
|
||||||
|
>>> print(img)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
response = self.client.images.create_variation(
|
||||||
|
img=open(img, "rb"), n=self.n, size=self.size
|
||||||
|
)
|
||||||
|
img = response.data[0].url
|
||||||
|
|
||||||
|
return img
|
||||||
|
except (Exception, openai.OpenAIError) as error:
|
||||||
|
print(
|
||||||
|
colored(
|
||||||
|
f"Error running Dalle3: {error} try optimizing your api key and or try again",
|
||||||
|
"red",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
print(colored(f"Error running Dalle3: {error.http_status}", "red"))
|
||||||
|
print(colored(f"Error running Dalle3: {error.error}", "red"))
|
||||||
|
raise error
|
@ -1,3 +1,160 @@
|
|||||||
|
import asyncio
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
from functools import wraps
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
import torch
|
||||||
|
from termcolor import colored
|
||||||
|
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor, pipeline
|
||||||
|
|
||||||
|
|
||||||
|
def async_retry(max_retries=3, exceptions=(Exception,), delay=1):
|
||||||
|
"""
|
||||||
|
A decorator for adding retry logic to async functions.
|
||||||
|
:param max_retries: Maximum number of retries before giving up.
|
||||||
|
:param exceptions: A tuple of exceptions to catch and retry on.
|
||||||
|
:param delay: Delay between retries.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def decorator(func):
|
||||||
|
@wraps(func)
|
||||||
|
async def wrapper(*args, **kwargs):
|
||||||
|
retries = max_retries
|
||||||
|
while retries:
|
||||||
|
try:
|
||||||
|
return await func(*args, **kwargs)
|
||||||
|
except exceptions as e:
|
||||||
|
retries -= 1
|
||||||
|
if retries <= 0:
|
||||||
|
raise
|
||||||
|
print(f"Retry after exception: {e}, Attempts remaining: {retries}")
|
||||||
|
await asyncio.sleep(delay)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
return decorator
|
||||||
|
|
||||||
|
|
||||||
|
class DistilWhisperModel:
|
||||||
|
"""
|
||||||
|
This class encapsulates the Distil-Whisper model for English speech recognition.
|
||||||
|
It allows for both synchronous and asynchronous transcription of short and long-form audio.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
model_id: The model ID to use. Defaults to "distil-whisper/distil-large-v2".
|
||||||
|
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
device: The device to use for inference.
|
||||||
|
torch_dtype: The torch data type to use for inference.
|
||||||
|
model_id: The model ID to use.
|
||||||
|
model: The model instance.
|
||||||
|
processor: The processor instance.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
model_wrapper = DistilWhisperModel()
|
||||||
|
transcription = model_wrapper('path/to/audio.mp3')
|
||||||
|
|
||||||
|
# For async usage
|
||||||
|
transcription = asyncio.run(model_wrapper.async_transcribe('path/to/audio.mp3'))
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, model_id="distil-whisper/distil-large-v2"):
|
||||||
|
self.device = "cuda:0" if torch.cuda.is_available() else "cpu"
|
||||||
|
self.torch_dtype = torch.float16 if torch.cuda.is_available() else torch.float32
|
||||||
|
self.model_id = model_id
|
||||||
|
self.model = AutoModelForSpeechSeq2Seq.from_pretrained(
|
||||||
|
model_id,
|
||||||
|
torch_dtype=self.torch_dtype,
|
||||||
|
low_cpu_mem_usage=True,
|
||||||
|
use_safetensors=True,
|
||||||
|
).to(self.device)
|
||||||
|
self.processor = AutoProcessor.from_pretrained(model_id)
|
||||||
|
|
||||||
|
def __call__(self, inputs: Union[str, dict]):
|
||||||
|
return self.transcribe(inputs)
|
||||||
|
|
||||||
|
def transcribe(self, inputs: Union[str, dict]):
|
||||||
"""
|
"""
|
||||||
|
Synchronously transcribe the given audio input using the Distil-Whisper model.
|
||||||
|
:param inputs: A string representing the file path or a dict with audio data.
|
||||||
|
:return: The transcribed text.
|
||||||
|
"""
|
||||||
|
pipe = pipeline(
|
||||||
|
"automatic-speech-recognition",
|
||||||
|
model=self.model,
|
||||||
|
tokenizer=self.processor.tokenizer,
|
||||||
|
feature_extractor=self.processor.feature_extractor,
|
||||||
|
max_new_tokens=128,
|
||||||
|
torch_dtype=self.torch_dtype,
|
||||||
|
device=self.device,
|
||||||
|
)
|
||||||
|
|
||||||
|
return pipe(inputs)["text"]
|
||||||
|
|
||||||
|
@async_retry()
|
||||||
|
async def async_transcribe(self, inputs: Union[str, dict]):
|
||||||
"""
|
"""
|
||||||
|
Asynchronously transcribe the given audio input using the Distil-Whisper model.
|
||||||
|
:param inputs: A string representing the file path or a dict with audio data.
|
||||||
|
:return: The transcribed text.
|
||||||
|
"""
|
||||||
|
loop = asyncio.get_event_loop()
|
||||||
|
return await loop.run_in_executor(None, self.transcribe, inputs)
|
||||||
|
|
||||||
|
def real_time_transcribe(self, audio_file_path, chunk_duration=5):
|
||||||
|
"""
|
||||||
|
Simulates real-time transcription of an audio file, processing and printing results
|
||||||
|
in chunks with colored output for readability.
|
||||||
|
|
||||||
|
:param audio_file_path: Path to the audio file to be transcribed.
|
||||||
|
:param chunk_duration: Duration in seconds of each audio chunk to be processed.
|
||||||
|
"""
|
||||||
|
if not os.path.isfile(audio_file_path):
|
||||||
|
print(colored("The audio file was not found.", "red"))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Assuming `chunk_duration` is in seconds and `processor` can handle chunk-wise processing
|
||||||
|
try:
|
||||||
|
with torch.no_grad():
|
||||||
|
# Load the whole audio file, but process and transcribe it in chunks
|
||||||
|
audio_input = self.processor.audio_file_to_array(audio_file_path)
|
||||||
|
sample_rate = audio_input.sampling_rate
|
||||||
|
total_duration = len(audio_input.array) / sample_rate
|
||||||
|
chunks = [
|
||||||
|
audio_input.array[i : i + sample_rate * chunk_duration]
|
||||||
|
for i in range(
|
||||||
|
0, len(audio_input.array), sample_rate * chunk_duration
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
print(colored("Starting real-time transcription...", "green"))
|
||||||
|
|
||||||
|
for i, chunk in enumerate(chunks):
|
||||||
|
# Process the current chunk
|
||||||
|
processed_inputs = self.processor(
|
||||||
|
chunk,
|
||||||
|
sampling_rate=sample_rate,
|
||||||
|
return_tensors="pt",
|
||||||
|
padding=True,
|
||||||
|
)
|
||||||
|
processed_inputs = processed_inputs.input_values.to(self.device)
|
||||||
|
|
||||||
|
# Generate transcription for the chunk
|
||||||
|
logits = self.model.generate(processed_inputs)
|
||||||
|
transcription = self.processor.batch_decode(
|
||||||
|
logits, skip_special_tokens=True
|
||||||
|
)[0]
|
||||||
|
|
||||||
|
# Print the chunk's transcription
|
||||||
|
print(
|
||||||
|
colored(f"Chunk {i+1}/{len(chunks)}: ", "yellow")
|
||||||
|
+ transcription
|
||||||
|
)
|
||||||
|
|
||||||
|
# Wait for the chunk's duration to simulate real-time processing
|
||||||
|
time.sleep(chunk_duration)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(colored(f"An error occurred during transcription: {e}", "red"))
|
||||||
|
@ -0,0 +1,288 @@
|
|||||||
|
import base64
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
from dataclasses import dataclass
|
||||||
|
from typing import List, Optional, Union
|
||||||
|
|
||||||
|
import requests
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from openai import OpenAI
|
||||||
|
from termcolor import colored
|
||||||
|
|
||||||
|
# ENV
|
||||||
|
load_dotenv()
|
||||||
|
|
||||||
|
|
||||||
|
def logging_config():
|
||||||
|
"""Configures logging"""
|
||||||
|
logging.basicConfig(
|
||||||
|
level=logging.INFO,
|
||||||
|
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
|
||||||
|
)
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
return logger
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GPT4VisionResponse:
|
||||||
|
"""A response structure for GPT-4"""
|
||||||
|
|
||||||
|
answer: str
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class GPT4Vision:
|
||||||
|
"""
|
||||||
|
GPT4Vision model class
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
-----------
|
||||||
|
max_retries: int
|
||||||
|
The maximum number of retries to make to the API
|
||||||
|
backoff_factor: float
|
||||||
|
The backoff factor to use for exponential backoff
|
||||||
|
timeout_seconds: int
|
||||||
|
The timeout in seconds for the API request
|
||||||
|
api_key: str
|
||||||
|
The API key to use for the API request
|
||||||
|
quality: str
|
||||||
|
The quality of the image to generate
|
||||||
|
max_tokens: int
|
||||||
|
The maximum number of tokens to use for the API request
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
--------
|
||||||
|
process_img(self, img_path: str) -> str:
|
||||||
|
Processes the image to be used for the API request
|
||||||
|
__call__(self, img: Union[str, List[str]], tasks: List[str]) -> GPT4VisionResponse:
|
||||||
|
Makes a call to the GPT-4 Vision API and returns the image url
|
||||||
|
|
||||||
|
Example:
|
||||||
|
>>> gpt4vision = GPT4Vision()
|
||||||
|
>>> img = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png"
|
||||||
|
>>> tasks = ["A painting of a dog"]
|
||||||
|
>>> answer = gpt4vision(img, tasks)
|
||||||
|
>>> print(answer)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
max_retries: int = 3
|
||||||
|
model: str = "gpt-4-vision-preview"
|
||||||
|
backoff_factor: float = 2.0
|
||||||
|
timeout_seconds: int = 10
|
||||||
|
api_key: Optional[str] = None
|
||||||
|
# 'Low' or 'High' for respesctively fast or high quality, but high more token usage
|
||||||
|
quality: str = "low"
|
||||||
|
# Max tokens to use for the API request, the maximum might be 3,000 but we don't know
|
||||||
|
max_tokens: int = 200
|
||||||
|
client = OpenAI(
|
||||||
|
api_key=api_key,
|
||||||
|
max_retries=max_retries,
|
||||||
|
)
|
||||||
|
logger = logging_config()
|
||||||
|
|
||||||
|
class Config:
|
||||||
|
"""Config class for the GPT4Vision model"""
|
||||||
|
|
||||||
|
arbitary_types_allowed = True
|
||||||
|
|
||||||
|
def process_img(self, img: str) -> str:
|
||||||
|
"""Processes the image to be used for the API request"""
|
||||||
|
with open(img, "rb") as image_file:
|
||||||
|
return base64.b64encode(image_file.read()).decode("utf-8")
|
||||||
|
|
||||||
|
def __call__(
|
||||||
|
self,
|
||||||
|
img: Union[str, List[str]],
|
||||||
|
tasks: List[str],
|
||||||
|
) -> GPT4VisionResponse:
|
||||||
|
"""
|
||||||
|
Calls the GPT-4 Vision API and returns the image url
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
-----------
|
||||||
|
img: Union[str, List[str]]
|
||||||
|
The image to be used for the API request
|
||||||
|
tasks: List[str]
|
||||||
|
The tasks to be used for the API request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
--------
|
||||||
|
answer: GPT4VisionResponse
|
||||||
|
The response from the API request
|
||||||
|
|
||||||
|
Example:
|
||||||
|
--------
|
||||||
|
>>> gpt4vision = GPT4Vision()
|
||||||
|
>>> img = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png"
|
||||||
|
>>> tasks = ["A painting of a dog"]
|
||||||
|
>>> answer = gpt4vision(img, tasks)
|
||||||
|
>>> print(answer)
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Authorization": f"Bearer {self.api_key}",
|
||||||
|
}
|
||||||
|
|
||||||
|
# Image content
|
||||||
|
image_content = [
|
||||||
|
{"type": "imavge_url", "image_url": img}
|
||||||
|
if img.startswith("http")
|
||||||
|
else {"type": "image", "data": img}
|
||||||
|
for img in img
|
||||||
|
]
|
||||||
|
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": image_content + [{"type": "text", "text": q} for q in tasks],
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
payload = {
|
||||||
|
"model": "gpt-4-vision-preview",
|
||||||
|
"messages": messages,
|
||||||
|
"max_tokens": self.max_tokens,
|
||||||
|
"detail": self.quality,
|
||||||
|
}
|
||||||
|
|
||||||
|
for attempt in range(self.max_retries):
|
||||||
|
try:
|
||||||
|
response = requests.post(
|
||||||
|
"https://api.openai.com/v1/chat/completions",
|
||||||
|
headers=headers,
|
||||||
|
json=payload,
|
||||||
|
timeout=self.timeout_seconds,
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
answer = response.json()["choices"][0]["message"]["content"]["text"]
|
||||||
|
return GPT4VisionResponse(answer=answer)
|
||||||
|
except requests.exceptions.HTTPError as error:
|
||||||
|
self.logger.error(
|
||||||
|
f"HTTP error: {error.response.status_code}, {error.response.text}"
|
||||||
|
)
|
||||||
|
if error.response.status_code in [429, 500, 503]:
|
||||||
|
# Exponential backoff = 429(too many requesys)
|
||||||
|
# And 503 = (Service unavailable) errors
|
||||||
|
time.sleep(self.backoff_factor**attempt)
|
||||||
|
else:
|
||||||
|
break
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as error:
|
||||||
|
self.logger.error(f"Request error: {error}")
|
||||||
|
time.sleep(self.backoff_factor**attempt)
|
||||||
|
except Exception as error:
|
||||||
|
self.logger.error(
|
||||||
|
f"Unexpected Error: {error} try optimizing your api key and try again"
|
||||||
|
)
|
||||||
|
raise error from None
|
||||||
|
|
||||||
|
raise TimeoutError("API Request timed out after multiple retries")
|
||||||
|
|
||||||
|
def run(self, task: str, img: str) -> str:
|
||||||
|
"""
|
||||||
|
Runs the GPT-4 Vision API
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
-----------
|
||||||
|
task: str
|
||||||
|
The task to be used for the API request
|
||||||
|
img: str
|
||||||
|
The image to be used for the API request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
--------
|
||||||
|
out: str
|
||||||
|
The response from the API request
|
||||||
|
|
||||||
|
Example:
|
||||||
|
--------
|
||||||
|
>>> gpt4vision = GPT4Vision()
|
||||||
|
>>> task = "A painting of a dog"
|
||||||
|
>>> img = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png"
|
||||||
|
>>> answer = gpt4vision.run(task, img)
|
||||||
|
>>> print(answer)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
response = self.client.chat.completions.create(
|
||||||
|
model=self.model,
|
||||||
|
messages=[
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": [
|
||||||
|
{"type": "text", "text": f"{task}"},
|
||||||
|
{
|
||||||
|
"type": "image_url",
|
||||||
|
"image_url": f"{img}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
max_tokens=self.max_tokens,
|
||||||
|
)
|
||||||
|
|
||||||
|
out = response.choices[0].text
|
||||||
|
return out
|
||||||
|
except Exception as error:
|
||||||
|
print(
|
||||||
|
colored(
|
||||||
|
f"Error when calling GPT4Vision, Error: {error} Try optimizing your key, and try again",
|
||||||
|
"red",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
async def arun(self, task: str, img: str) -> str:
|
||||||
|
"""
|
||||||
|
Asynchronous run method for GPT-4 Vision
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
-----------
|
||||||
|
task: str
|
||||||
|
The task to be used for the API request
|
||||||
|
img: str
|
||||||
|
The image to be used for the API request
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
--------
|
||||||
|
out: str
|
||||||
|
The response from the API request
|
||||||
|
|
||||||
|
Example:
|
||||||
|
--------
|
||||||
|
>>> gpt4vision = GPT4Vision()
|
||||||
|
>>> task = "A painting of a dog"
|
||||||
|
>>> img = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png"
|
||||||
|
>>> answer = await gpt4vision.arun(task, img)
|
||||||
|
>>> print(answer)
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
response = await self.client.chat.completions.create(
|
||||||
|
model=self.model,
|
||||||
|
messages=[
|
||||||
|
{
|
||||||
|
"role": "user",
|
||||||
|
"content": [
|
||||||
|
{"type": "text", "text": f"{task}"},
|
||||||
|
{
|
||||||
|
"type": "image_url",
|
||||||
|
"image_url": f"{img}",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
max_tokens=self.max_tokens,
|
||||||
|
)
|
||||||
|
out = response.choices[0].text
|
||||||
|
return out
|
||||||
|
except Exception as error:
|
||||||
|
print(
|
||||||
|
colored(
|
||||||
|
f"Error when calling GPT4Vision, Error: {error} Try optimizing your key, and try again",
|
||||||
|
"red",
|
||||||
|
)
|
||||||
|
)
|
@ -0,0 +1,74 @@
|
|||||||
|
from typing import Dict, List, Optional
|
||||||
|
from dataclass import dataclass
|
||||||
|
|
||||||
|
from swarms.models import OpenAI
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class OpenAIAssistant:
|
||||||
|
name: str = "OpenAI Assistant"
|
||||||
|
instructions: str = None
|
||||||
|
tools: List[Dict] = None
|
||||||
|
model: str = None
|
||||||
|
openai_api_key: str = None
|
||||||
|
temperature: float = 0.5
|
||||||
|
max_tokens: int = 100
|
||||||
|
stop: List[str] = None
|
||||||
|
echo: bool = False
|
||||||
|
stream: bool = False
|
||||||
|
log: bool = False
|
||||||
|
presence: bool = False
|
||||||
|
dashboard: bool = False
|
||||||
|
debug: bool = False
|
||||||
|
max_loops: int = 5
|
||||||
|
stopping_condition: Optional[str] = None
|
||||||
|
loop_interval: int = 1
|
||||||
|
retry_attempts: int = 3
|
||||||
|
retry_interval: int = 1
|
||||||
|
interactive: bool = False
|
||||||
|
dynamic_temperature: bool = False
|
||||||
|
state: Dict = None
|
||||||
|
response_filters: List = None
|
||||||
|
response_filter: Dict = None
|
||||||
|
response_filter_name: str = None
|
||||||
|
response_filter_value: str = None
|
||||||
|
response_filter_type: str = None
|
||||||
|
response_filter_action: str = None
|
||||||
|
response_filter_action_value: str = None
|
||||||
|
response_filter_action_type: str = None
|
||||||
|
response_filter_action_name: str = None
|
||||||
|
client = OpenAI()
|
||||||
|
role: str = "user"
|
||||||
|
instructions: str = None
|
||||||
|
|
||||||
|
def create_assistant(self, task: str):
|
||||||
|
assistant = self.client.create_assistant(
|
||||||
|
name=self.name,
|
||||||
|
instructions=self.instructions,
|
||||||
|
tools=self.tools,
|
||||||
|
model=self.model,
|
||||||
|
)
|
||||||
|
return assistant
|
||||||
|
|
||||||
|
def create_thread(self):
|
||||||
|
thread = self.client.beta.threads.create()
|
||||||
|
return thread
|
||||||
|
|
||||||
|
def add_message_to_thread(self, thread_id: str, message: str):
|
||||||
|
message = self.client.beta.threads.add_message(
|
||||||
|
thread_id=thread_id, role=self.user, content=message
|
||||||
|
)
|
||||||
|
return message
|
||||||
|
|
||||||
|
def run(self, task: str):
|
||||||
|
run = self.client.beta.threads.runs.create(
|
||||||
|
thread_id=self.create_thread().id,
|
||||||
|
assistant_id=self.create_assistant().id,
|
||||||
|
instructions=self.instructions,
|
||||||
|
)
|
||||||
|
|
||||||
|
out = self.client.beta.threads.runs.retrieve(
|
||||||
|
thread_id=run.thread_id, run_id=run.id
|
||||||
|
)
|
||||||
|
|
||||||
|
return out
|
@ -0,0 +1,150 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import logging
|
||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
import tiktoken
|
||||||
|
from attr import Factory, define, field
|
||||||
|
|
||||||
|
|
||||||
|
@define(frozen=True)
|
||||||
|
class BaseTokenizer(ABC):
|
||||||
|
DEFAULT_STOP_SEQUENCES = ["Observation:"]
|
||||||
|
|
||||||
|
stop_sequences: list[str] = field(
|
||||||
|
default=Factory(lambda: BaseTokenizer.DEFAULT_STOP_SEQUENCES),
|
||||||
|
kw_only=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
@abstractmethod
|
||||||
|
def max_tokens(self) -> int:
|
||||||
|
...
|
||||||
|
|
||||||
|
def count_tokens_left(self, text: str) -> int:
|
||||||
|
diff = self.max_tokens - self.count_tokens(text)
|
||||||
|
|
||||||
|
if diff > 0:
|
||||||
|
return diff
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def count_tokens(self, text: str) -> int:
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
|
@define(frozen=True)
|
||||||
|
class OpenAITokenizer(BaseTokenizer):
|
||||||
|
DEFAULT_OPENAI_GPT_3_COMPLETION_MODEL = "text-davinci-003"
|
||||||
|
DEFAULT_OPENAI_GPT_3_CHAT_MODEL = "gpt-3.5-turbo"
|
||||||
|
DEFAULT_OPENAI_GPT_4_MODEL = "gpt-4"
|
||||||
|
DEFAULT_ENCODING = "cl100k_base"
|
||||||
|
DEFAULT_MAX_TOKENS = 2049
|
||||||
|
TOKEN_OFFSET = 8
|
||||||
|
|
||||||
|
MODEL_PREFIXES_TO_MAX_TOKENS = {
|
||||||
|
"gpt-4-32k": 32768,
|
||||||
|
"gpt-4": 8192,
|
||||||
|
"gpt-3.5-turbo-16k": 16384,
|
||||||
|
"gpt-3.5-turbo": 4096,
|
||||||
|
"gpt-35-turbo-16k": 16384,
|
||||||
|
"gpt-35-turbo": 4096,
|
||||||
|
"text-davinci-003": 4097,
|
||||||
|
"text-davinci-002": 4097,
|
||||||
|
"code-davinci-002": 8001,
|
||||||
|
"text-embedding-ada-002": 8191,
|
||||||
|
"text-embedding-ada-001": 2046,
|
||||||
|
}
|
||||||
|
|
||||||
|
EMBEDDING_MODELS = ["text-embedding-ada-002", "text-embedding-ada-001"]
|
||||||
|
|
||||||
|
model: str = field(kw_only=True)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def encoding(self) -> tiktoken.Encoding:
|
||||||
|
try:
|
||||||
|
return tiktoken.encoding_for_model(self.model)
|
||||||
|
except KeyError:
|
||||||
|
return tiktoken.get_encoding(self.DEFAULT_ENCODING)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def max_tokens(self) -> int:
|
||||||
|
tokens = next(
|
||||||
|
v
|
||||||
|
for k, v in self.MODEL_PREFIXES_TO_MAX_TOKENS.items()
|
||||||
|
if self.model.startswith(k)
|
||||||
|
)
|
||||||
|
offset = 0 if self.model in self.EMBEDDING_MODELS else self.TOKEN_OFFSET
|
||||||
|
|
||||||
|
return (tokens if tokens else self.DEFAULT_MAX_TOKENS) - offset
|
||||||
|
|
||||||
|
def count_tokens(
|
||||||
|
self, text: str | list, model: Optional[str] = None
|
||||||
|
) -> int:
|
||||||
|
"""
|
||||||
|
Handles the special case of ChatML. Implementation adopted from the official OpenAI notebook:
|
||||||
|
https://github.com/openai/openai-cookbook/blob/main/examples/How_to_count_tokens_with_tiktoken.ipynb
|
||||||
|
"""
|
||||||
|
if isinstance(text, list):
|
||||||
|
model = model if model else self.model
|
||||||
|
|
||||||
|
try:
|
||||||
|
encoding = tiktoken.encoding_for_model(model)
|
||||||
|
except KeyError:
|
||||||
|
logging.warning("model not found. Using cl100k_base encoding.")
|
||||||
|
|
||||||
|
encoding = tiktoken.get_encoding("cl100k_base")
|
||||||
|
|
||||||
|
if model in {
|
||||||
|
"gpt-3.5-turbo-0613",
|
||||||
|
"gpt-3.5-turbo-16k-0613",
|
||||||
|
"gpt-4-0314",
|
||||||
|
"gpt-4-32k-0314",
|
||||||
|
"gpt-4-0613",
|
||||||
|
"gpt-4-32k-0613",
|
||||||
|
}:
|
||||||
|
tokens_per_message = 3
|
||||||
|
tokens_per_name = 1
|
||||||
|
elif model == "gpt-3.5-turbo-0301":
|
||||||
|
# every message follows <|start|>{role/name}\n{content}<|end|>\n
|
||||||
|
tokens_per_message = 4
|
||||||
|
# if there's a name, the role is omitted
|
||||||
|
tokens_per_name = -1
|
||||||
|
elif "gpt-3.5-turbo" in model or "gpt-35-turbo" in model:
|
||||||
|
logging.info(
|
||||||
|
"gpt-3.5-turbo may update over time. Returning num tokens assuming gpt-3.5-turbo-0613."
|
||||||
|
)
|
||||||
|
return self.count_tokens(text, model="gpt-3.5-turbo-0613")
|
||||||
|
elif "gpt-4" in model:
|
||||||
|
logging.info(
|
||||||
|
"gpt-4 may update over time. Returning num tokens assuming gpt-4-0613."
|
||||||
|
)
|
||||||
|
return self.count_tokens(text, model="gpt-4-0613")
|
||||||
|
else:
|
||||||
|
raise NotImplementedError(
|
||||||
|
f"""token_count() is not implemented for model {model}.
|
||||||
|
See https://github.com/openai/openai-python/blob/main/chatml.md for
|
||||||
|
information on how messages are converted to tokens."""
|
||||||
|
)
|
||||||
|
|
||||||
|
num_tokens = 0
|
||||||
|
|
||||||
|
for message in text:
|
||||||
|
num_tokens += tokens_per_message
|
||||||
|
for key, value in message.items():
|
||||||
|
num_tokens += len(encoding.encode(value))
|
||||||
|
if key == "name":
|
||||||
|
num_tokens += tokens_per_name
|
||||||
|
|
||||||
|
# every reply is primed with <|start|>assistant<|message|>
|
||||||
|
num_tokens += 3
|
||||||
|
|
||||||
|
return num_tokens
|
||||||
|
else:
|
||||||
|
return len(
|
||||||
|
self.encoding.encode(
|
||||||
|
text, allowed_special=set(self.stop_sequences)
|
||||||
|
)
|
||||||
|
)
|
@ -1,5 +1,6 @@
|
|||||||
from swarms.structs.workflow import Workflow
|
from swarms.structs.workflow import Workflow
|
||||||
from swarms.structs.task import Task
|
from swarms.structs.task import Task
|
||||||
from swarms.structs.flow import Flow
|
from swarms.structs.flow import Flow
|
||||||
|
from swarms.structs.sequential_workflow import SequentialWorkflow
|
||||||
|
|
||||||
__all__ = ["Workflow", "Task", "Flow"]
|
__all__ = ["Workflow", "Task", "Flow", "SequentialWorkflow"]
|
||||||
|
@ -0,0 +1,5 @@
|
|||||||
|
"""
|
||||||
|
Base Structure for all Swarm Structures
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
@ -1,20 +1,398 @@
|
|||||||
"""
|
"""
|
||||||
Sequential Workflow
|
TODO:
|
||||||
|
- Add a method to update the arguments of a task
|
||||||
|
- Add a method to get the results of each task
|
||||||
|
- Add a method to get the results of a specific task
|
||||||
|
- Add a method to get the results of the workflow
|
||||||
|
- Add a method to get the results of the workflow as a dataframe
|
||||||
|
|
||||||
from swarms.models import OpenAIChat, Mistral
|
|
||||||
from swarms.structs import SequentialWorkflow
|
|
||||||
|
|
||||||
|
- Add a method to run the workflow in parallel with a pool of workers and a queue and a dashboard
|
||||||
|
- Add a dashboard to visualize the workflow
|
||||||
|
- Add async support
|
||||||
|
- Add context manager support
|
||||||
|
- Add workflow history
|
||||||
|
"""
|
||||||
|
import json
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Any, Callable, Dict, List, Optional, Union
|
||||||
|
|
||||||
|
from termcolor import colored
|
||||||
|
|
||||||
|
from swarms.structs.flow import Flow
|
||||||
|
|
||||||
|
|
||||||
|
# Define a generic Task that can handle different types of callable objects
|
||||||
|
@dataclass
|
||||||
|
class Task:
|
||||||
|
"""
|
||||||
|
Task class for running a task in a sequential workflow.
|
||||||
|
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> from swarms.structs import Task, Flow
|
||||||
|
>>> from swarms.models import OpenAIChat
|
||||||
|
>>> flow = Flow(llm=OpenAIChat(openai_api_key=""), max_loops=1, dashboard=False)
|
||||||
|
>>> task = Task(description="What's the weather in miami", flow=flow)
|
||||||
|
>>> task.execute()
|
||||||
|
>>> task.result
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
description: str
|
||||||
|
flow: Union[Callable, Flow]
|
||||||
|
args: List[Any] = field(default_factory=list)
|
||||||
|
kwargs: Dict[str, Any] = field(default_factory=dict)
|
||||||
|
result: Any = None
|
||||||
|
history: List[Any] = field(default_factory=list)
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
"""
|
||||||
|
Execute the task.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If a Flow instance is used as a task and the 'task' argument is not provided.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
"""
|
||||||
|
if isinstance(self.flow, Flow):
|
||||||
|
# Add a prompt to notify the Flow of the sequential workflow
|
||||||
|
if "prompt" in self.kwargs:
|
||||||
|
self.kwargs["prompt"] += (
|
||||||
|
f"\n\nPrevious output: {self.result}" if self.result else ""
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
self.kwargs["prompt"] = f"Main task: {self.description}" + (
|
||||||
|
f"\n\nPrevious output: {self.result}" if self.result else ""
|
||||||
|
)
|
||||||
|
self.result = self.flow.run(*self.args, **self.kwargs)
|
||||||
|
else:
|
||||||
|
self.result = self.flow(*self.args, **self.kwargs)
|
||||||
|
|
||||||
|
self.history.append(self.result)
|
||||||
|
|
||||||
|
|
||||||
|
# SequentialWorkflow class definition using dataclasses
|
||||||
|
@dataclass
|
||||||
|
class SequentialWorkflow:
|
||||||
|
"""
|
||||||
|
SequentialWorkflow class for running a sequence of tasks using N number of autonomous agents.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
max_loops (int): The maximum number of times to run the workflow.
|
||||||
|
dashboard (bool): Whether to display the dashboard for the workflow.
|
||||||
|
|
||||||
|
|
||||||
|
Attributes:
|
||||||
|
tasks (List[Task]): The list of tasks to execute.
|
||||||
|
max_loops (int): The maximum number of times to run the workflow.
|
||||||
|
dashboard (bool): Whether to display the dashboard for the workflow.
|
||||||
|
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> from swarms.models import OpenAIChat
|
||||||
|
>>> from swarms.structs import SequentialWorkflow
|
||||||
|
>>> llm = OpenAIChat(openai_api_key="")
|
||||||
|
>>> workflow = SequentialWorkflow(max_loops=1)
|
||||||
|
>>> workflow.add("What's the weather in miami", llm)
|
||||||
|
>>> workflow.add("Create a report on these metrics", llm)
|
||||||
|
>>> workflow.run()
|
||||||
|
>>> workflow.tasks
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
tasks: List[Task] = field(default_factory=list)
|
||||||
|
max_loops: int = 1
|
||||||
|
autosave: bool = False
|
||||||
|
saved_state_filepath: Optional[str] = "sequential_workflow_state.json"
|
||||||
|
restore_state_filepath: Optional[str] = None
|
||||||
|
dashboard: bool = False
|
||||||
|
|
||||||
|
def add(self, task: str, flow: Union[Callable, Flow], *args, **kwargs) -> None:
|
||||||
|
"""
|
||||||
|
Add a task to the workflow.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
task (str): The task description or the initial input for the Flow.
|
||||||
|
flow (Union[Callable, Flow]): The model or flow to execute the task.
|
||||||
|
*args: Additional arguments to pass to the task execution.
|
||||||
|
**kwargs: Additional keyword arguments to pass to the task execution.
|
||||||
|
"""
|
||||||
|
# If the flow is a Flow instance, we include the task in kwargs for Flow.run()
|
||||||
|
if isinstance(flow, Flow):
|
||||||
|
kwargs["task"] = task # Set the task as a keyword argument for Flow
|
||||||
|
|
||||||
|
# Append the task to the tasks list
|
||||||
|
self.tasks.append(
|
||||||
|
Task(description=task, flow=flow, args=list(args), kwargs=kwargs)
|
||||||
|
)
|
||||||
|
|
||||||
|
def reset_workflow(self) -> None:
|
||||||
|
"""Resets the workflow by clearing the results of each task."""
|
||||||
|
for task in self.tasks:
|
||||||
|
task.result = None
|
||||||
|
|
||||||
|
def get_task_results(self) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Returns the results of each task in the workflow.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict[str, Any]: The results of each task in the workflow
|
||||||
|
"""
|
||||||
|
return {task.description: task.result for task in self.tasks}
|
||||||
|
|
||||||
|
def remove_task(self, task_description: str) -> None:
|
||||||
|
self.tasks = [
|
||||||
|
task for task in self.tasks if task.description != task_description
|
||||||
|
]
|
||||||
|
|
||||||
llm = OpenAIChat(openai_api_key="")
|
def update_task(self, task_description: str, **updates) -> None:
|
||||||
mistral = Mistral()
|
"""
|
||||||
|
Updates the arguments of a task in the workflow.
|
||||||
|
|
||||||
# Max loops will run over the sequential pipeline twice
|
Args:
|
||||||
workflow = SequentialWorkflow(max_loops=2)
|
task_description (str): The description of the task to update.
|
||||||
|
**updates: The updates to apply to the task.
|
||||||
|
|
||||||
workflow.add("What's the weather in miami", llm)
|
Raises:
|
||||||
|
ValueError: If the task is not found in the workflow.
|
||||||
|
|
||||||
workflow.add("Create a report on these metrics", mistral)
|
Examples:
|
||||||
|
>>> from swarms.models import OpenAIChat
|
||||||
|
>>> from swarms.structs import SequentialWorkflow
|
||||||
|
>>> llm = OpenAIChat(openai_api_key="")
|
||||||
|
>>> workflow = SequentialWorkflow(max_loops=1)
|
||||||
|
>>> workflow.add("What's the weather in miami", llm)
|
||||||
|
>>> workflow.add("Create a report on these metrics", llm)
|
||||||
|
>>> workflow.update_task("What's the weather in miami", max_tokens=1000)
|
||||||
|
>>> workflow.tasks[0].kwargs
|
||||||
|
{'max_tokens': 1000}
|
||||||
|
|
||||||
workflow.run()
|
"""
|
||||||
|
for task in self.tasks:
|
||||||
|
if task.description == task_description:
|
||||||
|
task.kwargs.update(updates)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise ValueError(f"Task {task_description} not found in workflow.")
|
||||||
|
|
||||||
|
def save_workflow_state(
|
||||||
|
self, filepath: Optional[str] = "sequential_workflow_state.json", **kwargs
|
||||||
|
) -> None:
|
||||||
"""
|
"""
|
||||||
|
Saves the workflow state to a json file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filepath (str): The path to save the workflow state to.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> from swarms.models import OpenAIChat
|
||||||
|
>>> from swarms.structs import SequentialWorkflow
|
||||||
|
>>> llm = OpenAIChat(openai_api_key="")
|
||||||
|
>>> workflow = SequentialWorkflow(max_loops=1)
|
||||||
|
>>> workflow.add("What's the weather in miami", llm)
|
||||||
|
>>> workflow.add("Create a report on these metrics", llm)
|
||||||
|
>>> workflow.save_workflow_state("sequential_workflow_state.json")
|
||||||
|
"""
|
||||||
|
filepath = filepath or self.saved_state_filepath
|
||||||
|
|
||||||
|
with open(filepath, "w") as f:
|
||||||
|
# Saving the state as a json for simplicuty
|
||||||
|
state = {
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"description": task.description,
|
||||||
|
"args": task.args,
|
||||||
|
"kwargs": task.kwargs,
|
||||||
|
"result": task.result,
|
||||||
|
"history": task.history,
|
||||||
|
}
|
||||||
|
for task in self.tasks
|
||||||
|
],
|
||||||
|
"max_loops": self.max_loops,
|
||||||
|
}
|
||||||
|
json.dump(state, f, indent=4)
|
||||||
|
|
||||||
|
def workflow_bootup(self, **kwargs) -> None:
|
||||||
|
print(
|
||||||
|
colored(
|
||||||
|
"""
|
||||||
|
Sequential Workflow Initializing...""",
|
||||||
|
"green",
|
||||||
|
attrs=["bold", "underline"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def workflow_dashboard(self, **kwargs) -> None:
|
||||||
|
"""
|
||||||
|
Displays a dashboard for the workflow.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
**kwargs: Additional keyword arguments to pass to the dashboard.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> from swarms.models import OpenAIChat
|
||||||
|
>>> from swarms.structs import SequentialWorkflow
|
||||||
|
>>> llm = OpenAIChat(openai_api_key="")
|
||||||
|
>>> workflow = SequentialWorkflow(max_loops=1)
|
||||||
|
>>> workflow.add("What's the weather in miami", llm)
|
||||||
|
>>> workflow.add("Create a report on these metrics", llm)
|
||||||
|
>>> workflow.workflow_dashboard()
|
||||||
|
|
||||||
|
"""
|
||||||
|
print(
|
||||||
|
colored(
|
||||||
|
f"""
|
||||||
|
Sequential Workflow Dashboard
|
||||||
|
--------------------------------
|
||||||
|
Tasks: {len(self.tasks)}
|
||||||
|
Max Loops: {self.max_loops}
|
||||||
|
Autosave: {self.autosave}
|
||||||
|
Autosave Filepath: {self.saved_state_filepath}
|
||||||
|
Restore Filepath: {self.restore_state_filepath}
|
||||||
|
--------------------------------
|
||||||
|
Metadata:
|
||||||
|
kwargs: {kwargs}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
""",
|
||||||
|
"cyan",
|
||||||
|
attrs=["bold", "underline"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
def load_workflow_state(self, filepath: str = None, **kwargs) -> None:
|
||||||
|
"""
|
||||||
|
Loads the workflow state from a json file and restores the workflow state.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filepath (str): The path to load the workflow state from.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
>>> from swarms.models import OpenAIChat
|
||||||
|
>>> from swarms.structs import SequentialWorkflow
|
||||||
|
>>> llm = OpenAIChat(openai_api_key="")
|
||||||
|
>>> workflow = SequentialWorkflow(max_loops=1)
|
||||||
|
>>> workflow.add("What's the weather in miami", llm)
|
||||||
|
>>> workflow.add("Create a report on these metrics", llm)
|
||||||
|
>>> workflow.save_workflow_state("sequential_workflow_state.json")
|
||||||
|
>>> workflow.load_workflow_state("sequential_workflow_state.json")
|
||||||
|
|
||||||
|
"""
|
||||||
|
filepath = filepath or self.restore_state_filepath
|
||||||
|
|
||||||
|
with open(filepath, "r") as f:
|
||||||
|
state = json.load(f)
|
||||||
|
self.max_loops = state["max_loops"]
|
||||||
|
self.tasks = []
|
||||||
|
for task_state in state["tasks"]:
|
||||||
|
task = Task(
|
||||||
|
description=task_state["description"],
|
||||||
|
flow=task_state["flow"],
|
||||||
|
args=task_state["args"],
|
||||||
|
kwargs=task_state["kwargs"],
|
||||||
|
result=task_state["result"],
|
||||||
|
history=task_state["history"],
|
||||||
|
)
|
||||||
|
self.tasks.append(task)
|
||||||
|
|
||||||
|
def run(self) -> None:
|
||||||
|
"""
|
||||||
|
Run the workflow.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If a Flow instance is used as a task and the 'task' argument is not provided.
|
||||||
|
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
self.workflow_bootup()
|
||||||
|
for _ in range(self.max_loops):
|
||||||
|
for task in self.tasks:
|
||||||
|
# Check if the current task can be executed
|
||||||
|
if task.result is None:
|
||||||
|
# Check if the flow is a Flow and a 'task' argument is needed
|
||||||
|
if isinstance(task.flow, Flow):
|
||||||
|
# Ensure that 'task' is provided in the kwargs
|
||||||
|
if "task" not in task.kwargs:
|
||||||
|
raise ValueError(
|
||||||
|
f"The 'task' argument is required for the Flow flow execution in '{task.description}'"
|
||||||
|
)
|
||||||
|
# Separate the 'task' argument from other kwargs
|
||||||
|
flow_task_arg = task.kwargs.pop("task")
|
||||||
|
task.result = task.flow.run(
|
||||||
|
flow_task_arg, *task.args, **task.kwargs
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# If it's not a Flow instance, call the flow directly
|
||||||
|
task.result = task.flow(*task.args, **task.kwargs)
|
||||||
|
|
||||||
|
# Pass the result as an argument to the next task if it exists
|
||||||
|
next_task_index = self.tasks.index(task) + 1
|
||||||
|
if next_task_index < len(self.tasks):
|
||||||
|
next_task = self.tasks[next_task_index]
|
||||||
|
if isinstance(next_task.flow, Flow):
|
||||||
|
# For Flow flows, 'task' should be a keyword argument
|
||||||
|
next_task.kwargs["task"] = task.result
|
||||||
|
else:
|
||||||
|
# For other callable flows, the result is added to args
|
||||||
|
next_task.args.insert(0, task.result)
|
||||||
|
|
||||||
|
# Autosave the workflow state
|
||||||
|
if self.autosave:
|
||||||
|
self.save_workflow_state("sequential_workflow_state.json")
|
||||||
|
except Exception as e:
|
||||||
|
print(
|
||||||
|
colored(
|
||||||
|
f"Error initializing the Sequential workflow: {e} try optimizing your inputs like the flow class and task description",
|
||||||
|
"red",
|
||||||
|
attrs=["bold", "underline"],
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
async def arun(self) -> None:
|
||||||
|
"""
|
||||||
|
Asynchronously run the workflow.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
ValueError: If a Flow instance is used as a task and the 'task' argument is not provided.
|
||||||
|
|
||||||
|
"""
|
||||||
|
for _ in range(self.max_loops):
|
||||||
|
for task in self.tasks:
|
||||||
|
# Check if the current task can be executed
|
||||||
|
if task.result is None:
|
||||||
|
# Check if the flow is a Flow and a 'task' argument is needed
|
||||||
|
if isinstance(task.flow, Flow):
|
||||||
|
# Ensure that 'task' is provided in the kwargs
|
||||||
|
if "task" not in task.kwargs:
|
||||||
|
raise ValueError(
|
||||||
|
f"The 'task' argument is required for the Flow flow execution in '{task.description}'"
|
||||||
|
)
|
||||||
|
# Separate the 'task' argument from other kwargs
|
||||||
|
flow_task_arg = task.kwargs.pop("task")
|
||||||
|
task.result = await task.flow.arun(
|
||||||
|
flow_task_arg, *task.args, **task.kwargs
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
# If it's not a Flow instance, call the flow directly
|
||||||
|
task.result = await task.flow(*task.args, **task.kwargs)
|
||||||
|
|
||||||
|
# Pass the result as an argument to the next task if it exists
|
||||||
|
next_task_index = self.tasks.index(task) + 1
|
||||||
|
if next_task_index < len(self.tasks):
|
||||||
|
next_task = self.tasks[next_task_index]
|
||||||
|
if isinstance(next_task.flow, Flow):
|
||||||
|
# For Flow flows, 'task' should be a keyword argument
|
||||||
|
next_task.kwargs["task"] = task.result
|
||||||
|
else:
|
||||||
|
# For other callable flows, the result is added to args
|
||||||
|
next_task.args.insert(0, task.result)
|
||||||
|
|
||||||
|
# Autosave the workflow state
|
||||||
|
if self.autosave:
|
||||||
|
self.save_workflow_state("sequential_workflow_state.json")
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
from swarms.workers.worker import Worker
|
# from swarms.workers.worker import Worker
|
||||||
from swarms.workers.base import AbstractWorker
|
from swarms.workers.base import AbstractWorker
|
||||||
|
@ -0,0 +1,127 @@
|
|||||||
|
import os
|
||||||
|
import pytest
|
||||||
|
from unittest.mock import Mock, patch
|
||||||
|
from swarms.models.anthropic import Anthropic
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_anthropic_env():
|
||||||
|
os.environ["ANTHROPIC_API_URL"] = "https://test.anthropic.com"
|
||||||
|
os.environ["ANTHROPIC_API_KEY"] = "test_api_key"
|
||||||
|
yield
|
||||||
|
del os.environ["ANTHROPIC_API_URL"]
|
||||||
|
del os.environ["ANTHROPIC_API_KEY"]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_requests_post():
|
||||||
|
with patch("requests.post") as mock_post:
|
||||||
|
yield mock_post
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def anthropic_instance():
|
||||||
|
return Anthropic(model="test-model")
|
||||||
|
|
||||||
|
|
||||||
|
def test_anthropic_init_default_values(anthropic_instance):
|
||||||
|
assert anthropic_instance.model == "test-model"
|
||||||
|
assert anthropic_instance.max_tokens_to_sample == 256
|
||||||
|
assert anthropic_instance.temperature is None
|
||||||
|
assert anthropic_instance.top_k is None
|
||||||
|
assert anthropic_instance.top_p is None
|
||||||
|
assert anthropic_instance.streaming is False
|
||||||
|
assert anthropic_instance.default_request_timeout == 600
|
||||||
|
assert anthropic_instance.anthropic_api_url == "https://test.anthropic.com"
|
||||||
|
assert anthropic_instance.anthropic_api_key == "test_api_key"
|
||||||
|
|
||||||
|
|
||||||
|
def test_anthropic_init_custom_values():
|
||||||
|
anthropic_instance = Anthropic(
|
||||||
|
model="custom-model",
|
||||||
|
max_tokens_to_sample=128,
|
||||||
|
temperature=0.8,
|
||||||
|
top_k=5,
|
||||||
|
top_p=0.9,
|
||||||
|
streaming=True,
|
||||||
|
default_request_timeout=300,
|
||||||
|
)
|
||||||
|
assert anthropic_instance.model == "custom-model"
|
||||||
|
assert anthropic_instance.max_tokens_to_sample == 128
|
||||||
|
assert anthropic_instance.temperature == 0.8
|
||||||
|
assert anthropic_instance.top_k == 5
|
||||||
|
assert anthropic_instance.top_p == 0.9
|
||||||
|
assert anthropic_instance.streaming is True
|
||||||
|
assert anthropic_instance.default_request_timeout == 300
|
||||||
|
|
||||||
|
|
||||||
|
def test_anthropic_default_params(anthropic_instance):
|
||||||
|
default_params = anthropic_instance._default_params()
|
||||||
|
assert default_params == {
|
||||||
|
"max_tokens_to_sample": 256,
|
||||||
|
"model": "test-model",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def test_anthropic_run(mock_anthropic_env, mock_requests_post, anthropic_instance):
|
||||||
|
mock_response = Mock()
|
||||||
|
mock_response.json.return_value = {"completion": "Generated text"}
|
||||||
|
mock_requests_post.return_value = mock_response
|
||||||
|
|
||||||
|
task = "Generate text"
|
||||||
|
stop = ["stop1", "stop2"]
|
||||||
|
|
||||||
|
completion = anthropic_instance.run(task, stop)
|
||||||
|
|
||||||
|
assert completion == "Generated text"
|
||||||
|
mock_requests_post.assert_called_once_with(
|
||||||
|
"https://test.anthropic.com/completions",
|
||||||
|
headers={"Authorization": "Bearer test_api_key"},
|
||||||
|
json={
|
||||||
|
"prompt": task,
|
||||||
|
"stop_sequences": stop,
|
||||||
|
"max_tokens_to_sample": 256,
|
||||||
|
"model": "test-model",
|
||||||
|
},
|
||||||
|
timeout=600,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_anthropic_call(mock_anthropic_env, mock_requests_post, anthropic_instance):
|
||||||
|
mock_response = Mock()
|
||||||
|
mock_response.json.return_value = {"completion": "Generated text"}
|
||||||
|
mock_requests_post.return_value = mock_response
|
||||||
|
|
||||||
|
task = "Generate text"
|
||||||
|
stop = ["stop1", "stop2"]
|
||||||
|
|
||||||
|
completion = anthropic_instance(task, stop)
|
||||||
|
|
||||||
|
assert completion == "Generated text"
|
||||||
|
mock_requests_post.assert_called_once_with(
|
||||||
|
"https://test.anthropic.com/completions",
|
||||||
|
headers={"Authorization": "Bearer test_api_key"},
|
||||||
|
json={
|
||||||
|
"prompt": task,
|
||||||
|
"stop_sequences": stop,
|
||||||
|
"max_tokens_to_sample": 256,
|
||||||
|
"model": "test-model",
|
||||||
|
},
|
||||||
|
timeout=600,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_anthropic_exception_handling(
|
||||||
|
mock_anthropic_env, mock_requests_post, anthropic_instance
|
||||||
|
):
|
||||||
|
mock_response = Mock()
|
||||||
|
mock_response.json.return_value = {"error": "An error occurred"}
|
||||||
|
mock_requests_post.return_value = mock_response
|
||||||
|
|
||||||
|
task = "Generate text"
|
||||||
|
stop = ["stop1", "stop2"]
|
||||||
|
|
||||||
|
with pytest.raises(Exception) as excinfo:
|
||||||
|
anthropic_instance(task, stop)
|
||||||
|
|
||||||
|
assert "An error occurred" in str(excinfo.value)
|
@ -0,0 +1,410 @@
|
|||||||
|
import os
|
||||||
|
from unittest.mock import Mock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from openai import OpenAIError
|
||||||
|
from PIL import Image
|
||||||
|
from termcolor import colored
|
||||||
|
|
||||||
|
from playground.models.dalle3 import Dalle3
|
||||||
|
|
||||||
|
|
||||||
|
# Mocking the OpenAI client to avoid making actual API calls during testing
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_openai_client():
|
||||||
|
return Mock()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def dalle3(mock_openai_client):
|
||||||
|
return Dalle3(client=mock_openai_client)
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_call_success(dalle3, mock_openai_client):
|
||||||
|
# Arrange
|
||||||
|
task = "A painting of a dog"
|
||||||
|
expected_img_url = (
|
||||||
|
"https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png"
|
||||||
|
)
|
||||||
|
mock_openai_client.images.generate.return_value = Mock(
|
||||||
|
data=[Mock(url=expected_img_url)]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act
|
||||||
|
img_url = dalle3(task)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert img_url == expected_img_url
|
||||||
|
mock_openai_client.images.generate.assert_called_once_with(prompt=task, n=4)
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_call_failure(dalle3, mock_openai_client, capsys):
|
||||||
|
# Arrange
|
||||||
|
task = "Invalid task"
|
||||||
|
expected_error_message = "Error running Dalle3: API Error"
|
||||||
|
|
||||||
|
# Mocking OpenAIError
|
||||||
|
mock_openai_client.images.generate.side_effect = OpenAIError(
|
||||||
|
expected_error_message, http_status=500, error="Internal Server Error"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act and assert
|
||||||
|
with pytest.raises(OpenAIError) as excinfo:
|
||||||
|
dalle3(task)
|
||||||
|
|
||||||
|
assert str(excinfo.value) == expected_error_message
|
||||||
|
mock_openai_client.images.generate.assert_called_once_with(prompt=task, n=4)
|
||||||
|
|
||||||
|
# Ensure the error message is printed in red
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert colored(expected_error_message, "red") in captured.out
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_create_variations_success(dalle3, mock_openai_client):
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png"
|
||||||
|
expected_variation_url = (
|
||||||
|
"https://cdn.openai.com/dall-e/encoded/feats/feats_02ABCDE.png"
|
||||||
|
)
|
||||||
|
mock_openai_client.images.create_variation.return_value = Mock(
|
||||||
|
data=[Mock(url=expected_variation_url)]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act
|
||||||
|
variation_img_url = dalle3.create_variations(img_url)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert variation_img_url == expected_variation_url
|
||||||
|
mock_openai_client.images.create_variation.assert_called_once()
|
||||||
|
_, kwargs = mock_openai_client.images.create_variation.call_args
|
||||||
|
assert kwargs["img"] is not None
|
||||||
|
assert kwargs["n"] == 4
|
||||||
|
assert kwargs["size"] == "1024x1024"
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_create_variations_failure(dalle3, mock_openai_client, capsys):
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png"
|
||||||
|
expected_error_message = "Error running Dalle3: API Error"
|
||||||
|
|
||||||
|
# Mocking OpenAIError
|
||||||
|
mock_openai_client.images.create_variation.side_effect = OpenAIError(
|
||||||
|
expected_error_message, http_status=500, error="Internal Server Error"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act and assert
|
||||||
|
with pytest.raises(OpenAIError) as excinfo:
|
||||||
|
dalle3.create_variations(img_url)
|
||||||
|
|
||||||
|
assert str(excinfo.value) == expected_error_message
|
||||||
|
mock_openai_client.images.create_variation.assert_called_once()
|
||||||
|
|
||||||
|
# Ensure the error message is printed in red
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert colored(expected_error_message, "red") in captured.out
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_read_img():
|
||||||
|
# Arrange
|
||||||
|
img_path = "test_image.png"
|
||||||
|
img = Image.new("RGB", (512, 512))
|
||||||
|
|
||||||
|
# Save the image temporarily
|
||||||
|
img.save(img_path)
|
||||||
|
|
||||||
|
# Act
|
||||||
|
dalle3 = Dalle3()
|
||||||
|
img_loaded = dalle3.read_img(img_path)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert isinstance(img_loaded, Image.Image)
|
||||||
|
|
||||||
|
# Clean up
|
||||||
|
os.remove(img_path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_set_width_height():
|
||||||
|
# Arrange
|
||||||
|
img = Image.new("RGB", (512, 512))
|
||||||
|
width = 256
|
||||||
|
height = 256
|
||||||
|
|
||||||
|
# Act
|
||||||
|
dalle3 = Dalle3()
|
||||||
|
img_resized = dalle3.set_width_height(img, width, height)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert img_resized.size == (width, height)
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_convert_to_bytesio():
|
||||||
|
# Arrange
|
||||||
|
img = Image.new("RGB", (512, 512))
|
||||||
|
expected_format = "PNG"
|
||||||
|
|
||||||
|
# Act
|
||||||
|
dalle3 = Dalle3()
|
||||||
|
img_bytes = dalle3.convert_to_bytesio(img, format=expected_format)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert isinstance(img_bytes, bytes)
|
||||||
|
assert img_bytes.startswith(b"\x89PNG")
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_call_multiple_times(dalle3, mock_openai_client):
|
||||||
|
# Arrange
|
||||||
|
task = "A painting of a dog"
|
||||||
|
expected_img_url = (
|
||||||
|
"https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png"
|
||||||
|
)
|
||||||
|
mock_openai_client.images.generate.return_value = Mock(
|
||||||
|
data=[Mock(url=expected_img_url)]
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act
|
||||||
|
img_url1 = dalle3(task)
|
||||||
|
img_url2 = dalle3(task)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert img_url1 == expected_img_url
|
||||||
|
assert img_url2 == expected_img_url
|
||||||
|
assert mock_openai_client.images.generate.call_count == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_call_with_large_input(dalle3, mock_openai_client):
|
||||||
|
# Arrange
|
||||||
|
task = "A" * 2048 # Input longer than API's limit
|
||||||
|
expected_error_message = "Error running Dalle3: API Error"
|
||||||
|
mock_openai_client.images.generate.side_effect = OpenAIError(
|
||||||
|
expected_error_message, http_status=500, error="Internal Server Error"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act and assert
|
||||||
|
with pytest.raises(OpenAIError) as excinfo:
|
||||||
|
dalle3(task)
|
||||||
|
|
||||||
|
assert str(excinfo.value) == expected_error_message
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_create_variations_with_invalid_image_url(dalle3, mock_openai_client):
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://invalid-image-url.com"
|
||||||
|
expected_error_message = "Error running Dalle3: Invalid image URL"
|
||||||
|
|
||||||
|
# Act and assert
|
||||||
|
with pytest.raises(ValueError) as excinfo:
|
||||||
|
dalle3.create_variations(img_url)
|
||||||
|
|
||||||
|
assert str(excinfo.value) == expected_error_message
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_set_width_height_invalid_dimensions(dalle3):
|
||||||
|
# Arrange
|
||||||
|
img = dalle3.read_img("test_image.png")
|
||||||
|
width = 0
|
||||||
|
height = -1
|
||||||
|
|
||||||
|
# Act and assert
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
dalle3.set_width_height(img, width, height)
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_convert_to_bytesio_invalid_format(dalle3):
|
||||||
|
# Arrange
|
||||||
|
img = dalle3.read_img("test_image.png")
|
||||||
|
invalid_format = "invalid_format"
|
||||||
|
|
||||||
|
# Act and assert
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
dalle3.convert_to_bytesio(img, format=invalid_format)
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_call_with_retry(dalle3, mock_openai_client):
|
||||||
|
# Arrange
|
||||||
|
task = "A painting of a dog"
|
||||||
|
expected_img_url = (
|
||||||
|
"https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Simulate a retry scenario
|
||||||
|
mock_openai_client.images.generate.side_effect = [
|
||||||
|
OpenAIError("Temporary error", http_status=500, error="Internal Server Error"),
|
||||||
|
Mock(data=[Mock(url=expected_img_url)]),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Act
|
||||||
|
img_url = dalle3(task)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert img_url == expected_img_url
|
||||||
|
assert mock_openai_client.images.generate.call_count == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_create_variations_with_retry(dalle3, mock_openai_client):
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png"
|
||||||
|
expected_variation_url = (
|
||||||
|
"https://cdn.openai.com/dall-e/encoded/feats/feats_02ABCDE.png"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Simulate a retry scenario
|
||||||
|
mock_openai_client.images.create_variation.side_effect = [
|
||||||
|
OpenAIError("Temporary error", http_status=500, error="Internal Server Error"),
|
||||||
|
Mock(data=[Mock(url=expected_variation_url)]),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Act
|
||||||
|
variation_img_url = dalle3.create_variations(img_url)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert variation_img_url == expected_variation_url
|
||||||
|
assert mock_openai_client.images.create_variation.call_count == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_call_exception_logging(dalle3, mock_openai_client, capsys):
|
||||||
|
# Arrange
|
||||||
|
task = "A painting of a dog"
|
||||||
|
expected_error_message = "Error running Dalle3: API Error"
|
||||||
|
|
||||||
|
# Mocking OpenAIError
|
||||||
|
mock_openai_client.images.generate.side_effect = OpenAIError(
|
||||||
|
expected_error_message, http_status=500, error="Internal Server Error"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act
|
||||||
|
with pytest.raises(OpenAIError):
|
||||||
|
dalle3(task)
|
||||||
|
|
||||||
|
# Assert that the error message is logged
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert expected_error_message in captured.err
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_create_variations_exception_logging(dalle3, mock_openai_client, capsys):
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png"
|
||||||
|
expected_error_message = "Error running Dalle3: API Error"
|
||||||
|
|
||||||
|
# Mocking OpenAIError
|
||||||
|
mock_openai_client.images.create_variation.side_effect = OpenAIError(
|
||||||
|
expected_error_message, http_status=500, error="Internal Server Error"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act
|
||||||
|
with pytest.raises(OpenAIError):
|
||||||
|
dalle3.create_variations(img_url)
|
||||||
|
|
||||||
|
# Assert that the error message is logged
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert expected_error_message in captured.err
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_read_img_invalid_path(dalle3):
|
||||||
|
# Arrange
|
||||||
|
invalid_img_path = "invalid_image_path.png"
|
||||||
|
|
||||||
|
# Act and assert
|
||||||
|
with pytest.raises(FileNotFoundError):
|
||||||
|
dalle3.read_img(invalid_img_path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_call_no_api_key():
|
||||||
|
# Arrange
|
||||||
|
task = "A painting of a dog"
|
||||||
|
dalle3 = Dalle3(api_key=None)
|
||||||
|
expected_error_message = "Error running Dalle3: API Key is missing"
|
||||||
|
|
||||||
|
# Act and assert
|
||||||
|
with pytest.raises(ValueError) as excinfo:
|
||||||
|
dalle3(task)
|
||||||
|
|
||||||
|
assert str(excinfo.value) == expected_error_message
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_create_variations_no_api_key():
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png"
|
||||||
|
dalle3 = Dalle3(api_key=None)
|
||||||
|
expected_error_message = "Error running Dalle3: API Key is missing"
|
||||||
|
|
||||||
|
# Act and assert
|
||||||
|
with pytest.raises(ValueError) as excinfo:
|
||||||
|
dalle3.create_variations(img_url)
|
||||||
|
|
||||||
|
assert str(excinfo.value) == expected_error_message
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_call_with_retry_max_retries_exceeded(dalle3, mock_openai_client):
|
||||||
|
# Arrange
|
||||||
|
task = "A painting of a dog"
|
||||||
|
|
||||||
|
# Simulate max retries exceeded
|
||||||
|
mock_openai_client.images.generate.side_effect = OpenAIError(
|
||||||
|
"Temporary error", http_status=500, error="Internal Server Error"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act and assert
|
||||||
|
with pytest.raises(OpenAIError) as excinfo:
|
||||||
|
dalle3(task)
|
||||||
|
|
||||||
|
assert "Retry limit exceeded" in str(excinfo.value)
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_create_variations_with_retry_max_retries_exceeded(
|
||||||
|
dalle3, mock_openai_client
|
||||||
|
):
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png"
|
||||||
|
|
||||||
|
# Simulate max retries exceeded
|
||||||
|
mock_openai_client.images.create_variation.side_effect = OpenAIError(
|
||||||
|
"Temporary error", http_status=500, error="Internal Server Error"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act and assert
|
||||||
|
with pytest.raises(OpenAIError) as excinfo:
|
||||||
|
dalle3.create_variations(img_url)
|
||||||
|
|
||||||
|
assert "Retry limit exceeded" in str(excinfo.value)
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_call_retry_with_success(dalle3, mock_openai_client):
|
||||||
|
# Arrange
|
||||||
|
task = "A painting of a dog"
|
||||||
|
expected_img_url = (
|
||||||
|
"https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Simulate success after a retry
|
||||||
|
mock_openai_client.images.generate.side_effect = [
|
||||||
|
OpenAIError("Temporary error", http_status=500, error="Internal Server Error"),
|
||||||
|
Mock(data=[Mock(url=expected_img_url)]),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Act
|
||||||
|
img_url = dalle3(task)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert img_url == expected_img_url
|
||||||
|
assert mock_openai_client.images.generate.call_count == 2
|
||||||
|
|
||||||
|
|
||||||
|
def test_dalle3_create_variations_retry_with_success(dalle3, mock_openai_client):
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png"
|
||||||
|
expected_variation_url = (
|
||||||
|
"https://cdn.openai.com/dall-e/encoded/feats/feats_02ABCDE.png"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Simulate success after a retry
|
||||||
|
mock_openai_client.images.create_variation.side_effect = [
|
||||||
|
OpenAIError("Temporary error", http_status=500, error="Internal Server Error"),
|
||||||
|
Mock(data=[Mock(url=expected_variation_url)]),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Act
|
||||||
|
variation_img_url = dalle3.create_variations(img_url)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert variation_img_url == expected_variation_url
|
||||||
|
assert mock_openai_client.images.create_variation.call_count == 2
|
@ -0,0 +1,119 @@
|
|||||||
|
# test_distilled_whisperx.py
|
||||||
|
|
||||||
|
from unittest.mock import AsyncMock, MagicMock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
import torch
|
||||||
|
from transformers import AutoModelForSpeechSeq2Seq, AutoProcessor
|
||||||
|
|
||||||
|
from swarms.models.distilled_whisperx import DistilWhisperModel, async_retry
|
||||||
|
|
||||||
|
|
||||||
|
# Fixtures for setting up model, processor, and audio files
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def model_id():
|
||||||
|
return "distil-whisper/distil-large-v2"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="module")
|
||||||
|
def whisper_model(model_id):
|
||||||
|
return DistilWhisperModel(model_id)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def audio_file_path(tmp_path_factory):
|
||||||
|
# You would create a small temporary MP3 file here for testing
|
||||||
|
# or use a public domain MP3 file's path
|
||||||
|
return "path/to/valid_audio.mp3"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def invalid_audio_file_path():
|
||||||
|
return "path/to/invalid_audio.mp3"
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def audio_dict():
|
||||||
|
# This should represent a valid audio dictionary as expected by the model
|
||||||
|
return {"array": torch.randn(1, 16000), "sampling_rate": 16000}
|
||||||
|
|
||||||
|
|
||||||
|
# Test initialization
|
||||||
|
def test_initialization(whisper_model):
|
||||||
|
assert whisper_model.model is not None
|
||||||
|
assert whisper_model.processor is not None
|
||||||
|
|
||||||
|
|
||||||
|
# Test successful transcription with file path
|
||||||
|
def test_transcribe_with_file_path(whisper_model, audio_file_path):
|
||||||
|
transcription = whisper_model.transcribe(audio_file_path)
|
||||||
|
assert isinstance(transcription, str)
|
||||||
|
|
||||||
|
|
||||||
|
# Test successful transcription with audio dict
|
||||||
|
def test_transcribe_with_audio_dict(whisper_model, audio_dict):
|
||||||
|
transcription = whisper_model.transcribe(audio_dict)
|
||||||
|
assert isinstance(transcription, str)
|
||||||
|
|
||||||
|
|
||||||
|
# Test for file not found error
|
||||||
|
def test_file_not_found(whisper_model, invalid_audio_file_path):
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
whisper_model.transcribe(invalid_audio_file_path)
|
||||||
|
|
||||||
|
|
||||||
|
# Asynchronous tests
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_async_transcription_success(whisper_model, audio_file_path):
|
||||||
|
transcription = await whisper_model.async_transcribe(audio_file_path)
|
||||||
|
assert isinstance(transcription, str)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_async_transcription_failure(whisper_model, invalid_audio_file_path):
|
||||||
|
with pytest.raises(Exception):
|
||||||
|
await whisper_model.async_transcribe(invalid_audio_file_path)
|
||||||
|
|
||||||
|
|
||||||
|
# Testing real-time transcription simulation
|
||||||
|
def test_real_time_transcription(whisper_model, audio_file_path, capsys):
|
||||||
|
whisper_model.real_time_transcribe(audio_file_path, chunk_duration=1)
|
||||||
|
captured = capsys.readouterr()
|
||||||
|
assert "Starting real-time transcription..." in captured.out
|
||||||
|
|
||||||
|
|
||||||
|
# Testing retry decorator for asynchronous function
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_async_retry():
|
||||||
|
@async_retry(max_retries=2, exceptions=(ValueError,), delay=0)
|
||||||
|
async def failing_func():
|
||||||
|
raise ValueError("Test")
|
||||||
|
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
await failing_func()
|
||||||
|
|
||||||
|
|
||||||
|
# Mocking the actual model to avoid GPU/CPU intensive operations during test
|
||||||
|
@pytest.fixture
|
||||||
|
def mocked_model(monkeypatch):
|
||||||
|
model_mock = AsyncMock(AutoModelForSpeechSeq2Seq)
|
||||||
|
processor_mock = MagicMock(AutoProcessor)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"swarms.models.distilled_whisperx.AutoModelForSpeechSeq2Seq.from_pretrained",
|
||||||
|
model_mock,
|
||||||
|
)
|
||||||
|
monkeypatch.setattr(
|
||||||
|
"swarms.models.distilled_whisperx.AutoProcessor.from_pretrained", processor_mock
|
||||||
|
)
|
||||||
|
return model_mock, processor_mock
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_async_transcribe_with_mocked_model(mocked_model, audio_file_path):
|
||||||
|
model_mock, processor_mock = mocked_model
|
||||||
|
# Set up what the mock should return when it's called
|
||||||
|
model_mock.return_value.generate.return_value = torch.tensor([[0]])
|
||||||
|
processor_mock.return_value.batch_decode.return_value = ["mocked transcription"]
|
||||||
|
model_wrapper = DistilWhisperModel()
|
||||||
|
transcription = await model_wrapper.async_transcribe(audio_file_path)
|
||||||
|
assert transcription == "mocked transcription"
|
@ -0,0 +1,386 @@
|
|||||||
|
import logging
|
||||||
|
import os
|
||||||
|
from unittest.mock import Mock
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
from requests.exceptions import ConnectionError, HTTPError, RequestException, Timeout
|
||||||
|
|
||||||
|
from swarms.models.gpt4v import GPT4Vision, GPT4VisionResponse
|
||||||
|
|
||||||
|
load_dotenv
|
||||||
|
|
||||||
|
api_key = os.getenv("OPENAI_API_KEY")
|
||||||
|
|
||||||
|
|
||||||
|
# Mock the OpenAI client
|
||||||
|
@pytest.fixture
|
||||||
|
def mock_openai_client():
|
||||||
|
return Mock()
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def gpt4vision(mock_openai_client):
|
||||||
|
return GPT4Vision(client=mock_openai_client)
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_default_values():
|
||||||
|
# Arrange and Act
|
||||||
|
gpt4vision = GPT4Vision()
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert gpt4vision.max_retries == 3
|
||||||
|
assert gpt4vision.model == "gpt-4-vision-preview"
|
||||||
|
assert gpt4vision.backoff_factor == 2.0
|
||||||
|
assert gpt4vision.timeout_seconds == 10
|
||||||
|
assert gpt4vision.api_key is None
|
||||||
|
assert gpt4vision.quality == "low"
|
||||||
|
assert gpt4vision.max_tokens == 200
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_api_key_from_env_variable():
|
||||||
|
# Arrange
|
||||||
|
api_key = os.environ["OPENAI_API_KEY"]
|
||||||
|
|
||||||
|
# Act
|
||||||
|
gpt4vision = GPT4Vision()
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert gpt4vision.api_key == api_key
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_set_api_key():
|
||||||
|
# Arrange
|
||||||
|
gpt4vision = GPT4Vision(api_key=api_key)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert gpt4vision.api_key == api_key
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_invalid_max_retries():
|
||||||
|
# Arrange and Act
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
GPT4Vision(max_retries=-1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_invalid_backoff_factor():
|
||||||
|
# Arrange and Act
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
GPT4Vision(backoff_factor=-1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_invalid_timeout_seconds():
|
||||||
|
# Arrange and Act
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
GPT4Vision(timeout_seconds=-1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_invalid_max_tokens():
|
||||||
|
# Arrange and Act
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
GPT4Vision(max_tokens=-1)
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_logger_initialized():
|
||||||
|
# Arrange
|
||||||
|
gpt4vision = GPT4Vision()
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert isinstance(gpt4vision.logger, logging.Logger)
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_process_img_nonexistent_file():
|
||||||
|
# Arrange
|
||||||
|
gpt4vision = GPT4Vision()
|
||||||
|
img_path = "nonexistent_image.jpg"
|
||||||
|
|
||||||
|
# Act and Assert
|
||||||
|
with pytest.raises(FileNotFoundError):
|
||||||
|
gpt4vision.process_img(img_path)
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_call_single_task_single_image_no_openai_client(gpt4vision):
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||||
|
task = "Describe this image."
|
||||||
|
|
||||||
|
# Act and Assert
|
||||||
|
with pytest.raises(AttributeError):
|
||||||
|
gpt4vision(img_url, [task])
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_call_single_task_single_image_empty_response(
|
||||||
|
gpt4vision, mock_openai_client
|
||||||
|
):
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||||
|
task = "Describe this image."
|
||||||
|
|
||||||
|
mock_openai_client.chat.completions.create.return_value.choices = []
|
||||||
|
|
||||||
|
# Act
|
||||||
|
response = gpt4vision(img_url, [task])
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert response.answer == ""
|
||||||
|
mock_openai_client.chat.completions.create.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_call_multiple_tasks_single_image_empty_responses(
|
||||||
|
gpt4vision, mock_openai_client
|
||||||
|
):
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||||
|
tasks = ["Describe this image.", "What's in this picture?"]
|
||||||
|
|
||||||
|
mock_openai_client.chat.completions.create.return_value.choices = []
|
||||||
|
|
||||||
|
# Act
|
||||||
|
responses = gpt4vision(img_url, tasks)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert all(response.answer == "" for response in responses)
|
||||||
|
assert (
|
||||||
|
mock_openai_client.chat.completions.create.call_count == 1
|
||||||
|
) # Should be called only once
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_call_single_task_single_image_timeout(
|
||||||
|
gpt4vision, mock_openai_client
|
||||||
|
):
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||||
|
task = "Describe this image."
|
||||||
|
|
||||||
|
mock_openai_client.chat.completions.create.side_effect = Timeout(
|
||||||
|
"Request timed out"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act and Assert
|
||||||
|
with pytest.raises(Timeout):
|
||||||
|
gpt4vision(img_url, [task])
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_call_retry_with_success_after_timeout(
|
||||||
|
gpt4vision, mock_openai_client
|
||||||
|
):
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||||
|
task = "Describe this image."
|
||||||
|
|
||||||
|
# Simulate success after a timeout and retry
|
||||||
|
mock_openai_client.chat.completions.create.side_effect = [
|
||||||
|
Timeout("Request timed out"),
|
||||||
|
{
|
||||||
|
"choices": [
|
||||||
|
{"message": {"content": {"text": "A description of the image."}}}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
# Act
|
||||||
|
response = gpt4vision(img_url, [task])
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert response.answer == "A description of the image."
|
||||||
|
assert (
|
||||||
|
mock_openai_client.chat.completions.create.call_count == 2
|
||||||
|
) # Should be called twice
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_process_img():
|
||||||
|
# Arrange
|
||||||
|
img_path = "test_image.jpg"
|
||||||
|
gpt4vision = GPT4Vision()
|
||||||
|
|
||||||
|
# Act
|
||||||
|
img_data = gpt4vision.process_img(img_path)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert img_data.startswith("/9j/") # Base64-encoded image data
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_call_single_task_single_image(gpt4vision, mock_openai_client):
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||||
|
task = "Describe this image."
|
||||||
|
|
||||||
|
expected_response = GPT4VisionResponse(answer="A description of the image.")
|
||||||
|
|
||||||
|
mock_openai_client.chat.completions.create.return_value.choices[
|
||||||
|
0
|
||||||
|
].text = expected_response.answer
|
||||||
|
|
||||||
|
# Act
|
||||||
|
response = gpt4vision(img_url, [task])
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert response == expected_response
|
||||||
|
mock_openai_client.chat.completions.create.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_call_single_task_multiple_images(gpt4vision, mock_openai_client):
|
||||||
|
# Arrange
|
||||||
|
img_urls = ["https://example.com/image1.jpg", "https://example.com/image2.jpg"]
|
||||||
|
task = "Describe these images."
|
||||||
|
|
||||||
|
expected_response = GPT4VisionResponse(answer="Descriptions of the images.")
|
||||||
|
|
||||||
|
mock_openai_client.chat.completions.create.return_value.choices[
|
||||||
|
0
|
||||||
|
].text = expected_response.answer
|
||||||
|
|
||||||
|
# Act
|
||||||
|
response = gpt4vision(img_urls, [task])
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert response == expected_response
|
||||||
|
mock_openai_client.chat.completions.create.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_call_multiple_tasks_single_image(gpt4vision, mock_openai_client):
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||||
|
tasks = ["Describe this image.", "What's in this picture?"]
|
||||||
|
|
||||||
|
expected_responses = [
|
||||||
|
GPT4VisionResponse(answer="A description of the image."),
|
||||||
|
GPT4VisionResponse(answer="It contains various objects."),
|
||||||
|
]
|
||||||
|
|
||||||
|
def create_mock_response(response):
|
||||||
|
return {"choices": [{"message": {"content": {"text": response.answer}}}]}
|
||||||
|
|
||||||
|
mock_openai_client.chat.completions.create.side_effect = [
|
||||||
|
create_mock_response(response) for response in expected_responses
|
||||||
|
]
|
||||||
|
|
||||||
|
# Act
|
||||||
|
responses = gpt4vision(img_url, tasks)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert responses == expected_responses
|
||||||
|
assert (
|
||||||
|
mock_openai_client.chat.completions.create.call_count == 1
|
||||||
|
) # Should be called only once
|
||||||
|
|
||||||
|
def test_gpt4vision_call_multiple_tasks_single_image(
|
||||||
|
gpt4vision, mock_openai_client
|
||||||
|
):
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||||
|
tasks = ["Describe this image.", "What's in this picture?"]
|
||||||
|
|
||||||
|
expected_responses = [
|
||||||
|
GPT4VisionResponse(answer="A description of the image."),
|
||||||
|
GPT4VisionResponse(answer="It contains various objects."),
|
||||||
|
]
|
||||||
|
|
||||||
|
mock_openai_client.chat.completions.create.side_effect = [
|
||||||
|
{
|
||||||
|
"choices": [
|
||||||
|
{"message": {"content": {"text": expected_responses[i].answer}}}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
for i in range(len(expected_responses))
|
||||||
|
]
|
||||||
|
|
||||||
|
# Act
|
||||||
|
responses = gpt4vision(img_url, tasks)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert responses == expected_responses
|
||||||
|
assert (
|
||||||
|
mock_openai_client.chat.completions.create.call_count == 1
|
||||||
|
) # Should be called only once
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_call_multiple_tasks_multiple_images(gpt4vision, mock_openai_client):
|
||||||
|
# Arrange
|
||||||
|
img_urls = [
|
||||||
|
"https://images.unsplash.com/photo-1694734479857-626882b6db37?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||||
|
"https://images.unsplash.com/photo-1694734479898-6ac4633158ac?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D",
|
||||||
|
]
|
||||||
|
tasks = ["Describe these images.", "What's in these pictures?"]
|
||||||
|
|
||||||
|
expected_responses = [
|
||||||
|
GPT4VisionResponse(answer="Descriptions of the images."),
|
||||||
|
GPT4VisionResponse(answer="They contain various objects."),
|
||||||
|
]
|
||||||
|
|
||||||
|
mock_openai_client.chat.completions.create.side_effect = [
|
||||||
|
{"choices": [{"message": {"content": {"text": response.answer}}}]}
|
||||||
|
for response in expected_responses
|
||||||
|
]
|
||||||
|
|
||||||
|
# Act
|
||||||
|
responses = gpt4vision(img_urls, tasks)
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert responses == expected_responses
|
||||||
|
assert (
|
||||||
|
mock_openai_client.chat.completions.create.call_count == 1
|
||||||
|
) # Should be called only once
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_call_http_error(gpt4vision, mock_openai_client):
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||||
|
task = "Describe this image."
|
||||||
|
|
||||||
|
mock_openai_client.chat.completions.create.side_effect = HTTPError("HTTP Error")
|
||||||
|
|
||||||
|
# Act and Assert
|
||||||
|
with pytest.raises(HTTPError):
|
||||||
|
gpt4vision(img_url, [task])
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_call_request_error(gpt4vision, mock_openai_client):
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||||
|
task = "Describe this image."
|
||||||
|
|
||||||
|
mock_openai_client.chat.completions.create.side_effect = RequestException(
|
||||||
|
"Request Error"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act and Assert
|
||||||
|
with pytest.raises(RequestException):
|
||||||
|
gpt4vision(img_url, [task])
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_call_connection_error(gpt4vision, mock_openai_client):
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||||
|
task = "Describe this image."
|
||||||
|
|
||||||
|
mock_openai_client.chat.completions.create.side_effect = ConnectionError(
|
||||||
|
"Connection Error"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Act and Assert
|
||||||
|
with pytest.raises(ConnectionError):
|
||||||
|
gpt4vision(img_url, [task])
|
||||||
|
|
||||||
|
|
||||||
|
def test_gpt4vision_call_retry_with_success(gpt4vision, mock_openai_client):
|
||||||
|
# Arrange
|
||||||
|
img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D"
|
||||||
|
task = "Describe this image."
|
||||||
|
|
||||||
|
# Simulate success after a retry
|
||||||
|
mock_openai_client.chat.completions.create.side_effect = [
|
||||||
|
RequestException("Temporary error"),
|
||||||
|
{
|
||||||
|
"choices": [{"text": "A description of the image."}]
|
||||||
|
}, # fixed dictionary syntax
|
||||||
|
]
|
||||||
|
|
||||||
|
# Act
|
||||||
|
response = gpt4vision(img_url, [task])
|
||||||
|
|
||||||
|
# Assert
|
||||||
|
assert response.answer == "A description of the image."
|
||||||
|
assert (
|
||||||
|
mock_openai_client.chat.completions.create.call_count == 2
|
||||||
|
) # Should be called twice
|
@ -0,0 +1,333 @@
|
|||||||
|
import asyncio
|
||||||
|
import os
|
||||||
|
from unittest.mock import patch
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from swarms.models import OpenAIChat
|
||||||
|
from swarms.structs.flow import Flow
|
||||||
|
from swarms.structs.sequential_workflow import SequentialWorkflow, Task
|
||||||
|
|
||||||
|
# Mock the OpenAI API key using environment variables
|
||||||
|
os.environ["OPENAI_API_KEY"] = "mocked_api_key"
|
||||||
|
|
||||||
|
|
||||||
|
# Mock OpenAIChat class for testing
|
||||||
|
class MockOpenAIChat:
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run(self, *args, **kwargs):
|
||||||
|
return "Mocked result"
|
||||||
|
|
||||||
|
|
||||||
|
# Mock Flow class for testing
|
||||||
|
class MockFlow:
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run(self, *args, **kwargs):
|
||||||
|
return "Mocked result"
|
||||||
|
|
||||||
|
|
||||||
|
# Mock SequentialWorkflow class for testing
|
||||||
|
class MockSequentialWorkflow:
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def add(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
# Test Task class
|
||||||
|
def test_task_initialization():
|
||||||
|
description = "Sample Task"
|
||||||
|
flow = MockOpenAIChat()
|
||||||
|
task = Task(description=description, flow=flow)
|
||||||
|
assert task.description == description
|
||||||
|
assert task.flow == flow
|
||||||
|
|
||||||
|
|
||||||
|
def test_task_execute():
|
||||||
|
description = "Sample Task"
|
||||||
|
flow = MockOpenAIChat()
|
||||||
|
task = Task(description=description, flow=flow)
|
||||||
|
task.execute()
|
||||||
|
assert task.result == "Mocked result"
|
||||||
|
|
||||||
|
|
||||||
|
# Test SequentialWorkflow class
|
||||||
|
def test_sequential_workflow_initialization():
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
assert isinstance(workflow, SequentialWorkflow)
|
||||||
|
assert len(workflow.tasks) == 0
|
||||||
|
assert workflow.max_loops == 1
|
||||||
|
assert workflow.autosave == False
|
||||||
|
assert workflow.saved_state_filepath == "sequential_workflow_state.json"
|
||||||
|
assert workflow.restore_state_filepath == None
|
||||||
|
assert workflow.dashboard == False
|
||||||
|
|
||||||
|
|
||||||
|
def test_sequential_workflow_add_task():
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
task_description = "Sample Task"
|
||||||
|
task_flow = MockOpenAIChat()
|
||||||
|
workflow.add(task_description, task_flow)
|
||||||
|
assert len(workflow.tasks) == 1
|
||||||
|
assert workflow.tasks[0].description == task_description
|
||||||
|
assert workflow.tasks[0].flow == task_flow
|
||||||
|
|
||||||
|
|
||||||
|
def test_sequential_workflow_reset_workflow():
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
task_description = "Sample Task"
|
||||||
|
task_flow = MockOpenAIChat()
|
||||||
|
workflow.add(task_description, task_flow)
|
||||||
|
workflow.reset_workflow()
|
||||||
|
assert workflow.tasks[0].result == None
|
||||||
|
|
||||||
|
|
||||||
|
def test_sequential_workflow_get_task_results():
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
task_description = "Sample Task"
|
||||||
|
task_flow = MockOpenAIChat()
|
||||||
|
workflow.add(task_description, task_flow)
|
||||||
|
workflow.run()
|
||||||
|
results = workflow.get_task_results()
|
||||||
|
assert len(results) == 1
|
||||||
|
assert task_description in results
|
||||||
|
assert results[task_description] == "Mocked result"
|
||||||
|
|
||||||
|
|
||||||
|
def test_sequential_workflow_remove_task():
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
task1_description = "Task 1"
|
||||||
|
task2_description = "Task 2"
|
||||||
|
task1_flow = MockOpenAIChat()
|
||||||
|
task2_flow = MockOpenAIChat()
|
||||||
|
workflow.add(task1_description, task1_flow)
|
||||||
|
workflow.add(task2_description, task2_flow)
|
||||||
|
workflow.remove_task(task1_description)
|
||||||
|
assert len(workflow.tasks) == 1
|
||||||
|
assert workflow.tasks[0].description == task2_description
|
||||||
|
|
||||||
|
|
||||||
|
def test_sequential_workflow_update_task():
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
task_description = "Sample Task"
|
||||||
|
task_flow = MockOpenAIChat()
|
||||||
|
workflow.add(task_description, task_flow)
|
||||||
|
workflow.update_task(task_description, max_tokens=1000)
|
||||||
|
assert workflow.tasks[0].kwargs["max_tokens"] == 1000
|
||||||
|
|
||||||
|
|
||||||
|
def test_sequential_workflow_save_workflow_state():
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
task_description = "Sample Task"
|
||||||
|
task_flow = MockOpenAIChat()
|
||||||
|
workflow.add(task_description, task_flow)
|
||||||
|
workflow.save_workflow_state("test_state.json")
|
||||||
|
assert os.path.exists("test_state.json")
|
||||||
|
os.remove("test_state.json")
|
||||||
|
|
||||||
|
|
||||||
|
def test_sequential_workflow_load_workflow_state():
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
task_description = "Sample Task"
|
||||||
|
task_flow = MockOpenAIChat()
|
||||||
|
workflow.add(task_description, task_flow)
|
||||||
|
workflow.save_workflow_state("test_state.json")
|
||||||
|
workflow.load_workflow_state("test_state.json")
|
||||||
|
assert len(workflow.tasks) == 1
|
||||||
|
assert workflow.tasks[0].description == task_description
|
||||||
|
os.remove("test_state.json")
|
||||||
|
|
||||||
|
|
||||||
|
def test_sequential_workflow_run():
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
task_description = "Sample Task"
|
||||||
|
task_flow = MockOpenAIChat()
|
||||||
|
workflow.add(task_description, task_flow)
|
||||||
|
workflow.run()
|
||||||
|
assert workflow.tasks[0].result == "Mocked result"
|
||||||
|
|
||||||
|
|
||||||
|
def test_sequential_workflow_workflow_bootup(capfd):
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
workflow.workflow_bootup()
|
||||||
|
out, _ = capfd.readouterr()
|
||||||
|
assert "Sequential Workflow Initializing..." in out
|
||||||
|
|
||||||
|
|
||||||
|
def test_sequential_workflow_workflow_dashboard(capfd):
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
workflow.workflow_dashboard()
|
||||||
|
out, _ = capfd.readouterr()
|
||||||
|
assert "Sequential Workflow Dashboard" in out
|
||||||
|
|
||||||
|
|
||||||
|
# Mock Flow class for async testing
|
||||||
|
class MockAsyncFlow:
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
async def arun(self, *args, **kwargs):
|
||||||
|
return "Mocked result"
|
||||||
|
|
||||||
|
|
||||||
|
# Test async execution in SequentialWorkflow
|
||||||
|
@pytest.mark.asyncio
|
||||||
|
async def test_sequential_workflow_arun():
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
task_description = "Sample Task"
|
||||||
|
task_flow = MockAsyncFlow()
|
||||||
|
workflow.add(task_description, task_flow)
|
||||||
|
await workflow.arun()
|
||||||
|
assert workflow.tasks[0].result == "Mocked result"
|
||||||
|
|
||||||
|
|
||||||
|
def test_real_world_usage_with_openai_key():
|
||||||
|
# Initialize the language model
|
||||||
|
llm = OpenAIChat()
|
||||||
|
assert isinstance(llm, OpenAIChat)
|
||||||
|
|
||||||
|
|
||||||
|
def test_real_world_usage_with_flow_and_openai_key():
|
||||||
|
# Initialize a flow with the language model
|
||||||
|
flow = Flow(llm=OpenAIChat())
|
||||||
|
assert isinstance(flow, Flow)
|
||||||
|
|
||||||
|
|
||||||
|
def test_real_world_usage_with_sequential_workflow():
|
||||||
|
# Initialize a sequential workflow
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
assert isinstance(workflow, SequentialWorkflow)
|
||||||
|
|
||||||
|
|
||||||
|
def test_real_world_usage_add_tasks():
|
||||||
|
# Create a sequential workflow and add tasks
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
task1_description = "Task 1"
|
||||||
|
task2_description = "Task 2"
|
||||||
|
task1_flow = OpenAIChat()
|
||||||
|
task2_flow = OpenAIChat()
|
||||||
|
workflow.add(task1_description, task1_flow)
|
||||||
|
workflow.add(task2_description, task2_flow)
|
||||||
|
assert len(workflow.tasks) == 2
|
||||||
|
assert workflow.tasks[0].description == task1_description
|
||||||
|
assert workflow.tasks[1].description == task2_description
|
||||||
|
|
||||||
|
|
||||||
|
def test_real_world_usage_run_workflow():
|
||||||
|
# Create a sequential workflow, add a task, and run the workflow
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
task_description = "Sample Task"
|
||||||
|
task_flow = OpenAIChat()
|
||||||
|
workflow.add(task_description, task_flow)
|
||||||
|
workflow.run()
|
||||||
|
assert workflow.tasks[0].result is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_real_world_usage_dashboard_display():
|
||||||
|
# Create a sequential workflow, add tasks, and display the dashboard
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
task1_description = "Task 1"
|
||||||
|
task2_description = "Task 2"
|
||||||
|
task1_flow = OpenAIChat()
|
||||||
|
task2_flow = OpenAIChat()
|
||||||
|
workflow.add(task1_description, task1_flow)
|
||||||
|
workflow.add(task2_description, task2_flow)
|
||||||
|
with patch("builtins.print") as mock_print:
|
||||||
|
workflow.workflow_dashboard()
|
||||||
|
mock_print.assert_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_real_world_usage_async_execution():
|
||||||
|
# Create a sequential workflow, add an async task, and run the workflow asynchronously
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
task_description = "Sample Task"
|
||||||
|
async_task_flow = OpenAIChat()
|
||||||
|
|
||||||
|
async def async_run_workflow():
|
||||||
|
await workflow.arun()
|
||||||
|
|
||||||
|
workflow.add(task_description, async_task_flow)
|
||||||
|
asyncio.run(async_run_workflow())
|
||||||
|
assert workflow.tasks[0].result is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_real_world_usage_multiple_loops():
|
||||||
|
# Create a sequential workflow with multiple loops, add a task, and run the workflow
|
||||||
|
workflow = SequentialWorkflow(max_loops=3)
|
||||||
|
task_description = "Sample Task"
|
||||||
|
task_flow = OpenAIChat()
|
||||||
|
workflow.add(task_description, task_flow)
|
||||||
|
workflow.run()
|
||||||
|
assert workflow.tasks[0].result is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_real_world_usage_autosave_state():
|
||||||
|
# Create a sequential workflow with autosave, add a task, run the workflow, and check if state is saved
|
||||||
|
workflow = SequentialWorkflow(autosave=True)
|
||||||
|
task_description = "Sample Task"
|
||||||
|
task_flow = OpenAIChat()
|
||||||
|
workflow.add(task_description, task_flow)
|
||||||
|
workflow.run()
|
||||||
|
assert workflow.tasks[0].result is not None
|
||||||
|
assert os.path.exists("sequential_workflow_state.json")
|
||||||
|
os.remove("sequential_workflow_state.json")
|
||||||
|
|
||||||
|
|
||||||
|
def test_real_world_usage_load_state():
|
||||||
|
# Create a sequential workflow, add a task, save state, load state, and run the workflow
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
task_description = "Sample Task"
|
||||||
|
task_flow = OpenAIChat()
|
||||||
|
workflow.add(task_description, task_flow)
|
||||||
|
workflow.run()
|
||||||
|
workflow.save_workflow_state("test_state.json")
|
||||||
|
workflow.load_workflow_state("test_state.json")
|
||||||
|
workflow.run()
|
||||||
|
assert workflow.tasks[0].result is not None
|
||||||
|
os.remove("test_state.json")
|
||||||
|
|
||||||
|
|
||||||
|
def test_real_world_usage_update_task_args():
|
||||||
|
# Create a sequential workflow, add a task, and update task arguments
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
task_description = "Sample Task"
|
||||||
|
task_flow = OpenAIChat()
|
||||||
|
workflow.add(task_description, task_flow)
|
||||||
|
workflow.update_task(task_description, max_tokens=1000)
|
||||||
|
assert workflow.tasks[0].kwargs["max_tokens"] == 1000
|
||||||
|
|
||||||
|
|
||||||
|
def test_real_world_usage_remove_task():
|
||||||
|
# Create a sequential workflow, add tasks, remove a task, and run the workflow
|
||||||
|
workflow = SequentialWorkflow()
|
||||||
|
task1_description = "Task 1"
|
||||||
|
task2_description = "Task 2"
|
||||||
|
task1_flow = OpenAIChat()
|
||||||
|
task2_flow = OpenAIChat()
|
||||||
|
workflow.add(task1_description, task1_flow)
|
||||||
|
workflow.add(task2_description, task2_flow)
|
||||||
|
workflow.remove_task(task1_description)
|
||||||
|
workflow.run()
|
||||||
|
assert len(workflow.tasks) == 1
|
||||||
|
assert workflow.tasks[0].description == task2_description
|
||||||
|
|
||||||
|
|
||||||
|
def test_real_world_usage_with_environment_variables():
|
||||||
|
# Ensure that the OpenAI API key is set using environment variables
|
||||||
|
assert "OPENAI_API_KEY" in os.environ
|
||||||
|
assert os.environ["OPENAI_API_KEY"] == "mocked_api_key"
|
||||||
|
del os.environ["OPENAI_API_KEY"] # Clean up after the test
|
||||||
|
|
||||||
|
|
||||||
|
def test_real_world_usage_no_openai_key():
|
||||||
|
# Ensure that an exception is raised when the OpenAI API key is not set
|
||||||
|
with pytest.raises(ValueError):
|
||||||
|
llm = OpenAIChat() # API key not provided, should raise an exception
|
@ -1,11 +0,0 @@
|
|||||||
from swarms.models import OpenAIChat
|
|
||||||
from swarms.structs import Workflow
|
|
||||||
|
|
||||||
|
|
||||||
llm = OpenAIChat(openai_api_key="")
|
|
||||||
|
|
||||||
workflow = Workflow(llm)
|
|
||||||
|
|
||||||
workflow.add("What's the weather in miami")
|
|
||||||
|
|
||||||
workflow.run()
|
|
Loading…
Reference in new issue