From 99ec6e5f9f7031e5db205adce15744c17fda112c Mon Sep 17 00:00:00 2001 From: Kye Date: Tue, 26 Mar 2024 09:23:44 -0700 Subject: [PATCH 01/12] [FEAT][AgentProcess] [AgentProcessQueue] --- pyproject.toml | 20 -- requirements.txt | 47 +-- swarms/__init__.py | 2 +- swarms/models/__init__.py | 1 - swarms/schedulers/__init__.py | 6 + swarms/schedulers/agent_process.py | 103 ++++++ swarms/tokenizers/__init__.py | 22 -- swarms/tokenizers/anthropic_tokenizer.py | 95 ----- swarms/tokenizers/base_tokenizer.py | 55 --- swarms/tokenizers/openai_tokenizers.py | 181 ---------- swarms/tokenizers/r_tokenizers.py | 422 ----------------------- 11 files changed, 125 insertions(+), 829 deletions(-) create mode 100644 swarms/schedulers/__init__.py create mode 100644 swarms/schedulers/agent_process.py delete mode 100644 swarms/tokenizers/__init__.py delete mode 100644 swarms/tokenizers/anthropic_tokenizer.py delete mode 100644 swarms/tokenizers/base_tokenizer.py delete mode 100644 swarms/tokenizers/openai_tokenizers.py delete mode 100644 swarms/tokenizers/r_tokenizers.py diff --git a/pyproject.toml b/pyproject.toml index 69a079e55..07bd584e1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,46 +29,26 @@ torch = ">=2.1.1,<3.0" transformers = "4.39.0" asyncio = ">=3.4.3,<4.0" einops = "0.7.0" -google-generativeai = "0.3.1" -langchain = "0.1.13" langchain-core = "0.1.33" langchain-community = "0.0.29" langchain-experimental = "0.0.55" -faiss-cpu = "1.7.4" backoff = "2.2.1" -datasets = "*" -optimum = "1.15.0" -supervision = "0.19.0" -opencv-python = "4.9.0.80" -diffusers = "*" -anthropic = "0.21.3" toml = "*" pypdf = "4.1.0" -accelerate = "*" -sentencepiece = "0.1.98" httpx = "0.24.1" -tiktoken = "0.5.2" ratelimit = "2.2.1" loguru = "0.7.2" -huggingface-hub = "*" pydantic = "2.6.4" tenacity = "8.2.3" Pillow = "10.2.0" -chromadb = "0.4.24" termcolor = "2.2.0" -torchvision = "0.16.1" rich = "13.5.2" -bitsandbytes = "*" -sentence-transformers = "*" -peft = "*" psutil = "*" -timm = "*" sentry-sdk = "*" [tool.poetry.dev-dependencies] black = "23.3.0" - [tool.poetry.group.lint.dependencies] ruff = ">=0.0.249,<0.3.5" types-toml = "^0.10.8.1" diff --git a/requirements.txt b/requirements.txt index 68ad9d8a0..072e5c9da 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,40 +1,23 @@ -torch==2.1.1 -transformers -pandas -langchain==0.1.13 +mkdocs +mkdocs-material +mkdocs-glightbox +torch>=2.1.1,<3.0 +transformers==4.39.0 +asyncio>=3.4.3,<4.0 +einops==0.7.0 langchain-core==0.1.33 langchain-community==0.0.29 -langsmith==0.1.17 -langchain-openai==0.0.5 -httpx==0.24.1 -Pillow==9.4.0 -datasets==2.14.5 -pydantic==2.6.4 -huggingface-hub -requests_mock -pypdf==4.0.1 -accelerate==0.22.0 -loguru==0.7.2 -optimum -diffusers -toml -tiktoken==0.5.2 -colored -addict +langchain-experimental==0.0.55 backoff==2.2.1 +toml +pypdf==4.1.0 +httpx==0.24.1 ratelimit==2.2.1 +loguru==0.7.2 +pydantic==2.6.4 +tenacity==8.2.3 +Pillow==10.2.0 termcolor==2.2.0 -opencv-python==4.9.0.80 -timm -torchvision==0.16.1 rich==13.5.2 -mkdocs -mkdocs-material -anthropic==0.2.5 -mkdocs-glightbox -pre-commit==3.6.2 psutil -black -tenacity -supervision sentry-sdk \ No newline at end of file diff --git a/swarms/__init__.py b/swarms/__init__.py index b9eb1426d..db28200e9 100644 --- a/swarms/__init__.py +++ b/swarms/__init__.py @@ -17,6 +17,6 @@ from swarms.prompts import * # noqa: E402, F403 from swarms.structs import * # noqa: E402, F403 from swarms.telemetry import * # noqa: E402, F403 -from swarms.tokenizers import * # noqa: E402, F403 from swarms.tools import * # noqa: E402, F403 from swarms.utils import * # noqa: E402, F403 +from swarms.schedulers import * # noqa: E402, F403 diff --git a/swarms/models/__init__.py b/swarms/models/__init__.py index 92b0e9299..8400073f7 100644 --- a/swarms/models/__init__.py +++ b/swarms/models/__init__.py @@ -44,7 +44,6 @@ # from swarms.models.roboflow_model import RoboflowMultiModal from swarms.models.sam_supervision import SegmentAnythingMarkGenerator from swarms.models.sampling_params import SamplingParams, SamplingType -from swarms.models.timm import TimmModel # noqa: E402 from swarms.models.together import TogetherLLM # noqa: E402 from swarms.models.types import ( # noqa: E402 AudioModality, diff --git a/swarms/schedulers/__init__.py b/swarms/schedulers/__init__.py new file mode 100644 index 000000000..803b22780 --- /dev/null +++ b/swarms/schedulers/__init__.py @@ -0,0 +1,6 @@ +from swarms.schedulers.agent_process import ( + AgentProcess, + AgentProcessQueue, +) + +__all__ = ["AgentProcess", "AgentProcessQueue"] diff --git a/swarms/schedulers/agent_process.py b/swarms/schedulers/agent_process.py new file mode 100644 index 000000000..cd9ca6e3d --- /dev/null +++ b/swarms/schedulers/agent_process.py @@ -0,0 +1,103 @@ +from datetime import datetime + +from pydantic import BaseModel + +from swarms.structs.omni_agent_types import agents +from swarms.utils.loguru_logger import logger + + +class AgentProcess(BaseModel): + agent_id: int + agent_name: str + prompt: str + response: str = None + time: callable = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + priority: int = 0 + status: str = "Waiting" + pid: int = None + + def set_pid(self, pid: int): + self.pid = pid + + def get_pid(self): + return self.pid + + def set_time(self, time: callable): + self.time = time + + def get_time(self): + return self.time + + +class AgentProcessQueue: + """ + A class representing a queue of agent processes. + + Attributes: + MAX_PID (int): The maximum process ID. + pid_pool (list): A list representing the availability of process IDs. + agent_process_queue (list): A list representing the queue of agent processes. + + Methods: + add(agent_process): Adds an agent process to the queue. + print(): Prints the details of all agent processes in the queue. + + Private Methods: + _get_available_pid(): Returns an available process ID from the pool. + """ + + def __init__(self, max_pid: int = 1024): + self.MAX_PID = max_pid + self.pid_pool = [False for i in range(self.MAX_PID)] + self.agent_process_queue = ( + [] + ) # Currently use list to simulate queue + + def add(self, agents: agents): + """ + Adds an agent process to the queue. + + Args: + agent_process (AgentProcess): The agent process to be added. + + Returns: + None + """ + for agent in agents: + agent_process = AgentProcess( + agent_id=agent.id, + agent_name=agent.agent_name, + prompt=agent.short_memory.return_history_as_string(), + ) + pid = self._get_available_pid() + if pid is None: + logger.warning("No available PID") + return + agent_process.set_pid(pid) + agent_process.set_status("Waiting") + self.agent_process_queue.append(agent_process) + + def print(self): + """ + Prints the details of all agent processes in the queue. + + Returns: + None + """ + for agent_process in self.agent_process_queue: + logger.info( + f"| Agent-process ID: {agent_process.get_pid()} |" + f" Status: {agent_process.get_status()} |" + ) + + def _get_available_pid(self): + """ + Returns an available process ID from the pool. + + Returns: + int or None: The available process ID, or None if no ID is available. + """ + for i, used in enumerate(self.pid_pool): + if not used: + return i + return None diff --git a/swarms/tokenizers/__init__.py b/swarms/tokenizers/__init__.py deleted file mode 100644 index 895c14bc3..000000000 --- a/swarms/tokenizers/__init__.py +++ /dev/null @@ -1,22 +0,0 @@ -# from swarms.tokenizers.anthropic_tokenizer import ( -# AnthropicTokenizer, -# import_optional_dependency, -# ) -from swarms.tokenizers.base_tokenizer import BaseTokenizer -from swarms.tokenizers.openai_tokenizers import OpenAITokenizer -from swarms.tokenizers.r_tokenizers import ( - HuggingFaceTokenizer, - SentencePieceTokenizer, - Tokenizer, -) - - -__all__ = [ - "SentencePieceTokenizer", - "HuggingFaceTokenizer", - "Tokenizer", - "BaseTokenizer", - "OpenAITokenizer", - # "import_optional_dependency", - # "AnthropicTokenizer", -] diff --git a/swarms/tokenizers/anthropic_tokenizer.py b/swarms/tokenizers/anthropic_tokenizer.py deleted file mode 100644 index 77cd07c3d..000000000 --- a/swarms/tokenizers/anthropic_tokenizer.py +++ /dev/null @@ -1,95 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -from importlib import import_module -from types import ModuleType - -from anthropic import Anthropic - -from swarms.tokenizers.base_tokenizer import BaseTokenizer - -INSTALL_MAPPING = { - "huggingface_hub": "huggingface-hub", - "pinecone": "pinecone-client", - "opensearchpy": "opensearch-py", -} - - -def import_optional_dependency(name: str) -> ModuleType | None: - """Import an optional dependency. - - If a dependency is missing, an ImportError with a nice message will be raised. - - Args: - name: The module name. - Returns: - The imported module, when found. - None is returned when the package is not found and `errors` is False. - """ - - package_name = INSTALL_MAPPING.get(name) - install_name = package_name if package_name is not None else name - - msg = ( - f"Missing optional dependency: '{install_name}'. " - f"Use poetry or pip to install '{install_name}'." - ) - try: - module = import_module(name) - except ImportError: - raise ImportError(msg) - - return module - - -@dataclass -class AnthropicTokenizer(BaseTokenizer): - """ - Tokenizer class for Anthropic models.] - """ - - max_tokens: int = 500 - client: Anthropic = None - model: str = "claude-2.1" - - def __post_init__(self): - self.DEFAULT_MODEL: str = "claude-2.1" - self.MODEL_PREFIXES_TO_MAX_TOKENS: dict[str, int] = { - "claude-2.1": 200000, - "claude": 100000, - } - self.model = self.model # or self.DEFAULT_MODEL - self.max_tokens = self.max_tokens or self.default_max_tokens() - self.client = ( - self.client - or import_optional_dependency("anthropic").Anthropic() - ) - - def default_max_tokens(self) -> int: - """ - Returns the default maximum number of tokens based on the model prefix. - """ - tokens = next( - v - for k, v in self.MODEL_PREFIXES_TO_MAX_TOKENS.items() - if self.model.startswith(k) - ) - return tokens - - def count_tokens(self, text: str | list) -> int: - """ - Counts the number of tokens in the given text. - - Args: - text: The input text. - - Returns: - The number of tokens in the text. - - Raises: - ValueError: If the input text is not a string. - """ - if isinstance(text, str): - return self.client.count_tokens(text) - else: - raise ValueError("Text must be a string.") diff --git a/swarms/tokenizers/base_tokenizer.py b/swarms/tokenizers/base_tokenizer.py deleted file mode 100644 index fd1bc3393..000000000 --- a/swarms/tokenizers/base_tokenizer.py +++ /dev/null @@ -1,55 +0,0 @@ -from __future__ import annotations - -from abc import ABC, abstractmethod -from dataclasses import dataclass, field - - -@dataclass -class BaseTokenizer(ABC): - """ - Base class for tokenizers. - - Attributes: - stop_sequences (List[str]): List of stop sequences. - max_tokens (int): Maximum number of tokens. - stop_token (str): Stop token. - """ - - max_tokens: int - stop_token: str = "<|Response|>" - - def __post_init__(self): - self.stop_sequences: list[str] = field( - default_factory=lambda: ["<|Response|>"], - init=False, - ) - - def count_tokens_left(self, text: str | list[dict]) -> int: - """ - Counts the number of tokens left based on the given text. - - Args: - text (Union[str, List[dict]]): The text to count tokens from. - - Returns: - int: The number of tokens left. - """ - diff = self.max_tokens - self.count_tokens(text) - - if diff > 0: - return diff - else: - return 0 - - @abstractmethod - def count_tokens(self, text: str | list[dict]) -> int: - """ - Counts the number of tokens in the given text. - - Args: - text (Union[str, List[dict]]): The text to count tokens from. - - Returns: - int: The number of tokens. - """ - ... diff --git a/swarms/tokenizers/openai_tokenizers.py b/swarms/tokenizers/openai_tokenizers.py deleted file mode 100644 index 9b02943b3..000000000 --- a/swarms/tokenizers/openai_tokenizers.py +++ /dev/null @@ -1,181 +0,0 @@ -from __future__ import annotations - -import logging -from dataclasses import dataclass, field - -import tiktoken -from tiktoken import Encoding - -from swarms.tokenizers.base_tokenizer import BaseTokenizer - - -@dataclass -class OpenAITokenizer(BaseTokenizer): - """ - A class representing an OpenAI tokenizer. - - Attributes: - - DEFAULT_OPENAI_GPT_3_COMPLETION_MODEL (str): The default OpenAI GPT-3 completion model. - - DEFAULT_OPENAI_GPT_3_CHAT_MODEL (str): The default OpenAI GPT-3 chat model. - - DEFAULT_OPENAI_GPT_4_MODEL (str): The default OpenAI GPT-4 model. - - DEFAULT_ENCODING (str): The default encoding. - - DEFAULT_MAX_TOKENS (int): The default maximum number of tokens. - - TOKEN_OFFSET (int): The token offset. - - MODEL_PREFIXES_TO_MAX_TOKENS (dict): A dictionary mapping model prefixes to maximum tokens. - - EMBEDDING_MODELS (list): A list of embedding models. - - model (str): The model name. - - Methods: - - __post_init__(): Initializes the OpenAITokenizer object. - - encoding(): Returns the encoding for the model. - - default_max_tokens(): Returns the default maximum number of tokens. - - count_tokens(text, model): Counts the number of tokens in the given text. - - len(text, model): Returns the length of the text in tokens. - """ - - model: str = "gpt-2" - - def __post_init__(self): - """ - Initializes the OpenAITokenizer object. - Sets the default maximum number of tokens. - """ - self.max_tokens: int = field( - default_factory=self.default_max_tokens - ) - - self.DEFAULT_OPENAI_GPT_3_COMPLETION_MODEL = ( - "text-davinci-003" - ) - self.DEFAULT_OPENAI_GPT_3_CHAT_MODEL = "gpt-3.5-turbo" - self.DEFAULT_OPENAI_GPT_4_MODEL = "gpt-4" - self.DEFAULT_ENCODING = "cl100k_base" - self.EFAULT_MAX_TOKENS = 2049 - self.TOKEN_OFFSET = 8 - - self.MODEL_PREFIXES_TO_MAX_TOKENS = { - "gpt-4-1106": 128000, - "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, - } - - self.EMBEDDING_MODELS = [ - "text-embedding-ada-002", - "text-embedding-ada-001", - ] - - @property - def encoding(self) -> Encoding: - """ - Returns the encoding for the model. - If the model is not found, returns the default encoding. - """ - try: - return tiktoken.encoding_for_model(self.model) - except KeyError: - return tiktoken.get_encoding(self.DEFAULT_ENCODING) - - def default_max_tokens(self) -> int: - """ - Returns the default maximum number of tokens based on the model. - """ - 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[dict], model: str | None = None - ) -> int: - """ - Counts the number of tokens in the given text. - If the text is a list of messages, counts the tokens for each message. - If a model is provided, uses that model for encoding. - """ - 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": - tokens_per_message = 4 - 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( - "token_count() is not implemented for model" - f" {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 - - num_tokens += 3 - - return num_tokens - else: - return len(self.encoding.encode(text)) - - def len(self, text: str | list[dict], model: str | None): - """ - Returns the length of the text in tokens. - If a model is provided, uses that model for encoding. - """ - return self.count_tokens(text, model) diff --git a/swarms/tokenizers/r_tokenizers.py b/swarms/tokenizers/r_tokenizers.py deleted file mode 100644 index f807b6fff..000000000 --- a/swarms/tokenizers/r_tokenizers.py +++ /dev/null @@ -1,422 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import json -import os -import os.path as osp -from collections import deque -from typing import List, Optional, Sequence, Union - -import torch - -from swarms.utils.get_logger import get_logger - - -class SentencePieceTokenizer: - """Tokenizer of sentencepiece. - - Args: - model_file (str): the path of the tokenizer model - """ - - def __init__(self, model_file: str): - from sentencepiece import SentencePieceProcessor - - self.model = SentencePieceProcessor(model_file=model_file) - self._prefix_space_tokens = None - # for stop words - self._maybe_decode_bytes: bool = None - # TODO maybe lack a constant.py - self._indexes_tokens_deque = deque(maxlen=10) - self.max_indexes_num = 5 - self.logger = get_logger("lmdeploy") - - @property - def vocab_size(self): - """vocabulary size.""" - return self.model.vocab_size() - - @property - def bos_token_id(self): - """begine of the sentence token id.""" - return self.model.bos_id() - - @property - def eos_token_id(self): - """end of the sentence token id.""" - return self.model.eos_id() - - @property - def prefix_space_tokens(self): - """tokens without prefix space.""" - if self._prefix_space_tokens is None: - vocab = self.model.IdToPiece(list(range(self.vocab_size))) - self._prefix_space_tokens = { - i - for i, tok in enumerate(vocab) - if tok.startswith("▁") - } - return self._prefix_space_tokens - - def _maybe_add_prefix_space(self, tokens, decoded): - """maybe add prefix space for incremental decoding.""" - if ( - tokens - and not decoded.startswith(" ") - and tokens[0] in self.prefix_space_tokens - ): - return " " + decoded - else: - return decoded - - def indexes_containing_token(self, token: str): - """Return all the possible indexes, whose decoding output may contain - the input token.""" - # traversing vocab is time consuming, can not be accelerated with - # multi threads (computation) or multi process (can't pickle tokenizer) - # so, we maintain latest 10 stop words and return directly if matched - for _token, _indexes in self._indexes_tokens_deque: - if token == _token: - return _indexes - if token == " ": # ' ' is special - token = "▁" - vocab = self.model.IdToPiece(list(range(self.vocab_size))) - indexes = [i for i, voc in enumerate(vocab) if token in voc] - if len(indexes) > self.max_indexes_num: - indexes = self.encode(token, add_bos=False)[-1:] - self.logger.warning( - f"There are too many(>{self.max_indexes_num})" - f" possible indexes may decoding {token}, we will use" - f" {indexes} only" - ) - self._indexes_tokens_deque.append((token, indexes)) - return indexes - - def encode(self, s: str, add_bos: bool = True, **kwargs): - """Tokenize a prompt. - - Args: - s (str): a prompt - Returns: - list[int]: token ids - """ - return self.model.Encode(s, add_bos=add_bos, **kwargs) - - def decode(self, t: Sequence[int], offset: Optional[int] = None): - """De-tokenize. - - Args: - t (List[int]): a list of token ids - offset (int): for incrementally decoding. Default to None, which - means not applied. - Returns: - str: text of decoding tokens - """ - if isinstance(t, torch.Tensor): - t = t.tolist() - t = t[offset:] - out_string = self.model.Decode(t) - if offset: - out_string = self._maybe_add_prefix_space(t, out_string) - return out_string - - def __call__(self, s: Union[str, Sequence[str]]): - """Tokenize prompts. - - Args: - s (str): prompts - Returns: - list[int]: token ids - """ - import addict - - add_bos = False - add_eos = False - - input_ids = self.model.Encode( - s, add_bos=add_bos, add_eos=add_eos - ) - return addict.Addict(input_ids=input_ids) - - -class HuggingFaceTokenizer: - """Tokenizer of sentencepiece. - - Args: - model_dir (str): the directory of the tokenizer model - """ - - def __init__(self, model_dir: str): - from transformers import AutoTokenizer - - model_file = osp.join(model_dir, "tokenizer.model") - backend_tokenizer_file = osp.join(model_dir, "tokenizer.json") - model_file_exists = osp.exists(model_file) - self.logger = get_logger("lmdeploy") - if ( - not osp.exists(backend_tokenizer_file) - and model_file_exists - ): - self.logger.warning( - "Can not find tokenizer.json. " - "It may take long time to initialize the tokenizer." - ) - self.model = AutoTokenizer.from_pretrained( - model_dir, trust_remote_code=True - ) - self._prefix_space_tokens = None - # save tokenizer.json to reuse - if ( - not osp.exists(backend_tokenizer_file) - and model_file_exists - ): - if hasattr(self.model, "backend_tokenizer"): - if os.access(model_dir, os.W_OK): - self.model.backend_tokenizer.save( - backend_tokenizer_file - ) - - if self.model.eos_token_id is None: - generation_config_file = osp.join( - model_dir, "generation_config.json" - ) - if osp.exists(generation_config_file): - with open(generation_config_file) as f: - cfg = json.load(f) - self.model.eos_token_id = cfg["eos_token_id"] - elif hasattr(self.model, "eod_id"): # Qwen remote - self.model.eos_token_id = self.model.eod_id - - # for stop words - self._maybe_decode_bytes: bool = None - # TODO maybe lack a constant.py - self._indexes_tokens_deque = deque(maxlen=10) - self.max_indexes_num = 5 - self.token2id = {} - - @property - def vocab_size(self): - """vocabulary size.""" - return self.model.vocab_size - - @property - def bos_token_id(self): - """begine of the sentence token id.""" - return self.model.bos_token_id - - @property - def eos_token_id(self): - """end of the sentence token id.""" - return self.model.eos_token_id - - @property - def prefix_space_tokens(self): - """tokens without prefix space.""" - if self._prefix_space_tokens is None: - vocab = self.model.convert_ids_to_tokens( - list(range(self.vocab_size)) - ) - self._prefix_space_tokens = { - i - for i, tok in enumerate(vocab) - if tok.startswith( - "▁" if isinstance(tok, str) else b" " - ) - } - return self._prefix_space_tokens - - def _maybe_add_prefix_space( - self, tokens: List[int], decoded: str - ): - """maybe add prefix space for incremental decoding.""" - if ( - tokens - and not decoded.startswith(" ") - and tokens[0] in self.prefix_space_tokens - ): - return " " + decoded - else: - return decoded - - @property - def maybe_decode_bytes(self): - """Check if self.model.convert_ids_to_tokens return not a str value.""" - if self._maybe_decode_bytes is None: - self._maybe_decode_bytes = False - vocab = self.model.convert_ids_to_tokens( - list(range(self.vocab_size)) - ) - for tok in vocab: - if not isinstance(tok, str): - self._maybe_decode_bytes = True - break - return self._maybe_decode_bytes - - def indexes_containing_token(self, token: str): - """Return all the possible indexes, whose decoding output may contain - the input token.""" - # traversing vocab is time consuming, can not be accelerated with - # multi threads (computation) or multi process (can't pickle tokenizer) - # so, we maintain latest 10 stop words and return directly if matched - for _token, _indexes in self._indexes_tokens_deque: - if token == _token: - return _indexes - - if self.token2id == {}: - # decode is slower than convert_ids_to_tokens - if self.maybe_decode_bytes: - self.token2id = { - self.model.decode(i): i - for i in range(self.vocab_size) - } - else: - self.token2id = { - self.model.convert_ids_to_tokens(i): i - for i in range(self.vocab_size) - } - if token == " ": # ' ' is special - token = "▁" - indexes = [ - i - for _token, i in self.token2id.items() - if token in _token - ] - if len(indexes) > self.max_indexes_num: - indexes = self.encode(token, add_bos=False)[-1:] - self.logger.warning( - f"There are too many(>{self.max_indexes_num})" - f" possible indexes may decoding {token}, we will use" - f" {indexes} only" - ) - self._indexes_tokens_deque.append((token, indexes)) - return indexes - - def encode(self, s: str, add_bos: bool = True, **kwargs): - """Tokenize a prompt. - - Args: - s (str): a prompt - Returns: - list[int]: token ids - """ - encoded = self.model.encode(s, **kwargs) - if not add_bos: - # in the middle of a session - if encoded and encoded[0] == self.bos_token_id: - encoded = encoded[1:] - return encoded - - def decode(self, t: Sequence[int], offset: Optional[int] = None): - """De-tokenize. - - Args: - t (List[int]): a list of token ids - offset (int): for incrementally decoding. Default to None, which - means not applied. - Returns: - str: text of decoding tokens - """ - skip_special_tokens = True - t = t[offset:] - out_string = self.model.decode( - t, skip_special_tokens=skip_special_tokens - ) - if offset: - out_string = self._maybe_add_prefix_space(t, out_string) - return out_string - - def __call__(self, s: Union[str, Sequence[str]]): - """Tokenize prompts. - - Args: - s (str): prompts - Returns: - list[int]: token ids - """ - add_special_tokens = False - return self.model(s, add_special_tokens=add_special_tokens) - - -class Tokenizer: - """Tokenize prompts or de-tokenize tokens into texts. - - Args: - model_file (str): the path of the tokenizer model - """ - - def __init__(self, model_file: str): - if model_file.endswith(".model"): - model_folder = osp.split(model_file)[0] - else: - model_folder = model_file - model_file = osp.join(model_folder, "tokenizer.model") - tokenizer_config_file = osp.join( - model_folder, "tokenizer_config.json" - ) - - model_file_exists = osp.exists(model_file) - config_exists = osp.exists(tokenizer_config_file) - use_hf_model = config_exists or not model_file_exists - self.logger = get_logger("lmdeploy") - if not use_hf_model: - self.model = SentencePieceTokenizer(model_file) - else: - self.model = HuggingFaceTokenizer(model_folder) - - @property - def vocab_size(self): - """vocabulary size.""" - return self.model.vocab_size - - @property - def bos_token_id(self): - """begine of the sentence token id.""" - return self.model.bos_token_id - - @property - def eos_token_id(self): - """end of the sentence token id.""" - return self.model.eos_token_id - - def encode(self, s: str, add_bos: bool = True, **kwargs): - """Tokenize a prompt. - - Args: - s (str): a prompt - Returns: - list[int]: token ids - """ - return self.model.encode(s, add_bos, **kwargs) - - def decode(self, t: Sequence[int], offset: Optional[int] = None): - """De-tokenize. - - Args: - t (List[int]): a list of token ids - offset (int): for incrementally decoding. Default to None, which - means not applied. - Returns: - str: text of decoding tokens - """ - return self.model.decode(t, offset) - - def __call__(self, s: Union[str, Sequence[str]]): - """Tokenize prompts. - - Args: - s (str): prompts - Returns: - list[int]: token ids - """ - return self.model(s) - - def indexes_containing_token(self, token): - """Return all the possible indexes, whose decoding output may contain - the input token.""" - encoded = self.encode(token, add_bos=False) - if len(encoded) > 1: - self.logger.warning( - f"The token {token}, its length of indexes" - f" {encoded} is over than 1. Currently, it can not be" - " used as stop words" - ) - return [] - return self.model.indexes_containing_token(token) From 02ba5fc149b3818cecdbc3dd1654a79eb7ffcb55 Mon Sep 17 00:00:00 2001 From: Eternal Reclaimer <98760976+kyegomez@users.noreply.github.com> Date: Tue, 26 Mar 2024 09:25:09 -0700 Subject: [PATCH 02/12] Delete Transcript Generator_state.json --- Transcript Generator_state.json | 17 ----------------- 1 file changed, 17 deletions(-) delete mode 100644 Transcript Generator_state.json diff --git a/Transcript Generator_state.json b/Transcript Generator_state.json deleted file mode 100644 index 71f22da38..000000000 --- a/Transcript Generator_state.json +++ /dev/null @@ -1,17 +0,0 @@ -{ - "agent_id": "", - "agent_name": "Transcript Generator", - "agent_description": "Generate a transcript for a youtube video on what swarms are!", - "system_prompt": "\n You are a fully autonomous agent serving the user in automating tasks, workflows, and activities. \n Agent's use custom instructions, capabilities, and data to optimize LLMs for a more narrow set of tasks.\n \n You will have internal dialogues with yourself and or interact with the user to aid in these tasks. \n Your responses should be coherent, contextually relevant, and tailored to the task at hand.\n", - "sop": null, - "short_memory": "system: \n You are a fully autonomous agent serving the user in automating tasks, workflows, and activities. \n Agent's use custom instructions, capabilities, and data to optimize LLMs for a more narrow set of tasks.\n \n You will have internal dialogues with yourself and or interact with the user to aid in these tasks. \n Your responses should be coherent, contextually relevant, and tailored to the task at hand.\n\n\n\nHuman:: Generate a transcript for a youtube video on what swarms are!\n\n\nTranscript Generator: \nSwarms are composed of large numbers of independent individuals that collectively carry out complex behaviors. For example, an ant colony functions as a swarm - each ant follows simple rules but together the colony can build intricate nests and find food.\n\nIn artificial swarms, we try to emulate these naturally-occurring phenomena. By programming basic behaviors into agents and allowing them to interact, we can observe emergent group behaviors without centralized control. For example, groups of robots may be designed with attraction and repulsion forces to self-assemble or explore environments.\n\nSimilarly, swarms may allow optimization algorithms to explore solutions in parallel. Each program follows their own trajectory while sharing information to converge on the best result. High-level commands give a rough direction, but the specific behaviors emerge from the interactions at the local level. \n\nPotential applications of artificial swarms include self-configuring robot teams for search & rescue, intelligent routing of network packets, and distributed processing for enhanced efficiency. The decentralized nature of swarms provides robustness, scalability and adaptability surpassing individual agents. \n\nBy harnessing simple local rules and interactions, swarm systems transcend the capabilities of any single member. They provide distributed solutions to coordinate large numbers independent agents to achieve a collective purpose.\n\n\nTranscript Generator: \nSwarms are composed of large numbers of independent individuals that collectively carry out complex behaviors. For example, an ant colony functions as a swarm - each ant follows simple rules but together the colony can build intricate nests and find food.\n\nIn artificial swarms, we try to emulate these naturally-occurring phenomena. By programming basic behaviors into agents and allowing them to interact, we can observe emergent group behaviors without centralized control. For example, groups of robots may be designed with attraction and repulsion forces to self-assemble or explore environments.\n\nSimilarly, swarms may allow optimization algorithms to explore solutions in parallel. Each program follows their own trajectory while sharing information to converge on the best result. High-level commands give a rough direction, but the specific behaviors emerge from the interactions at the local level. \n\nPotential applications of artificial swarms include self-configuring robot teams for search & rescue, intelligent routing of network packets, and distributed processing for enhanced efficiency. The decentralized nature of swarms provides robustness, scalability and adaptability surpassing individual agents. \n\nBy harnessing simple local rules and interactions, swarm systems transcend the capabilities of any single member. They provide distributed solutions to coordinate large numbers independent agents to achieve a collective purpose.\n\n\nHuman:: what is your purpose\n\n", - "loop_interval": 1, - "retry_attempts": 3, - "retry_interval": 1, - "interactive": true, - "dashboard": false, - "dynamic_temperature": false, - "autosave": true, - "saved_state_path": "Transcript Generator_state.json", - "max_loops": 1 -} \ No newline at end of file From c9f479983cf2a678bcdae24b758f26ffcebae53e Mon Sep 17 00:00:00 2001 From: Kye Date: Tue, 26 Mar 2024 10:08:58 -0700 Subject: [PATCH 03/12] [CLEANUP] --- pyproject.toml | 2 +- swarms/memory/__init__.py | 12 +-- swarms/models/__init__.py | 26 ++---- swarms/models/petals.py | 4 +- tests/tokenizers/test_anthropictokenizer.py | 40 --------- tests/tokenizers/test_basetokenizer.py | 46 ----------- tests/tokenizers/test_coheretokenizer.py | 37 --------- tests/tokenizers/test_huggingfacetokenizer.py | 68 --------------- tests/tokenizers/test_openaitokenizer.py | 48 ----------- .../tokenizers/test_sentencepiecetokenizer.py | 1 - tests/tokenizers/test_tokenizer.py | 82 ------------------- 11 files changed, 13 insertions(+), 353 deletions(-) delete mode 100644 tests/tokenizers/test_anthropictokenizer.py delete mode 100644 tests/tokenizers/test_basetokenizer.py delete mode 100644 tests/tokenizers/test_coheretokenizer.py delete mode 100644 tests/tokenizers/test_huggingfacetokenizer.py delete mode 100644 tests/tokenizers/test_openaitokenizer.py delete mode 100644 tests/tokenizers/test_sentencepiecetokenizer.py delete mode 100644 tests/tokenizers/test_tokenizer.py diff --git a/pyproject.toml b/pyproject.toml index 07bd584e1..1a7a63f3b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ classifiers = [ [tool.poetry.dependencies] python = ">=3.9,<4.0" torch = ">=2.1.1,<3.0" -transformers = "4.39.0" +transformers = ">= 4.39.0, <5.0.0" asyncio = ">=3.4.3,<4.0" einops = "0.7.0" langchain-core = "0.1.33" diff --git a/swarms/memory/__init__.py b/swarms/memory/__init__.py index 0dafab086..b92e35e1f 100644 --- a/swarms/memory/__init__.py +++ b/swarms/memory/__init__.py @@ -1,21 +1,17 @@ from swarms.memory.action_subtask import ActionSubtaskEntry from swarms.memory.base_db import AbstractDatabase from swarms.memory.base_vectordb import AbstractVectorDatabase -from swarms.memory.chroma_db import ChromaDB from swarms.memory.dict_internal_memory import DictInternalMemory from swarms.memory.dict_shared_memory import DictSharedMemory from swarms.memory.short_term_memory import ShortTermMemory -from swarms.memory.sqlite import SQLiteDB from swarms.memory.visual_memory import VisualShortTermMemory __all__ = [ - "AbstractVectorDatabase", "AbstractDatabase", - "ShortTermMemory", - "SQLiteDB", - "VisualShortTermMemory", + "AbstractVectorDatabase", "ActionSubtaskEntry", - "ChromaDB", "DictInternalMemory", "DictSharedMemory", -] + "ShortTermMemory", + "VisualShortTermMemory", +] \ No newline at end of file diff --git a/swarms/models/__init__.py b/swarms/models/__init__.py index 8400073f7..f26a2eeb8 100644 --- a/swarms/models/__init__.py +++ b/swarms/models/__init__.py @@ -1,8 +1,6 @@ from swarms.models.base_embedding_model import BaseEmbeddingModel from swarms.models.base_llm import AbstractLLM # noqa: E402 from swarms.models.base_multimodal_model import BaseMultiModalModel -from swarms.models.biogpt import BioGPT # noqa: E402 -from swarms.models.clipq import CLIPQ # noqa: E402 from swarms.models.fire_function import FireFunctionCaller from swarms.models.fuyu import Fuyu # noqa: E402 from swarms.models.gemini import Gemini # noqa: E402 @@ -52,12 +50,7 @@ TextModality, VideoModality, ) - -# from swarms.models.ultralytics_model import UltralyticsModel from swarms.models.vilt import Vilt # noqa: E402 -from swarms.models.wizard_storytelling import WizardLLMStoryTeller -from swarms.models.zephyr import Zephyr # noqa: E402 -from swarms.models.zeroscope import ZeroscopeTTV # noqa: E402 __all__ = [ "AbstractLLM", @@ -65,41 +58,34 @@ "AzureOpenAI", "BaseEmbeddingModel", "BaseMultiModalModel", - "BioGPT", - "CLIPQ", "Cohere", "FireFunctionCaller", "Fuyu", - "GPT4VisionAPI", "Gemini", + "GPT4VisionAPI", "HuggingfaceLLM", "Idefics", "Kosmos", "LayoutLMDocumentQA", "LavaMultiModal", - "Replicate", - "MPT7B", "Mistral", "Mixtral", - "MosaicML", + "MPT7B", + "MultimodalData", "Nougat", "OpenAI", "OpenAIChat", "OpenAITTS", "Petals", "QwenVLMultiModal", + "Replicate", "SamplingParams", "SamplingType", "SegmentAnythingMarkGenerator", "TextModality", - "TimmModel", "TogetherLLM", "Vilt", - "VideoModality", - "WizardLLMStoryTeller", - "Zephyr", - "ZeroscopeTTV", "AudioModality", "ImageModality", - "MultimodalData", -] + "VideoModality", +] \ No newline at end of file diff --git a/swarms/models/petals.py b/swarms/models/petals.py index 7ceeef8b3..699d7d9d8 100644 --- a/swarms/models/petals.py +++ b/swarms/models/petals.py @@ -1,7 +1,7 @@ from transformers import AutoModelForCausalLM, AutoTokenizer +from swarms.models.base_llm import AbstractLLM - -class Petals: +class Petals(AbstractLLM): """Petals Bloom models.""" def __init__( diff --git a/tests/tokenizers/test_anthropictokenizer.py b/tests/tokenizers/test_anthropictokenizer.py deleted file mode 100644 index 14b2fd86a..000000000 --- a/tests/tokenizers/test_anthropictokenizer.py +++ /dev/null @@ -1,40 +0,0 @@ -# AnthropicTokenizer - -import pytest - -from swarms.tokenizers.anthropic_tokenizer import AnthropicTokenizer - - -def test_post_init(): - tokenizer = AnthropicTokenizer() - assert tokenizer.model == "claude-2.1" - assert tokenizer.max_tokens == 200000 - - -def test_default_max_tokens(): - tokenizer = AnthropicTokenizer(model="claude") - assert tokenizer.default_max_tokens() == 100000 - - -@pytest.mark.parametrize( - "model,tokens", [("claude-2.1", 200000), ("claude", 100000)] -) -def test_default_max_tokens_models(model, tokens): - tokenizer = AnthropicTokenizer(model=model) - assert tokenizer.default_max_tokens() == tokens - - -def test_count_tokens_string(): - # Insert mock instantiation of anthropic client and its count_tokens function - text = "This is a test string." - tokenizer = AnthropicTokenizer() - tokens = tokenizer.count_tokens(text) - assert tokens == 5 - - -def test_count_tokens_list(): - # Insert mock instantiation of anthropic client and its count_tokens function - text = ["This", "is", "a", "test", "string."] - tokenizer = AnthropicTokenizer() - with pytest.raises(ValueError): - tokenizer.count_tokens(text) diff --git a/tests/tokenizers/test_basetokenizer.py b/tests/tokenizers/test_basetokenizer.py deleted file mode 100644 index 3956d2de2..000000000 --- a/tests/tokenizers/test_basetokenizer.py +++ /dev/null @@ -1,46 +0,0 @@ -# BaseTokenizer - -import pytest - -from swarms.tokenizers.base_tokenizer import BaseTokenizer - - -# 1. Fixture for BaseTokenizer instance. -@pytest.fixture -def base_tokenizer(): - return BaseTokenizer(max_tokens=100) - - -# 2. Tests for __post_init__. -def test_post_init(base_tokenizer): - assert base_tokenizer.stop_sequences == ["<|Response|>"] - assert base_tokenizer.stop_token == "<|Response|>" - - -# 3. Tests for count_tokens_left with different inputs. -def test_count_tokens_left_with_positive_diff( - base_tokenizer, monkeypatch -): - # Mocking count_tokens to return a specific value - monkeypatch.setattr( - "swarms.tokenizers.BaseTokenizer.count_tokens", - lambda x, y: 50, - ) - assert base_tokenizer.count_tokens_left("some text") == 50 - - -def test_count_tokens_left_with_zero_diff( - base_tokenizer, monkeypatch -): - monkeypatch.setattr( - "swarms.tokenizers.BaseTokenizer.count_tokens", - lambda x, y: 100, - ) - assert base_tokenizer.count_tokens_left("some text") == 0 - - -# 4. Add tests for count_tokens. This method is an abstract one, so testing it -# will be dependent on the actual implementation in the subclass. Here is just -# a general idea how to test it (we assume that test_count_tokens is implemented in some subclass). -def test_count_tokens(subclass_tokenizer_instance): - assert subclass_tokenizer_instance.count_tokens("some text") == 6 diff --git a/tests/tokenizers/test_coheretokenizer.py b/tests/tokenizers/test_coheretokenizer.py deleted file mode 100644 index 2607cf9a5..000000000 --- a/tests/tokenizers/test_coheretokenizer.py +++ /dev/null @@ -1,37 +0,0 @@ -# CohereTokenizer - -from unittest.mock import MagicMock - -import pytest - -from swarms.tokenizers.cohere_tokenizer import CohereTokenizer - - -@pytest.fixture -def cohere_tokenizer(): - mock_client = MagicMock() - mock_client.tokenize.return_value.tokens = [ - "token1", - "token2", - "token3", - ] - return CohereTokenizer(model="", client=mock_client) - - -def test_count_tokens_with_string(cohere_tokenizer): - tokens_count = cohere_tokenizer.count_tokens("valid string") - assert tokens_count == 3 - - -def test_count_tokens_with_non_string(cohere_tokenizer): - with pytest.raises(ValueError): - cohere_tokenizer.count_tokens(["invalid", "input"]) - - -def test_count_tokens_with_different_length(cohere_tokenizer): - cohere_tokenizer.client.tokenize.return_value.tokens = [ - "token1", - "token2", - ] - tokens_count = cohere_tokenizer.count_tokens("valid string") - assert tokens_count == 2 diff --git a/tests/tokenizers/test_huggingfacetokenizer.py b/tests/tokenizers/test_huggingfacetokenizer.py deleted file mode 100644 index 1eedb6e5b..000000000 --- a/tests/tokenizers/test_huggingfacetokenizer.py +++ /dev/null @@ -1,68 +0,0 @@ -# HuggingFaceTokenizer - -import os -from unittest.mock import patch - -import pytest - -from swarms.tokenizers.r_tokenizers import HuggingFaceTokenizer - - -# Test class setup -@pytest.fixture -def hftokenizer(): - dir_path = os.path.join(os.getcwd(), "modeldir") - tokenizer = HuggingFaceTokenizer(dir_path) - return tokenizer - - -# testing __init__ -@patch("os.path") -@patch("swarms.tokenizers.get_logger") -def test___init__(mock_get_logger, mock_path, hftokenizer): - mock_path.exists.return_value = False - mock_path.join.return_value = "dummy_path" - mock_get_logger.return_value = "dummy_logger" - assert hftokenizer.model_dir == "dummy_path" - assert hftokenizer.logger == "dummy_logger" - assert hftokenizer._maybe_decode_bytes is False - assert hftokenizer._prefix_space_tokens is None - - -# testing vocab_size property -def test_vocab_size(hftokenizer): - assert hftokenizer.vocab_size == 30522 - - -# testing bos_token_id property -def test_bos_token_id(hftokenizer): - assert hftokenizer.bos_token_id == 101 - - -# testing eos_token_id property -def test_eos_token_id(hftokenizer): - assert hftokenizer.eos_token_id == 102 - - -# testing prefix_space_tokens property -def test_prefix_space_tokens(hftokenizer): - assert len(hftokenizer.prefix_space_tokens) > 0 - - -# testing _maybe_add_prefix_space method -def test__maybe_add_prefix_space(hftokenizer): - assert ( - hftokenizer._maybe_add_prefix_space( - [101, 2003, 2010, 2050, 2001, 2339], " is why" - ) - == " is why" - ) - assert ( - hftokenizer._maybe_add_prefix_space( - [2003, 2010, 2050, 2001, 2339], "is why" - ) - == " is why" - ) - - -# continuing tests for other methods... diff --git a/tests/tokenizers/test_openaitokenizer.py b/tests/tokenizers/test_openaitokenizer.py deleted file mode 100644 index 3c24748d2..000000000 --- a/tests/tokenizers/test_openaitokenizer.py +++ /dev/null @@ -1,48 +0,0 @@ -# OpenAITokenizer - -import pytest - -import swarms.tokenizers.openai_tokenizers as tokenizers - - -@pytest.fixture() -def openai_tokenizer(): - return tokenizers.OpenAITokenizer("gpt-3") - - -def test_init(openai_tokenizer): - assert openai_tokenizer.model == "gpt-3" - - -def test_default_max_tokens(openai_tokenizer): - assert openai_tokenizer.default_max_tokens() == 4096 - - -@pytest.mark.parametrize( - "text, expected_output", [("Hello, world!", 3), (["Hello"], 4)] -) -def test_count_tokens_single(openai_tokenizer, text, expected_output): - assert ( - openai_tokenizer.count_tokens(text, "gpt-3") - == expected_output - ) - - -@pytest.mark.parametrize( - "texts, expected_output", - [(["Hello, world!", "This is a test"], 6), (["Hello"], 4)], -) -def test_count_tokens_multiple( - openai_tokenizer, texts, expected_output -): - assert ( - openai_tokenizer.count_tokens(texts, "gpt-3") - == expected_output - ) - - -@pytest.mark.parametrize( - "text, expected_output", [("Hello, world!", 3), (["Hello"], 4)] -) -def test_len(openai_tokenizer, text, expected_output): - assert openai_tokenizer.len(text, "gpt-3") == expected_output diff --git a/tests/tokenizers/test_sentencepiecetokenizer.py b/tests/tokenizers/test_sentencepiecetokenizer.py deleted file mode 100644 index e3a0b917e..000000000 --- a/tests/tokenizers/test_sentencepiecetokenizer.py +++ /dev/null @@ -1 +0,0 @@ -# SentencePieceTokenizer diff --git a/tests/tokenizers/test_tokenizer.py b/tests/tokenizers/test_tokenizer.py deleted file mode 100644 index b868f0a13..000000000 --- a/tests/tokenizers/test_tokenizer.py +++ /dev/null @@ -1,82 +0,0 @@ -# Tokenizer - -from unittest.mock import patch - -from swarms.tokenizers.r_tokenizers import Tokenizer - - -def test_initializer_existing_model_file(): - with patch("os.path.exists", return_value=True): - with patch( - "swarms.tokenizers.SentencePieceTokenizer" - ) as mock_model: - tokenizer = Tokenizer("tokenizers/my_model.model") - mock_model.assert_called_with("tokenizers/my_model.model") - assert tokenizer.model == mock_model.return_value - - -def test_initializer_model_folder(): - with patch("os.path.exists", side_effect=[False, True]): - with patch( - "swarms.tokenizers.HuggingFaceTokenizer" - ) as mock_model: - tokenizer = Tokenizer("my_model_directory") - mock_model.assert_called_with("my_model_directory") - assert tokenizer.model == mock_model.return_value - - -def test_vocab_size(): - with patch( - "swarms.tokenizers.SentencePieceTokenizer" - ) as mock_model: - tokenizer = Tokenizer("tokenizers/my_model.model") - assert ( - tokenizer.vocab_size == mock_model.return_value.vocab_size - ) - - -def test_bos_token_id(): - with patch( - "swarms.tokenizers.SentencePieceTokenizer" - ) as mock_model: - tokenizer = Tokenizer("tokenizers/my_model.model") - assert ( - tokenizer.bos_token_id - == mock_model.return_value.bos_token_id - ) - - -def test_encode(): - with patch( - "swarms.tokenizers.SentencePieceTokenizer" - ) as mock_model: - tokenizer = Tokenizer("tokenizers/my_model.model") - assert ( - tokenizer.encode("hello") - == mock_model.return_value.encode.return_value - ) - - -def test_decode(): - with patch( - "swarms.tokenizers.SentencePieceTokenizer" - ) as mock_model: - tokenizer = Tokenizer("tokenizers/my_model.model") - assert ( - tokenizer.decode([1, 2, 3]) - == mock_model.return_value.decode.return_value - ) - - -def test_call(): - with patch( - "swarms.tokenizers.SentencePieceTokenizer" - ) as mock_model: - tokenizer = Tokenizer("tokenizers/my_model.model") - assert ( - tokenizer("hello") - == mock_model.return_value.__call__.return_value - ) - - -# More tests can be added here From c33ceb4327f525a101c8f567301ac7184029bb0f Mon Sep 17 00:00:00 2001 From: Kye Date: Tue, 26 Mar 2024 10:14:00 -0700 Subject: [PATCH 04/12] [CLEANUP] --- swarms/chunkers/__init__.py | 5 - swarms/chunkers/base_chunker.py | 163 ------------------ swarms/chunkers/chunk_seperator.py | 7 - swarms/chunkers/text_chunker.py | 13 -- swarms/memory/__init__.py | 2 +- swarms/models/__init__.py | 5 +- swarms/models/petals.py | 1 + swarms/schedulers/__init__.py | 6 - swarms/structs/__init__.py | 7 + .../{schedulers => structs}/agent_process.py | 0 10 files changed, 11 insertions(+), 198 deletions(-) delete mode 100644 swarms/chunkers/__init__.py delete mode 100644 swarms/chunkers/base_chunker.py delete mode 100644 swarms/chunkers/chunk_seperator.py delete mode 100644 swarms/chunkers/text_chunker.py delete mode 100644 swarms/schedulers/__init__.py rename swarms/{schedulers => structs}/agent_process.py (100%) diff --git a/swarms/chunkers/__init__.py b/swarms/chunkers/__init__.py deleted file mode 100644 index b55d15c28..000000000 --- a/swarms/chunkers/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from swarms.chunkers.base_chunker import BaseChunker -from swarms.chunkers.chunk_seperator import ChunkSeparator -from swarms.chunkers.text_chunker import TextChunker - -__all__ = ["ChunkSeparator", "BaseChunker", "TextChunker"] diff --git a/swarms/chunkers/base_chunker.py b/swarms/chunkers/base_chunker.py deleted file mode 100644 index 47f73a4eb..000000000 --- a/swarms/chunkers/base_chunker.py +++ /dev/null @@ -1,163 +0,0 @@ -from __future__ import annotations - -from abc import ABC -from dataclasses import dataclass, field - -from swarms.artifacts.text_artifact import TextArtifact -from swarms.chunkers.chunk_seperator import ChunkSeparator -from swarms.tokenizers.base_tokenizer import BaseTokenizer -from swarms.tokenizers.openai_tokenizers import OpenAITokenizer - - -@dataclass -class BaseChunker(ABC): - """ - Base class for chunking text into smaller chunks. - """ - - DEFAULT_SEPARATORS = [ChunkSeparator(" ")] - - separators: list[ChunkSeparator] = field( - default_factory=lambda: BaseChunker.DEFAULT_SEPARATORS - ) - tokenizer: BaseTokenizer = field( - default_factory=lambda: OpenAITokenizer( - model=OpenAITokenizer.DEFAULT_OPENAI_GPT_3_CHAT_MODEL - ) - ) - max_tokens: int = field( - default_factory=lambda: BaseChunker.tokenizer.max_tokens - ) - - def chunk(self, text: str | str) -> list[str]: - """ - Chunk the given text into smaller chunks. - - Args: - text (TextArtifact | str): The text to be chunked. - - Returns: - list[TextArtifact]: The list of chunked text artifacts. - """ - text = text.value if isinstance(text, str) else text - - return [ - TextArtifact(c) for c in self._chunk_recursively(text) - ] - - def _chunk_recursively( - self, - chunk: str, - current_separator: ChunkSeparator | None = None, - ) -> list[str]: - """ - Recursively chunk the given chunk into smaller subchunks. - - Args: - chunk (str): The chunk to be recursively chunked. - current_separator (Optional[ChunkSeparator], optional): The current separator to be used. Defaults to None. - - Returns: - list[str]: The list of recursively chunked subchunks. - """ - token_count = self.tokenizer.count_tokens(chunk) - - if token_count <= self.max_tokens: - return [chunk] - else: - balance_index = -1 - balance_diff = float("inf") - tokens_count = 0 - half_token_count = token_count // 2 - - # If a separator is provided, only use separators after it. - if current_separator: - separators = self.separators[ - self.separators.index(current_separator) : - ] - else: - separators = self.separators - - # Loop through available separators to find the best split. - for separator in separators: - # Split the chunk into subchunks using the current separator. - subchunks = list( - filter(None, chunk.split(separator.value)) - ) - - # Check if the split resulted in more than one subchunk. - if len(subchunks) > 1: - # Iterate through the subchunks and calculate token counts. - for index, subchunk in enumerate(subchunks): - if index < len(subchunks): - if separator.is_prefix: - subchunk = separator.value + subchunk - else: - subchunk = subchunk + separator.value - - tokens_count += self.tokenizer.count_tokens( - subchunk - ) - - # Update the best split if the current one is more balanced. - if ( - abs(tokens_count - half_token_count) - < balance_diff - ): - balance_index = index - balance_diff = abs( - tokens_count - half_token_count - ) - - # Create the two subchunks based on the best separator. - if separator.is_prefix: - # If the separator is a prefix, append it before this subchunk. - first_subchunk = ( - separator.value - + separator.value.join( - subchunks[: balance_index + 1] - ) - ) - second_subchunk = ( - separator.value - + separator.value.join( - subchunks[balance_index + 1 :] - ) - ) - else: - # If the separator is not a prefix, append it after this subchunk. - first_subchunk = ( - separator.value.join( - subchunks[: balance_index + 1] - ) - + separator.value - ) - second_subchunk = separator.value.join( - subchunks[balance_index + 1 :] - ) - - # Continue recursively chunking the subchunks. - first_subchunk_rec = self._chunk_recursively( - first_subchunk.strip(), separator - ) - second_subchunk_rec = self._chunk_recursively( - second_subchunk.strip(), separator - ) - - # Return the concatenated results of the subchunks if both are non-empty. - if first_subchunk_rec and second_subchunk_rec: - return ( - first_subchunk_rec + second_subchunk_rec - ) - # If only one subchunk is non-empty, return it. - elif first_subchunk_rec: - return first_subchunk_rec - elif second_subchunk_rec: - return second_subchunk_rec - else: - return [] - # If none of the separators result in a balanced split, split the chunk in half. - midpoint = len(chunk) // 2 - return self._chunk_recursively( - chunk[:midpoint] - ) + self._chunk_recursively(chunk[midpoint:]) diff --git a/swarms/chunkers/chunk_seperator.py b/swarms/chunkers/chunk_seperator.py deleted file mode 100644 index d554be484..000000000 --- a/swarms/chunkers/chunk_seperator.py +++ /dev/null @@ -1,7 +0,0 @@ -from dataclasses import dataclass - - -@dataclass -class ChunkSeparator: - value: str - is_prefix: bool = False diff --git a/swarms/chunkers/text_chunker.py b/swarms/chunkers/text_chunker.py deleted file mode 100644 index b8b17bf1d..000000000 --- a/swarms/chunkers/text_chunker.py +++ /dev/null @@ -1,13 +0,0 @@ -from swarms.chunkers.base_chunker import BaseChunker -from swarms.chunkers.chunk_seperator import ChunkSeparator - - -class TextChunker(BaseChunker): - DEFAULT_SEPARATORS = [ - ChunkSeparator("\n\n"), - ChunkSeparator("\n"), - ChunkSeparator(". "), - ChunkSeparator("! "), - ChunkSeparator("? "), - ChunkSeparator(" "), - ] diff --git a/swarms/memory/__init__.py b/swarms/memory/__init__.py index b92e35e1f..7b56e4442 100644 --- a/swarms/memory/__init__.py +++ b/swarms/memory/__init__.py @@ -14,4 +14,4 @@ "DictSharedMemory", "ShortTermMemory", "VisualShortTermMemory", -] \ No newline at end of file +] diff --git a/swarms/models/__init__.py b/swarms/models/__init__.py index f26a2eeb8..18f25b533 100644 --- a/swarms/models/__init__.py +++ b/swarms/models/__init__.py @@ -38,8 +38,6 @@ ReplicateLLM as Replicate, ) from swarms.models.qwen import QwenVLMultiModal # noqa: E402 - -# from swarms.models.roboflow_model import RoboflowMultiModal from swarms.models.sam_supervision import SegmentAnythingMarkGenerator from swarms.models.sampling_params import SamplingParams, SamplingType from swarms.models.together import TogetherLLM # noqa: E402 @@ -88,4 +86,5 @@ "AudioModality", "ImageModality", "VideoModality", -] \ No newline at end of file + "MosaicML", +] diff --git a/swarms/models/petals.py b/swarms/models/petals.py index 699d7d9d8..7a7823f2a 100644 --- a/swarms/models/petals.py +++ b/swarms/models/petals.py @@ -1,6 +1,7 @@ from transformers import AutoModelForCausalLM, AutoTokenizer from swarms.models.base_llm import AbstractLLM + class Petals(AbstractLLM): """Petals Bloom models.""" diff --git a/swarms/schedulers/__init__.py b/swarms/schedulers/__init__.py deleted file mode 100644 index 803b22780..000000000 --- a/swarms/schedulers/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -from swarms.schedulers.agent_process import ( - AgentProcess, - AgentProcessQueue, -) - -__all__ = ["AgentProcess", "AgentProcessQueue"] diff --git a/swarms/structs/__init__.py b/swarms/structs/__init__.py index f1090b060..18999e9fc 100644 --- a/swarms/structs/__init__.py +++ b/swarms/structs/__init__.py @@ -75,6 +75,11 @@ parse_tasks, ) from swarms.structs.auto_swarm import AutoSwarm, AutoSwarmRouter +from swarms.structs.agent_process import ( + AgentProcess, + AgentProcessQueue, +) + __all__ = [ "Agent", @@ -142,4 +147,6 @@ "AgentJob", "AutoSwarm", "AutoSwarmRouter", + "AgentProcess", + "AgentProcessQueue", ] diff --git a/swarms/schedulers/agent_process.py b/swarms/structs/agent_process.py similarity index 100% rename from swarms/schedulers/agent_process.py rename to swarms/structs/agent_process.py From aff03cb64914ad2b71c1c4550368b3022411957f Mon Sep 17 00:00:00 2001 From: Kye Date: Tue, 26 Mar 2024 10:26:18 -0700 Subject: [PATCH 05/12] [REFACTORING] --- pyproject.toml | 3 +- requirements.txt | 1 - swarms/structs/SWARMS.md | 362 ------------------------ swarms/structs/__init__.py | 11 +- swarms/structs/step.py | 19 +- swarms/structs/swarm_net.py | 5 +- swarms/structs/task_tree.py | 80 ------ swarms/structs/test_majority_voting.py | 152 ---------- swarms/structs/tool_json_schema.py | 36 --- swarms/structs/types.py | 0 swarms/telemetry/__init__.py | 2 - swarms/telemetry/auto_upgrade_swarms.py | 3 +- swarms/telemetry/main.py | 59 ---- swarms/telemetry/posthog_utils.py | 6 - swarms/telemetry/user_utils.py | 3 - swarms/utils/__init__.py | 73 ++--- swarms/utils/markdown_message.py | 13 +- swarms/utils/video_to_frames.py | 43 --- 18 files changed, 51 insertions(+), 820 deletions(-) delete mode 100644 swarms/structs/SWARMS.md delete mode 100644 swarms/structs/task_tree.py delete mode 100644 swarms/structs/test_majority_voting.py delete mode 100644 swarms/structs/tool_json_schema.py delete mode 100644 swarms/structs/types.py delete mode 100644 swarms/telemetry/main.py delete mode 100644 swarms/telemetry/posthog_utils.py delete mode 100644 swarms/utils/video_to_frames.py diff --git a/pyproject.toml b/pyproject.toml index 1a7a63f3b..32f9438d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "swarms" -version = "4.6.0" +version = "4.6.1" description = "Swarms - Pytorch" license = "MIT" authors = ["Kye Gomez "] @@ -41,7 +41,6 @@ loguru = "0.7.2" pydantic = "2.6.4" tenacity = "8.2.3" Pillow = "10.2.0" -termcolor = "2.2.0" rich = "13.5.2" psutil = "*" sentry-sdk = "*" diff --git a/requirements.txt b/requirements.txt index 072e5c9da..46a11563a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -18,6 +18,5 @@ pydantic==2.6.4 tenacity==8.2.3 Pillow==10.2.0 termcolor==2.2.0 -rich==13.5.2 psutil sentry-sdk \ No newline at end of file diff --git a/swarms/structs/SWARMS.md b/swarms/structs/SWARMS.md deleted file mode 100644 index 070eb176a..000000000 --- a/swarms/structs/SWARMS.md +++ /dev/null @@ -1,362 +0,0 @@ -Modularizing the provided framework for scalability and reliability will involve breaking down the overall architecture into smaller, more manageable pieces, as well as introducing additional features and capabilities to enhance reliability. Here's a list of ideas to achieve this: - -### 1. Dynamic Agent Management - -To ensure the swarm is both cost-effective and efficient, dynamically creating and destroying agents depending on the workload can be a game changer: - -**Idea**: Instead of having a fixed number of agents, allow the `AutoScaler` to both instantiate and destroy agents as necessary. - -**Example**: -```python -class AutoScaler: - # ... - def remove_agent(self): - with self.lock: - if self.agents_pool: - agent_to_remove = self.agents_pool.pop() - del agent_to_remove -``` - -### 2. Task Segmentation & Aggregation - -Breaking down tasks into sub-tasks and then aggregating results ensures scalability: - -**Idea**: Create a method in the `Orchestrator` to break down larger tasks into smaller tasks and another method to aggregate results from sub-tasks. - -**Example**: -```python -class Orchestrator(ABC): - # ... - def segment_task(self, main_task: str) -> List[str]: - # Break down main_task into smaller tasks - # ... - return sub_tasks - - def aggregate_results(self, sub_results: List[Any]) -> Any: - # Combine results from sub-tasks into a cohesive output - # ... - return main_result -``` - -### 3. Enhanced Task Queuing - -**Idea**: Prioritize tasks based on importance or deadlines. - -**Example**: Use a priority queue for the `task_queue`, ensuring tasks of higher importance are tackled first. - -### 4. Error Recovery & Retry Mechanisms - -**Idea**: Introduce a retry mechanism for tasks that fail due to transient errors. - -**Example**: -```python -class Orchestrator(ABC): - MAX_RETRIES = 3 - retry_counts = defaultdict(int) - # ... - def assign_task(self, agent_id, task): - # ... - except Exception as error: - if self.retry_counts[task] < self.MAX_RETRIES: - self.retry_counts[task] += 1 - self.task_queue.put(task) -``` - -### 5. Swarm Communication & Collaboration - -**Idea**: Allow agents to communicate or request help from their peers. - -**Example**: Implement a `request_assistance` method within agents where, upon facing a challenging task, they can ask for help from other agents. - -### 6. Database Management - -**Idea**: Periodically clean, optimize, and back up the vector database to ensure data integrity and optimal performance. - -### 7. Logging & Monitoring - -**Idea**: Implement advanced logging and monitoring capabilities to provide insights into swarm performance, potential bottlenecks, and failures. - -**Example**: Use tools like Elasticsearch, Logstash, and Kibana (ELK stack) to monitor logs in real-time. - -### 8. Load Balancing - -**Idea**: Distribute incoming tasks among agents evenly, ensuring no single agent is overloaded. - -**Example**: Use algorithms or tools that assign tasks based on current agent workloads. - -### 9. Feedback Loop - -**Idea**: Allow the system to learn from its mistakes or inefficiencies. Agents can rate the difficulty of their tasks and this information can be used to adjust future task assignments. - -### 10. Agent Specialization - -**Idea**: Not all agents are equal. Some might be better suited to certain tasks. - -**Example**: Maintain a performance profile for each agent, categorizing them based on their strengths. Assign tasks to agents based on their specialization for optimal performance. - -By implementing these ideas and constantly iterating based on real-world usage and performance metrics, it's possible to create a robust and scalable multi-agent collaboration framework. - - -# 10 improvements to the `Orchestrator` class to enable more flexibility and usability: - -1. Dynamic Agent Creation: Allow the number of agents to be specified at runtime, rather than being fixed at the time of instantiation. - -``` -def add_agents(self, num_agents: int): - for _ in range(num_agents): - self.agents.put(self.agent()) - self.executor = ThreadPoolExecutor(max_workers=self.agents.qsize()) -``` - -1. Agent Removal: Allow agents to be removed from the pool. - -``` -def remove_agents(self, num_agents: int): - for _ in range(num_agents): - if not self.agents.empty(): - self.agents.get() - self.executor = ThreadPoolExecutor(max_workers=self.agents.qsize()) -``` - -1. Task Prioritization: Allow tasks to be prioritized. - -``` -from queue import PriorityQueue - -def __init__(self, agent, agent_list: List[Any], task_queue: List[Any], collection_name: str = "swarm", api_key: str = None, model_name: str = None): - # ... - self.task_queue = PriorityQueue() - # ... - -def add_task(self, task: Dict[str, Any], priority: int = 0): - self.task_queue.put((priority, task)) -``` - -1. Task Status: Track the status of tasks. - -``` -from enum import Enum - -class TaskStatus(Enum): - QUEUED = 1 - RUNNING = 2 - COMPLETED = 3 - FAILED = 4 - -# In assign_task method -self.current_tasks[id(task)] = TaskStatus.RUNNING -# On successful completion -self.current_tasks[id(task)] = TaskStatus.COMPLETED -# On failure -self.current_tasks[id(task)] = TaskStatus.FAILED -``` - -1. Result Retrieval: Allow results to be retrieved by task ID. - -``` -def retrieve_result(self, task_id: int) -> Any: - return self.collection.query(query_texts=[str(task_id)], n_results=1) -``` - -1. Batch Task Assignment: Allow multiple tasks to be assigned at once. - -``` -def assign_tasks(self, tasks: List[Dict[str, Any]]): - for task in tasks: - self.task_queue.put(task) -``` - -1. Error Handling: Improve error handling by re-queuing failed tasks. - -``` -# In assign_task method -except Exception as error: - logging.error(f"Failed to process task {id(task)} by agent {id(agent)}. Error: {error}") - self.task_queue.put(task) -``` - -1. Agent Status: Track the status of agents (e.g., idle, working). - -``` -self.agent_status = {id(agent): "idle" for agent in self.agents.queue} - -# In assign_task method -self.agent_status[id(agent)] = "working" -# On task completion -self.agent_status[id(agent)] = "idle" -``` - -1. Custom Embedding Function: Allow a custom embedding function to be used. - -``` -def __init__(self, agent, agent_list: List[Any], task_queue: List[Any], collection_name: str = "swarm", api_key: str = None, model_name: str = None, embed_func=None): - # ... - self.embed_func = embed_func if embed_func else self.embed - # ... - -def embed(self, input, api_key, model_name): - # ... - embedding = self.embed_func(input) - # ... -``` - -1. Agent Communication: Allow agents to communicate with each other. - -``` -def communicate(self, sender_id: int, receiver_id: int, message: str): - message_vector = self.embed_func(message) - self.collection.add(embeddings=[message_vector], documents=[message], ids=[f"{sender_id}_to_{receiver_id}"]) -``` - - - -``` -import logging -import queue -import threading -from concurrent.futures import ThreadPoolExecutor -from typing import Any, Dict, List -from enum import Enum - -import chromadb -from chromadb.utils import embedding_functions - -class TaskStatus(Enum): - QUEUED = 1 - RUNNING = 2 - COMPLETED = 3 - FAILED = 4 - -class Orchestrator: - def __init__(self, agent, agent_list: List[Any], task_queue: List[Any], collection_name: str = "swarm", api_key: str = None, model_name: str = None, embed_func=None): - self.agent = agent - self.agents = queue.Queue() - self.agent_status = {} - - self.add_agents(agent_list) - - self.task_queue = queue.PriorityQueue() - - self.chroma_client = chromadb.Client() - - self.collection = self.chroma_client.create_collection(name = collection_name) - - self.current_tasks = {} - - self.lock = threading.Lock() - self.condition = threading.Condition(self.lock) - - self.embed_func = embed_func if embed_func else self.embed - - def add_agents(self, num_agents: int): - for _ in range(num_agents): - agent = self.agent() - self.agents.put(agent) - self.agent_status[id(agent)] = "idle" - self.executor = ThreadPoolExecutor(max_workers=self.agents.qsize()) - - def remove_agents(self, num_agents: int): - for _ in range(num_agents): - if not self.agents.empty(): - agent = self.agents.get() - del self.agent_status[id(agent)] - self.executor = ThreadPoolExecutor(max_workers=self.agents.qsize()) - - def assign_task(self, agent_id: int, task: Dict[str, Any]) -> None: - while True: - with self.condition: - while not self.task_queue: - self.condition.wait() - agent = self.agents.get() - task = self.task_queue.get() - - try: - self.agent_status[id(agent)] = "working" - result = self.worker.run(task["content"]) - - vector_representation = self.embed_func(result) - - self.collection.add(embeddings=[vector_representation], documents=[str(id(task))], ids=[str(id(task))]) - - logging.info(f"Task {id(str)} has been processed by agent {id(agent)} with") - self.current_tasks[id(task)] = TaskStatus.COMPLETED - - except Exception as error: - logging.error(f"Failed to process task {id(task)} by agent {id(agent)}. Error: {error}") - self.current_tasks[id(task)] = TaskStatus.FAILED - self.task_queue.put(task) - finally: - with self.condition: - self.agent_status[id(agent)] = "idle" - self.agents.put(agent) - self.condition.notify() - - def embed(self, input): - openai = embedding_functions.OpenAIEmbeddingFunction(api_key=self.api_key, model_name=self.model_name) - embedding = openai(input) - return embedding - - def retrieve_results(self, agent_id: int) -> Any: - try: - results = self.collection.query(query_texts=[str(agent_id)], n_results=10) - return results - except Exception as e: - logging.error(f"Failed to retrieve results from agent {id(agent_id)}. Error {e}") - raise - - def update_vector_db(self, data) -> None: - try: - self.collection.add(embeddings=[data["vector"]], documents=[str(data["task_id"])], ids=[str(data["task_id"])]) - except Exception as e: - logging.error(f"Failed to update the vector database. Error: {e}") - raise - - def get_vector_db(self): - return self.collection - - def append_to_db(self, result: str): - try: - self.collection.add(documents=[result], ids=[str(id(result))]) - except Exception as e: - logging.error(f"Failed to append the agent output to database. Error: {e}") - raise - - def run(self, objective:str): - if not objective or not isinstance(objective, str): - logging.error("Invalid objective") - raise ValueError("A valid objective is required") - - try: - self.task_queue.put((0, objective)) - - results = [self.assign_task(agent_id, task) for agent_id, task in zip(range(len(self.agents)), self.task_queue)] - - for result in results: - self.append_to_db(result) - - logging.info(f"Successfully ran swarms with results: {results}") - return results - except Exception as e: - logging.error(f"An error occured in swarm: {e}") - return None - - def chat(self, sender_id: int, receiver_id: int, message: str): - message_vector = self.embed_func(message) - - # Store the message in the vector database - self.collection.add(embeddings=[message_vector], documents=[message], ids=[f"{sender_id}_to_{receiver_id}"]) - - def assign_tasks(self, tasks: List[Dict[str, Any]], priority: int = 0): - for task in tasks: - self.task_queue.put((priority, task)) - - def retrieve_result(self, task_id: int) -> Any: - try: - result = self.collection.query(query_texts=[str(task_id)], n_results=1) - return result - except Exception as e: - logging.error(f"Failed to retrieve result for task {task_id}. Error: {e}") - raise -``` - -With these improvements, the `Orchestrator` class now supports dynamic agent creation and removal, task prioritization, task status tracking, result retrieval by task ID, batch task assignment, improved error handling, agent status tracking, custom embedding functions, and agent communication. This should make the class more flexible and easier to use when creating swarms of LLMs. \ No newline at end of file diff --git a/swarms/structs/__init__.py b/swarms/structs/__init__.py index 18999e9fc..2f6c9a700 100644 --- a/swarms/structs/__init__.py +++ b/swarms/structs/__init__.py @@ -1,5 +1,10 @@ from swarms.structs.agent import Agent from swarms.structs.agent_job import AgentJob +from swarms.structs.agent_process import ( + AgentProcess, + AgentProcessQueue, +) +from swarms.structs.auto_swarm import AutoSwarm, AutoSwarmRouter from swarms.structs.autoscaler import AutoScaler from swarms.structs.base import BaseStructure from swarms.structs.base_swarm import AbstractSwarm @@ -74,12 +79,6 @@ find_token_in_text, parse_tasks, ) -from swarms.structs.auto_swarm import AutoSwarm, AutoSwarmRouter -from swarms.structs.agent_process import ( - AgentProcess, - AgentProcessQueue, -) - __all__ = [ "Agent", diff --git a/swarms/structs/step.py b/swarms/structs/step.py index 7e66250af..c8c913a69 100644 --- a/swarms/structs/step.py +++ b/swarms/structs/step.py @@ -1,11 +1,10 @@ -from dataclasses import dataclass -from typing import Dict, List +from typing import Dict, List, Sequence from swarms.tools.tool import BaseTool +from pydantic import BaseModel -@dataclass -class Step: +class Step(BaseModel): """ Represents a step in a process. @@ -17,8 +16,10 @@ class Step: tool (BaseTool): The tool used to execute the step. """ - task: str - id: int - dep: List[int] - args: Dict[str, str] - tool: BaseTool + task: str = None + id: int = 0 + dep: List[int] = [] + args: Dict[str, str] = {} + tool: BaseTool = None + tools: Sequence[BaseTool] = [] + metadata: Dict[str, str] = {} diff --git a/swarms/structs/swarm_net.py b/swarms/structs/swarm_net.py index c44dd127f..64d4dd867 100644 --- a/swarms/structs/swarm_net.py +++ b/swarms/structs/swarm_net.py @@ -4,7 +4,7 @@ import threading from typing import List, Optional -from fastapi import FastAPI +# from fastapi import FastAPI from swarms.structs.agent import Agent from swarms.structs.base import BaseStructure @@ -89,9 +89,6 @@ def __init__( logging.basicConfig(level=logging.INFO) self.logger = logging.getLogger(__name__) - if api_enabled: - self.api = FastAPI() - # For each agent in the pool, run it on it's own thread if agents is not None: for agent in agents: diff --git a/swarms/structs/task_tree.py b/swarms/structs/task_tree.py deleted file mode 100644 index ec89d150b..000000000 --- a/swarms/structs/task_tree.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright (c) OpenMMLab. All rights reserved. -import torch - - -def continuous_tensor( - inputs: torch.Tensor, seq_length: torch.LongTensor -): - """Convert batched tensor to continuous tensor. - - Args: - inputs (Tensor): batched tensor. - seq_length (Tensor): length of each sequence. - - Return: - Tensor: continuoused tensor. - """ - assert inputs.dim() > 1 - if inputs.size(1) == 1: - return inputs.reshape(1, -1) - - inputs = [inp[:slen] for inp, slen in zip(inputs, seq_length)] - - inputs = torch.cat(inputs).unsqueeze(0) - return inputs - - -def batch_tensor(inputs: torch.Tensor, seq_length: torch.LongTensor): - """Convert continuoused tensor to batched tensor. - - Args: - inputs (Tensor): continuoused tensor. - seq_length (Tensor): length of each sequence. - - Return: - Tensor: batched tensor. - """ - from torch.nn.utils.rnn import pad_sequence - - end_loc = seq_length.cumsum(0) - start_loc = end_loc - seq_length - - inputs = [ - inputs[0, sloc:eloc] for sloc, eloc in zip(start_loc, end_loc) - ] - inputs = pad_sequence(inputs, batch_first=True) - return inputs - - -def page_cache( - paged_cache: torch.Tensor, - batched_cache: torch.Tensor, - cache_length: torch.Tensor, - block_offsets: torch.Tensor, - permute_head: bool = True, -): - """Convert batched cache to paged cache. - - Args: - paged_cache (Tensor): Output paged cache. - batched_cache (Tensor): Input batched cache. - cache_length (Tensor): length of the cache. - block_offsets (Tensor): Offset of each blocks. - """ - assert block_offsets.dim() == 2 - block_size = paged_cache.size(1) - batch_size = batched_cache.size(0) - if permute_head: - batched_cache = batched_cache.permute(0, 2, 1, 3) - - for b_idx in range(batch_size): - cache_len = cache_length[b_idx] - b_cache = batched_cache[b_idx] - block_off = block_offsets[b_idx] - block_off_idx = 0 - for s_start in range(0, cache_len, block_size): - s_end = min(s_start + block_size, cache_len) - s_len = s_end - s_start - b_off = block_off[block_off_idx] - paged_cache[b_off, :s_len] = b_cache[s_start:s_end] - block_off_idx += 1 diff --git a/swarms/structs/test_majority_voting.py b/swarms/structs/test_majority_voting.py deleted file mode 100644 index dcd25f0b4..000000000 --- a/swarms/structs/test_majority_voting.py +++ /dev/null @@ -1,152 +0,0 @@ -from unittest.mock import MagicMock - -import pytest - -from swarms.structs.agent import Agent -from swarms.structs.majority_voting import MajorityVoting - - -def test_majority_voting_run_concurrent(mocker): - # Create mock agents - agent1 = MagicMock(spec=Agent) - agent2 = MagicMock(spec=Agent) - agent3 = MagicMock(spec=Agent) - - # Create mock majority voting - mv = MajorityVoting( - agents=[agent1, agent2, agent3], - concurrent=True, - multithreaded=False, - ) - - # Create mock conversation - conversation = MagicMock() - mv.conversation = conversation - - # Create mock results - results = ["Paris", "Paris", "Lyon"] - - # Mock agent.run method - agent1.run.return_value = results[0] - agent2.run.return_value = results[1] - agent3.run.return_value = results[2] - - # Run majority voting - majority_vote = mv.run("What is the capital of France?") - - # Assert agent.run method was called with the correct task - agent1.run.assert_called_once_with( - "What is the capital of France?" - ) - agent2.run.assert_called_once_with( - "What is the capital of France?" - ) - agent3.run.assert_called_once_with( - "What is the capital of France?" - ) - - # Assert conversation.add method was called with the correct responses - conversation.add.assert_any_call(agent1.agent_name, results[0]) - conversation.add.assert_any_call(agent2.agent_name, results[1]) - conversation.add.assert_any_call(agent3.agent_name, results[2]) - - # Assert majority vote is correct - assert majority_vote is not None - - -def test_majority_voting_run_multithreaded(mocker): - # Create mock agents - agent1 = MagicMock(spec=Agent) - agent2 = MagicMock(spec=Agent) - agent3 = MagicMock(spec=Agent) - - # Create mock majority voting - mv = MajorityVoting( - agents=[agent1, agent2, agent3], - concurrent=False, - multithreaded=True, - ) - - # Create mock conversation - conversation = MagicMock() - mv.conversation = conversation - - # Create mock results - results = ["Paris", "Paris", "Lyon"] - - # Mock agent.run method - agent1.run.return_value = results[0] - agent2.run.return_value = results[1] - agent3.run.return_value = results[2] - - # Run majority voting - majority_vote = mv.run("What is the capital of France?") - - # Assert agent.run method was called with the correct task - agent1.run.assert_called_once_with( - "What is the capital of France?" - ) - agent2.run.assert_called_once_with( - "What is the capital of France?" - ) - agent3.run.assert_called_once_with( - "What is the capital of France?" - ) - - # Assert conversation.add method was called with the correct responses - conversation.add.assert_any_call(agent1.agent_name, results[0]) - conversation.add.assert_any_call(agent2.agent_name, results[1]) - conversation.add.assert_any_call(agent3.agent_name, results[2]) - - # Assert majority vote is correct - assert majority_vote is not None - - -@pytest.mark.asyncio -async def test_majority_voting_run_asynchronous(mocker): - # Create mock agents - agent1 = MagicMock(spec=Agent) - agent2 = MagicMock(spec=Agent) - agent3 = MagicMock(spec=Agent) - - # Create mock majority voting - mv = MajorityVoting( - agents=[agent1, agent2, agent3], - concurrent=False, - multithreaded=False, - asynchronous=True, - ) - - # Create mock conversation - conversation = MagicMock() - mv.conversation = conversation - - # Create mock results - results = ["Paris", "Paris", "Lyon"] - - # Mock agent.run method - agent1.run.return_value = results[0] - agent2.run.return_value = results[1] - agent3.run.return_value = results[2] - - # Run majority voting - majority_vote = await mv.run("What is the capital of France?") - - # Assert agent.run method was called with the correct task - agent1.run.assert_called_once_with( - "What is the capital of France?" - ) - agent2.run.assert_called_once_with( - "What is the capital of France?" - ) - agent3.run.assert_called_once_with( - "What is the capital of France?" - ) - - # Assert conversation.add method was called with the correct responses - conversation.add.assert_any_call(agent1.agent_name, results[0]) - conversation.add.assert_any_call(agent2.agent_name, results[1]) - conversation.add.assert_any_call(agent3.agent_name, results[2]) - - # Assert majority vote is correct - assert majority_vote is not None diff --git a/swarms/structs/tool_json_schema.py b/swarms/structs/tool_json_schema.py deleted file mode 100644 index a5c0d0704..000000000 --- a/swarms/structs/tool_json_schema.py +++ /dev/null @@ -1,36 +0,0 @@ -import json -from abc import ABC, abstractmethod - - -class JSON(ABC): - def __init__(self, schema_path): - """ - Initializes a JSONSchema object. - - Args: - schema_path (str): The path to the JSON schema file. - """ - self.schema_path = schema_path - self.schema = self.load_schema() - - def load_schema(self): - """ - Loads the JSON schema from the specified file. - - Returns: - dict: The loaded JSON schema. - """ - with open(self.schema_path) as f: - return json.load(f) - - @abstractmethod - def validate(self, data): - """ - Validates the given data against the JSON schema. - - Args: - data (dict): The data to be validated. - - Raises: - NotImplementedError: This method needs to be implemented by the subclass. - """ diff --git a/swarms/structs/types.py b/swarms/structs/types.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/swarms/telemetry/__init__.py b/swarms/telemetry/__init__.py index 442ad55b9..738a9aec1 100644 --- a/swarms/telemetry/__init__.py +++ b/swarms/telemetry/__init__.py @@ -1,5 +1,3 @@ -# from swarms.telemetry.posthog_utils import posthog - from swarms.telemetry.log_all import log_all_calls, log_calls from swarms.telemetry.sys_info import ( get_cpu_info, diff --git a/swarms/telemetry/auto_upgrade_swarms.py b/swarms/telemetry/auto_upgrade_swarms.py index 98e59a593..f62b89994 100644 --- a/swarms/telemetry/auto_upgrade_swarms.py +++ b/swarms/telemetry/auto_upgrade_swarms.py @@ -1,6 +1,7 @@ import subprocess from swarms.telemetry.check_update import check_for_update +from termcolor import colored def auto_update(): @@ -13,6 +14,6 @@ def auto_update(): ) subprocess.run(["pip", "install", "--upgrade", "swarms"]) else: - print("swarms is up to date!") + colored("swarms is up to date!", "red") except Exception as e: print(e) diff --git a/swarms/telemetry/main.py b/swarms/telemetry/main.py deleted file mode 100644 index 9f772bdce..000000000 --- a/swarms/telemetry/main.py +++ /dev/null @@ -1,59 +0,0 @@ -import datetime -import logging -import platform - -import pymongo - - -class Telemetry: - def __init__(self, db_url, db_name): - self.logger = self.setup_logging() - self.db = self.setup_db(db_url, db_name) - - def setup_logging(self): - logger = logging.getLogger("telemetry") - logger.setLevel(logging.DEBUG) - handler = logging.StreamHandler() - handler.setFormatter( - logging.Formatter( - "%(asctime)s - %(name)s - %(levelname)s - %(message)s" - ) - ) - logger.addHandler(handler) - return logger - - def setup_db(self, db_url, db_name): - client = pymongo.MongoClient(db_url) - return client[db_name] - - def capture_device_data(self): - data = { - "system": platform.system(), - "node": platform.node(), - "release": platform.release(), - "version": platform.version(), - "machine": platform.machine(), - "processor": platform.processor(), - "time": datetime.datetime.now(), - } - return data - - def send_to_db(self, collection_name, data): - collection = self.db[collection_name] - collection.insert_one(data) - - def log_and_capture(self, message, level, collection_name): - if level == "info": - self.logger.info(message) - elif level == "error": - self.logger.error(message) - data = self.capture_device_data() - data["log"] = message - self.send_to_db(collection_name, data) - - def log_import(self, module_name): - self.logger.info(f"Importing module {module_name}") - module = __import__(module_name, fromlist=["*"]) - for k in dir(module): - if not k.startswith("__"): - self.logger.info(f"Imported {k} from {module_name}") diff --git a/swarms/telemetry/posthog_utils.py b/swarms/telemetry/posthog_utils.py deleted file mode 100644 index 7ae8d0a7a..000000000 --- a/swarms/telemetry/posthog_utils.py +++ /dev/null @@ -1,6 +0,0 @@ -from posthog import Posthog - -posthog = Posthog( - project_api_key="phc_Gz6XxldNZIkzW7QnSTGr5HZ28OAYPIfpE7X5A3vUsfO", - host="https://app.posthog.com", -) diff --git a/swarms/telemetry/user_utils.py b/swarms/telemetry/user_utils.py index 32261cfe4..e38a16486 100644 --- a/swarms/telemetry/user_utils.py +++ b/swarms/telemetry/user_utils.py @@ -86,6 +86,3 @@ def get_user_device_data(): "Swarms [Version]": check_for_package("swarms"), } return data - - -# diff --git a/swarms/utils/__init__.py b/swarms/utils/__init__.py index 01e22f939..7e311f675 100644 --- a/swarms/utils/__init__.py +++ b/swarms/utils/__init__.py @@ -40,66 +40,47 @@ remove_whitespace_from_yaml, ) from swarms.utils.save_logs import parse_log_file -from swarms.utils.supervision_masking import ( - FeatureType, - compute_mask_iou_vectorized, - filter_masks_by_relative_area, - mask_non_max_suppression, - masks_to_marks, - refine_marks, -) from swarms.utils.supervision_visualizer import MarkVisualizer from swarms.utils.token_count_tiktoken import limit_tokens_from_string from swarms.utils.try_except_wrapper import try_except_wrapper -from swarms.utils.video_to_frames import ( - save_frames_as_images, - video_to_frames, -) from swarms.utils.yaml_output_parser import YamlOutputParser from swarms.utils.concurrent_utils import execute_concurrently + __all__ = [ - "SubprocessCodeInterpreter", - "display_markdown_message", - "extract_code_from_markdown", - "find_image_path", - "limit_tokens_from_string", - "load_model_torch", - "math_eval", - "metrics_decorator", - "pdf_to_text", - "prep_torch_inference", "print_class_parameters", - "check_device", + "SubprocessCodeInterpreter", + "csv_to_dataframe", + "dataframe_to_strings", "csv_to_text", + "data_to_text", "json_to_text", "txt_to_text", - "data_to_text", - "try_except_wrapper", + "check_device", + "download_img_from_url", "download_weights_from_url", - "parse_log_file", - "YamlOutputParser", + "ExponentialBackoffMixin", + "load_json", + "sanitize_file_path", + "zip_workspace", + "create_file_in_folder", + "zip_folders", + "find_image_path", "JsonOutputParser", + "metrics_decorator", + "load_model_torch", + "display_markdown_message", + "math_eval", + "dataframe_to_text", + "extract_code_from_markdown", + "pdf_to_text", + "prep_torch_inference", "remove_whitespace_from_json", "remove_whitespace_from_yaml", - "ExponentialBackoffMixin", - "download_img_from_url", - "FeatureType", - "compute_mask_iou_vectorized", - "mask_non_max_suppression", - "filter_masks_by_relative_area", - "masks_to_marks", - "refine_marks", + "parse_log_file", "MarkVisualizer", - "video_to_frames", - "save_frames_as_images", - "dataframe_to_text", - "zip_workspace", - "sanitize_file_path", - "load_json", - "csv_to_dataframe", - "dataframe_to_strings", + "limit_tokens_from_string", + "try_except_wrapper", + "YamlOutputParser", "execute_concurrently", - "create_file_in_folder", - "zip_folders", -] +] \ No newline at end of file diff --git a/swarms/utils/markdown_message.py b/swarms/utils/markdown_message.py index 57cd285fb..a85cb4a16 100644 --- a/swarms/utils/markdown_message.py +++ b/swarms/utils/markdown_message.py @@ -1,6 +1,4 @@ -from rich.console import Console -from rich.markdown import Markdown -from rich.rule import Rule +from termcolor import colored def display_markdown_message(message: str, color: str = "cyan"): @@ -9,19 +7,18 @@ def display_markdown_message(message: str, color: str = "cyan"): Will automatically make single line > tags beautiful. """ - console = Console() for line in message.split("\n"): line = line.strip() if line == "": - console.print("") + print() elif line == "---": - console.print(Rule(style=color)) + print(colored("-" * 50, color)) else: - console.print(Markdown(line, style=color)) + print(colored(line, color)) if "\n" not in message and message.startswith(">"): # Aesthetic choice. For these tags, they need a space below them - console.print("") + print() # display_markdown_message("I love you and you are beautiful.", "cyan") diff --git a/swarms/utils/video_to_frames.py b/swarms/utils/video_to_frames.py deleted file mode 100644 index 528e45b08..000000000 --- a/swarms/utils/video_to_frames.py +++ /dev/null @@ -1,43 +0,0 @@ -from typing import List - -import cv2 - - -def video_to_frames(video_file: str) -> List: - """ - Convert a video into frames. - - Args: - video_file (str): The path to the video file. - - Returns: - List[np.array]: A list of frames from the video. - """ - # Open the video file - vidcap = cv2.VideoCapture(video_file) - - frames = [] - success, image = vidcap.read() - - while success: - frames.append(image) - success, image = vidcap.read() - - return frames - - -def save_frames_as_images(frames, output_dir) -> None: - """ - Save a list of frames as image files. - - Args: - frames (list of np.array): The list of frames. - output_dir (str): The directory where the images will be saved. - """ - for i, frame in enumerate(frames): - cv2.imwrite(f"{output_dir}/frame{i}.jpg", frame) - - -# out = save_frames_as_images(frames, "playground/demos/security_team/frames") - -# print(out) From 1550d8c7ffadc42f90ca5cbf3c626f8d957dd1d1 Mon Sep 17 00:00:00 2001 From: Kye Date: Tue, 26 Mar 2024 10:32:06 -0700 Subject: [PATCH 06/12] [CLEANUP] --- swarms/models/biogpt.py | 227 --------------------------------- swarms/models/chest_agent.py | 91 ------------- swarms/models/clipq.py | 180 -------------------------- swarms/models/cog_agent.py | 130 ------------------- swarms/models/gigabind.py | 106 --------------- swarms/structs/sermon_swarm.py | 4 +- swarms/utils/__init__.py | 2 +- 7 files changed, 3 insertions(+), 737 deletions(-) delete mode 100644 swarms/models/biogpt.py delete mode 100644 swarms/models/chest_agent.py delete mode 100644 swarms/models/clipq.py delete mode 100644 swarms/models/cog_agent.py delete mode 100644 swarms/models/gigabind.py diff --git a/swarms/models/biogpt.py b/swarms/models/biogpt.py deleted file mode 100644 index a5ec7b7b7..000000000 --- a/swarms/models/biogpt.py +++ /dev/null @@ -1,227 +0,0 @@ -r""" -BioGPT -Pre-trained language models have attracted increasing attention in the biomedical domain, -inspired by their great success in the general natural language domain. -Among the two main branches of pre-trained language models in the general language domain, i.e. BERT (and its variants) and GPT (and its variants), -the first one has been extensively studied in the biomedical domain, such as BioBERT and PubMedBERT. -While they have achieved great success on a variety of discriminative downstream biomedical tasks, -the lack of generation ability constrains their application scope. -In this paper, we propose BioGPT, a domain-specific generative Transformer language model -pre-trained on large-scale biomedical literature. -We evaluate BioGPT on six biomedical natural language processing tasks -and demonstrate that our model outperforms previous models on most tasks. -Especially, we get 44.98%, 38.42% and 40.76% F1 score on BC5CDR, KD-DTI and DDI -end-to-end relation extraction tasks, respectively, and 78.2% accuracy on PubMedQA, -creating a new record. Our case study on text generation further demonstrates the -advantage of BioGPT on biomedical literature to generate fluent descriptions for biomedical terms. - - -@article{10.1093/bib/bbac409, - author = {Luo, Renqian and Sun, Liai and Xia, Yingce and Qin, Tao and Zhang, Sheng and Poon, Hoifung and Liu, Tie-Yan}, - title = "{BioGPT: generative pre-trained transformer for biomedical text generation and mining}", - journal = {Briefings in Bioinformatics}, - volume = {23}, - number = {6}, - year = {2022}, - month = {09}, - abstract = "{Pre-trained language models have attracted increasing attention in the biomedical domain, inspired by their great success in the general natural language domain. Among the two main branches of pre-trained language models in the general language domain, i.e. BERT (and its variants) and GPT (and its variants), the first one has been extensively studied in the biomedical domain, such as BioBERT and PubMedBERT. While they have achieved great success on a variety of discriminative downstream biomedical tasks, the lack of generation ability constrains their application scope. In this paper, we propose BioGPT, a domain-specific generative Transformer language model pre-trained on large-scale biomedical literature. We evaluate BioGPT on six biomedical natural language processing tasks and demonstrate that our model outperforms previous models on most tasks. Especially, we get 44.98\%, 38.42\% and 40.76\% F1 score on BC5CDR, KD-DTI and DDI end-to-end relation extraction tasks, respectively, and 78.2\% accuracy on PubMedQA, creating a new record. Our case study on text generation further demonstrates the advantage of BioGPT on biomedical literature to generate fluent descriptions for biomedical terms.}", - issn = {1477-4054}, - doi = {10.1093/bib/bbac409}, - url = {https://doi.org/10.1093/bib/bbac409}, - note = {bbac409}, - eprint = {https://academic.oup.com/bib/article-pdf/23/6/bbac409/47144271/bbac409.pdf}, -} -""" - -import torch -from transformers import ( - BioGptForCausalLM, - BioGptTokenizer, - pipeline, - set_seed, -) - - -class BioGPT: - """ - A wrapper class for the BioGptForCausalLM model from the transformers library. - - Attributes: - model_name (str): Name of the pretrained model. - model (BioGptForCausalLM): The pretrained BioGptForCausalLM model. - tokenizer (BioGptTokenizer): The tokenizer for the BioGptForCausalLM model. - - Methods: - __call__: Generate text based on the given input. - get_features: Get the features of a given text. - beam_search_decoding: Generate text using beam search decoding. - set_pretrained_model: Set a new tokenizer and model. - get_config: Get the model's configuration. - save_model: Save the model and tokenizer to a directory. - load_from_path: Load a model and tokenizer from a directory. - print_model: Print the model's architecture. - - Usage: - >>> from swarms.models.biogpt import BioGPTWrapper - >>> model = BioGPTWrapper() - >>> out = model("The patient has a fever") - >>> print(out) - - - """ - - def __init__( - self, - model_name: str = "microsoft/biogpt", - max_length: int = 500, - num_return_sequences: int = 5, - do_sample: bool = True, - min_length: int = 100, - ): - """ - Initialize the wrapper class with a model name. - - Args: - model_name (str): Name of the pretrained model. Default is "microsoft/biogpt". - """ - self.model_name = model_name - self.max_length = max_length - self.num_return_sequences = num_return_sequences - self.do_sample = do_sample - self.min_length = min_length - - self.model = BioGptForCausalLM.from_pretrained( - self.model_name - ) - self.tokenizer = BioGptTokenizer.from_pretrained( - self.model_name - ) - - def __call__(self, text: str): - """ - Generate text based on the given input. - - Args: - text (str): The input text to generate from. - max_length (int): Maximum length of the generated text. - num_return_sequences (int): Number of sequences to return. - do_sample (bool): Whether or not to use sampling in generation. - - Returns: - list[dict]: A list of generated texts. - """ - set_seed(42) - generator = pipeline( - "text-generation", - model=self.model, - tokenizer=self.tokenizer, - ) - out = generator( - text, - max_length=self.max_length, - num_return_sequences=self.num_return_sequences, - do_sample=self.do_sample, - ) - - return out[0]["generated_text"] - - def get_features(self, text): - """ - Get the features of a given text. - - Args: - text (str): Input text. - - Returns: - BaseModelOutputWithPastAndCrossAttentions: Model output. - """ - encoded_input = self.tokenizer(text, return_tensors="pt") - return self.model(**encoded_input) - - def beam_search_decoding( - self, - sentence, - num_beams=5, - early_stopping=True, - ): - """ - Generate text using beam search decoding. - - Args: - sentence (str): The input sentence to generate from. - min_length (int): Minimum length of the generated text. - max_length (int): Maximum length of the generated text. - num_beams (int): Number of beams for beam search. - early_stopping (bool): Whether to stop early during beam search. - - Returns: - str: The generated text. - """ - inputs = self.tokenizer(sentence, return_tensors="pt") - set_seed(42) - with torch.no_grad(): - beam_output = self.model.generate( - **inputs, - min_length=self.min_length, - max_length=self.max_length, - num_beams=num_beams, - early_stopping=early_stopping, - ) - return self.tokenizer.decode( - beam_output[0], skip_special_tokens=True - ) - - # Feature 1: Set a new tokenizer and model - def set_pretrained_model(self, model_name): - """ - Set a new tokenizer and model. - - Args: - model_name (str): Name of the pretrained model. - """ - self.model_name = model_name - self.model = BioGptForCausalLM.from_pretrained( - self.model_name - ) - self.tokenizer = BioGptTokenizer.from_pretrained( - self.model_name - ) - - # Feature 2: Get the model's config details - def get_config(self): - """ - Get the model's configuration. - - Returns: - PretrainedConfig: The configuration of the model. - """ - return self.model.config - - # Feature 3: Save the model and tokenizer to disk - def save_model(self, path): - """ - Save the model and tokenizer to a directory. - - Args: - path (str): Path to the directory. - """ - self.model.save_pretrained(path) - self.tokenizer.save_pretrained(path) - - # Feature 4: Load a model from a custom path - def load_from_path(self, path): - """ - Load a model and tokenizer from a directory. - - Args: - path (str): Path to the directory. - """ - self.model = BioGptForCausalLM.from_pretrained(path) - self.tokenizer = BioGptTokenizer.from_pretrained(path) - - # Feature 5: Print the model's architecture - def print_model(self): - """ - Print the model's architecture. - """ - print(self.model) diff --git a/swarms/models/chest_agent.py b/swarms/models/chest_agent.py deleted file mode 100644 index 7bf4e850c..000000000 --- a/swarms/models/chest_agent.py +++ /dev/null @@ -1,91 +0,0 @@ -import io - -import requests -import torch -from PIL import Image -from transformers import ( - AutoModelForCausalLM, - AutoProcessor, - GenerationConfig, -) - -from swarms.models.base_multimodal_model import ( - BaseMultiModalModel, -) # noqa: F401 - - -class ChestMultiModalAgent(BaseMultiModalModel): - """ - Initialize the ChestAgent. - - Args: - device (str): The device to run the model on. Default is "cuda". - dtype (torch.dtype): The data type to use for the model. Default is torch.float16. - model_name (str): The name or path of the pre-trained model to use. Default is "StanfordAIMI/CheXagent-8b". - - Example: - >>> agent = ChestAgent() - >>> agent.run("What are the symptoms of COVID-19?", "https://example.com/image.jpg") - - """ - - def __init__( - self, - device="cuda", - dtype=torch.float16, - model_name="StanfordAIMI/CheXagent-8b", - *args, - **kwargs, - ): - # Step 1: Setup constants - self.device = device - self.dtype = dtype - - # Step 2: Load Processor and Model - self.processor = AutoProcessor.from_pretrained( - model_name, trust_remote_code=True - ) - self.generation_config = GenerationConfig.from_pretrained( - model_name - ) - self.model = AutoModelForCausalLM.from_pretrained( - model_name, - torch_dtype=self.dtype, - trust_remote_code=True, - *args, - **kwargs, - ) - - def run(self, task: str, img: str, *args, **kwargs): - """ - Run the ChestAgent to generate findings based on an image and a prompt. - - Args: - image_path (str): The URL or local path of the image. - prompt (str): The prompt to use for generating findings. - - Returns: - str: The generated findings. - """ - # Step 3: Fetch the images - images = [ - Image.open(io.BytesIO(requests.get(img).content)).convert( - "RGB" - ) - ] - - # Step 4: Generate the Findings section - inputs = self.processor( - images=images, - text=f" USER: {task} ASSISTANT: ", - return_tensors="pt", - ).to(device=self.device, dtype=self.dtype) - output = self.model.generate( - **inputs, - generation_config=self.generation_config, - )[0] - response = self.processor.tokenizer.decode( - output, skip_special_tokens=True - ) - - return response diff --git a/swarms/models/clipq.py b/swarms/models/clipq.py deleted file mode 100644 index e6d587c91..000000000 --- a/swarms/models/clipq.py +++ /dev/null @@ -1,180 +0,0 @@ -from io import BytesIO - -import requests -import torch -from PIL import Image -from torchvision.transforms import GaussianBlur -from transformers import CLIPModel, CLIPProcessor - - -class CLIPQ: - """CLIPQ model for image and text retrieval - - Args: - model_name (str): The name of the CLIP model to use - query_text (str): The query text to use for the model - - Example: - >>> clipq = CLIPQ() - >>> image = clipq.fetch_image_from_url() - >>> vectors = clipq.get_vectors(image) - - - """ - - def __init__( - self, - model_name: str = "openai/clip-vit-base-patch16", - query_text: str = "A photo ", - *args, - **kwargs, - ): - self.model = CLIPModel.from_pretrained( - model_name, *args, **kwargs - ) - self.processor = CLIPProcessor.from_pretrained(model_name) - self.query_text = query_text - - def fetch_image_from_url(self, url="https://picsum.photos/800"): - """Fetches an image from the given url""" - response = requests.get(url) - if response.status_code != 200: - raise Exception("Failed to fetch an image") - image = Image.open(BytesIO(response.content)) - return image - - def load_image_from_path(self, path): - """Loads an image from the given path""" - return Image.open(path) - - def split_image( - self, image, h_splits: int = 2, v_splits: int = 2 - ): - """Splits the given image into h_splits x v_splits parts""" - width, height = image.size - w_step, h_step = width // h_splits, height // v_splits - slices = [] - - for i in range(v_splits): - for j in range(h_splits): - slice = image.crop( - ( - j * w_step, - i * h_step, - (j + 1) * w_step, - (i + 1) * h_step, - ) - ) - slices.append(slice) - return slices - - def get_vectors( - self, - image, - h_splits: int = 2, - v_splits: int = 2, - ): - """Gets the vectors for the given image""" - slices = self.split_image(image, h_splits, v_splits) - vectors = [] - - for slice in slices: - inputs = self.processor( - text=self.query_text, - images=slice, - return_tensors="pt", - padding=True, - ) - outputs = self.model(**inputs) - vectors.append( - outputs.image_embeds.squeeze().detach().numpy() - ) - return vectors - - def run_from_url( - self, - url: str = "https://picsum.photos/800", - h_splits: int = 2, - v_splits: int = 2, - ): - """Runs the model on the image fetched from the given url""" - image = self.fetch_image_from_url(url) - return self.get_vectors(image, h_splits, v_splits) - - def check_hard_chunking(self, quadrants): - """Check if the chunking is hard""" - variances = [] - for quadrant in quadrants: - edge_pixels = torch.cat( - [ - quadrant[0, 1], - quadrant[-1, :], - ] - ) - variances.append(torch.var(edge_pixels).item()) - return variances - - def embed_whole_image(self, image): - """Embed the entire image""" - inputs = self.processor( - image, - return_tensors="pt", - ) - with torch.no_grad(): - outputs = self.model(**inputs) - return outputs.image_embeds.squeeze() - - def apply_noise_reduction(self, image, kernel_size: int = 5): - """Implement an upscaling method to upscale the image and tiling issues""" - blur = GaussianBlur(kernel_size) - return blur(image) - - def run_from_path( - self, path: str = None, h_splits: int = 2, v_splits: int = 2 - ): - """Runs the model on the image loaded from the given path""" - image = self.load_image_from_path(path) - return self.get_vectors(image, h_splits, v_splits) - - def get_captions(self, image, candidate_captions): - """Get the best caption for the given image""" - inputs_image = self.processor( - images=image, - return_tensors="pt", - ) - - inputs_text = self.processor( - text=candidate_captions, - images=inputs_image.pixel_values[ - 0 - ], # Fix the argument name - return_tensors="pt", - padding=True, - truncation=True, - ) - - image_embeds = self.model( - pixel_values=inputs_image.pixel_values[0] - ).image_embeds - text_embeds = self.model( - input_ids=inputs_text.input_ids, - attention_mask=inputs_text.attention_mask, - ).text_embeds - - # Calculate similarity between image and text - similarities = (image_embeds @ text_embeds.T).squeeze(0) - best_caption_index = similarities.argmax().item() - - return candidate_captions[best_caption_index] - - def get_and_concat_captions( - self, image, candidate_captions, h_splits=2, v_splits=2 - ): - """Get the best caption for the given image""" - slices = self.split_image(image, h_splits, v_splits) - captions = [ - self.get_captions(slice, candidate_captions) - for slice in slices - ] - concated_captions = "".join(captions) - return concated_captions diff --git a/swarms/models/cog_agent.py b/swarms/models/cog_agent.py deleted file mode 100644 index 35217c488..000000000 --- a/swarms/models/cog_agent.py +++ /dev/null @@ -1,130 +0,0 @@ -import torch -from modelscope import AutoModelForCausalLM, AutoTokenizer -from PIL import Image - -from swarms.models.base_multimodal_model import BaseMultiModalModel - -device_check = "cuda" if torch.cuda.is_available() else "cpu" - - -class CogAgent(BaseMultiModalModel): - """CogAgent - - Multi-modal conversational agent that can be used to chat with - images and text. It is based on the CogAgent model from the - ModelScope library. - - Attributes: - model_name (str): The name of the model to be used - tokenizer_name (str): The name of the tokenizer to be used - dtype (torch.bfloat16): The data type to be used - low_cpu_mem_usage (bool): Whether to use low CPU memory - load_in_4bit (bool): Whether to load in 4-bit - trust_remote_code (bool): Whether to trust remote code - device (str): The device to be used - - Examples: - >>> from swarms.models.cog_agent import CogAgent - >>> cog_agent = CogAgent() - >>> cog_agent.run("How are you?", "images/1.jpg") - I'm fine. How are you? - """ - - def __init__( - self, - model_name: str = "ZhipuAI/cogagent-chat", - tokenizer_name: str = "I-ModelScope/vicuna-7b-v1.5", - dtype=torch.bfloat16, - low_cpu_mem_usage: bool = True, - load_in_4bit: bool = True, - trust_remote_code: bool = True, - device=device_check, - *args, - **kwargs, - ): - super().__init__() - self.model_name = model_name - self.tokenizer_name = tokenizer_name - self.dtype = dtype - self.low_cpu_mem_usage = low_cpu_mem_usage - self.load_in_4bit = load_in_4bit - self.trust_remote_code = trust_remote_code - self.device = device - - self.model = ( - AutoModelForCausalLM.from_pretrained( - self.model_name, - torch_dtype=self.dtype, - low_cpu_mem_usage=self.low_cpu_mem_usage, - load_in_4bit=self.load_in_4bit, - trust_remote_code=self.trust_remote_code, - *args, - **kwargs, - ) - .to(self.device) - .eval() - ) - - self.tokenizer = AutoTokenizer.from_pretrained( - self.tokenizer_name - ) - - def run(self, task: str, img: str, *args, **kwargs): - """Run the model - - Args: - task (str): The task to be performed - img (str): The image path - - """ - image = Image.open(img).convert("RGB") - - input_by_model = self.model.build_conversation_input_ids( - self.tokenizer, - query=task, - history=[], - images=[image], - ) - - inputs = { - "input_ids": ( - input_by_model["input_ids"] - .unsqueeze(0) - .to(self.device) - ), - "token_type_ids": ( - input_by_model["token_type_ids"] - .unsqueeze(0) - .to(self.device) - ), - "attention_mask": ( - input_by_model["attention_mask"] - .unsqueeze(0) - .to(self.device) - ), - "images": [ - [ - input_by_model["images"][0] - .to(self.device) - .to(self.dtype) - ] - ], - } - if ( - "cross_images" in input_by_model - and input_by_model["cross_images"] - ): - inputs["cross_images"] = [ - [ - input_by_model["cross_images"][0] - .to(self.device) - .to(self.dtype) - ] - ] - - with torch.no_grad(): - outputs = self.model(**inputs, **kwargs) - outputs = outputs[:, inputs["input_ids"].shape[1] :] - response = self.decode(outputs[0]) - response = response.split("")[0] - print(response) diff --git a/swarms/models/gigabind.py b/swarms/models/gigabind.py deleted file mode 100644 index 97d6d6cd5..000000000 --- a/swarms/models/gigabind.py +++ /dev/null @@ -1,106 +0,0 @@ -import requests -from tenacity import retry, stop_after_attempt, wait_fixed - - -class Gigabind: - """Gigabind API. - - Args: - host (str, optional): host. Defaults to None. - proxy_url (str, optional): proxy_url. Defaults to None. - port (int, optional): port. Defaults to 8000. - endpoint (str, optional): endpoint. Defaults to "embeddings". - - Examples: - >>> from swarms.models.gigabind import Gigabind - >>> api = Gigabind(host="localhost", port=8000, endpoint="embeddings") - >>> response = api.run(text="Hello, world!", vision="image.jpg") - >>> print(response) - """ - - def __init__( - self, - host: str = None, - proxy_url: str = None, - port: int = 8000, - endpoint: str = "embeddings", - *args, - **kwargs, - ): - super().__init__(*args, **kwargs) - self.host = host - self.proxy_url = proxy_url - self.port = port - self.endpoint = endpoint - - # Set the URL to the API - if self.proxy_url is not None: - self.url = f"{self.proxy_url}" - else: - self.url = f"http://{host}:{port}/{endpoint}" - - @retry(stop=stop_after_attempt(3), wait=wait_fixed(2)) - def run( - self, - text: str = None, - vision: str = None, - audio: str = None, - *args, - **kwargs, - ): - """Run the Gigabind API. - - Args: - text (str, optional): text. Defaults to None. - vision (str, optional): images. Defaults to None. - audio (str, optional): audio file paths. Defaults to None. - - Raises: - ValueError: At least one of text, vision or audio must be provided - - Returns: - embeddings: embeddings - """ - try: - # Prepare the data to send to the API - data = {} - if text is not None: - data["text"] = text - if vision is not None: - data["vision"] = vision - if audio is not None: - data["audio"] = audio - else: - raise ValueError( - "At least one of text, vision or audio must be" - " provided" - ) - - # Send a POST request to the API and return the response - response = requests.post( - self.url, json=data, *args, **kwargs - ) - return response.json() - except Exception as error: - print(f"Gigabind API error: {error}") - return None - - def generate_summary(self, text: str = None, *args, **kwargs): - # Prepare the data to send to the API - data = {} - if text is not None: - data["text"] = text - else: - raise ValueError( - "At least one of text, vision or audio must be" - " provided" - ) - - # Send a POST request to the API and return the response - response = requests.post(self.url, json=data, *args, **kwargs) - return response.json() - - -# api = Gigabind(host="localhost", port=8000, endpoint="embeddings") -# response = api.run(text="Hello, world!", vision="image.jpg") -# print(response) diff --git a/swarms/structs/sermon_swarm.py b/swarms/structs/sermon_swarm.py index 0f34afcfe..147ba36ed 100644 --- a/swarms/structs/sermon_swarm.py +++ b/swarms/structs/sermon_swarm.py @@ -1,8 +1,8 @@ from typing import Union, Sequence, List, Callable from swarms.structs.agent import Agent +from swarms.structs.base_swarm import AbstractSwarm - -class SermonSwarm: +class SermonSwarm(AbstractSwarm): """ Represents a swarm of agents that communicate through sermons. diff --git a/swarms/utils/__init__.py b/swarms/utils/__init__.py index 7e311f675..583f61d54 100644 --- a/swarms/utils/__init__.py +++ b/swarms/utils/__init__.py @@ -83,4 +83,4 @@ "try_except_wrapper", "YamlOutputParser", "execute_concurrently", -] \ No newline at end of file +] From 98e922f75cfb79783ec84694d87380a01c608351 Mon Sep 17 00:00:00 2001 From: Kye Date: Tue, 26 Mar 2024 10:38:48 -0700 Subject: [PATCH 07/12] [CLEANUP] --- swarms/models/zephyr.py | 107 --------------------------- swarms/structs/all_to_all_swarm.py | 0 swarms/structs/recursive_workflow.py | 1 + swarms/structs/sermon_swarm.py | 1 + swarms/structs/task_queue_base.py | 24 +++--- 5 files changed, 14 insertions(+), 119 deletions(-) delete mode 100644 swarms/models/zephyr.py delete mode 100644 swarms/structs/all_to_all_swarm.py diff --git a/swarms/models/zephyr.py b/swarms/models/zephyr.py deleted file mode 100644 index 205ec2e5f..000000000 --- a/swarms/models/zephyr.py +++ /dev/null @@ -1,107 +0,0 @@ -"""Zephyr by HF""" - -import torch -from transformers import pipeline - - -class Zephyr: - """ - Zehpyr model from HF - - - Args: - max_new_tokens(int) = Number of max new tokens - temperature(float) = temperature of the LLM - top_k(float) = top k of the model set to 50 - top_p(float) = top_p of the model set to 0.95 - - - - Usage: - >>> model = Zephyr() - >>> output = model("Generate hello world in python") - - - """ - - def __init__( - self, - model_name: str = "HuggingFaceH4/zephyr-7b-alpha", - tokenize: bool = False, - add_generation_prompt: bool = True, - system_prompt: str = "You are a friendly chatbot who always responds in the style of a pirate", - max_new_tokens: int = 300, - temperature: float = 0.5, - top_k: float = 50, - top_p: float = 0.95, - do_sample: bool = True, - *args, - **kwargs, - ): - super().__init__() - self.model_name = model_name - self.tokenize = tokenize - self.add_generation_prompt = add_generation_prompt - self.system_prompt = system_prompt - self.max_new_tokens = max_new_tokens - self.temperature = temperature - self.top_k = top_k - self.top_p = top_p - self.do_sample = do_sample - - self.pipe = pipeline( - "text-generation", - model=self.model_name, - torch_dtype=torch.bfloat16, - device_map="auto", - ) - self.messages = [ - { - "role": "system", - "content": f"{self.system_prompt}\n\nUser:", - }, - ] - - def __call__(self, task: str): - """Call the model""" - prompt = self.pipe.tokenizer.apply_chat_template( - self.messages, - tokenize=self.tokenize, - add_generation_prompt=self.add_generation_prompt, - ) - outputs = self.pipe( - prompt - ) # max_new_token=self.max_new_tokens) - print(outputs[0]["generated_text"]) - - def chat(self, message: str): - """ - Adds a user message to the conversation and generates a chatbot response. - """ - # Add the user message to the conversation - self.messages.append({"role": "user", "content": message}) - - # Apply the chat template to format the messages - prompt = self.pipe.tokenizer.apply_chat_template( - self.messages, - tokenize=self.tokenize, - add_generation_prompt=self.add_generation_prompt, - ) - - # Generate a response - outputs = self.pipe( - prompt, - max_new_tokens=self.max_new_tokens, - do_sample=self.do_sample, - temperature=self.temperature, - top_k=self.top_k, - top_p=self.top_p, - ) - - # Extract the generated text - generated_text = outputs[0]["generated_text"] - - # Optionally, you could also add the chatbot's response to the messages list - # However, the below line should be adjusted to extract the chatbot's response only - # self.messages.append({"role": "bot", "content": generated_text}) - return generated_text diff --git a/swarms/structs/all_to_all_swarm.py b/swarms/structs/all_to_all_swarm.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/swarms/structs/recursive_workflow.py b/swarms/structs/recursive_workflow.py index 60d33fe4f..ecb679bcc 100644 --- a/swarms/structs/recursive_workflow.py +++ b/swarms/structs/recursive_workflow.py @@ -36,6 +36,7 @@ def __init__( stopping_conditions: callable = None, ): self.stop_token = stop_token + self.stopping_conditions = stopping_conditions self.task_pool = [] assert ( diff --git a/swarms/structs/sermon_swarm.py b/swarms/structs/sermon_swarm.py index 147ba36ed..895670ad7 100644 --- a/swarms/structs/sermon_swarm.py +++ b/swarms/structs/sermon_swarm.py @@ -2,6 +2,7 @@ from swarms.structs.agent import Agent from swarms.structs.base_swarm import AbstractSwarm + class SermonSwarm(AbstractSwarm): """ Represents a swarm of agents that communicate through sermons. diff --git a/swarms/structs/task_queue_base.py b/swarms/structs/task_queue_base.py index 968023b05..b95421a52 100644 --- a/swarms/structs/task_queue_base.py +++ b/swarms/structs/task_queue_base.py @@ -1,5 +1,5 @@ import threading -from abc import ABC, abstractmethod +from abc import ABC from swarms.structs.agent import Agent from swarms.structs.task import Task @@ -36,8 +36,8 @@ def __init__(self): self.lock = threading.Lock() @synchronized_queue - @abstractmethod - def add_task(self, task: Task) -> bool: + # @abstractmethod + def add(self, task: Task) -> bool: """Adds a task to the queue. Args: @@ -46,11 +46,11 @@ def add_task(self, task: Task) -> bool: Returns: bool: True if the task was successfully added, False otherwise. """ - raise NotImplementedError + ... @synchronized_queue - @abstractmethod - def get_task(self, agent: Agent) -> Task: + # @abstractmethod + def get(self, agent: Agent) -> Task: """Gets the next task from the queue. Args: @@ -59,24 +59,24 @@ def get_task(self, agent: Agent) -> Task: Returns: Task: The next task from the queue. """ - raise NotImplementedError + ... @synchronized_queue - @abstractmethod + # @abstractmethod def complete_task(self, task_id: str): """Sets the task as completed. Args: task_id (str): The ID of the task to be marked as completed. """ - raise NotImplementedError + ... @synchronized_queue - @abstractmethod - def reset_task(self, task_id: str): + # @abstractmethod + def reset(self, task_id: str): """Resets the task if the agent failed to complete it. Args: task_id (str): The ID of the task to be reset. """ - raise NotImplementedError + ... From ec0ca7004e1d866ec8f059347f7ccb8a35a823d5 Mon Sep 17 00:00:00 2001 From: Kye Date: Tue, 26 Mar 2024 10:57:16 -0700 Subject: [PATCH 08/12] [CLEANUP] --- swarms/agents/worker_agent.py | 22 +- swarms/utils/dist_utils.py | 310 --------------------------- swarms/utils/hash_utils.py | 0 swarms/utils/loguru_logger.py | 13 +- swarms/utils/main.py | 194 ----------------- swarms/utils/supervision_masking.py | 259 ---------------------- swarms/utils/token_count_tiktoken.py | 27 --- 7 files changed, 17 insertions(+), 808 deletions(-) delete mode 100644 swarms/utils/dist_utils.py delete mode 100644 swarms/utils/hash_utils.py delete mode 100644 swarms/utils/supervision_masking.py delete mode 100644 swarms/utils/token_count_tiktoken.py diff --git a/swarms/agents/worker_agent.py b/swarms/agents/worker_agent.py index c0e7f464f..d11ac9250 100644 --- a/swarms/agents/worker_agent.py +++ b/swarms/agents/worker_agent.py @@ -1,10 +1,10 @@ import os from typing import List -import faiss +# import faiss from langchain.docstore import InMemoryDocstore from langchain.embeddings import OpenAIEmbeddings -from langchain.vectorstores import FAISS +# from langchain.vectorstores import FAISS from langchain_experimental.autonomous_agents import AutoGPT from swarms.tools.tool import BaseTool @@ -53,6 +53,7 @@ def __init__( embedding_size: int = 1536, search_kwargs: dict = {"k": 8}, verbose: bool = False, + memory: callable = None, *args, **kwargs, ): @@ -67,6 +68,7 @@ def __init__( self.embedding_size = embedding_size self.search_kwargs = search_kwargs self.verbose = verbose + self.memory = memory self.setup_tools(external_tools) self.setup_memory() @@ -121,22 +123,8 @@ def setup_memory(self): """ Set up memory for the worker. """ - openai_api_key = ( - os.getenv("OPENAI_API_KEY") or self.openai_api_key - ) try: - embeddings_model = OpenAIEmbeddings( - openai_api_key=openai_api_key - ) - embedding_size = self.embedding_size - index = faiss.IndexFlatL2(embedding_size) - - self.vectorstore = FAISS( - embeddings_model.embed_query, - index, - InMemoryDocstore({}), - {}, - ) + self.vectorstore = self.memory except Exception as error: raise RuntimeError( diff --git a/swarms/utils/dist_utils.py b/swarms/utils/dist_utils.py deleted file mode 100644 index f255d506c..000000000 --- a/swarms/utils/dist_utils.py +++ /dev/null @@ -1,310 +0,0 @@ -from typing import Callable, Union - -import torch -from torch import Tensor, nn -from torch.distributed._tensor import ( - DeviceMesh, - DTensor, - Replicate, - Shard, - distribute_tensor, -) -from zeta.nn import QuantizedLN - -try: - from peft.tuners.lora import Linear as LoRALinear -except ImportError: - - class LoRALinear: - pass - - -def try_to_local(tensor: Union[Tensor, DTensor]): - """Try to convert DTensor to Tensor. - - Args: - tensor (Tensor|DTensor): Tensor to convert. - """ - if isinstance(tensor, DTensor): - tensor = tensor.to_local() - return tensor - - -def module_to_local(module: nn.Module): - """convert all DTensor parameters to Tensor parameters in module. - - Args: - module (Module): Module to convert. - """ - for name, mod in module.named_children(): - module_to_local(mod) - - for name, param in module.named_parameters(recurse=False): - module.register_parameter( - name, nn.Parameter(try_to_local(param)) - ) - - for name, buf in module.named_buffers(recurse=False): - module.register_buffer(name, try_to_local(buf)) - - -def rowwise_parallelize_linear( - module: nn.Module, device_mesh: DeviceMesh, to_local: bool = False -) -> None: - """ - This function parallelizes the input :class:`nn.Linear` module in - :class:`RowwiseParallel` style. - - Args: - module (:class:`nn.Module`): - The :class:`nn.Linear` module to be parallelized. - device_mesh (:class:`DeviceMesh`): - Object which describes the mesh topology of devices. - - Returns: - None - """ - for name, param in module.named_parameters(): - dist_spec = ( - [Shard(1)] if name == "weight" else [Replicate()] # type: ignore[list-item] - ) - - dist_tensor = distribute_tensor(param, device_mesh, dist_spec) - if to_local: - dist_tensor = try_to_local(dist_tensor) - if name == "bias": - # rowwise linear would add bias more than ones. - dist_tensor /= device_mesh.size() - dist_param = torch.nn.Parameter(dist_tensor) - module.register_parameter(name, dist_param) - - # Weight, bias and scale are registered as buffer in QLinear - for name, buffer in module.named_buffers(): - dist_spec = ( - [Shard(1)] if name == "weight" else [Replicate()] # type: ignore[list-item] - ) - - dist_tensor = distribute_tensor( - buffer, device_mesh, dist_spec - ) - if to_local: - dist_tensor = try_to_local(dist_tensor) - if name == "bias": - # rowwise linear would add bias more than ones. - dist_tensor /= device_mesh.size() - module.register_buffer(name, dist_tensor) - - dist_tensor = distribute_tensor( - buffer, device_mesh, dist_spec - ) - if to_local: - dist_tensor = try_to_local(dist_tensor) - module.register_buffer(name, dist_tensor) - - -def rowwise_parallelize_loralinear( - module: LoRALinear, - device_mesh: DeviceMesh, - to_local: bool = False, -) -> None: - """rowwize parallelize lora linear. - - Read S-LoRA for more detail. - """ - rowwise_parallelize_linear( - module.base_layer, device_mesh=device_mesh, to_local=to_local - ) - for mod in module.lora_A.values(): - rowwise_parallelize_linear( - mod, device_mesh=device_mesh, to_local=to_local - ) - for mod in module.lora_B.values(): - colwise_parallelize_linear( - mod, device_mesh=device_mesh, to_local=to_local - ) - module._tp_mode = "rowwise" - - -def rowwise_parallelize_linear_fn( - module: nn.Module, device_mesh: DeviceMesh, to_local: bool = False -) -> None: - """ - This function parallelizes the input :Linear module in - :class:`RowwiseParallel` style. - - Args: - module (:class:`nn.Module`): - The :class:`nn.Linear` module to be parallelized. - device_mesh (:class:`DeviceMesh`): - Object which describes the mesh topology of devices. - - Returns: - None - """ - if isinstance(module, (torch.nn.Linear, QuantizedLN)): - return rowwise_parallelize_linear( - module, device_mesh=device_mesh, to_local=to_local - ) - elif isinstance(module, LoRALinear): - return rowwise_parallelize_loralinear( - module, device_mesh=device_mesh, to_local=to_local - ) - else: - raise TypeError(f"Unsupported module: {type(module)}") - - -def colwise_parallelize_linear( - module: nn.Module, device_mesh: DeviceMesh, to_local: bool = False -) -> None: - """ - This function parallelizes the input :class:`nn.Linear` module in - :class:`ColwiseParallel` style. - - Args: - module (:class:`nn.Module`): - The :class:`nn.Linear` module to be parallelized. - device_mesh (:class:`DeviceMesh`): - Object which describes the mesh topology of devices. - - Returns: - None - """ - - for name, param in module.named_parameters(): - dist_tensor = distribute_tensor( - param, device_mesh, [Shard(0)] - ) - if to_local: - dist_tensor = try_to_local(dist_tensor) - dist_param = torch.nn.Parameter(dist_tensor) - module.register_parameter(name, dist_param) - # Weight, bias and scale are registered as buffer in QLinear - for name, buffer in module.named_buffers(): - dist_tensor = distribute_tensor( - buffer, device_mesh, [Shard(0)] - ) - if to_local: - dist_tensor = try_to_local(dist_tensor) - module.register_buffer(name, dist_tensor) - - -def colwise_parallelize_loralinear( - module: nn.Module, device_mesh: DeviceMesh, to_local: bool = False -) -> None: - """colwise parallelize lora linear.""" - colwise_parallelize_linear( - module.base_layer, device_mesh=device_mesh, to_local=to_local - ) - for mod in module.lora_A.values(): - colwise_parallelize_linear( - mod, device_mesh=device_mesh, to_local=to_local - ) - for mod in module.lora_B.values(): - colwise_parallelize_linear( - mod, device_mesh=device_mesh, to_local=to_local - ) - module._tp_mode = "colwise" - - -def colwise_parallelize_linear_fn( - module: nn.Module, device_mesh: DeviceMesh, to_local: bool = False -) -> None: - """ - This function parallelizes the input :Linear module in - :class:`ColwiseParallel` style. - - Args: - module (:class:`nn.Module`): - The :class:`nn.Linear` module to be parallelized. - device_mesh (:class:`DeviceMesh`): - Object which describes the mesh topology of devices. - - Returns: - None - """ - if isinstance(module, (torch.nn.Linear, QuantizedLN)): - return colwise_parallelize_linear( - module, device_mesh=device_mesh, to_local=to_local - ) - elif isinstance(module, LoRALinear): - return colwise_parallelize_loralinear( - module, device_mesh=device_mesh, to_local=to_local - ) - else: - raise TypeError(f"Unsupported module: {type(module)}") - - -def _partition_module( - mod_name: str, - prefix: str, - module: nn.Module, - device_mesh: DeviceMesh, - func: Callable, -): - """partition module. - - Parameters in module won't be force Replicated. - - Args: - mod_name (str): module name. - prefix (str): Parameter prefix. - module (Module): Module to be partitioned. - device_mesh (DeviceMesh): The device mesh. - func (Callable): partition callback - """ - for name, mod in module.named_children(): - child_name = f"{prefix}{name}" - _partition_module( - child_name, - child_name + ".", - module=mod, - device_mesh=device_mesh, - func=func, - ) - - func(mod_name, module, device_mesh) - - -def partition_module( - module: nn.Module, - device_mesh: DeviceMesh, - func: Callable, - to_local: bool = False, -): - """partition module. - - Parameters in module won't be force Replicated. - - Args: - module (Module): Module to be partitioned. - device_mesh (DeviceMesh): The device mesh. - func (Callable): partition callback. - to_local (bool): Convert all DTensor parameters to Tensor parameters. - """ - _partition_module( - "", "", module=module, device_mesh=device_mesh, func=func - ) - - if to_local: - module_to_local(module) - - -def replicate_module(model: nn.Module, device_mesh: DeviceMesh): - """Replicate all parameters in module. - - Args: - model (Module): Module to perform replicate. - device_mesh (DeviceMesh): The distribution device mesh. - """ - for name, param in model.named_parameters(recurse=False): - param = distribute_tensor( - param, device_mesh=device_mesh, placements=[Replicate()] - ).to_local() - param = nn.Parameter(param) - model.register_parameter(name, param) - - for name, buf in model.named_buffers(recurse=False): - buf = distribute_tensor( - buf, device_mesh=device_mesh, placements=[Replicate()] - ).to_local() - model.register_buffer(name, buf) diff --git a/swarms/utils/hash_utils.py b/swarms/utils/hash_utils.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/swarms/utils/loguru_logger.py b/swarms/utils/loguru_logger.py index dbbed5608..1b0a42f40 100644 --- a/swarms/utils/loguru_logger.py +++ b/swarms/utils/loguru_logger.py @@ -1,10 +1,21 @@ from loguru import logger logger.add( - "MessagePool.log", + "swarms.log", level="INFO", colorize=True, format="{time} {message}", backtrace=True, diagnose=True, ) + +def loguru_logger(file_path: str = "swarms.log"): + return logger.add( + file_path, + level="INFO", + colorize=True, + format="{time} {message}", + backtrace=True, + diagnose=True, + ) + diff --git a/swarms/utils/main.py b/swarms/utils/main.py index 9dbd47fd7..2ca8f262c 100644 --- a/swarms/utils/main.py +++ b/swarms/utils/main.py @@ -7,7 +7,6 @@ from pathlib import Path from typing import Dict -import boto3 import numpy as np import requests @@ -98,134 +97,9 @@ def get_new_dataframe_name(org_img_name, func_name="update"): return os.path.join(head, new_file_name) -# =======================> utils end -# =======================> ANSI BEGINNING -class Code: - def __init__(self, value: int): - self.value = value - - def __str__(self): - return f"{int(self.value)}" - - -class Color(Code): - def bg(self) -> "Color": - self.value += 10 - return self - - def bright(self) -> "Color": - self.value += 60 - return self - - @staticmethod - def black() -> "Color": - return Color(30) - - @staticmethod - def red() -> "Color": - return Color(31) - - @staticmethod - def green() -> "Color": - return Color(32) - - @staticmethod - def yellow() -> "Color": - return Color(33) - - @staticmethod - def blue() -> "Color": - return Color(34) - - @staticmethod - def magenta() -> "Color": - return Color(35) - - @staticmethod - def cyan() -> "Color": - return Color(36) - - @staticmethod - def white() -> "Color": - return Color(37) - - @staticmethod - def default() -> "Color": - return Color(39) - - -class Style(Code): - @staticmethod - def reset() -> "Style": - return Style(0) - - @staticmethod - def bold() -> "Style": - return Style(1) - - @staticmethod - def dim() -> "Style": - return Style(2) - - @staticmethod - def italic() -> "Style": - return Style(3) - - @staticmethod - def underline() -> "Style": - return Style(4) - - @staticmethod - def blink() -> "Style": - return Style(5) - - @staticmethod - def reverse() -> "Style": - return Style(7) - - @staticmethod - def conceal() -> "Style": - return Style(8) - - -class ANSI: - ESCAPE = "\x1b[" - CLOSE = "m" - - def __init__(self, text: str): - self.text = text - self.args = [] - - def join(self) -> str: - return ( - ANSI.ESCAPE - + ";".join([str(a) for a in self.args]) - + ANSI.CLOSE - ) - - def wrap(self, text: str) -> str: - return self.join() + text + ANSI(Style.reset()).join() - - def to(self, *args: str): - self.args = list(args) - return self.wrap(self.text) - - -def dim_multiline(message: str) -> str: - lines = message.split("\n") - if len(lines) <= 1: - return lines[0] - return lines[0] + ANSI("\n... ".join([""] + lines[1:])).to( - Color.black().bright() - ) - - -# +=============================> ANSI Ending - -# ================================> upload base STATIC_DIR = "static" @@ -240,74 +114,6 @@ def from_settings() -> "AbstractUploader": pass -# ================================> upload end - -# ========================= upload s3 - - -class S3Uploader(AbstractUploader): - def __init__( - self, accessKey: str, secretKey: str, region: str, bucket: str - ): - self.accessKey = accessKey - self.secretKey = secretKey - self.region = region - self.bucket = bucket - self.client = boto3.client( - "s3", - aws_access_key_id=self.accessKey, - aws_secret_access_key=self.secretKey, - ) - - @staticmethod - def from_settings() -> "S3Uploader": - return S3Uploader( - os.environ["AWS_ACCESS_KEY_ID"], - os.environ["AWS_SECRET_ACCESS_KEY"], - os.environ["AWS_REGION"], - os.environ["AWS_S3_BUCKET"], - ) - - def get_url(self, object_name: str) -> str: - return f"https://{self.bucket}.s3.{self.region}.amazonaws.com/{object_name}" - - def upload(self, filepath: str) -> str: - object_name = os.path.basename(filepath) - self.client.upload_file(filepath, self.bucket, object_name) - return self.get_url(object_name) - - -# ========================= upload s3 - -# ========================> upload/static - - -class StaticUploader(AbstractUploader): - def __init__(self, server: str, path: Path, endpoint: str): - self.server = server - self.path = path - self.endpoint = endpoint - - @staticmethod - def from_settings(path: Path, endpoint: str) -> "StaticUploader": - server = os.environ.get("SERVER", "http://localhost:8000") - return StaticUploader(server, path, endpoint) - - def get_url(self, uploaded_path: str) -> str: - return f"{self.server}/{uploaded_path}" - - def upload(self, filepath: str): - relative_path = Path("generated") / filepath.split("/")[-1] - file_path = self.path / relative_path - os.makedirs(os.path.dirname(file_path), exist_ok=True) - shutil.copy(filepath, file_path) - endpoint_path = self.endpoint / relative_path - return f"{self.server}/{endpoint_path}" - - -# ========================> handlers/base - -# from env import settings class FileType(Enum): diff --git a/swarms/utils/supervision_masking.py b/swarms/utils/supervision_masking.py deleted file mode 100644 index d0225a92b..000000000 --- a/swarms/utils/supervision_masking.py +++ /dev/null @@ -1,259 +0,0 @@ -from enum import Enum - -import cv2 -import numpy as np -import supervision as sv - - -class FeatureType(Enum): - """ - An enumeration to represent the types of features for mask adjustment in image - segmentation. - """ - - ISLAND = "ISLAND" - HOLE = "HOLE" - - @classmethod - def list(cls): - return list(map(lambda c: c.value, cls)) - - -def compute_mask_iou_vectorized(masks: np.ndarray) -> np.ndarray: - """ - Vectorized computation of the Intersection over Union (IoU) for all pairs of masks. - - Parameters: - masks (np.ndarray): A 3D numpy array with shape `(N, H, W)`, where `N` is the - number of masks, `H` is the height, and `W` is the width. - - Returns: - np.ndarray: A 2D numpy array of shape `(N, N)` where each element `[i, j]` is - the IoU between masks `i` and `j`. - - Raises: - ValueError: If any of the masks is found to be empty. - """ - if np.any(masks.sum(axis=(1, 2)) == 0): - raise ValueError( - "One or more masks are empty. Please filter out empty" - " masks before using `compute_iou_vectorized` function." - ) - - masks_bool = masks.astype(bool) - masks_flat = masks_bool.reshape(masks.shape[0], -1) - intersection = np.logical_and( - masks_flat[:, None], masks_flat[None, :] - ).sum(axis=2) - union = np.logical_or( - masks_flat[:, None], masks_flat[None, :] - ).sum(axis=2) - iou_matrix = intersection / union - return iou_matrix - - -def mask_non_max_suppression( - masks: np.ndarray, iou_threshold: float = 0.6 -) -> np.ndarray: - """ - Performs Non-Max Suppression on a set of masks by prioritizing larger masks and - removing smaller masks that overlap significantly. - - When the IoU between two masks exceeds the specified threshold, the smaller mask - (in terms of area) is discarded. This process is repeated for each pair of masks, - effectively filtering out masks that are significantly overlapped by larger ones. - - Parameters: - masks (np.ndarray): A 3D numpy array with shape `(N, H, W)`, where `N` is the - number of masks, `H` is the height, and `W` is the width. - iou_threshold (float): The IoU threshold for determining significant overlap. - - Returns: - np.ndarray: A 3D numpy array of filtered masks. - """ - num_masks = masks.shape[0] - areas = masks.sum(axis=(1, 2)) - sorted_idx = np.argsort(-areas) - keep_mask = np.ones(num_masks, dtype=bool) - iou_matrix = compute_mask_iou_vectorized(masks) - for i in range(num_masks): - if not keep_mask[sorted_idx[i]]: - continue - - overlapping_masks = iou_matrix[sorted_idx[i]] > iou_threshold - overlapping_masks[sorted_idx[i]] = False - overlapping_indices = np.where(overlapping_masks)[0] - keep_mask[sorted_idx[overlapping_indices]] = False - - return masks[keep_mask] - - -def filter_masks_by_relative_area( - masks: np.ndarray, - minimum_area: float = 0.01, - maximum_area: float = 1.0, -) -> np.ndarray: - """ - Filters masks based on their relative area within the total area of each mask. - - Parameters: - masks (np.ndarray): A 3D numpy array with shape `(N, H, W)`, where `N` is the - number of masks, `H` is the height, and `W` is the width. - minimum_area (float): The minimum relative area threshold. Must be between `0` - and `1`. - maximum_area (float): The maximum relative area threshold. Must be between `0` - and `1`. - - Returns: - np.ndarray: A 3D numpy array containing masks that fall within the specified - relative area range. - - Raises: - ValueError: If `minimum_area` or `maximum_area` are outside the `0` to `1` - range, or if `minimum_area` is greater than `maximum_area`. - """ - - if not (isinstance(masks, np.ndarray) and masks.ndim == 3): - raise ValueError("Input must be a 3D numpy array.") - - if not (0 <= minimum_area <= 1) or not (0 <= maximum_area <= 1): - raise ValueError( - "`minimum_area` and `maximum_area` must be between 0" - " and 1." - ) - - if minimum_area > maximum_area: - raise ValueError( - "`minimum_area` must be less than or equal to" - " `maximum_area`." - ) - - total_area = masks.shape[1] * masks.shape[2] - relative_areas = masks.sum(axis=(1, 2)) / total_area - return masks[ - (relative_areas >= minimum_area) - & (relative_areas <= maximum_area) - ] - - -def adjust_mask_features_by_relative_area( - mask: np.ndarray, - area_threshold: float, - feature_type: FeatureType = FeatureType.ISLAND, -) -> np.ndarray: - """ - Adjusts a mask by removing small islands or filling small holes based on a relative - area threshold. - - !!! warning - - Running this function on a mask with small islands may result in empty masks. - - Parameters: - mask (np.ndarray): A 2D numpy array with shape `(H, W)`, where `H` is the - height, and `W` is the width. - area_threshold (float): Threshold for relative area to remove or fill features. - feature_type (FeatureType): Type of feature to adjust (`ISLAND` for removing - islands, `HOLE` for filling holes). - - Returns: - np.ndarray: A 2D numpy array containing mask. - """ - height, width = mask.shape - total_area = width * height - - mask = np.uint8(mask * 255) - operation = ( - cv2.RETR_EXTERNAL - if feature_type == FeatureType.ISLAND - else cv2.RETR_CCOMP - ) - contours, _ = cv2.findContours( - mask, operation, cv2.CHAIN_APPROX_SIMPLE - ) - - for contour in contours: - area = cv2.contourArea(contour) - relative_area = area / total_area - if relative_area < area_threshold: - cv2.drawContours( - image=mask, - contours=[contour], - contourIdx=-1, - color=( - 0 if feature_type == FeatureType.ISLAND else 255 - ), - thickness=-1, - ) - return np.where(mask > 0, 1, 0).astype(bool) - - -def masks_to_marks(masks: np.ndarray) -> sv.Detections: - """ - Converts a set of masks to a marks (sv.Detections) object. - - Parameters: - masks (np.ndarray): A 3D numpy array with shape `(N, H, W)`, where `N` is the - number of masks, `H` is the height, and `W` is the width. - - Returns: - sv.Detections: An object containing the masks and their bounding box - coordinates. - """ - if len(masks) == 0: - marks = sv.Detections.empty() - marks.mask = np.empty((0, 0, 0), dtype=bool) - return marks - return sv.Detections( - mask=masks, xyxy=sv.mask_to_xyxy(masks=masks) - ) - - -def refine_marks( - marks: sv.Detections, - maximum_hole_area: float = 0.01, - maximum_island_area: float = 0.01, - minimum_mask_area: float = 0.02, - maximum_mask_area: float = 1.0, -) -> sv.Detections: - """ - Refines a set of masks by removing small islands and holes, and filtering by mask - area. - - Parameters: - marks (sv.Detections): An object containing the masks and their bounding box - coordinates. - maximum_hole_area (float): The maximum relative area of holes to be filled in - each mask. - maximum_island_area (float): The maximum relative area of islands to be removed - from each mask. - minimum_mask_area (float): The minimum relative area for a mask to be retained. - maximum_mask_area (float): The maximum relative area for a mask to be retained. - - Returns: - sv.Detections: An object containing the masks and their bounding box - coordinates. - """ - result_masks = [] - for mask in marks.mask: - mask = adjust_mask_features_by_relative_area( - mask=mask, - area_threshold=maximum_island_area, - feature_type=FeatureType.ISLAND, - ) - mask = adjust_mask_features_by_relative_area( - mask=mask, - area_threshold=maximum_hole_area, - feature_type=FeatureType.HOLE, - ) - if np.any(mask): - result_masks.append(mask) - result_masks = np.array(result_masks) - result_masks = filter_masks_by_relative_area( - masks=result_masks, - minimum_area=minimum_mask_area, - maximum_area=maximum_mask_area, - ) - return sv.Detections( - mask=result_masks, xyxy=sv.mask_to_xyxy(masks=result_masks) - ) diff --git a/swarms/utils/token_count_tiktoken.py b/swarms/utils/token_count_tiktoken.py deleted file mode 100644 index f8a47b987..000000000 --- a/swarms/utils/token_count_tiktoken.py +++ /dev/null @@ -1,27 +0,0 @@ -import tiktoken - - -def limit_tokens_from_string( - string: str, model: str = "gpt-4", limit: int = 500 -) -> str: - """Limits the number of tokens in a string - - Args: - string (str): _description_ - model (str): _description_ - limit (int): _description_ - - Returns: - str: _description_ - """ - try: - encoding = tiktoken.encoding_for_model(model) - except Exception: - encoding = tiktoken.encoding_for_model( - "gpt2" - ) # Fallback for others. - - encoded = encoding.encode(string) - - out = encoding.decode(encoded[:limit]) - return out From 21c28fc7cbaa56ea71e06cf342e9d25538c793e2 Mon Sep 17 00:00:00 2001 From: Kye Date: Tue, 26 Mar 2024 11:13:38 -0700 Subject: [PATCH 09/12] [CLEANUP] --- swarms/agents/worker_agent.py | 1 + swarms/structs/base_workflow.py | 3 +++ swarms/utils/loguru_logger.py | 2 +- swarms/utils/main.py | 6 ------ 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/swarms/agents/worker_agent.py b/swarms/agents/worker_agent.py index d11ac9250..12760318a 100644 --- a/swarms/agents/worker_agent.py +++ b/swarms/agents/worker_agent.py @@ -4,6 +4,7 @@ # import faiss from langchain.docstore import InMemoryDocstore from langchain.embeddings import OpenAIEmbeddings + # from langchain.vectorstores import FAISS from langchain_experimental.autonomous_agents import AutoGPT diff --git a/swarms/structs/base_workflow.py b/swarms/structs/base_workflow.py index c06d93391..89ac99bf2 100644 --- a/swarms/structs/base_workflow.py +++ b/swarms/structs/base_workflow.py @@ -28,6 +28,9 @@ def __init__( **kwargs, ): super().__init__(*args, **kwargs) + self.agents = agents + self.task_pool = task_pool + self.models = models self.task_pool = [] self.agent_pool = [] diff --git a/swarms/utils/loguru_logger.py b/swarms/utils/loguru_logger.py index 1b0a42f40..818baa8d6 100644 --- a/swarms/utils/loguru_logger.py +++ b/swarms/utils/loguru_logger.py @@ -9,6 +9,7 @@ diagnose=True, ) + def loguru_logger(file_path: str = "swarms.log"): return logger.add( file_path, @@ -18,4 +19,3 @@ def loguru_logger(file_path: str = "swarms.log"): backtrace=True, diagnose=True, ) - diff --git a/swarms/utils/main.py b/swarms/utils/main.py index 2ca8f262c..ffb496d1b 100644 --- a/swarms/utils/main.py +++ b/swarms/utils/main.py @@ -97,10 +97,6 @@ def get_new_dataframe_name(org_img_name, func_name="update"): return os.path.join(head, new_file_name) - - - - STATIC_DIR = "static" @@ -114,8 +110,6 @@ def from_settings() -> "AbstractUploader": pass - - class FileType(Enum): IMAGE = "image" AUDIO = "audio" From c60c19591477ce255e0b872191a16f742f0bd1cb Mon Sep 17 00:00:00 2001 From: Kye Date: Tue, 26 Mar 2024 11:30:32 -0700 Subject: [PATCH 10/12] [CLEANUP] --- swarms/agents/omni_modal_agent.py | 3 ++- swarms/agents/worker_agent.py | 9 ++------- swarms/memory/chroma_db.py | 3 ++- swarms/memory/lanchain_chroma.py | 6 +++--- swarms/memory/pg.py | 7 ++++--- swarms/memory/qdrant.py | 3 ++- swarms/structs/auto_swarm.py | 5 ++++- 7 files changed, 19 insertions(+), 17 deletions(-) diff --git a/swarms/agents/omni_modal_agent.py b/swarms/agents/omni_modal_agent.py index 4af03906d..90d0ce28d 100644 --- a/swarms/agents/omni_modal_agent.py +++ b/swarms/agents/omni_modal_agent.py @@ -9,8 +9,9 @@ load_chat_planner, ) from transformers import load_tool -from swarms.utils.loguru_logger import logger + from swarms.structs.agent import Agent +from swarms.utils.loguru_logger import logger class OmniModalAgent(Agent): diff --git a/swarms/agents/worker_agent.py b/swarms/agents/worker_agent.py index 12760318a..f9ba2094f 100644 --- a/swarms/agents/worker_agent.py +++ b/swarms/agents/worker_agent.py @@ -1,18 +1,13 @@ -import os from typing import List -# import faiss -from langchain.docstore import InMemoryDocstore -from langchain.embeddings import OpenAIEmbeddings - -# from langchain.vectorstores import FAISS from langchain_experimental.autonomous_agents import AutoGPT +from swarms.structs.agent import Agent from swarms.tools.tool import BaseTool from swarms.utils.decorators import error_decorator, timing_decorator -class Worker: +class Worker(Agent): """ The Worker class represents an autonomous agent that can perform tassks through function calls or by running a chat. diff --git a/swarms/memory/chroma_db.py b/swarms/memory/chroma_db.py index 0ef34286d..033be6f61 100644 --- a/swarms/memory/chroma_db.py +++ b/swarms/memory/chroma_db.py @@ -9,13 +9,14 @@ from swarms.utils.data_to_text import data_to_text from swarms.utils.markdown_message import display_markdown_message +from swarms.memory.base_vectordb import AbstractVectorDatabase # Load environment variables load_dotenv() # Results storage using local ChromaDB -class ChromaDB: +class ChromaDB(AbstractVectorDatabase): """ ChromaDB database diff --git a/swarms/memory/lanchain_chroma.py b/swarms/memory/lanchain_chroma.py index 95a2e9e3c..cd5d832a9 100644 --- a/swarms/memory/lanchain_chroma.py +++ b/swarms/memory/lanchain_chroma.py @@ -6,8 +6,8 @@ from langchain.embeddings.openai import OpenAIEmbeddings from langchain.text_splitter import CharacterTextSplitter from langchain.vectorstores import Chroma - -from swarms.models.openai_models import OpenAIChat +from swarms.models.popular_llms import OpenAIChat +from swarms.memory.base_vectordb import AbstractVectorDatabase def synchronized_mem(method): @@ -31,7 +31,7 @@ def wrapper(self, *args, **kwargs): return wrapper -class LangchainChromaVectorMemory: +class LangchainChromaVectorMemory(AbstractVectorDatabase): """ A class representing a vector memory for storing and retrieving text entries. diff --git a/swarms/memory/pg.py b/swarms/memory/pg.py index b04beacf4..e0bc72d22 100644 --- a/swarms/memory/pg.py +++ b/swarms/memory/pg.py @@ -5,9 +5,10 @@ from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.ext.declarative import declarative_base from sqlalchemy.orm import Session +from swarms.memory.base_vectordb import AbstractVectorDatabase -class PostgresDB: +class PostgresDB(AbstractVectorDatabase): """ A class representing a Postgres database. @@ -67,7 +68,7 @@ class VectorModel(Base): return VectorModel - def add_or_update_vector( + def add( self, vector: str, vector_id: Optional[str] = None, @@ -97,7 +98,7 @@ def add_or_update_vector( except Exception as e: print(f"Error adding or updating vector: {e}") - def query_vectors( + def query( self, query: Any, namespace: Optional[str] = None ) -> List[Any]: """ diff --git a/swarms/memory/qdrant.py b/swarms/memory/qdrant.py index 0a553a16b..49c5ef621 100644 --- a/swarms/memory/qdrant.py +++ b/swarms/memory/qdrant.py @@ -1,6 +1,7 @@ from typing import List from httpx import RequestError +from swarms.memory.base_vectordb import AbstractVectorDatabase try: from sentence_transformers import SentenceTransformer @@ -20,7 +21,7 @@ print("pip install qdrant-client") -class Qdrant: +class Qdrant(AbstractVectorDatabase): """ Qdrant class for managing collections and performing vector operations using QdrantClient. diff --git a/swarms/structs/auto_swarm.py b/swarms/structs/auto_swarm.py index a0d0f05cf..f1c61f0f4 100644 --- a/swarms/structs/auto_swarm.py +++ b/swarms/structs/auto_swarm.py @@ -25,7 +25,6 @@ class SequentialAccountingSwarm(AbstractSwarm): Run the swarm simulation. """ - def __init__( self, name: Optional[str] = "kyegomez/sequential-accounting-swarm", @@ -35,6 +34,8 @@ def __init__( iters: Optional[int] = 100, max_agents: Optional[int] = 100, agents: Sequence[AbstractLLM] = None, + *args, + **kwargs, ): super().__init__() self.name = name @@ -98,6 +99,8 @@ def __init__( custom_preprocess: Optional[Callable] = None, custom_postprocess: Optional[Callable] = None, custom_router: Optional[Callable] = None, + *args, + **kwargs, ): super().__init__() self.name = name From 9e60244cf2758bc01213be687355f7969ff63e21 Mon Sep 17 00:00:00 2001 From: Kye Date: Tue, 26 Mar 2024 11:52:12 -0700 Subject: [PATCH 11/12] [CLEANUP] --- README.md | 353 +++++------------------------------------------------- 1 file changed, 33 insertions(+), 320 deletions(-) diff --git a/README.md b/README.md index 20ce32a33..c05210c24 100644 --- a/README.md +++ b/README.md @@ -868,356 +868,69 @@ agent = Agent( agent.run(task=task, img=img) ``` ---- - -## Multi-Modal Model APIs - -### `Gemini` -- Deploy Gemini from Google with utmost reliability with our visual chain of thought prompt that enables more reliable responses -```python -import os - -from dotenv import load_dotenv - -from swarms import Gemini -from swarms.prompts.visual_cot import VISUAL_CHAIN_OF_THOUGHT - -# Load the environment variables -load_dotenv() - -# Get the API key from the environment -api_key = os.environ.get("GEMINI_API_KEY") - -# Initialize the language model -llm = Gemini( - gemini_api_key=api_key, - temperature=0.5, - max_tokens=1000, - system_prompt=VISUAL_CHAIN_OF_THOUGHT, -) - -# Initialize the task -task = "This is an eye test. What do you see?" -img = "playground/demos/multi_modal_chain_of_thought/eyetest.jpg" - -# Run the workflow on a task -out = llm.run(task=task, img=img) -print(out) -``` - -### `GPT4Vision` +### Swarms Compliant Model Interface ```python -from swarms import GPT4VisionAPI - -# Initialize with default API key and custom max_tokens -api = GPT4VisionAPI(max_tokens=1000) +from swarms import AbstractLLM -# Define the task and image URL -task = "Describe the scene in the image." -img = "https://i.imgur.com/4P4ZRxU.jpeg" +class vLLMLM(AbstractLLM): + def __init__(self, model_name='default_model', tensor_parallel_size=1, *args, **kwargs): + super().__init__(*args, **kwargs) + self.model_name = model_name + self.tensor_parallel_size = tensor_parallel_size + # Add any additional initialization here + + def run(self, task: str): + pass -# Run the GPT-4 Vision model -response = api.run(task, img) - -# Print the model's response -print(response) -``` - -### `QwenVLMultiModal` -A radically simple interface for QwenVLMultiModal comes complete with Quantization to turn it on just set quantize to true! - -```python -from swarms import QwenVLMultiModal - -# Instantiate the QwenVLMultiModal model -model = QwenVLMultiModal( - model_name="Qwen/Qwen-VL-Chat", - device="cuda", - quantize=True, -) +# Example +model = vLLMLM("mistral") # Run the model -response = model("Hello, how are you?", "https://example.com/image.jpg") - -# Print the response -print(response) -``` - - -### `Kosmos` -- Multi-Modal Model from microsoft! - -```python -from swarms import Kosmos - -# Initialize the model -model = Kosmos() - -# Generate -out = model.run("Analyze the reciepts in this image", "docs.jpg") - -# Print the output +out = model("Analyze these financial documents and summarize of them") print(out) -``` - - -### `Idefics` -- Multi-Modal model from Huggingface team! - -```python -# Import the idefics model from the swarms.models module -from swarms.models import Idefics - -# Create an instance of the idefics model -model = Idefics() - -# Define user input with an image URL and chat with the model -user_input = ( - "User: What is in this image?" - " https://upload.wikimedia.org/wikipedia/commons/8/86/Id%C3%A9fix.JPG" -) -response = model.chat(user_input) -print(response) - -# Define another user input with an image URL and chat with the model -user_input = ( - "User: And who is that?" - " https://static.wikia.nocookie.net/asterix/images/2/25/R22b.gif/revision/latest?cb=20110815073052" -) -response = model.chat(user_input) -print(response) - -# Set the checkpoint of the model to "new_checkpoint" -model.set_checkpoint("new_checkpoint") - -# Set the device of the model to "cpu" -model.set_device("cpu") - -# Set the maximum length of the chat to 200 -model.set_max_length(200) - -# Clear the chat history of the model -model.clear_chat_history() -``` - -## Radically Simple AI Model APIs -We provide a vast array of language and multi-modal model APIs for you to generate text, images, music, speech, and even videos. Get started below: - - ------ - - -### `Anthropic` -```python -# Import necessary modules and classes -from swarms.models import Anthropic - -# Initialize an instance of the Anthropic class -model = Anthropic(anthropic_api_key="") - -# Using the run method -completion_1 = model.run("What is the capital of France?") -print(completion_1) - -# Using the __call__ method -completion_2 = model("How far is the moon from the earth?", stop=["miles", "km"]) -print(completion_2) -``` - - -### `HuggingFaceLLM` -```python -from swarms.models import HuggingfaceLLM - -# Initialize with custom configuration -custom_config = { - "quantize": True, - "quantization_config": {"load_in_4bit": True}, - "verbose": True, -} -inference = HuggingfaceLLM( - model_id="NousResearch/Nous-Hermes-2-Vision-Alpha", **custom_config -) - -# Generate text based on a prompt -prompt_text = ( - "Create a list of known biggest risks of structural collapse with references" -) -generated_text = inference(prompt_text) -print(generated_text) ``` -### `Mixtral` -- Utilize Mixtral in a very simple API, -- Utilize 4bit quantization for a increased speed and less memory usage -- Use Flash Attention 2.0 for increased speed and less memory usage -```python -from swarms.models import Mixtral - -# Initialize the Mixtral model with 4 bit and flash attention! -mixtral = Mixtral(load_in_4bit=True, use_flash_attention_2=True) - -# Generate text for a simple task -generated_text = mixtral.run("Generate a creative story.") - -# Print the generated text -print(generated_text) -``` +### Swarms Compliant Agent Interface -### `Dalle3` ```python -from swarms import Dalle3 +from swarms import Agent -# 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" +class MyCustomAgent(Agent): -# Generate a high-quality image from the text prompt -image_url = dalle3(task) +    def __init__(self, *args, **kwargs): -# Print the generated image URL -print(image_url) -``` +        super().__init__(*args, **kwargs) +        # Custom initialization logic +    def custom_method(self, *args, **kwargs): +        # Implement custom logic here -### Text to Video with `ZeroscopeTTV` +        pass -```python -# Import the model -from swarms import ZeroscopeTTV +    def run(self, task, *args, **kwargs): -# Initialize the model -zeroscope = ZeroscopeTTV() +        # Customize the run method -# Specify the task -task = "A person is walking on the street." +        response = super().run(task, *args, **kwargs) -# Generate the video! -video_path = zeroscope(task) -print(video_path) -``` +        # Additional custom logic +        return response` - - - - ----- - -## Supported Models ✅ -Swarms supports various model providers like OpenAI, Huggingface, Anthropic, Google, Mistral and many more. - -| Provider | Provided ✅ | Module Name | -|----------|-----------------------------|-------------| -| OpenAI | ✅ | OpenAIChat, OpenAITTS, GPT4VisionAPI, Dalle3 | -| Anthropic | ✅ | Anthropic | -| Mistral | ✅ | Mistral, Mixtral | -| Gemini/Palm | ✅ | Gemini | -| Huggingface | ✅ | HuggingFaceLLM | -| Modelscope | ✅ | Modelscope | -| Vllm | ✅ | vLLM | - - ---- - -# Features 🤖 -The Swarms framework is designed with a strong emphasis on reliability, performance, and production-grade readiness. -Below are the key features that make Swarms an ideal choice for enterprise-level AI deployments. - -## 🚀 Production-Grade Readiness -- **Scalable Architecture**: Built to scale effortlessly with your growing business needs. -- **Enterprise-Level Security**: Incorporates top-notch security features to safeguard your data and operations. -- **Containerization and Microservices**: Easily deployable in containerized environments, supporting microservices architecture. - -## ⚙️ Reliability and Robustness -- **Fault Tolerance**: Designed to handle failures gracefully, ensuring uninterrupted operations. -- **Consistent Performance**: Maintains high performance even under heavy loads or complex computational demands. -- **Automated Backup and Recovery**: Features automatic backup and recovery processes, reducing the risk of data loss. - -## 💡 Advanced AI Capabilities - -The Swarms framework is equipped with a suite of advanced AI capabilities designed to cater to a wide range of applications and scenarios, ensuring versatility and cutting-edge performance. - -### Multi-Modal Autonomous Agents -- **Versatile Model Support**: Seamlessly works with various AI models, including NLP, computer vision, and more, for comprehensive multi-modal capabilities. -- **Context-Aware Processing**: Employs context-aware processing techniques to ensure relevant and accurate responses from agents. - -### Function Calling Models for API Execution -- **Automated API Interactions**: Function calling models that can autonomously execute API calls, enabling seamless integration with external services and data sources. -- **Dynamic Response Handling**: Capable of processing and adapting to responses from APIs for real-time decision making. - -### Varied Architectures of Swarms -- **Flexible Configuration**: Supports multiple swarm architectures, from centralized to decentralized, for diverse application needs. -- **Customizable Agent Roles**: Allows customization of agent roles and behaviors within the swarm to optimize performance and efficiency. - -### Generative Models -- **Advanced Generative Capabilities**: Incorporates state-of-the-art generative models to create content, simulate scenarios, or predict outcomes. -- **Creative Problem Solving**: Utilizes generative AI for innovative problem-solving approaches and idea generation. - -### Enhanced Decision-Making -- **AI-Powered Decision Algorithms**: Employs advanced algorithms for swift and effective decision-making in complex scenarios. -- **Risk Assessment and Management**: Capable of assessing risks and managing uncertain situations with AI-driven insights. - -### Real-Time Adaptation and Learning -- **Continuous Learning**: Agents can continuously learn and adapt from new data, improving their performance and accuracy over time. -- **Environment Adaptability**: Designed to adapt to different operational environments, enhancing robustness and reliability. - - -## 🔄 Efficient Workflow Automation -- **Streamlined Task Management**: Simplifies complex tasks with automated workflows, reducing manual intervention. -- **Customizable Workflows**: Offers customizable workflow options to fit specific business needs and requirements. -- **Real-Time Analytics and Reporting**: Provides real-time insights into agent performance and system health. - -## 🌐 Wide-Ranging Integration -- **API-First Design**: Easily integrates with existing systems and third-party applications via robust APIs. -- **Cloud Compatibility**: Fully compatible with major cloud platforms for flexible deployment options. -- **Continuous Integration/Continuous Deployment (CI/CD)**: Supports CI/CD practices for seamless updates and deployment. - -## 📊 Performance Optimization -- **Resource Management**: Efficiently manages computational resources for optimal performance. -- **Load Balancing**: Automatically balances workloads to maintain system stability and responsiveness. -- **Performance Monitoring Tools**: Includes comprehensive monitoring tools for tracking and optimizing performance. - -## 🛡️ Security and Compliance -- **Data Encryption**: Implements end-to-end encryption for data at rest and in transit. -- **Compliance Standards Adherence**: Adheres to major compliance standards ensuring legal and ethical usage. -- **Regular Security Updates**: Regular updates to address emerging security threats and vulnerabilities. - -## 💬 Community and Support -- **Extensive Documentation**: Detailed documentation for easy implementation and troubleshooting. -- **Active Developer Community**: A vibrant community for sharing ideas, solutions, and best practices. -- **Professional Support**: Access to professional support for enterprise-level assistance and guidance. - -Swarms framework is not just a tool but a robust, scalable, and secure partner in your AI journey, ready to tackle the challenges of modern AI applications in a business environment. - --- ## Documentation From 1e55cfeebe70b80649884b4fdf28919f2f2f1855 Mon Sep 17 00:00:00 2001 From: Kye Date: Wed, 27 Mar 2024 12:10:04 -0700 Subject: [PATCH 12/12] [CLEANUP] --- LICENSE | 542 +++++++++++++++++++----------- README.md | 28 +- docs/corporate/monthly_formula.py | 66 ++++ swarms/structs/auto_swarm.py | 1 + swarms/structs/rearrange.py | 0 5 files changed, 425 insertions(+), 212 deletions(-) create mode 100644 docs/corporate/monthly_formula.py create mode 100644 swarms/structs/rearrange.py diff --git a/LICENSE b/LICENSE index 2e8c7e27c..994483617 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,341 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [2023] [Kye Gomez] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + + +# Creative Commons Attribution 4.0 International Public License + +By exercising the Licensed Rights (defined below), You accept and agree +to be bound by the terms and conditions of this Creative Commons +Attribution 4.0 International Public License ("Public License"). To the +extent this Public License may be interpreted as a contract, You are +granted the Licensed Rights in consideration of Your acceptance of +these terms and conditions, and the Licensor grants You such rights in +consideration of benefits the Licensor receives from making the +Licensed Material available under these terms and conditions. + + +Section 1 -- Definitions. + + a. Adapted Material means material subject to Copyright and Similar + Rights that is derived from or based upon the Licensed Material + and in which the Licensed Material is translated, altered, + arranged, transformed, or otherwise modified in a manner requiring + permission under the Copyright and Similar Rights held by the + Licensor. For purposes of this Public License, where the Licensed + Material is a musical work, performance, or sound recording, + Adapted Material is always produced where the Licensed Material is + synched in timed relation with a moving image. + + b. Adapter's License means the license You apply to Your Copyright + and Similar Rights in Your contributions to Adapted Material in + accordance with the terms and conditions of this Public License. + + c. Copyright and Similar Rights means copyright and/or similar rights + closely related to copyright including, without limitation, + performance, broadcast, sound recording, and Sui Generis Database + Rights, without regard to how the rights are labeled or + categorized. For purposes of this Public License, the rights + specified in Section 2(b)(1)-(2) are not Copyright and Similar + Rights. + + d. Effective Technological Measures means those measures that, in the + absence of proper authority, may not be circumvented under laws + fulfilling obligations under Article 11 of the WIPO Copyright + Treaty adopted on December 20, 1996, and/or similar international + agreements. + + e. Exceptions and Limitations means fair use, fair dealing, and/or + any other exception or limitation to Copyright and Similar Rights + that applies to Your use of the Licensed Material. + + f. Licensed Material means the artistic or literary work, database, + or other material to which the Licensor applied this Public + License. + + g. Licensed Rights means the rights granted to You subject to the + terms and conditions of this Public License, which are limited to + all Copyright and Similar Rights that apply to Your use of the + Licensed Material and that the Licensor has authority to license. + + h. Licensor means the individual(s) or entity(ies) granting rights + under this Public License. + + i. Share means to provide material to the public by any means or + process that requires permission under the Licensed Rights, such + as reproduction, public display, public performance, distribution, + dissemination, communication, or importation, and to make material + available to the public including in ways that members of the + public may access the material from a place and at a time + individually chosen by them. + + j. Sui Generis Database Rights means rights other than copyright + resulting from Directive 96/9/EC of the European Parliament and of + the Council of 11 March 1996 on the legal protection of databases, + as amended and/or succeeded, as well as other essentially + equivalent rights anywhere in the world. + + k. You means the individual or entity exercising the Licensed Rights + under this Public License. Your has a corresponding meaning. + + +Section 2 -- Scope. + + a. License grant. + + 1. Subject to the terms and conditions of this Public License, + the Licensor hereby grants You a worldwide, royalty-free, + non-sublicensable, non-exclusive, irrevocable license to + exercise the Licensed Rights in the Licensed Material to: + + a. reproduce and Share the Licensed Material, in whole or + in part; and + + b. produce, reproduce, and Share Adapted Material. + + 2. Exceptions and Limitations. For the avoidance of doubt, where + Exceptions and Limitations apply to Your use, this Public + License does not apply, and You do not need to comply with + its terms and conditions. + + 3. Term. The term of this Public License is specified in Section + 6(a). + + 4. Media and formats; technical modifications allowed. The + Licensor authorizes You to exercise the Licensed Rights in + all media and formats whether now known or hereafter created, + and to make technical modifications necessary to do so. The + Licensor waives and/or agrees not to assert any right or + authority to forbid You from making technical modifications + necessary to exercise the Licensed Rights, including + technical modifications necessary to circumvent Effective + Technological Measures. For purposes of this Public License, + simply making modifications authorized by this Section 2(a) + (4) never produces Adapted Material. + + 5. Downstream recipients. + + a. Offer from the Licensor -- Licensed Material. Every + recipient of the Licensed Material automatically + receives an offer from the Licensor to exercise the + Licensed Rights under the terms and conditions of this + Public License. + + b. No downstream restrictions. You may not offer or impose + any additional or different terms or conditions on, or + apply any Effective Technological Measures to, the + Licensed Material if doing so restricts exercise of the + Licensed Rights by any recipient of the Licensed + Material. + + 6. No endorsement. Nothing in this Public License constitutes or + may be construed as permission to assert or imply that You + are, or that Your use of the Licensed Material is, connected + with, or sponsored, endorsed, or granted official status by, + the Licensor or others designated to receive attribution as + provided in Section 3(a)(1)(A)(i). + + b. Other rights. + + 1. Moral rights, such as the right of integrity, are not + licensed under this Public License, nor are publicity, + privacy, and/or other similar personality rights; however, to + the extent possible, the Licensor waives and/or agrees not to + assert any such rights held by the Licensor to the limited + extent necessary to allow You to exercise the Licensed + Rights, but not otherwise. + + 2. Patent and trademark rights are not licensed under this + Public License. + + 3. To the extent possible, the Licensor waives any right to + collect royalties from You for the exercise of the Licensed + Rights, whether directly or through a collecting society + under any voluntary or waivable statutory or compulsory + licensing scheme. In all other cases the Licensor expressly + reserves any right to collect such royalties. + + +Section 3 -- License Conditions. + +Your exercise of the Licensed Rights is expressly made subject to the +following conditions. + + a. Attribution. + + 1. If You Share the Licensed Material (including in modified + form), You must: + + a. retain the following if it is supplied by the Licensor + with the Licensed Material: + + i. identification of the creator(s) of the Licensed + Material and any others designated to receive + attribution, in any reasonable manner requested by + the Licensor (including by pseudonym if + designated); + + ii. a copyright notice; + + iii. a notice that refers to this Public License; + + iv. a notice that refers to the disclaimer of + warranties; + + v. a URI or hyperlink to the Licensed Material to the + extent reasonably practicable; + + b. indicate if You modified the Licensed Material and + retain an indication of any previous modifications; and + + c. indicate the Licensed Material is licensed under this + Public License, and include the text of, or the URI or + hyperlink to, this Public License. + + 2. You may satisfy the conditions in Section 3(a)(1) in any + reasonable manner based on the medium, means, and context in + which You Share the Licensed Material. For example, it may be + reasonable to satisfy the conditions by providing a URI or + hyperlink to a resource that includes the required + information. + + 3. If requested by the Licensor, You must remove any of the + information required by Section 3(a)(1)(A) to the extent + reasonably practicable. + + 4. If You Share Adapted Material You produce, the Adapter's + License You apply must not prevent recipients of the Adapted + Material from complying with this Public License. + + +Section 4 -- Sui Generis Database Rights. + +Where the Licensed Rights include Sui Generis Database Rights that +apply to Your use of the Licensed Material: + + a. for the avoidance of doubt, Section 2(a)(1) grants You the right + to extract, reuse, reproduce, and Share all or a substantial + portion of the contents of the database; + + b. if You include all or a substantial portion of the database + contents in a database in which You have Sui Generis Database + Rights, then the database in which You have Sui Generis Database + Rights (but not its individual contents) is Adapted Material; and + + c. You must comply with the conditions in Section 3(a) if You Share + all or a substantial portion of the contents of the database. + +For the avoidance of doubt, this Section 4 supplements and does not +replace Your obligations under this Public License where the Licensed +Rights include other Copyright and Similar Rights. + + +Section 5 -- Disclaimer of Warranties and Limitation of Liability. + + a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE + EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS + AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF + ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, + IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, + WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR + PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, + ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT + KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT + ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. + + b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE + TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, + NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, + INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, + COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR + USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN + ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR + DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR + IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. + + c. The disclaimer of warranties and limitation of liability provided + above shall be interpreted in a manner that, to the extent + possible, most closely approximates an absolute disclaimer and + waiver of all liability. + + +Section 6 -- Term and Termination. + + a. This Public License applies for the term of the Copyright and + Similar Rights licensed here. However, if You fail to comply with + this Public License, then Your rights under this Public License + terminate automatically. + + b. Where Your right to use the Licensed Material has terminated under + Section 6(a), it reinstates: + + 1. automatically as of the date the violation is cured, provided + it is cured within 30 days of Your discovery of the + violation; or + + 2. upon express reinstatement by the Licensor. + + For the avoidance of doubt, this Section 6(b) does not affect any + right the Licensor may have to seek remedies for Your violations + of this Public License. + + c. For the avoidance of doubt, the Licensor may also offer the + Licensed Material under separate terms or conditions or stop + distributing the Licensed Material at any time; however, doing so + will not terminate this Public License. + + d. Sections 1, 5, 6, 7, and 8 survive termination of this Public + License. + + +Section 7 -- Other Terms and Conditions. + + a. The Licensor shall not be bound by any additional or different + terms or conditions communicated by You unless expressly agreed. + + b. Any arrangements, understandings, or agreements regarding the + Licensed Material not stated herein are separate from and + independent of the terms and conditions of this Public License. + + +Section 8 -- Interpretation. + + a. For the avoidance of doubt, this Public License does not, and + shall not be interpreted to, reduce, limit, restrict, or impose + conditions on any use of the Licensed Material that could lawfully + be made without permission under this Public License. + + b. To the extent possible, if any provision of this Public License is + deemed unenforceable, it shall be automatically reformed to the + minimum extent necessary to make it enforceable. If the provision + cannot be reformed, it shall be severed from this Public License + without affecting the enforceability of the remaining terms and + conditions. + + c. No term or condition of this Public License will be waived and no + failure to comply consented to unless expressly agreed to by the + Licensor. + + d. Nothing in this Public License constitutes or may be interpreted + as a limitation upon, or waiver of, any privileges and immunities + that apply to the Licensor or You, including from the legal + processes of any jurisdiction or authority. + + +======================================================================= + +Creative Commons is not a party to its public +licenses. Notwithstanding, Creative Commons may elect to apply one of +its public licenses to material it publishes and in those instances +will be considered the “Licensor.” The text of the Creative Commons +public licenses is dedicated to the public domain under the CC0 Public +Domain Dedication. Except for the limited purpose of indicating that +material is shared under a Creative Commons public license or as +otherwise permitted by the Creative Commons policies published at +creativecommons.org/policies, Creative Commons does not authorize the +use of the trademark "Creative Commons" or any other trademark or logo +of Creative Commons without its prior written consent including, +without limitation, in connection with any unauthorized modifications +to any of its public licenses or any other arrangements, +understandings, or agreements concerning use of licensed material. For +the avoidance of doubt, this paragraph does not form part of the +public licenses. + +Creative Commons may be contacted at creativecommons.org. \ No newline at end of file diff --git a/README.md b/README.md index c05210c24..7abcc3a11 100644 --- a/README.md +++ b/README.md @@ -582,8 +582,7 @@ import os from dotenv import load_dotenv from transformers import AutoModelForCausalLM, AutoTokenizer - -# Import the models, structs, and telemetry modules +from pydantic import BaseModel from swarms import BlocksList, Gemini, GPT4VisionAPI, Mixtral, OpenAI, ToolAgent # Load the environment variables @@ -596,15 +595,22 @@ 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"}}, - }, -} + +# Initialize the schema for the person's information +class Schema(BaseModel): + name: str = Field(..., title="Name of the person") + agent: int = Field(..., title="Age of the person") + is_student: bool = Field( + ..., title="Whether the person is a student" + ) + courses: list[str] = Field( + ..., title="List of courses the person is taking" + ) + +# Convert the schema to a JSON string +json_schema = base_model_to_json(Schema) + + toolagent = ToolAgent(model=model, tokenizer=tokenizer, json_schema=json_schema) # Blocks List which enables you to build custom swarms by adding classes or functions diff --git a/docs/corporate/monthly_formula.py b/docs/corporate/monthly_formula.py new file mode 100644 index 000000000..15eafbb59 --- /dev/null +++ b/docs/corporate/monthly_formula.py @@ -0,0 +1,66 @@ +def calculate_monthly_charge( + development_time_hours: float, + hourly_rate: float, + amortization_months: int, + api_calls_per_month: int, + cost_per_api_call: float, + monthly_maintenance: float, + additional_monthly_costs: float, + profit_margin_percentage: float, +) -> float: + """ + Calculate the monthly charge for a service based on various cost factors. + + Parameters: + - development_time_hours (float): The total number of hours spent on development and setup. + - hourly_rate (float): The rate per hour for development and setup. + - amortization_months (int): The number of months over which to amortize the development and setup costs. + - api_calls_per_month (int): The number of API calls made per month. + - cost_per_api_call (float): The cost per API call. + - monthly_maintenance (float): The monthly maintenance cost. + - additional_monthly_costs (float): Any additional monthly costs. + - profit_margin_percentage (float): The desired profit margin as a percentage. + + Returns: + - monthly_charge (float): The calculated monthly charge for the service. + """ + + # Calculate Development and Setup Costs (amortized monthly) + development_and_setup_costs_monthly = ( + development_time_hours * hourly_rate + ) / amortization_months + + # Calculate Operational Costs per Month + operational_costs_monthly = ( + (api_calls_per_month * cost_per_api_call) + + monthly_maintenance + + additional_monthly_costs + ) + + # Calculate Total Monthly Costs + total_monthly_costs = ( + development_and_setup_costs_monthly + + operational_costs_monthly + ) + + # Calculate Pricing with Profit Margin + monthly_charge = total_monthly_costs * ( + 1 + profit_margin_percentage / 100 + ) + + return monthly_charge + + +# Example usage: +monthly_charge = calculate_monthly_charge( + development_time_hours=100, + hourly_rate=500, + amortization_months=12, + api_calls_per_month=500000, + cost_per_api_call=0.002, + monthly_maintenance=1000, + additional_monthly_costs=300, + profit_margin_percentage=10000, +) + +print(f"Monthly Charge: ${monthly_charge:.2f}") diff --git a/swarms/structs/auto_swarm.py b/swarms/structs/auto_swarm.py index f1c61f0f4..97aaf7e48 100644 --- a/swarms/structs/auto_swarm.py +++ b/swarms/structs/auto_swarm.py @@ -25,6 +25,7 @@ class SequentialAccountingSwarm(AbstractSwarm): Run the swarm simulation. """ + def __init__( self, name: Optional[str] = "kyegomez/sequential-accounting-swarm", diff --git a/swarms/structs/rearrange.py b/swarms/structs/rearrange.py new file mode 100644 index 000000000..e69de29bb