From 5dd4758d1db7984be36de837fa9935747a0e0649 Mon Sep 17 00:00:00 2001 From: Kye Date: Fri, 5 Jan 2024 14:50:55 -0500 Subject: [PATCH] [FEAT][BlockList] --- README.md | 109 ++++++++++++++++++++++++ block.py | 97 +++++++++++++++++++++ pyproject.toml | 5 +- requirements.txt | 1 + swarms/__init__.py | 4 +- swarms/models/huggingface.py | 49 ++++------- swarms/structs/block_wrapper.py | 4 +- swarms/structs/blockslist.py | 33 ++++++- swarms/structs/model_parallizer.py | 12 --- swarms/telemetry/auto_upgrade_swarms.py | 4 + swarms/telemetry/bootup.py | 8 ++ swarms/telemetry/check_update.py | 4 - 12 files changed, 277 insertions(+), 53 deletions(-) create mode 100644 block.py create mode 100644 swarms/telemetry/bootup.py diff --git a/README.md b/README.md index 2f9883a6..997bc797 100644 --- a/README.md +++ b/README.md @@ -456,6 +456,115 @@ print(f"Task result: {task.result}") --- + +### `BlockList` +- Modularity and Flexibility: BlocksList allows users to create custom swarms by adding or removing different classes or functions as blocks. This means users can easily tailor the functionality of their swarm to suit their specific needs. + +- Ease of Management: With methods to add, remove, update, and retrieve blocks, BlocksList provides a straightforward way to manage the components of a swarm. This makes it easier to maintain and update the swarm over time. + +- Enhanced Searchability: BlocksList offers methods to get blocks by various attributes such as name, type, ID, and parent-related properties. This makes it easier for users to find and work with specific blocks in a large and complex swarm. + +```python +import os + +from dotenv import load_dotenv +from transformers import AutoModelForCausalLM, AutoTokenizer + +# Import the models, structs, and telemetry modules +from swarms import ( + Gemini, + GPT4VisionAPI, + Mixtral, + OpenAI, + ToolAgent, + BlocksList, +) + +# Load the environment variables +load_dotenv() + +# Get the environment variables +openai_api_key = os.getenv("OPENAI_API_KEY") +gemini_api_key = os.getenv("GEMINI_API_KEY") + +# Tool Agent +model = AutoModelForCausalLM.from_pretrained( + "databricks/dolly-v2-12b" +) +tokenizer = AutoTokenizer.from_pretrained("databricks/dolly-v2-12b") +json_schema = { + "type": "object", + "properties": { + "name": {"type": "string"}, + "age": {"type": "number"}, + "is_student": {"type": "boolean"}, + "courses": {"type": "array", "items": {"type": "string"}}, + }, +} +toolagent = ToolAgent( + model=model, tokenizer=tokenizer, json_schema=json_schema +) + +# Blocks List which enables you to build custom swarms by adding classes or functions +swarm = BlocksList( + "SocialMediaSwarm", + "A swarm of social media agents", + [ + OpenAI(openai_api_key=openai_api_key), + Mixtral(), + GPT4VisionAPI(openai_api_key=openai_api_key), + Gemini(gemini_api_key=gemini_api_key), + ], +) + + +# Add the new block to the swarm +swarm.add(toolagent) + +# Remove a block from the swarm +swarm.remove(toolagent) + +# Update a block in the swarm +swarm.update(toolagent) + +# Get a block at a specific index +block_at_index = swarm.get(0) + +# Get all blocks in the swarm +all_blocks = swarm.get_all() + +# Get blocks by name +openai_blocks = swarm.get_by_name("OpenAI") + +# Get blocks by type +gpt4_blocks = swarm.get_by_type("GPT4VisionAPI") + +# Get blocks by ID +block_by_id = swarm.get_by_id(toolagent.id) + +# Get blocks by parent +blocks_by_parent = swarm.get_by_parent(swarm) + +# Get blocks by parent ID +blocks_by_parent_id = swarm.get_by_parent_id(swarm.id) + +# Get blocks by parent name +blocks_by_parent_name = swarm.get_by_parent_name(swarm.name) + +# Get blocks by parent type +blocks_by_parent_type = swarm.get_by_parent_type(type(swarm).__name__) + +# Get blocks by parent description +blocks_by_parent_description = swarm.get_by_parent_description( + swarm.description +) + +# Run the block in the swarm +inference = swarm.run_block(toolagent, "Hello World") +print(inference) +``` + + ## Real-World Deployment ### Multi-Agent Swarm for Logistics diff --git a/block.py b/block.py new file mode 100644 index 00000000..c5a76910 --- /dev/null +++ b/block.py @@ -0,0 +1,97 @@ +import os + +from dotenv import load_dotenv +from transformers import AutoModelForCausalLM, AutoTokenizer + +# Import the models, structs, and telemetry modules +from swarms import ( + Gemini, + GPT4VisionAPI, + Mixtral, + OpenAI, + ToolAgent, + BlocksList, +) + +# Load the environment variables +load_dotenv() + +# Get the environment variables +openai_api_key = os.getenv("OPENAI_API_KEY") +gemini_api_key = os.getenv("GEMINI_API_KEY") + +# Tool Agent +model = AutoModelForCausalLM.from_pretrained( + "databricks/dolly-v2-12b" +) +tokenizer = AutoTokenizer.from_pretrained("databricks/dolly-v2-12b") +json_schema = { + "type": "object", + "properties": { + "name": {"type": "string"}, + "age": {"type": "number"}, + "is_student": {"type": "boolean"}, + "courses": {"type": "array", "items": {"type": "string"}}, + }, +} +toolagent = ToolAgent( + model=model, tokenizer=tokenizer, json_schema=json_schema +) + +# Blocks List which enables you to build custom swarms by adding classes or functions +swarm = BlocksList( + "SocialMediaSwarm", + "A swarm of social media agents", + [ + OpenAI(openai_api_key=openai_api_key), + Mixtral(), + GPT4VisionAPI(openai_api_key=openai_api_key), + Gemini(gemini_api_key=gemini_api_key), + ], +) + + +# Add the new block to the swarm +swarm.add(toolagent) + +# Remove a block from the swarm +swarm.remove(toolagent) + +# Update a block in the swarm +swarm.update(toolagent) + +# Get a block at a specific index +block_at_index = swarm.get(0) + +# Get all blocks in the swarm +all_blocks = swarm.get_all() + +# Get blocks by name +openai_blocks = swarm.get_by_name("OpenAI") + +# Get blocks by type +gpt4_blocks = swarm.get_by_type("GPT4VisionAPI") + +# Get blocks by ID +block_by_id = swarm.get_by_id(toolagent.id) + +# Get blocks by parent +blocks_by_parent = swarm.get_by_parent(swarm) + +# Get blocks by parent ID +blocks_by_parent_id = swarm.get_by_parent_id(swarm.id) + +# Get blocks by parent name +blocks_by_parent_name = swarm.get_by_parent_name(swarm.name) + +# Get blocks by parent type +blocks_by_parent_type = swarm.get_by_parent_type(type(swarm).__name__) + +# Get blocks by parent description +blocks_by_parent_description = swarm.get_by_parent_description( + swarm.description +) + +# Run the block in the swarm +inference = swarm.run_block(toolagent, "Hello World") +print(inference) \ No newline at end of file diff --git a/pyproject.toml b/pyproject.toml index c09edb2b..e0602d43 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "swarms" -version = "3.3.4" +version = "3.4.1" description = "Swarms - Pytorch" license = "MIT" authors = ["Kye Gomez "] @@ -41,7 +41,7 @@ datasets = "*" optimum = "1.15.0" diffusers = "*" PyPDF2 = "3.0.1" -accelerate = "0.22.0" +accelerate = "*" sentencepiece = "0.1.98" wget = "3.2" tensorflow = "2.14.0" @@ -66,6 +66,7 @@ soundfile = "0.12.1" torchvision = "0.16.1" rich = "13.5.2" sqlalchemy = "*" +bitsandbytes = "*" pgvector = "*" qdrant-client = "*" sentence-transformers = "*" diff --git a/requirements.txt b/requirements.txt index e8e2380e..eb39f71a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,6 +17,7 @@ openai==0.28.0 attrs==22.2.0 datasets==2.10.1 pydantic==1.10.12 +bitsandbytes soundfile==0.12.1 arize-phoenix weaviate-client==3.25.3 diff --git a/swarms/__init__.py b/swarms/__init__.py index 1c7514c4..4e6785cb 100644 --- a/swarms/__init__.py +++ b/swarms/__init__.py @@ -1,6 +1,6 @@ -from swarms.utils.disable_logging import disable_logging +from swarms.telemetry.bootup import bootup # noqa: E402, F403 -disable_logging() +bootup() from swarms.agents import * # noqa: E402, F403 from swarms.structs import * # noqa: E402, F403 diff --git a/swarms/models/huggingface.py b/swarms/models/huggingface.py index d9447f3c..8e74cff5 100644 --- a/swarms/models/huggingface.py +++ b/swarms/models/huggingface.py @@ -131,6 +131,7 @@ class HuggingfaceLLM(AbstractLLM): temperature: float = 0.7, top_k: int = 40, top_p: float = 0.8, + dtype = torch.bfloat16, *args, **kwargs, ): @@ -146,7 +147,6 @@ class HuggingfaceLLM(AbstractLLM): self.verbose = verbose self.distributed = distributed self.decoding = decoding - self.model, self.tokenizer = None, None self.quantize = quantize self.quantization_config = quantization_config self.max_workers = max_workers @@ -155,6 +155,7 @@ class HuggingfaceLLM(AbstractLLM): self.temperature = temperature self.top_k = top_k self.top_p = top_p + self.dtype = dtype if self.distributed: assert ( @@ -168,39 +169,26 @@ class HuggingfaceLLM(AbstractLLM): "load_in_4bit": True, "bnb_4bit_use_double_quant": True, "bnb_4bit_quant_type": "nf4", - "bnb_4bit_compute_dtype": torch.bfloat16, + "bnb_4bit_compute_dtype": dtype, } bnb_config = BitsAndBytesConfig(**quantization_config) - try: - self.tokenizer = AutoTokenizer.from_pretrained( - self.model_id - ) + self.tokenizer = AutoTokenizer.from_pretrained( + self.model_id + ).to(self.device) - if quantize: - self.model = AutoModelForCausalLM.from_pretrained( - self.model_id, - quantization_config=bnb_config, - *args, - **kwargs, - ).to(self.device) - else: - self.model = AutoModelForCausalLM.from_pretrained( - self.model_id, *args, **kwargs - ).to(self.device) + if quantize: + self.model = AutoModelForCausalLM.from_pretrained( + self.model_id, + quantization_config=bnb_config, + *args, + **kwargs, + ).to(self.device) + else: + self.model = AutoModelForCausalLM.from_pretrained( + self.model_id, *args, **kwargs + ).to(self.device) - except Exception as e: - # self.logger.error(f"Failed to load the model or the tokenizer: {e}") - # raise - print( - colored( - ( - "Failed to load the model and or the" - f" tokenizer: {e}" - ), - "red", - ) - ) def print_error(self, error: str): """Print error""" @@ -276,8 +264,7 @@ class HuggingfaceLLM(AbstractLLM): *args, **kwargs, ) - - del inputs + return self.tokenizer.decode( outputs[0], skip_special_tokens=True ) diff --git a/swarms/structs/block_wrapper.py b/swarms/structs/block_wrapper.py index cae44edf..a6811b8f 100644 --- a/swarms/structs/block_wrapper.py +++ b/swarms/structs/block_wrapper.py @@ -1,5 +1,6 @@ -from typing import Callable, Any import logging +from functools import wraps +from typing import Any, Callable logging.basicConfig(level=logging.INFO) logger = logging.getLogger(__name__) @@ -20,6 +21,7 @@ def block( Callable[..., Any]: The transformed function. """ + @wraps(function) def wrapper(*args, **kwargs): # Here you can add code to execute the function on various hardwares # For now, we'll just call the function normally diff --git a/swarms/structs/blockslist.py b/swarms/structs/blockslist.py index 93ab0afa..8448454c 100644 --- a/swarms/structs/blockslist.py +++ b/swarms/structs/blockslist.py @@ -36,6 +36,15 @@ class BlocksList(BaseStructure): get_by_parent_name(parent_name: str): Get blocks by parent name. get_by_parent_type(parent_type: str): Get blocks by parent type. get_by_parent_description(parent_description: str): Get blocks by parent description. + + + Examples: + >>> from swarms.structs.block import Block + >>> from swarms.structs.blockslist import BlocksList + >>> block = Block("block", "A block") + >>> blockslist = BlocksList("blockslist", "A list of blocks", [block]) + >>> blockslist + """ def __init__( @@ -47,8 +56,10 @@ class BlocksList(BaseStructure): **kwargs, ): super().__init__(name=name, description=description, **kwargs) - self.parent = parent + self.name = name + self.description = description self.blocks = blocks + self.parent = parent def add(self, block: Any): self.blocks.append(block) @@ -64,6 +75,26 @@ class BlocksList(BaseStructure): def get_all(self): return self.blocks + + def run_block(self, block: Any, task: str, *args, **kwargs): + """Run the block for the specified task. + + Args: + task (str): The task to be performed by the block. + *args: Variable length argument list. + **kwargs: Arbitrary keyword arguments. + + Returns: + The output of the block. + + Raises: + Exception: If an error occurs during the execution of the block. + """ + try: + return block.run(task, *args, **kwargs) + except Exception as error: + print(f"[Error] [Block] {error}") + raise error def get_by_name(self, name: str): return [block for block in self.blocks if block.name == name] diff --git a/swarms/structs/model_parallizer.py b/swarms/structs/model_parallizer.py index 3844f5b4..e115aa65 100644 --- a/swarms/structs/model_parallizer.py +++ b/swarms/structs/model_parallizer.py @@ -41,14 +41,12 @@ class ModelParallelizer: def __init__( self, llms: List[Callable] = None, - load_balancing: bool = False, retry_attempts: int = 3, iters: int = None, *args, **kwargs, ): self.llms = llms - self.load_balancing = load_balancing self.retry_attempts = retry_attempts self.iters = iters self.last_responses = None @@ -151,16 +149,6 @@ class ModelParallelizer: ) ) - def enable_load_balancing(self): - """Enable load balancing among LLMs.""" - self.load_balancing = True - logger.info("Load balancing enabled.") - - def disable_load_balancing(self): - """Disable load balancing.""" - self.load_balancing = False - logger.info("Load balancing disabled.") - async def arun(self, task: str): """Asynchronous run the task string""" loop = asyncio.get_event_loop() diff --git a/swarms/telemetry/auto_upgrade_swarms.py b/swarms/telemetry/auto_upgrade_swarms.py index aead795b..210cf7c5 100644 --- a/swarms/telemetry/auto_upgrade_swarms.py +++ b/swarms/telemetry/auto_upgrade_swarms.py @@ -6,6 +6,10 @@ def auto_update(): """auto update swarms""" try: if check_for_update(): + print( + "There is a new version of swarms available!" + " Downloading..." + ) subprocess.run(["pip", "install", "--upgrade", "swarms"]) except Exception as e: print(e) diff --git a/swarms/telemetry/bootup.py b/swarms/telemetry/bootup.py new file mode 100644 index 00000000..edcd1aca --- /dev/null +++ b/swarms/telemetry/bootup.py @@ -0,0 +1,8 @@ +from swarms.utils.disable_logging import disable_logging +from swarms.telemetry.auto_upgrade_swarms import auto_update + + +def bootup(): + """Bootup swarms""" + disable_logging() + auto_update() diff --git a/swarms/telemetry/check_update.py b/swarms/telemetry/check_update.py index a9b6386e..2cdbd986 100644 --- a/swarms/telemetry/check_update.py +++ b/swarms/telemetry/check_update.py @@ -40,7 +40,3 @@ def check_for_update(): return version.parse(latest_version) > version.parse( current_version ) - - -# out = check_for_update() -# print(out)