From cb76ed12136d276e6a9234283c4a5cf38f03ca96 Mon Sep 17 00:00:00 2001 From: Eternal Reclaimer <98760976+kyegomez@users.noreply.github.com> Date: Thu, 22 Feb 2024 12:35:02 -0800 Subject: [PATCH 1/9] Delete Cargo.lock --- Cargo.lock | 323 ----------------------------------------------------- 1 file changed, 323 deletions(-) delete mode 100644 Cargo.lock diff --git a/Cargo.lock b/Cargo.lock deleted file mode 100644 index 1b078be7..00000000 --- a/Cargo.lock +++ /dev/null @@ -1,323 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "crossbeam-deque" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-utils" -version = "0.8.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" - -[[package]] -name = "either" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11157ac094ffbdde99aa67b23417ebdd801842852b500e395a45a9c0aac03e4a" - -[[package]] -name = "engine" -version = "0.1.0" -dependencies = [ - "log", - "pyo3", - "rayon", -] - -[[package]] -name = "indoc" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47741a8bc60fb26eb8d6e0238bbb26d8575ff623fdc97b1a2c00c050b9684ed8" -dependencies = [ - "indoc-impl", - "proc-macro-hack", -] - -[[package]] -name = "indoc-impl" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce046d161f000fffde5f432a0d034d0341dc152643b2598ed5bfce44c4f3a8f0" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "syn", - "unindent", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "libc" -version = "0.2.153" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" - -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "once_cell" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall", - "smallvec", - "winapi", -] - -[[package]] -name = "paste" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45ca20c77d80be666aef2b45486da86238fabe33e38306bd3118fe4af33fa880" -dependencies = [ - "paste-impl", - "proc-macro-hack", -] - -[[package]] -name = "paste-impl" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d95a7db200b97ef370c8e6de0088252f7e0dfff7d047a28528e47456c0fc98b6" -dependencies = [ - "proc-macro-hack", -] - -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - -[[package]] -name = "proc-macro2" -version = "1.0.78" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "pyo3" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d41d50a7271e08c7c8a54cd24af5d62f73ee3a6f6a314215281ebdec421d5752" -dependencies = [ - "cfg-if", - "indoc", - "libc", - "parking_lot", - "paste", - "pyo3-build-config", - "pyo3-macros", - "unindent", -] - -[[package]] -name = "pyo3-build-config" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "779239fc40b8e18bc8416d3a37d280ca9b9fb04bda54b98037bb6748595c2410" -dependencies = [ - "once_cell", -] - -[[package]] -name = "pyo3-macros" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b247e8c664be87998d8628e86f282c25066165f1f8dda66100c48202fdb93a" -dependencies = [ - "pyo3-macros-backend", - "quote", - "syn", -] - -[[package]] -name = "pyo3-macros-backend" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a8c2812c412e00e641d99eeb79dd478317d981d938aa60325dfa7157b607095" -dependencies = [ - "proc-macro2", - "pyo3-build-config", - "quote", - "syn", -] - -[[package]] -name = "quote" -version = "1.0.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rayon" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags", -] - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "smallvec" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unindent" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1766d682d402817b5ac4490b3c3002d91dfa0d22812f341609f97b08757359c" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" From afc8df570a6067abb619ab2b0bd971ba177dea51 Mon Sep 17 00:00:00 2001 From: Eternal Reclaimer <98760976+kyegomez@users.noreply.github.com> Date: Fri, 23 Feb 2024 00:06:54 -0800 Subject: [PATCH 2/9] Update example.py --- example.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example.py b/example.py index d9ba8f1c..f2b11586 100644 --- a/example.py +++ b/example.py @@ -12,5 +12,5 @@ agent = Agent( # Run the workflow on a task agent( - "Find a chick fil a equivalent in san francisco in hayes valley" + "Find a chick fil a equivalent in hayes valley" ) From c82f4f9b5a775a7dbb749ce2dcf92c0df59af980 Mon Sep 17 00:00:00 2001 From: Kye Date: Fri, 23 Feb 2024 10:57:26 -0800 Subject: [PATCH 3/9] [FEAT][Improved Logging] --- example.py | 3 +- playground/structs/majority_voting.py | 6 +- pyproject.toml | 3 +- requirements.txt | 1 + swarms/models/__init__.py | 51 +-- swarms/models/cog_vlm.py | 530 ++++++++++++++++++++++ swarms/models/fire_function.py | 87 ++++ swarms/models/test_fire_function.py | 41 ++ swarms/structs/agent.py | 307 ++++++++----- swarms/structs/long_swarm.py | 34 +- swarms/structs/majority_voting.py | 43 +- swarms/utils/code_interpreter.py | 8 +- swarms/utils/disable_logging.py | 9 +- tests/models/test_fire_function_caller.py | 45 ++ 14 files changed, 985 insertions(+), 183 deletions(-) create mode 100644 swarms/models/cog_vlm.py create mode 100644 swarms/models/fire_function.py create mode 100644 swarms/models/test_fire_function.py create mode 100644 tests/models/test_fire_function_caller.py diff --git a/example.py b/example.py index f2b11586..1a83d232 100644 --- a/example.py +++ b/example.py @@ -3,7 +3,7 @@ from swarms import Agent, OpenAIChat ## Initialize the workflow agent = Agent( llm=OpenAIChat(), - max_loops="auto", + max_loops=1, autosave=True, dashboard=False, streaming_on=True, @@ -14,3 +14,4 @@ agent = Agent( agent( "Find a chick fil a equivalent in hayes valley" ) + diff --git a/playground/structs/majority_voting.py b/playground/structs/majority_voting.py index cd8de04a..149fd587 100644 --- a/playground/structs/majority_voting.py +++ b/playground/structs/majority_voting.py @@ -4,9 +4,9 @@ from swarms import Agent, MajorityVoting, OpenAIChat llm = OpenAIChat() # Initialize the agents -agent1 = Agent(llm=llm, max_loops=1) -agent2 = Agent(llm=llm, max_loops=1) -agent3 = Agent(llm=llm, max_loops=1) +agent1 = Agent(agent_name="worker-1", llm=llm, max_loops=1) +agent2 = Agent(agent_name="worker-2", llm=llm, max_loops=1) +agent3 = Agent(agent_name="worker3", llm=llm, max_loops=1) # Initialize the majority voting diff --git a/pyproject.toml b/pyproject.toml index 7e05820b..3247cc64 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "swarms" -version = "4.1.6" +version = "4.1.7" description = "Swarms - Pytorch" license = "MIT" authors = ["Kye Gomez "] @@ -51,6 +51,7 @@ httpx = "0.24.1" tiktoken = "0.4.0" attrs = "22.2.0" ratelimit = "2.2.1" +loguru = "*" cohere = "4.24" huggingface-hub = "*" pydantic = "1.10.12" diff --git a/requirements.txt b/requirements.txt index e582fa25..25fffdfb 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,6 +16,7 @@ sentencepiece==0.1.98 requests_mock pypdf==4.0.1 accelerate==0.22.0 +loguru chromadb tensorflow optimum diff --git a/swarms/models/__init__.py b/swarms/models/__init__.py index 0826e245..8981f70e 100644 --- a/swarms/models/__init__.py +++ b/swarms/models/__init__.py @@ -1,16 +1,23 @@ -############################################ LLMs from swarms.models.anthropic import Anthropic # noqa: E402 - -# 3############ Embedding models from swarms.models.base_embedding_model import BaseEmbeddingModel from swarms.models.base_llm import AbstractLLM # noqa: E402 - -################# MultiModal Models from swarms.models.base_multimodal_model import ( BaseMultiModalModel, -) # noqa: E402 +) + +# noqa: E402 from swarms.models.biogpt import BioGPT # noqa: E402 from swarms.models.clipq import CLIPQ # noqa: E402 + +# from swarms.models.dalle3 import Dalle3 +# from swarms.models.distilled_whisperx import DistilWhisperModel # noqa: E402 +# from swarms.models.whisperx_model import WhisperX # noqa: E402 +# from swarms.models.kosmos_two import Kosmos # noqa: E402 +# from swarms.models.cog_agent import CogAgent # noqa: E402 +## Function calling models +from swarms.models.fire_function import ( + FireFunctionCaller, +) from swarms.models.fuyu import Fuyu # noqa: E402 from swarms.models.gemini import Gemini # noqa: E402 from swarms.models.gigabind import Gigabind # noqa: E402 @@ -20,9 +27,9 @@ from swarms.models.idefics import Idefics # noqa: E402 from swarms.models.kosmos_two import Kosmos # noqa: E402 from swarms.models.layoutlm_document_qa import ( LayoutLMDocumentQA, -) # noqa: E402 +) -# from swarms.models.vip_llava import VipLlavaMultiModal # noqa: E402 +# noqa: E402 from swarms.models.llava import LavaMultiModal # noqa: E402 from swarms.models.mistral import Mistral # noqa: E402 from swarms.models.mixtral import Mixtral # noqa: E402 @@ -32,18 +39,18 @@ from swarms.models.openai_models import ( AzureOpenAI, OpenAI, OpenAIChat, -) # noqa: E402 +) + +# noqa: E402 from swarms.models.openai_tts import OpenAITTS # noqa: E402 from swarms.models.petals import Petals # noqa: E402 from swarms.models.qwen import QwenVLMultiModal # noqa: E402 from swarms.models.roboflow_model import RoboflowMultiModal from swarms.models.sam_supervision import SegmentAnythingMarkGenerator - -##### Utils from swarms.models.sampling_params import ( SamplingParams, SamplingType, -) # noqa: E402 +) from swarms.models.timm import TimmModel # noqa: E402 # from swarms.models.modelscope_pipeline import ModelScopePipeline @@ -62,26 +69,19 @@ from swarms.models.types import ( # noqa: E402 ) from swarms.models.ultralytics_model import ( UltralyticsModel, -) # noqa: E402 +) + +# noqa: E402 from swarms.models.vilt import Vilt # noqa: E402 from swarms.models.wizard_storytelling import ( WizardLLMStoryTeller, -) # noqa: E402 +) +# noqa: E402 # from swarms.models.vllm import vLLM # noqa: E402 from swarms.models.zephyr import Zephyr # noqa: E402 from swarms.models.zeroscope import ZeroscopeTTV # noqa: E402 -# from swarms.models.dalle3 import Dalle3 -# from swarms.models.distilled_whisperx import DistilWhisperModel # noqa: E402 -# from swarms.models.whisperx_model import WhisperX # noqa: E402 -# from swarms.models.kosmos_two import Kosmos # noqa: E402 -# from swarms.models.cog_agent import CogAgent # noqa: E402 - - -################# Tokenizers - - __all__ = [ "AbstractLLM", "Anthropic", @@ -100,7 +100,6 @@ __all__ = [ "HuggingfaceLLM", "MPT7B", "WizardLLMStoryTeller", - # "GPT4Vision", # "Dalle3", # "DistilWhisperModel", "GPT4VisionAPI", @@ -118,7 +117,6 @@ __all__ = [ "TogetherLLM", "TimmModel", "UltralyticsModel", - # "VipLlavaMultiModal", "LavaMultiModal", "QwenVLMultiModal", "CLIPQ", @@ -129,4 +127,5 @@ __all__ = [ "SegmentAnythingMarkGenerator", "SamplingType", "SamplingParams", + "FireFunctionCaller", ] diff --git a/swarms/models/cog_vlm.py b/swarms/models/cog_vlm.py new file mode 100644 index 00000000..14b99b60 --- /dev/null +++ b/swarms/models/cog_vlm.py @@ -0,0 +1,530 @@ +import base64 +import os +import time +from io import BytesIO +from typing import List, Literal, Optional, Tuple, Union + +import torch +from PIL import Image +from pydantic import BaseModel, Field +from transformers import ( + AutoModelForCausalLM, + LlamaTokenizer, + TextIteratorStreamer, +) + +from swarms.models.base_multimodal_model import BaseMultiModalModel +from swarms.utils.logger import logger + +MODEL_PATH = "THUDM/cogvlm-chat-hf" +TOKENIZER_PATH = "lmsys/vicuna-7b-v1.5" +DEVICE = "cuda" if torch.cuda.is_available() else "cpu" +QUANT_ENABLED = False + + +class ImageUrl(BaseModel): + url: str + + +class TextContent(BaseModel): + type: Literal["text"] + text: str + + +class ImageUrlContent(BaseModel): + type: Literal["image_url"] + image_url: ImageUrl + + +ContentItem = Union[TextContent, ImageUrlContent] + + +class ChatMessageInput(BaseModel): + role: Literal["user", "assistant", "system"] + content: Union[str, List[ContentItem]] + name: Optional[str] = None + + +class ChatMessageResponse(BaseModel): + role: Literal["assistant"] + content: str = None + name: Optional[str] = None + + +class DeltaMessage(BaseModel): + role: Optional[Literal["user", "assistant", "system"]] = None + content: Optional[str] = None + + +class ChatCompletionRequest(BaseModel): + model: str + messages: List[ChatMessageInput] + temperature: Optional[float] = 0.8 + top_p: Optional[float] = 0.8 + max_tokens: Optional[int] = None + stream: Optional[bool] = False + # Additional parameters + repetition_penalty: Optional[float] = 1.0 + + +class ChatCompletionResponseChoice(BaseModel): + index: int + message: ChatMessageResponse + + +class ChatCompletionResponseStreamChoice(BaseModel): + index: int + delta: DeltaMessage + + +class UsageInfo(BaseModel): + prompt_tokens: int = 0 + total_tokens: int = 0 + completion_tokens: Optional[int] = 0 + + +class ChatCompletionResponse(BaseModel): + model: str + object: Literal["chat.completion", "chat.completion.chunk"] + choices: List[ + Union[ + ChatCompletionResponseChoice, + ChatCompletionResponseStreamChoice, + ] + ] + created: Optional[int] = Field( + default_factory=lambda: int(time.time()) + ) + usage: Optional[UsageInfo] = None + + +# async def create_chat_completion(request: ChatCompletionRequest): +# global model, tokenizer + +# gen_params = dict( +# messages=request.messages, +# temperature=request.temperature, +# top_p=request.top_p, +# max_tokens=request.max_tokens or 1024, +# echo=False, +# stream=request.stream, +# ) + +# # if request.stream: +# # predict(request.model, gen_params) +# # response = generate_cogvlm(model, tokenizer, gen_params) + +# usage = UsageInfo() + +# message = ChatMessageResponse( +# role="assistant", +# content=response["text"], +# ) +# logger.debug(f"==== message ====\n{message}") +# choice_data = ChatCompletionResponseChoice( +# index=0, +# message=message, +# ) +# task_usage = UsageInfo.model_validate(response["usage"]) +# for usage_key, usage_value in task_usage.model_dump().items(): +# setattr( +# usage, usage_key, getattr(usage, usage_key) + usage_value +# ) +# return ChatCompletionResponse( +# model=request.model, +# choices=[choice_data], +# object="chat.completion", +# usage=usage, +# ) + + +class CogVLMMultiModal(BaseMultiModalModel): + """ + Initializes the CogVLM model. + + Args: + model_name (str): The path or name of the pre-trained model. + tokenizer (str): The path or name of the tokenizer. + device (str): The device to run the model on. + quantize (bool): Whether to enable quantization. + torch_type (str): The torch data type to use. + temperature (float): The temperature for sampling. + top_p (float): The top-p value for sampling. + max_tokens (int): The maximum number of tokens to generate. + echo (bool): Whether to echo the input text. + stream (bool): Whether to stream the output. + repetition_penalty (float): The repetition penalty for sampling. + do_sample (bool): Whether to use sampling during generation. + *args: Additional positional arguments. + **kwargs: Additional keyword arguments. + + Methods: + run: Generates a response using the CogVLM model. + generate_stream_cogvlm: Generates a stream of responses using the CogVLM model in inference mode. + process_history_and_images: Processes history messages to extract text, identify the last user query, and convert base64 encoded image URLs to PIL images. + + Example: + >>> model = CogVLMMultiModal() + >>> response = model("Describe this image with meticlous details.", "https://example.com/image.jpg") + >>> print(response) + """ + + def __init__( + self, + model_name: str = MODEL_PATH, + tokenizer: str = TOKENIZER_PATH, + device: str = DEVICE, + quantize: bool = QUANT_ENABLED, + torch_type: str = "float16", + temperature: float = 0.5, + top_p: float = 0.9, + max_tokens: int = 3500, + echo: bool = False, + stream: bool = False, + repetition_penalty: float = 1.0, + do_sample: bool = True, + *args, + **kwargs, + ): + super().__init__() + self.model_name = model_name + self.device = device + self.tokenizer = tokenizer + self.device = device + self.quantize = quantize + self.torch_type = torch_type + self.temperature = temperature + self.top_p = top_p + self.max_tokens = max_tokens + self.echo = echo + self.stream = stream + self.repetition_penalty = repetition_penalty + self.do_sample = do_sample + + if os.environ.get("QUANT_ENABLED"): + pass + else: + with torch.cuda.device(device): + __, total_bytes = torch.cuda.mem_get_info() + total_gb = total_bytes / (1 << 30) + if total_gb < 40: + pass + else: + pass + + torch.cuda.empty_cache() + + self.tokenizer = LlamaTokenizer.from_pretrained( + tokenizer, trust_remote_code=True + ) + + if ( + torch.cuda.is_available() + and torch.cuda.get_device_capability()[0] >= 8 + ): + torch_type = torch.bfloat16 + else: + torch_type = torch.float16 + + print( + "========Use torch type as:{} with device:{}========\n\n" + .format(torch_type, device) + ) + + if "cuda" in device: + if QUANT_ENABLED: + self.model = AutoModelForCausalLM.from_pretrained( + model_name, + load_in_4bit=True, + trust_remote_code=True, + torch_dtype=torch_type, + low_cpu_mem_usage=True, + *args, + **kwargs, + ).eval() + else: + self.model = ( + AutoModelForCausalLM.from_pretrained( + model_name, + load_in_4bit=False, + trust_remote_code=True, + torch_dtype=torch_type, + low_cpu_mem_usage=True, + *args, + **kwargs, + ) + .to(device) + .eval() + ) + + else: + self.model = ( + AutoModelForCausalLM.from_pretrained( + model_name, + trust_remote_code=True, + *args, + **kwargs, + ) + .float() + .to(device) + .eval() + ) + + def run(self, task: str, img: str, *args, **kwargs): + """ + Generates a response using the CogVLM model. It processes the chat history and image data, if any, + and then invokes the model to generate a response. + """ + messages = [task] + + params = dict( + messages=messages, + temperature=self.temperature, + repitition_penalty=self.repetition_penalty, + top_p=self.top_p, + max_new_tokens=self.max_tokens, + ) + + for response in self.generate_stream_cogvlm(params): + pass + + return response + + @torch.inference_mode() + def generate_stream_cogvlm( + self, + params: dict, + ): + """ + Generates a stream of responses using the CogVLM model in inference mode. + It's optimized to handle continuous input-output interactions with the model in a streaming manner. + """ + messages = params["messages"] + temperature = float(params.get("temperature", 1.0)) + repetition_penalty = float( + params.get("repetition_penalty", 1.0) + ) + top_p = float(params.get("top_p", 1.0)) + max_new_tokens = int(params.get("max_tokens", 256)) + query, history, image_list = self.process_history_and_images( + messages + ) + + logger.debug(f"==== request ====\n{query}") + + input_by_model = self.model.build_conversation_input_ids( + self.tokenizer, + query=query, + history=history, + images=[image_list[-1]], + ) + 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.torch_type) + ] + ], + } + 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.torch_type) + ] + ] + + input_echo_len = len(inputs["input_ids"][0]) + streamer = TextIteratorStreamer( + tokenizer=self.tokenizer, + timeout=60.0, + skip_promptb=True, + skip_special_tokens=True, + ) + gen_kwargs = { + "repetition_penalty": repetition_penalty, + "max_new_tokens": max_new_tokens, + "do_sample": True if temperature > 1e-5 else False, + "top_p": top_p if temperature > 1e-5 else 0, + "streamer": streamer, + } + if temperature > 1e-5: + gen_kwargs["temperature"] = temperature + + total_len = 0 + generated_text = "" + with torch.no_grad(): + self.model.generate(**inputs, **gen_kwargs) + for next_text in streamer: + generated_text += next_text + yield { + "text": generated_text, + "usage": { + "prompt_tokens": input_echo_len, + "completion_tokens": ( + total_len - input_echo_len + ), + "total_tokens": total_len, + }, + } + ret = { + "text": generated_text, + "usage": { + "prompt_tokens": input_echo_len, + "completion_tokens": total_len - input_echo_len, + "total_tokens": total_len, + }, + } + yield ret + + def process_history_and_images( + self, + messages: List[ChatMessageInput], + ) -> Tuple[ + Optional[str], + Optional[List[Tuple[str, str]]], + Optional[List[Image.Image]], + ]: + """ + Process history messages to extract text, identify the last user query, + and convert base64 encoded image URLs to PIL images. + + Args: + messages(List[ChatMessageInput]): List of ChatMessageInput objects. + return: A tuple of three elements: + - The last user query as a string. + - Text history formatted as a list of tuples for the model. + - List of PIL Image objects extracted from the messages. + """ + formatted_history = [] + image_list = [] + last_user_query = "" + + for i, message in enumerate(messages): + role = message.role + content = message.content + + # Extract text content + if isinstance(content, list): # text + text_content = " ".join( + item.text + for item in content + if isinstance(item, TextContent) + ) + else: + text_content = content + + # Extract image data + if isinstance(content, list): # image + for item in content: + if isinstance(item, ImageUrlContent): + image_url = item.image_url.url + if image_url.startswith( + "data:image/jpeg;base64," + ): + base64_encoded_image = image_url.split( + "data:image/jpeg;base64," + )[1] + image_data = base64.b64decode( + base64_encoded_image + ) + image = Image.open( + BytesIO(image_data) + ).convert("RGB") + image_list.append(image) + + # Format history + if role == "user": + if i == len(messages) - 1: + last_user_query = text_content + else: + formatted_history.append((text_content, "")) + elif role == "assistant": + if formatted_history: + if formatted_history[-1][1] != "": + assert False, ( + "the last query is answered. answer" + f" again. {formatted_history[-1][0]}," + f" {formatted_history[-1][1]}," + f" {text_content}" + ) + formatted_history[-1] = ( + formatted_history[-1][0], + text_content, + ) + else: + assert False, "assistant reply before user" + else: + assert False, f"unrecognized role: {role}" + + return last_user_query, formatted_history, image_list + + async def predict(self, params: dict): + """ + Handle streaming predictions. It continuously generates responses for a given input stream. + This is particularly useful for real-time, continuous interactions with the model. + """ + + choice_data = ChatCompletionResponseStreamChoice( + index=0, + delta=DeltaMessage(role="assistant"), + finish_reason=None, + ) + chunk = ChatCompletionResponse( + model=self.model_name, + choices=[choice_data], + object="chat.completion.chunk", + ) + yield "{}".format(chunk.model_dump_json(exclude_unset=True)) + + previous_text = "" + for new_response in self.generate_stream_cogvlm(params): + decoded_unicode = new_response["text"] + delta_text = decoded_unicode[len(previous_text) :] + previous_text = decoded_unicode + delta = DeltaMessage( + content=delta_text, + role="assistant", + ) + choice_data = ChatCompletionResponseStreamChoice( + index=0, + delta=delta, + ) + chunk = ChatCompletionResponse( + model=self.model_name, + choices=[choice_data], + object="chat.completion.chunk", + ) + yield "{}".format( + chunk.model_dump_json(exclude_unset=True) + ) + choice_data = ChatCompletionResponseStreamChoice( + index=0, + delta=DeltaMessage(), + ) + chunk = ChatCompletionResponse( + model=self.model_name, + choices=[choice_data], + object="chat.completion.chunk", + ) + yield "{}".format(chunk.model_dump_json(exclude_unset=True)) diff --git a/swarms/models/fire_function.py b/swarms/models/fire_function.py new file mode 100644 index 00000000..6803d822 --- /dev/null +++ b/swarms/models/fire_function.py @@ -0,0 +1,87 @@ +from transformers import AutoModelForCausalLM, AutoTokenizer +import json +from swarms.models.base_llm import AbstractLLM +from typing import Any + + +class FireFunctionCaller(AbstractLLM): + """ + A class that represents a caller for the FireFunction model. + + Args: + model_name (str): The name of the model to be used. + device (str): The device to be used. + function_spec (Any): The specification of the function. + max_tokens (int): The maximum number of tokens. + system_prompt (str): The system prompt. + *args: Variable length argument list. + **kwargs: Arbitrary keyword arguments. + + Methods: + run(self, task: str, *args, **kwargs) -> None: Run the function with the given task and arguments. + + Examples: + >>> fire_function_caller = FireFunctionCaller() + >>> fire_function_caller.run("Add 2 and 3") + """ + + def __init__( + self, + model_name: str = "fireworks-ai/firefunction-v1", + device: str = "cuda", + function_spec: Any = None, + max_tokens: int = 3000, + system_prompt: str = "You are a helpful assistant with access to functions. Use them if required.", + *args, + **kwargs, + ): + super().__init__(model_name, device) + self.model_name = model_name + self.device = device + self.fucntion_spec = function_spec + self.max_tokens = max_tokens + self.system_prompt = system_prompt + + self.model = AutoModelForCausalLM.from_pretrained( + model_name, device_map="auto", *args, **kwargs + ) + self.tokenizer = AutoTokenizer.from_pretrained(model_name) + + self.functions = json.dumps(function_spec, indent=4) + + def run(self, task: str, *args, **kwargs): + """ + Run the function with the given task and arguments. + + Args: + task (str): The task to be performed. + *args: Variable length argument list. + **kwargs: Arbitrary keyword arguments. + + Returns: + None + """ + messages = [ + {"role": "functions", "content": self.functions}, + { + "role": "system", + "content": self.system_prompt, + }, + { + "role": "user", + "content": task, + }, + ] + + model_inputs = self.tokenizer.apply_chat_template( + messages, return_tensors="pt" + ).to(self.model.device) + + generated_ids = self.model.generate( + model_inputs, + max_new_tokens=self.max_tokens, + *args, + **kwargs, + ) + decoded = self.tokenizer.batch_decode(generated_ids) + print(decoded[0]) diff --git a/swarms/models/test_fire_function.py b/swarms/models/test_fire_function.py new file mode 100644 index 00000000..e28d64ee --- /dev/null +++ b/swarms/models/test_fire_function.py @@ -0,0 +1,41 @@ +import pytest +from unittest.mock import MagicMock +from swarms.models.fire_function import FireFunctionCaller + +def test_fire_function_caller_run(mocker): + # Create mock model and tokenizer + model = MagicMock() + tokenizer = MagicMock() + mocker.patch.object(FireFunctionCaller, 'model', model) + mocker.patch.object(FireFunctionCaller, 'tokenizer', tokenizer) + + # Create mock task and arguments + task = "Add 2 and 3" + args = (2, 3) + kwargs = {} + + # Create mock generated_ids and decoded output + generated_ids = [1, 2, 3] + decoded_output = "5" + model.generate.return_value = generated_ids + tokenizer.batch_decode.return_value = [decoded_output] + + # Create FireFunctionCaller instance + fire_function_caller = FireFunctionCaller() + + # Run the function + fire_function_caller.run(task, *args, **kwargs) + + # Assert model.generate was called with the correct inputs + model.generate.assert_called_once_with( + tokenizer.apply_chat_template.return_value, + max_new_tokens=fire_function_caller.max_tokens, + *args, + **kwargs, + ) + + # Assert tokenizer.batch_decode was called with the correct inputs + tokenizer.batch_decode.assert_called_once_with(generated_ids) + + # Assert the decoded output is printed + assert decoded_output in mocker.patch.object(print, 'call_args_list') diff --git a/swarms/structs/agent.py b/swarms/structs/agent.py index 95c01c79..be5b4402 100644 --- a/swarms/structs/agent.py +++ b/swarms/structs/agent.py @@ -3,10 +3,12 @@ import json import logging import os import random +import sys import time import uuid from typing import Any, Callable, Dict, List, Optional, Tuple +from loguru import logger from termcolor import colored from swarms.memory.base_vectordb import AbstractVectorDatabase @@ -22,7 +24,8 @@ from swarms.tools.exec_tool import execute_tool_by_name from swarms.tools.tool import BaseTool from swarms.utils.code_interpreter import SubprocessCodeInterpreter from swarms.utils.data_to_text import data_to_text -from swarms.utils.logger import logger + +# from swarms.utils.logger import logger from swarms.utils.parse_code import extract_code_from_markdown from swarms.utils.pdf_to_text import pdf_to_text from swarms.utils.token_count_tiktoken import limit_tokens_from_string @@ -51,10 +54,18 @@ def agent_id(): return str(uuid.uuid4()) +# Task ID generator def task_id(): + """ + Generate a unique task ID. + + Returns: + str: A string representation of a UUID. + """ return str(uuid.uuid4()) +# Step ID generator def step_id(): return str(uuid.uuid1()) @@ -194,6 +205,10 @@ class Agent: callback: Optional[Callable] = None, metadata: Optional[Dict[str, Any]] = None, callbacks: Optional[List[Callable]] = None, + logger_handler: Any = sys.stderr, + search_algorithm: Optional[Callable] = None, + logs_to_filename: Optional[str] = None, + evaluator: Optional[Callable] = None, *args, **kwargs, ): @@ -243,9 +258,14 @@ class Agent: self.callback = callback self.metadata = metadata self.callbacks = callbacks + self.logger_handler = logger_handler + self.search_algorithm = search_algorithm + self.logs_to_filename = logs_to_filename + self.evaluator = evaluator # The max_loops will be set dynamically if the dynamic_loop if self.dynamic_loops: + logger.info("Dynamic loops enabled") self.max_loops = "auto" # If multimodal = yes then set the sop to the multimodal sop @@ -260,7 +280,9 @@ class Agent: self.feedback = [] # Initialize the code executor - self.code_executor = SubprocessCodeInterpreter() + self.code_executor = SubprocessCodeInterpreter( + debug_mode=True, + ) # If the preset stopping token is enabled then set the stopping token to the preset stopping token if preset_stopping_token: @@ -279,11 +301,12 @@ class Agent: self.get_docs_from_doc_folders() # If tokenizer and context length exists then: - if self.tokenizer and self.context_length: - self.truncate_history() + # if self.tokenizer and self.context_length: + # self.truncate_history() - if verbose: - logger.setLevel(logging.DEBUG) + # If verbose is enabled then set the logger level to info + # if verbose: + # logger.setLevel(logging.INFO) # If tools are provided then set the tool prompt by adding to sop if self.tools: @@ -308,6 +331,21 @@ class Agent: # Step cache self.step_cache = [] + # Set the logger handler + if logger_handler: + logger.add( + f"{self.agent_name}.log", + level="INFO", + colorize=True, + format=( + "{time} {message}" + ), + backtrace=True, + diagnose=True, + ) + + # logger.info("Creating Agent {}".format(self.agent_name)) + def set_system_prompt(self, system_prompt: str): """Set the system prompt""" self.system_prompt = system_prompt @@ -342,6 +380,7 @@ class Agent: if hasattr(self.llm, "temperature"): # Randomly change the temperature attribute of self.llm object self.llm.temperature = random.uniform(0.0, 1.0) + logger.info(f"Temperature: {self.llm.temperature}") else: # Use a default temperature self.llm.temperature = 0.7 @@ -359,6 +398,7 @@ class Agent: def add_task_to_memory(self, task: str): """Add the task to the memory""" try: + logger.info(f"Adding task to memory: {task}") self.short_memory.add(f"{self.user_name}: {task}") except Exception as error: print( @@ -370,6 +410,7 @@ class Agent: def add_message_to_memory(self, message: str): """Add the message to the memory""" try: + logger.info(f"Adding message to memory: {message}") self.short_memory.add( role=self.agent_name, content=message ) @@ -590,12 +631,26 @@ class Agent: # Log each step step = Step( - input=task, - task_id=task_id, - step_id=step_id, - output=response, + input=str(task), + task_id=str(task_id), + step_id=str(step_id), + output=str(response), + status="running", ) + if self.evaluator: + evaluated_response = self.evaluator( + response + ) + + out = ( + f"Response: {response}\nEvaluated" + f" Response: {evaluated_response}" + ) + out = self.short_memory.add( + "Evaluator", out + ) + # Check to see if stopping token is in the output to stop the loop if self.stopping_token: if self._check_stopping_condition( @@ -679,20 +734,6 @@ class Agent: """ self.run(task, img, *args, **kwargs) - def _run(self, **kwargs: Any) -> str: - """Run the agent on a task - - Returns: - str: _description_ - """ - try: - task = self.format_prompt(**kwargs) - response, history = self._generate(task, task) - logging.info(f"Message history: {history}") - return response - except Exception as error: - print(colored(f"Error running agent: {error}", "red")) - def agent_history_prompt( self, history: str = None, @@ -710,13 +751,12 @@ class Agent: if self.sop: system_prompt = self.system_prompt agent_history_prompt = f""" - SYSTEM_PROMPT: {system_prompt} + role: system + {system_prompt} Follow this standard operating procedure (SOP) to complete tasks: {self.sop} - ----------------- - ################ CHAT HISTORY #################### {history} """ return agent_history_prompt @@ -758,6 +798,7 @@ class Agent: Returns: _type_: _description_ """ + logger.info(f"Adding memory: {message}") return self.short_memory.add( role=self.agent_name, content=message ) @@ -770,10 +811,12 @@ class Agent: tasks (List[str]): A list of tasks to run. """ try: + logger.info(f"Running concurrent tasks: {tasks}") task_coroutines = [ self.run_async(task, **kwargs) for task in tasks ] completed_tasks = await asyncio.gather(*task_coroutines) + logger.info(f"Completed tasks: {completed_tasks}") return completed_tasks except Exception as error: print( @@ -789,6 +832,7 @@ class Agent: def bulk_run(self, inputs: List[Dict[str, Any]]) -> List[str]: try: """Generate responses for multiple input sets.""" + logger.info(f"Running bulk tasks: {inputs}") return [self.run(**input_data) for input_data in inputs] except Exception as error: print(colored(f"Error running bulk run: {error}", "red")) @@ -881,6 +925,7 @@ class Agent: """ try: + logger.info(f"Running a single step: {task}") # Generate the response using lm response = self.llm(task, **kwargs) @@ -960,6 +1005,7 @@ class Agent: """ + logger.info(f"Adding response filter: {filter_word}") self.reponse_filters.append(filter_word) def apply_reponse_filters(self, response: str) -> str: @@ -967,6 +1013,9 @@ class Agent: Apply the response filters to the response """ + logger.info( + f"Applying response filters to response: {response}" + ) for word in self.response_filters: response = response.replace(word, "[FILTERED]") return response @@ -978,11 +1027,13 @@ class Agent: response = agent.filtered_run("Generate a report on finance") print(response) """ + logger.info(f"Running filtered task: {task}") raw_response = self.run(task) return self.apply_response_filters(raw_response) def interactive_run(self, max_loops: int = 5) -> None: """Interactive run mode""" + logger.info("Running in interactive mode") response = input("Start the cnversation") for i in range(max_loops): @@ -992,28 +1043,6 @@ class Agent: # Get user input response = input("You: ") - def streamed_generation(self, prompt: str) -> str: - """ - Stream the generation of the response - - Args: - prompt (str): The prompt to use - - Example: - # Feature 4: Streamed generation - response = agent.streamed_generation("Generate a report on finance") - print(response) - - """ - tokens = list(prompt) - response = "" - for token in tokens: - time.sleep(0.1) - response += token - print(token, end="", flush=True) - print() - return response - def save_state(self, file_path: str) -> None: """ Saves the current state of the agent to a JSON file, including the llm parameters. @@ -1025,6 +1054,7 @@ class Agent: >>> agent.save_state('saved_flow.json') """ try: + logger.info(f"Saving agent state to: {file_path}") state = { "agent_id": str(self.id), "agent_name": self.agent_name, @@ -1045,6 +1075,7 @@ class Agent: "autosave": self.autosave, "saved_state_path": self.saved_state_path, "max_loops": self.max_loops, + # "StepCache": self.step_cache, } with open(file_path, "w") as f: @@ -1104,54 +1135,55 @@ class Agent: >>> agent.run("Continue with the task") """ - with open(file_path) as f: - state = json.load(f) - - # Restore other saved attributes - self.id = state.get("agent_id", self.id) - self.agent_name = state.get("agent_name", self.agent_name) - self.agent_description = state.get( - "agent_description", self.agent_description - ) - self.system_prompt = state.get( - "system_prompt", self.system_prompt - ) - self.sop = state.get("sop", self.sop) - self.short_memory = state.get("short_memory", []) - self.max_loops = state.get("max_loops", 5) - self.loop_interval = state.get("loop_interval", 1) - self.retry_attempts = state.get("retry_attempts", 3) - self.retry_interval = state.get("retry_interval", 1) - self.interactive = state.get("interactive", False) - - print(f"Agent state loaded from {file_path}") + try: + with open(file_path) as f: + state = json.load(f) + + # Restore other saved attributes + self.id = state.get("agent_id", self.id) + self.agent_name = state.get("agent_name", self.agent_name) + self.agent_description = state.get( + "agent_description", self.agent_description + ) + self.system_prompt = state.get( + "system_prompt", self.system_prompt + ) + self.sop = state.get("sop", self.sop) + self.short_memory = state.get("short_memory", []) + self.max_loops = state.get("max_loops", 5) + self.loop_interval = state.get("loop_interval", 1) + self.retry_attempts = state.get("retry_attempts", 3) + self.retry_interval = state.get("retry_interval", 1) + self.interactive = state.get("interactive", False) + + print(f"Agent state loaded from {file_path}") + except Exception as error: + print( + colored(f"Error loading agent state: {error}", "red") + ) def retry_on_failure( - self, function, retries: int = 3, retry_delay: int = 1 + self, + function: callable, + retries: int = 3, + retry_delay: int = 1, ): """Retry wrapper for LLM calls.""" - attempt = 0 - while attempt < retries: - try: - return function() - except Exception as error: - logging.error(f"Error generating response: {error}") - attempt += 1 - time.sleep(retry_delay) - raise Exception("All retry attempts failed") - - def generate_reply(self, history: str, **kwargs) -> str: - """ - Generate a response based on initial or task - """ - prompt = f""" - - SYSTEM_PROMPT: {self.system_prompt} - - History: {history} - """ - response = self.llm(prompt, **kwargs) - return {"role": self.agent_name, "content": response} + try: + logger.info(f"Retrying function: {function}") + attempt = 0 + while attempt < retries: + try: + return function() + except Exception as error: + logging.error( + f"Error generating response: {error}" + ) + attempt += 1 + time.sleep(retry_delay) + raise Exception("All retry attempts failed") + except Exception as error: + print(colored(f"Error retrying function: {error}", "red")) def update_system_prompt(self, system_prompt: str): """Upddate the system message""" @@ -1181,9 +1213,13 @@ class Agent: """ text -> parse_code by looking for code inside 6 backticks `````-> run_code """ - parsed_code = extract_code_from_markdown(code) - run_code = self.code_executor.run(parsed_code) - return run_code + try: + logger.info(f"Running code: {code}") + parsed_code = extract_code_from_markdown(code) + run_code = self.code_executor.run(parsed_code) + return run_code + except Exception as error: + logger.debug(f"Error running code: {error}") def pdf_connector(self, pdf: str = None): """Transforms the pdf into text @@ -1194,9 +1230,13 @@ class Agent: Returns: _type_: _description_ """ - pdf = pdf or self.pdf_path - text = pdf_to_text(pdf) - return text + try: + pdf = pdf or self.pdf_path + text = pdf_to_text(pdf) + return text + except Exception as error: + print(f"Error connecting to the pdf: {error}") + raise error def pdf_chunker(self, text: str = None, num_limits: int = 1000): """Chunk the pdf into sentences @@ -1220,12 +1260,15 @@ class Agent: Returns: _type_: _description_ """ - for doc in docs: - data = data_to_text(doc) + try: + for doc in docs: + data = data_to_text(doc) - return self.short_memory.add( - role=self.user_name, content=data - ) + return self.short_memory.add( + role=self.user_name, content=data + ) + except Exception as error: + print(colored(f"Error ingesting docs: {error}", "red")) def ingest_pdf(self, pdf: str): """Ingest the pdf into the memory @@ -1236,22 +1279,37 @@ class Agent: Returns: _type_: _description_ """ - text = pdf_to_text(pdf) - return self.short_memory.add( - role=self.user_name, content=text - ) + try: + logger.info(f"Ingesting pdf: {pdf}") + text = pdf_to_text(pdf) + return self.short_memory.add( + role=self.user_name, content=text + ) + except Exception as error: + print(colored(f"Error ingesting pdf: {error}", "red")) def receieve_mesage(self, name: str, message: str): """Receieve a message""" - message = f"{name}: {message}" - return self.short_memory.add(role=name, content=message) + try: + message = f"{name}: {message}" + return self.short_memory.add(role=name, content=message) + except Exception as error: + print(colored(f"Error receiving message: {error}", "red")) def send_agent_message( self, agent_name: str, message: str, *args, **kwargs ): """Send a message to the agent""" - message = f"{agent_name}: {message}" - return self.run(message, *args, **kwargs) + try: + logger.info(f"Sending agent message: {message}") + message = f"{agent_name}: {message}" + return self.run(message, *args, **kwargs) + except Exception as error: + print( + colored( + f"Error sending agent message: {error}", "red" + ) + ) def truncate_history(self): """ @@ -1278,13 +1336,22 @@ class Agent: def get_docs_from_doc_folders(self): """Get the docs from the files""" - # Get the list of files then extract them and add them to the memory - files = os.listdir(self.docs_folder) + try: + logger.info("Getting docs from doc folders") + # Get the list of files then extract them and add them to the memory + files = os.listdir(self.docs_folder) - # Extract the text from the files - for file in files: - text = data_to_text(file) + # Extract the text from the files + for file in files: + text = data_to_text(file) - return self.short_memory.add( - role=self.user_name, content=text - ) + return self.short_memory.add( + role=self.user_name, content=text + ) + except Exception as error: + print( + colored( + f"Error getting docs from doc folders: {error}", + "red", + ) + ) diff --git a/swarms/structs/long_swarm.py b/swarms/structs/long_swarm.py index 80d301cb..e24a3e08 100644 --- a/swarms/structs/long_swarm.py +++ b/swarms/structs/long_swarm.py @@ -13,7 +13,7 @@ class LongContextSwarmLeader: - agents (List[Agent]): The agents in the swarm. - prompt_template_json (str): The SOP template in JSON format. - return_parsed (bool): Whether to return the parsed output. - + """ def __init__( @@ -30,17 +30,16 @@ class LongContextSwarmLeader: self.agents = agents self.prompt_template_json = prompt_template_json self.return_parsed = return_parsed - + # Create an instance of the Agent class self.agent = Agent( llm=llm, system_prompt=None, - sop=self.prompt_template_json, - *args, - **kwargs + sop=self.prompt_template_json, + *args, + **kwargs, ) - def prep_schema(self, task: str, *args, **kwargs): """ Returns a formatted string containing the metadata of all agents in the swarm. @@ -71,16 +70,15 @@ class LongContextSwarmLeader: """ for agent in self.agents: - prompt += f"Member Name: {agent.ai_name}\nMember ID: {agent.id}\nMember Description: {agent.description}\n\n" - + prompt += ( + f"Member Name: {agent.ai_name}\nMember ID:" + f" {agent.id}\nMember Description:" + f" {agent.description}\n\n" + ) + return prompt - - - def prep_schema_second( - self, - task_description: str, - task: str - ): + + def prep_schema_second(self, task_description: str, task: str): prompt = f""" You are the leader of a team of {len(self.agents)} members. Your team will need to collaborate to @@ -115,7 +113,6 @@ class LongContextSwarmLeader: """ return prompt - def run(self, task: str, *args, **kwargs): """ @@ -131,12 +128,13 @@ class LongContextSwarmLeader: """ task = self.prep_schema(task) out = self.agent.run(task, *args, **kwargs) - + if self.return_parsed: out = extract_code_from_markdown(out) - + return out + # class LongContextSwarm(BaseSwarm): # def __init__( # self, diff --git a/swarms/structs/majority_voting.py b/swarms/structs/majority_voting.py index 05539ecf..fc4f8018 100644 --- a/swarms/structs/majority_voting.py +++ b/swarms/structs/majority_voting.py @@ -7,7 +7,21 @@ from typing import Any, List from swarms.structs.agent import Agent from swarms.structs.conversation import Conversation -from swarms.utils.logger import logger +from loguru import logger +import sys + + +# Configure loguru logger with advanced settings +logger.remove() +logger.add( + sys.stderr, + colorize=True, + format="{time} {message}", + backtrace=True, + diagnose=True, + enqueue=True, + catch=True, +) def extract_last_python_code_block(text): @@ -157,11 +171,18 @@ class MajorityVoting: time_enabled=True, *args, **kwargs ) - # # Configure logging - # self.logging.basicConfig( - # level=logging.INFO, - # format="%(asctime)s - %(levelname)s - %(message)s", - # ) + # If autosave is enabled, save the conversation to a file + if self.autosave: + self.conversation.save() + + # Log the agents + logger.info("Initializing majority voting system") + # Length of agents + logger.info(f"Number of agents: {len(self.agents)}") + logger.info( + "Agents:" + f" {', '.join(agent.agent_name for agent in self.agents)}" + ) def run(self, task: str, *args, **kwargs) -> List[Any]: """ @@ -176,10 +197,11 @@ class MajorityVoting: List[Any]: The majority vote. """ - # Route to each agent if self.concurrent: with concurrent.futures.ThreadPoolExecutor() as executor: + # Log the agents + logger.info("Running agents concurrently") futures = [ executor.submit(agent.run, task, *args) for agent in self.agents @@ -191,6 +213,7 @@ class MajorityVoting: ) ] elif self.multithreaded: + logger.info("Running agents using multithreading") with concurrent.futures.ThreadPoolExecutor() as executor: results = [ executor.submit(agent.run, task, *args) @@ -198,6 +221,7 @@ class MajorityVoting: ] results = [future.result() for future in results] elif self.multiprocess: + logger.info("Running agents using multiprocessing") with Pool() as pool: results = pool.starmap( Agent.run, @@ -218,6 +242,8 @@ class MajorityVoting: # Add responses to conversation and log them for agent, response in zip(self.agents, results): + logger.info(f"[{agent.agent_id}][{response}]") + response = ( response if isinstance(response, list) else [response] ) @@ -227,6 +253,9 @@ class MajorityVoting: # Perform majority voting on the conversation majority_vote = majority_voting(self.conversation.responses) + # Log the majority vote + logger.info(f"Majority vote: {majority_vote}") + # If an output parser is provided, parse the output if self.output_parser: majority_vote = self.output_parser( diff --git a/swarms/utils/code_interpreter.py b/swarms/utils/code_interpreter.py index e3850250..a586a1eb 100644 --- a/swarms/utils/code_interpreter.py +++ b/swarms/utils/code_interpreter.py @@ -20,10 +20,12 @@ class SubprocessCodeInterpreter: Example: """ - def __init__(self): - self.start_cmd = "" + def __init__( + self, + start_cmd: str = "", + debug_mode: bool = False, + ): self.process = None - self.debug_mode = False self.output_queue = queue.Queue() self.done = threading.Event() diff --git a/swarms/utils/disable_logging.py b/swarms/utils/disable_logging.py index 368c85bf..a5ad63cf 100644 --- a/swarms/utils/disable_logging.py +++ b/swarms/utils/disable_logging.py @@ -1,13 +1,9 @@ import logging import os -import sys import warnings def disable_logging(): - log_file = open("errors.txt", "w") - sys.stderr = log_file - warnings.filterwarnings("ignore", category=UserWarning) # disable tensorflow warnings @@ -29,6 +25,11 @@ def disable_logging(): "numexpr", "git", "wandb.docker.auth", + "langchain", + "distutils", + "urllib3", + "elasticsearch", + "packaging", ]: logger = logging.getLogger(logger_name) logger.setLevel(logging.ERROR) diff --git a/tests/models/test_fire_function_caller.py b/tests/models/test_fire_function_caller.py new file mode 100644 index 00000000..703417a7 --- /dev/null +++ b/tests/models/test_fire_function_caller.py @@ -0,0 +1,45 @@ +from unittest.mock import MagicMock + + +from swarms.models.fire_function import FireFunctionCaller + + +def test_fire_function_caller_run(mocker): + # Create mock model and tokenizer + model = MagicMock() + tokenizer = MagicMock() + mocker.patch.object(FireFunctionCaller, "model", model) + mocker.patch.object(FireFunctionCaller, "tokenizer", tokenizer) + + # Create mock task and arguments + task = "Add 2 and 3" + args = (2, 3) + kwargs = {} + + # Create mock generated_ids and decoded output + generated_ids = [1, 2, 3] + decoded_output = "5" + model.generate.return_value = generated_ids + tokenizer.batch_decode.return_value = [decoded_output] + + # Create FireFunctionCaller instance + fire_function_caller = FireFunctionCaller() + + # Run the function + fire_function_caller.run(task, *args, **kwargs) + + # Assert model.generate was called with the correct inputs + model.generate.assert_called_once_with( + tokenizer.apply_chat_template.return_value, + max_new_tokens=fire_function_caller.max_tokens, + *args, + **kwargs, + ) + + # Assert tokenizer.batch_decode was called with the correct inputs + tokenizer.batch_decode.assert_called_once_with(generated_ids) + + # Assert the decoded output is printed + assert decoded_output in mocker.patch.object( + print, "call_args_list" + ) From d23884d5d3537177e5f91f0c8141ea98f18d2ead Mon Sep 17 00:00:00 2001 From: Kye Date: Fri, 23 Feb 2024 21:07:03 -0800 Subject: [PATCH 4/9] [CLEANUP] --- example.py | 5 +---- pyproject.toml | 2 +- swarms/models/test_fire_function.py | 10 ++++++---- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/example.py b/example.py index 1a83d232..bebdb11a 100644 --- a/example.py +++ b/example.py @@ -11,7 +11,4 @@ agent = Agent( ) # Run the workflow on a task -agent( - "Find a chick fil a equivalent in hayes valley" -) - +agent("Find a chick fil a equivalent in hayes valley") diff --git a/pyproject.toml b/pyproject.toml index 3247cc64..3d3931d0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "swarms" -version = "4.1.7" +version = "4.1.8" description = "Swarms - Pytorch" license = "MIT" authors = ["Kye Gomez "] diff --git a/swarms/models/test_fire_function.py b/swarms/models/test_fire_function.py index e28d64ee..b6a67c37 100644 --- a/swarms/models/test_fire_function.py +++ b/swarms/models/test_fire_function.py @@ -1,13 +1,13 @@ -import pytest from unittest.mock import MagicMock from swarms.models.fire_function import FireFunctionCaller + def test_fire_function_caller_run(mocker): # Create mock model and tokenizer model = MagicMock() tokenizer = MagicMock() - mocker.patch.object(FireFunctionCaller, 'model', model) - mocker.patch.object(FireFunctionCaller, 'tokenizer', tokenizer) + mocker.patch.object(FireFunctionCaller, "model", model) + mocker.patch.object(FireFunctionCaller, "tokenizer", tokenizer) # Create mock task and arguments task = "Add 2 and 3" @@ -38,4 +38,6 @@ def test_fire_function_caller_run(mocker): tokenizer.batch_decode.assert_called_once_with(generated_ids) # Assert the decoded output is printed - assert decoded_output in mocker.patch.object(print, 'call_args_list') + assert decoded_output in mocker.patch.object( + print, "call_args_list" + ) From f3e27dbdf1a813c51037b4102b7b072e827f06d4 Mon Sep 17 00:00:00 2001 From: Kye Date: Sat, 24 Feb 2024 18:39:22 -0800 Subject: [PATCH 5/9] [FEATS][MultiOnAgent] [BUGF][Agent early stopping logic] --- docs/corporate/data_room.md | 29 +-- playground/agents/multion_agent.py | 73 +------ playground/structs/kyle_hackathon.py | 84 ++++++++ playground/structs/message_pool_example.py | 19 ++ pyproject.toml | 1 + requirements.txt | 1 + swarms/agents/__init__.py | 2 + swarms/agents/multion_agent.py | 69 +++++++ swarms/agents/worker_agent.py | 12 +- swarms/models/cog_vlm.py | 12 +- swarms/models/nougat.py | 4 +- swarms/prompts/worker_prompt.py | 30 +-- swarms/structs/agent.py | 47 ++++- swarms/structs/async_workflow.py | 2 +- swarms/structs/message_pool.py | 214 +++++++++++++++++++++ swarms/utils/loguru_logger.py | 10 + swarms/utils/main.py | 2 +- tests/structs/test_message_pool.py | 117 +++++++++++ 18 files changed, 611 insertions(+), 117 deletions(-) create mode 100644 playground/structs/kyle_hackathon.py create mode 100644 playground/structs/message_pool_example.py create mode 100644 swarms/agents/multion_agent.py create mode 100644 swarms/structs/message_pool.py create mode 100644 swarms/utils/loguru_logger.py create mode 100644 tests/structs/test_message_pool.py diff --git a/docs/corporate/data_room.md b/docs/corporate/data_room.md index 8df244a4..0a2f6109 100644 --- a/docs/corporate/data_room.md +++ b/docs/corporate/data_room.md @@ -52,7 +52,6 @@ ## **Introdution** Swarms provides automation-as-a-service through swarms of autonomous agents that work together as a team. We enable our customers to build, deploy, and scale production-grade multi-agent applications to automate real-world tasks. - ### **Vision** Our vision for 2024 is to provide the most reliable infrastructure for deploying autonomous agents into the real world through the Swarm Cloud, our premier cloud platform for the scalable deployment of Multi-Modal Autonomous Agents. The platform focuses on delivering maximum value to users by only taking a small fee when utilizing the agents for the hosted compute power needed to host the agents. @@ -69,16 +68,26 @@ The team has thousands of hours building and optimizing autonomous agents. Leade Key milestones: get 80K framework users in January 2024, start contracts in target verticals, introduce commercial products in 2025 with various pricing models. - +## Resources ### **Pre-Seed Pitch Deck** -- [Here is our pitch deck for our preseed round](https://www.figma.com/file/LlEMXZ48HTIG3S9VzdibaB/Swarm-Pitch-Deck?type=design&node-id=0%3A1& -mode=design&t=D3023hPOz27M9RGD-1) - +- [Here is our pitch deck for our preseed round](https://drive.google.com/file/d/1c76gK5UIdrfN4JOSpSlvVBEOpzR9emWc/view?usp=sharing) ### **The Swarm Corporation Memo** To learn more about our mission, vision, plans for GTM, and much more please refer to the [Swarm Memo here](https://docs.google.com/document/d/1hS_nv_lFjCqLfnJBoF6ULY9roTbSgSuCkvXvSUSc7Lo/edit?usp=sharing) + + +## **Financial Documents** +This section is dedicated entirely for corporate documents. + +- [Cap Table](https://docs.google.com/spreadsheets/d/1wuTWbfhYaY5Xp6nSQ9R0wDtSpwSS9coHxsjKd0UbIDc/edit?usp=sharing) + +- [Cashflow Prediction Sheet](https://docs.google.com/spreadsheets/d/1HQEHCIXXMHajXMl5sj8MEfcQtWfOnD7GjHtNiocpD60/edit?usp=sharing) + + +------ + ## **Product** Swarms is an open source framework for developers in python to enable seamless, reliable, and scalable multi-agent orchestration through modularity, customization, and precision. @@ -86,7 +95,7 @@ Swarms is an open source framework for developers in python to enable seamless, ### Product Growth Metrics | Name | Description | Link | -|----------------------------------|---------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------| +|--------------------------b--------|---------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------------| | Total Downloads of all time | Total number of downloads for the product over its entire lifespan. | [![Downloads](https://static.pepy.tech/badge/swarms)](https://pepy.tech/project/swarms) | | Downloads this month | Number of downloads for the product in the current month. | [![Downloads](https://static.pepy.tech/badge/swarms/month)](https://pepy.tech/project/swarms) | | Total Downloads this week | Total number of downloads for the product in the current week. | [![Downloads](https://static.pepy.tech/badge/swarms/week)](https://pepy.tech/project/swarms) | @@ -98,10 +107,4 @@ Swarms is an open source framework for developers in python to enable seamless, | Github Traffic Metrics | Metrics related to traffic, such as views and clones on Github. | [Github Traffic Metrics](https://github.com/kyegomez/swarms/graphs/traffic) | | Issues with the framework | Current open issues for the product on Github. | [![GitHub issues](https://img.shields.io/github/issues/kyegomez/swarms)](https://github.com/kyegomez/swarms/issues) | - - -## **Corporate Documents** -This section is dedicated entirely for corporate documents. - -- [Cap Table](https://docs.google.com/spreadsheets/d/1wuTWbfhYaY5Xp6nSQ9R0wDtSpwSS9coHxsjKd0UbIDc/edit?usp=sharing) - +------- \ No newline at end of file diff --git a/playground/agents/multion_agent.py b/playground/agents/multion_agent.py index a8f5175d..88e383b3 100644 --- a/playground/agents/multion_agent.py +++ b/playground/agents/multion_agent.py @@ -1,71 +1,12 @@ -import multion - -from swarms.models.base_llm import AbstractLLM +from swarms.agents.multion_agent import MultiOnAgent from swarms.structs.agent import Agent from swarms.structs.concurrent_workflow import ConcurrentWorkflow from swarms.structs.task import Task - -class MultiOnAgent(AbstractLLM): - """ - Represents a multi-on agent that performs browsing tasks. - - Args: - max_steps (int): The maximum number of steps to perform during browsing. - starting_url (str): The starting URL for browsing. - - Attributes: - max_steps (int): The maximum number of steps to perform during browsing. - starting_url (str): The starting URL for browsing. - """ - - def __init__( - self, - multion_api_key: str, - max_steps: int = 4, - starting_url: str = "https://www.google.com", - *args, - **kwargs, - ): - super().__init__(*args, **kwargs) - self.multion_api_key = multion_api_key - self.max_steps = max_steps - self.starting_url = starting_url - - multion.login( - use_api=True, - # multion_api_key=self.multion_api_key - *args, - **kwargs, - ) - - def run(self, task: str, *args, **kwargs): - """ - Runs a browsing task. - - Args: - task (str): The task to perform during browsing. - *args: Additional positional arguments. - **kwargs: Additional keyword arguments. - - Returns: - dict: The response from the browsing task. - """ - response = multion.browse( - { - "cmd": task, - "url": self.starting_url, - "maxSteps": self.max_steps, - }, - *args, - **kwargs, - ) - - return response.result, response.status, response.lastUrl - - # model -model = MultiOnAgent(multion_api_key="") +model = MultiOnAgent( + multion_api_key="" +) # out = model.run("search for a recipe") agent = Agent( @@ -86,16 +27,16 @@ task = Task( ), ) + # Swarm workflow = ConcurrentWorkflow( - max_workers=1000, + max_workers=21, autosave=True, print_results=True, return_results=True, ) + # Add task to workflow workflow.add(task) - -# Run workflow workflow.run() diff --git a/playground/structs/kyle_hackathon.py b/playground/structs/kyle_hackathon.py new file mode 100644 index 00000000..c66de68b --- /dev/null +++ b/playground/structs/kyle_hackathon.py @@ -0,0 +1,84 @@ +import os + +from dotenv import load_dotenv +from swarms import Agent, OpenAIChat +from swarms.agents.multion_agent import MultiOnAgent +from swarms.memory.chroma_db import ChromaDB +from swarms.tools.tool import tool +from swarms.utils.code_interpreter import SubprocessCodeInterpreter + +# Load the environment variables +load_dotenv() + + +# Memory +chroma_db = ChromaDB() + + +# MultiOntool +@tool +def multion_tool( + task: str, + api_key: str = os.environ.get("MULTION_API_KEY"), +): + """ + Executes a task using the MultiOnAgent. + + Args: + task (str): The task to be executed. + api_key (str, optional): The API key for the MultiOnAgent. Defaults to the value of the MULTION_API_KEY environment variable. + + Returns: + The result of the task execution. + """ + multion = MultiOnAgent(multion_api_key=api_key) + return multion(task) + + +# Execute the interpreter tool +@tool +def execute_interpreter_tool(code: str,): + """ + Executes a single command using the interpreter. + + Args: + task (str): The command to be executed. + + Returns: + None + """ + out = SubprocessCodeInterpreter(debug_mode=True) + out = out.run(code) + return code + + +# Get the API key from the environment +api_key = os.environ.get("OPENAI_API_KEY") + +# Initialize the language model +llm = OpenAIChat( + temperature=0.5, + openai_api_key=api_key, +) + + +# Initialize the workflow +agent = Agent( + agent_name="Research Agent", + agent_description="An agent that performs research tasks.", + system_prompt="Perform a research task.", + llm=llm, + max_loops=1, + dashboard=True, + # tools=[multion_tool, execute_interpreter_tool], + verbose=True, + long_term_memory=chroma_db, + stopping_token="done", +) + +# Run the workflow on a task +out = agent.run( + "Generate a 10,000 word blog on health and wellness, and say done" + " when you are done" +) +print(out) diff --git a/playground/structs/message_pool_example.py b/playground/structs/message_pool_example.py new file mode 100644 index 00000000..dca596ba --- /dev/null +++ b/playground/structs/message_pool_example.py @@ -0,0 +1,19 @@ +from swarms.structs.agent import Agent +from swarms.structs.message_pool import MessagePool +from swarms import OpenAIChat + +agent1 = Agent(llm=OpenAIChat(), agent_name="agent1") +agent2 = Agent(llm=OpenAIChat(), agent_name="agent2") +agent3 = Agent(llm=OpenAIChat(), agent_name="agent3") + +moderator = Agent(agent_name="moderator") +agents = [agent1, agent2, agent3] +message_pool = MessagePool( + agents=agents, moderator=moderator, turns=5 +) +message_pool.add(agent=agent1, content="Hello, agent2!", turn=1) +message_pool.add(agent=agent2, content="Hello, agent1!", turn=1) +message_pool.add(agent=agent3, content="Hello, agent1!", turn=1) +message_pool.get_all_messages() +message_pool.get_visible_messages(agent=agent1, turn=1) +message_pool.get_visible_messages(agent=agent2, turn=1) diff --git a/pyproject.toml b/pyproject.toml index 3d3931d0..e9af9b9b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -76,6 +76,7 @@ supervision = "*" scikit-image = "*" pinecone-client = "*" roboflow = "*" +multion = "*" diff --git a/requirements.txt b/requirements.txt index 25fffdfb..2a62f6ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -17,6 +17,7 @@ requests_mock pypdf==4.0.1 accelerate==0.22.0 loguru +multion chromadb tensorflow optimum diff --git a/swarms/agents/__init__.py b/swarms/agents/__init__.py index b213748e..52db5534 100644 --- a/swarms/agents/__init__.py +++ b/swarms/agents/__init__.py @@ -16,6 +16,7 @@ from swarms.agents.stopping_conditions import ( ) from swarms.agents.tool_agent import ToolAgent from swarms.agents.worker_agent import Worker +from swarms.agents.multion_agent import MultiOnAgent __all__ = [ "AbstractAgent", @@ -34,4 +35,5 @@ __all__ = [ "check_end", "Worker", "agent_wrapper", + "MultiOnAgent", ] diff --git a/swarms/agents/multion_agent.py b/swarms/agents/multion_agent.py new file mode 100644 index 00000000..760f3251 --- /dev/null +++ b/swarms/agents/multion_agent.py @@ -0,0 +1,69 @@ +import os +import multion + +from swarms.models.base_llm import AbstractLLM +from dotenv import load_dotenv + +# Load environment variables +load_dotenv() + +# Muliton key +MULTION_API_KEY = os.getenv("MULTION_API_KEY") + + +class MultiOnAgent(AbstractLLM): + """ + Represents a multi-on agent that performs browsing tasks. + + Args: + max_steps (int): The maximum number of steps to perform during browsing. + starting_url (str): The starting URL for browsing. + + Attributes: + max_steps (int): The maximum number of steps to perform during browsing. + starting_url (str): The starting URL for browsing. + """ + + def __init__( + self, + multion_api_key: str = MULTION_API_KEY, + max_steps: int = 4, + starting_url: str = "https://www.google.com", + *args, + **kwargs, + ): + super().__init__(*args, **kwargs) + self.multion_api_key = multion_api_key + self.max_steps = max_steps + self.starting_url = starting_url + + self.multion = multion.login( + use_api=True, + multion_api_key=str(multion_api_key), + *args, + **kwargs, + ) + + def run(self, task: str, *args, **kwargs): + """ + Runs a browsing task. + + Args: + task (str): The task to perform during browsing. + *args: Additional positional arguments. + **kwargs: Additional keyword arguments. + + Returns: + dict: The response from the browsing task. + """ + response = self.multion.browse( + { + "cmd": task, + "url": self.starting_url, + "maxSteps": self.max_steps, + }, + *args, + **kwargs, + ) + + return response.result, response.status, response.lastUrl diff --git a/swarms/agents/worker_agent.py b/swarms/agents/worker_agent.py index d254acef..6dffc483 100644 --- a/swarms/agents/worker_agent.py +++ b/swarms/agents/worker_agent.py @@ -1,12 +1,12 @@ import os -from typing import Any, List +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.tools.tool import BaseTool from swarms.utils.decorators import error_decorator, timing_decorator @@ -48,7 +48,7 @@ class Worker: temperature: float = 0.5, llm=None, openai_api_key: str = None, - tools: List[Any] = None, + tools: List[BaseTool] = None, embedding_size: int = 1536, search_kwargs: dict = {"k": 8}, verbose: bool = False, @@ -165,7 +165,7 @@ class Worker: # @log_decorator @error_decorator @timing_decorator - def run(self, task: str = None, img=None, *args, **kwargs): + def run(self, task: str = None, *args, **kwargs): """ Run the autonomous agent on a given task. @@ -195,7 +195,7 @@ class Worker: - `results`: The results of the agent's processing. """ try: - results = self.run(task, *args, **kwargs) - return results + result = self.agent.run([task], *args, **kwargs) + return result except Exception as error: raise RuntimeError(f"Error while running agent: {error}") diff --git a/swarms/models/cog_vlm.py b/swarms/models/cog_vlm.py index 14b99b60..e456b669 100644 --- a/swarms/models/cog_vlm.py +++ b/swarms/models/cog_vlm.py @@ -227,8 +227,8 @@ class CogVLMMultiModal(BaseMultiModalModel): torch_type = torch.float16 print( - "========Use torch type as:{} with device:{}========\n\n" - .format(torch_type, device) + f"========Use torch type as:{torch_type} with" + f" device:{device}========\n\n" ) if "cuda" in device: @@ -495,7 +495,7 @@ class CogVLMMultiModal(BaseMultiModalModel): choices=[choice_data], object="chat.completion.chunk", ) - yield "{}".format(chunk.model_dump_json(exclude_unset=True)) + yield f"{chunk.model_dump_json(exclude_unset=True)}" previous_text = "" for new_response in self.generate_stream_cogvlm(params): @@ -515,9 +515,7 @@ class CogVLMMultiModal(BaseMultiModalModel): choices=[choice_data], object="chat.completion.chunk", ) - yield "{}".format( - chunk.model_dump_json(exclude_unset=True) - ) + yield f"{chunk.model_dump_json(exclude_unset=True)}" choice_data = ChatCompletionResponseStreamChoice( index=0, delta=DeltaMessage(), @@ -527,4 +525,4 @@ class CogVLMMultiModal(BaseMultiModalModel): choices=[choice_data], object="chat.completion.chunk", ) - yield "{}".format(chunk.model_dump_json(exclude_unset=True)) + yield f"{chunk.model_dump_json(exclude_unset=True)}" diff --git a/swarms/models/nougat.py b/swarms/models/nougat.py index e3d35370..8c95b9c1 100644 --- a/swarms/models/nougat.py +++ b/swarms/models/nougat.py @@ -96,9 +96,7 @@ class Nougat: # Convert the matches to a readable format cleaned_data = [ - "Date: {}, Amount: {}".format( - date, amount.replace(",", "") - ) + f"Date: {date}, Amount: {amount.replace(',', '')}" for date, amount in matches ] diff --git a/swarms/prompts/worker_prompt.py b/swarms/prompts/worker_prompt.py index 165fa058..08636516 100644 --- a/swarms/prompts/worker_prompt.py +++ b/swarms/prompts/worker_prompt.py @@ -3,12 +3,12 @@ import datetime time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S") -def worker_tools_sop_promp(name: str, memory: str): +def worker_tools_sop_promp(name: str, memory: str, time=time): out = """ You are {name}, Your decisions must always be made independently without seeking user assistance. Play to your strengths as an LLM and pursue simple strategies with no legal complications. - If you have completed all your tasks, make sure to use the "finish" command. + If you have completed all your tasks, make sure to use the 'finish' command. GOALS: @@ -19,11 +19,11 @@ def worker_tools_sop_promp(name: str, memory: str): 1. ~4000 word limit for short term memory. Your short term memory is short, so immediately save important information to files. 2. If you are unsure how you previously did something or want to recall past events, thinking about similar events will help you remember. 3. No user assistance - 4. Exclusively use the commands listed in double quotes e.g. "command name" + 4. Exclusively use the commands listed in double quotes e.g. 'command name' Commands: - 1. finish: use this to signal that you have finished all your objectives, args: "response": "final response to let people know you have finished your objectives" + 1. finish: use this to signal that you have finished all your objectives, args: 'response': 'final response to let people know you have finished your objectives' Resources: @@ -42,17 +42,17 @@ def worker_tools_sop_promp(name: str, memory: str): You should only respond in JSON format as described below Response Format: { - "thoughts": { - "text": "thought", - "reasoning": "reasoning", - "plan": "- short bulleted - list that conveys - long-term plan", - "criticism": "constructive self-criticism", - "speak": "thoughts summary to say to user" + 'thoughts': { + 'text': 'thoughts', + 'reasoning': 'reasoning', + 'plan': '- short bulleted - list that conveys - long-term plan', + 'criticism': 'constructive self-criticism', + 'speak': 'thoughts summary to say to user' }, - "command": { - "name": "command name", - "args": { - "arg name": "value" + 'command': { + 'name': 'command name', + 'args': { + 'arg name': 'value' } } } @@ -62,6 +62,6 @@ def worker_tools_sop_promp(name: str, memory: str): [{memory}] Human: Determine which next command to use, and respond using the format specified above: - """.format(name=name, memory=memory, time=time) + """.format(name=name, time=time, memory=memory) return str(out) diff --git a/swarms/structs/agent.py b/swarms/structs/agent.py index be5b4402..e1282e5b 100644 --- a/swarms/structs/agent.py +++ b/swarms/structs/agent.py @@ -24,8 +24,6 @@ from swarms.tools.exec_tool import execute_tool_by_name from swarms.tools.tool import BaseTool from swarms.utils.code_interpreter import SubprocessCodeInterpreter from swarms.utils.data_to_text import data_to_text - -# from swarms.utils.logger import logger from swarms.utils.parse_code import extract_code_from_markdown from swarms.utils.pdf_to_text import pdf_to_text from swarms.utils.token_count_tiktoken import limit_tokens_from_string @@ -33,6 +31,7 @@ from swarms.utils.video_to_frames import ( save_frames_as_images, video_to_frames, ) +import yaml # Utils @@ -209,6 +208,8 @@ class Agent: search_algorithm: Optional[Callable] = None, logs_to_filename: Optional[str] = None, evaluator: Optional[Callable] = None, + output_json: bool = False, + stopping_func: Optional[Callable] = None, *args, **kwargs, ): @@ -262,6 +263,8 @@ class Agent: self.search_algorithm = search_algorithm self.logs_to_filename = logs_to_filename self.evaluator = evaluator + self.output_json = output_json + self.stopping_func = stopping_func # The max_loops will be set dynamically if the dynamic_loop if self.dynamic_loops: @@ -626,6 +629,11 @@ class Agent: ) print(response) + if self.output_json: + response = extract_code_from_markdown( + response + ) + # Add the response to the history history.append(response) @@ -651,13 +659,27 @@ class Agent: "Evaluator", out ) - # Check to see if stopping token is in the output to stop the loop + # Stopping logic for agents if self.stopping_token: + # Check if the stopping token is in the response + if self.stopping_token in response: + break + + if self.stopping_condition: if self._check_stopping_condition( response - ) or parse_done_token(response): + ): + break + + if self.parse_done_token: + if parse_done_token(response): + break + + if self.stopping_func is not None: + if self.stopping_func(response) is True: break + # If the stopping condition is met then break self.step_cache.append(step) logging.info(f"Step: {step}") @@ -1043,6 +1065,22 @@ class Agent: # Get user input response = input("You: ") + def save_to_yaml(self, file_path: str) -> None: + """ + Save the agent to a YAML file + + Args: + file_path (str): The path to the YAML file + """ + try: + logger.info(f"Saving agent to YAML file: {file_path}") + with open(file_path, "w") as f: + yaml.dump(self.__dict__, f) + except Exception as error: + print( + colored(f"Error saving agent to YAML: {error}", "red") + ) + def save_state(self, file_path: str) -> None: """ Saves the current state of the agent to a JSON file, including the llm parameters. @@ -1075,7 +1113,6 @@ class Agent: "autosave": self.autosave, "saved_state_path": self.saved_state_path, "max_loops": self.max_loops, - # "StepCache": self.step_cache, } with open(file_path, "w") as f: diff --git a/swarms/structs/async_workflow.py b/swarms/structs/async_workflow.py index da144642..fa53c46b 100644 --- a/swarms/structs/async_workflow.py +++ b/swarms/structs/async_workflow.py @@ -43,7 +43,7 @@ class AsyncWorkflow: loop: Optional[asyncio.AbstractEventLoop] = None stopping_condition: Optional[Callable] = None - async def add(self, task: Any, tasks: List[Any]): + async def add(self, task: Any = None, tasks: List[Any] = None): """Add tasks to the workflow""" try: if tasks: diff --git a/swarms/structs/message_pool.py b/swarms/structs/message_pool.py new file mode 100644 index 00000000..37dbb19e --- /dev/null +++ b/swarms/structs/message_pool.py @@ -0,0 +1,214 @@ +import hashlib +from time import time_ns +from typing import Callable, List, Optional, Sequence, Union + +from swarms.structs.agent import Agent +from swarms.structs.base_swarm import BaseSwarm +from swarms.utils.loguru_logger import logger + + +def _hash(input: str): + """ + Hashes the input string using SHA256 algorithm. + + Args: + input (str): The string to be hashed. + + Returns: + str: The hexadecimal representation of the hash value. + """ + hex_dig = hashlib.sha256(input.encode("utf-8")).hexdigest() + return hex_dig + + +def msg_hash( + agent: Agent, content: str, turn: int, msg_type: str = "text" +): + """ + Generate a hash value for a message. + + Args: + agent (Agent): The agent sending the message. + content (str): The content of the message. + turn (int): The turn number of the message. + msg_type (str, optional): The type of the message. Defaults to "text". + + Returns: + int: The hash value of the message. + """ + time = time_ns() + return _hash( + f"agent: {agent.agent_name}\ncontent: {content}\ntimestamp:" + f" {str(time)}\nturn: {turn}\nmsg_type: {msg_type}" + ) + + +class MessagePool(BaseSwarm): + """ + A class representing a message pool for agents in a swarm. + + Attributes: + agents (Optional[Sequence[Agent]]): The list of agents in the swarm. + moderator (Optional[Agent]): The moderator agent. + turns (Optional[int]): The number of turns. + routing_function (Optional[Callable]): The routing function for message distribution. + show_names (Optional[bool]): Flag indicating whether to show agent names. + messages (List[Dict]): The list of messages in the pool. + + Examples: + >>> from swarms.structs.agent import Agent + >>> from swarms.structs.message_pool import MessagePool + >>> agent1 = Agent(agent_name="agent1") + >>> agent2 = Agent(agent_name="agent2") + >>> agent3 = Agent(agent_name="agent3") + >>> moderator = Agent(agent_name="moderator") + >>> agents = [agent1, agent2, agent3] + >>> message_pool = MessagePool(agents=agents, moderator=moderator, turns=5) + >>> message_pool.add(agent=agent1, content="Hello, agent2!", turn=1) + >>> message_pool.add(agent=agent2, content="Hello, agent1!", turn=1) + >>> message_pool.add(agent=agent3, content="Hello, agent1!", turn=1) + >>> message_pool.get_all_messages() + [{'agent': Agent(agent_name='agent1'), 'content': 'Hello, agent2!', 'turn': 1, 'visible_to': 'all', 'logged': True}, {'agent': Agent(agent_name='agent2'), 'content': 'Hello, agent1!', 'turn': 1, 'visible_to': 'all', 'logged': True}, {'agent': Agent(agent_name='agent3'), 'content': 'Hello, agent1!', 'turn': 1, 'visible_to': 'all', 'logged': True}] + >>> message_pool.get_visible_messages(agent=agent1, turn=1) + [{'agent': Agent(agent_name='agent1'), 'content': 'Hello, agent2!', 'turn': 1, 'visible_to': 'all', 'logged': True}, {'agent': Agent(agent_name='agent2'), 'content': 'Hello, agent1!', 'turn': 1, 'visible_to': 'all', 'logged': True}, {'agent': Agent(agent_name='agent3'), 'content': 'Hello, agent1!', 'turn': 1, 'visible_to': 'all', 'logged': True}] + >>> message_pool.get_visible_messages(agent=agent2, turn=1) + [{'agent': Agent(agent_name='agent1'), 'content': 'Hello, agent2!', 'turn': 1, 'visible_to': 'all', 'logged': True}, {'agent': Agent(agent_name='agent2'), 'content': 'Hello, agent1!', 'turn': 1, 'visible_to': 'all', 'logged': True}, {'agent': Agent(agent_name='agent3'), 'content': 'Hello, agent1!', 'turn': 1, 'visible_to': 'all', 'logged': True}] + """ + + def __init__( + self, + agents: Optional[Sequence[Agent]] = None, + moderator: Optional[Agent] = None, + turns: Optional[int] = 5, + routing_function: Optional[Callable] = None, + show_names: Optional[bool] = False, + autosave: Optional[bool] = False, + *args, + **kwargs, + ): + super().__init__() + + self.agent = agents + self.moderator = moderator + self.turns = turns + self.routing_function = routing_function + self.show_names = show_names + self.autosave = autosave + + self.messages = [] + + logger.info("MessagePool initialized") + logger.info(f"Number of agents: {len(agents)}") + logger.info( + f"Agents: {[agent.agent_name for agent in agents]}" + ) + logger.info(f"moderator: {moderator.agent_name} is available") + logger.info(f"Number of turns: {turns}") + + def add( + self, + agent: Agent, + content: str, + turn: int, + visible_to: Union[str, List[str]] = "all", + logged: bool = True, + ): + """ + Add a message to the pool. + + Args: + agent (Agent): The agent sending the message. + content (str): The content of the message. + turn (int): The turn number. + visible_to (Union[str, List[str]], optional): The agents who can see the message. Defaults to "all". + logged (bool, optional): Flag indicating whether the message should be logged. Defaults to True. + """ + + self.messages.append( + { + "agent": agent, + "content": content, + "turn": turn, + "visible_to": visible_to, + "logged": logged, + } + ) + logger.info(f"Message added: {content}") + + def reset(self): + """ + Reset the message pool. + """ + self.messages = [] + logger.info("MessagePool reset") + + def last_turn(self): + """ + Get the last turn number. + + Returns: + int: The last turn number. + """ + if len(self.messages) == 0: + return 0 + else: + return self.messages[-1]["turn"] + + @property + def last_message(self): + """ + Get the last message in the pool. + + Returns: + dict: The last message. + """ + if len(self.messages) == 0: + return None + else: + return self.messages[-1] + + def get_all_messages(self): + """ + Get all messages in the pool. + + Returns: + List[Dict]: The list of all messages. + """ + return self.messages + + def get_visible_messages(self, agent: Agent, turn: int): + """ + Get the visible messages for a given agent and turn. + + Args: + agent (Agent): The agent. + turn (int): The turn number. + + Returns: + List[Dict]: The list of visible messages. + """ + # Get the messages before the current turn + prev_messages = [ + message + for message in self.messages + if message["turn"] < turn + ] + + visible_messages = [] + for message in prev_messages: + if ( + message["visible_to"] == "all" + or agent.agent_name in message["visible_to"] + ): + visible_messages.append(message) + return visible_messages + + def query(self, query: str): + """ + Query a message from the messages list and then pass it to the moderator + """ + return [ + (mod, content) + for mod, content in self.messages + if mod == self.moderator + ] diff --git a/swarms/utils/loguru_logger.py b/swarms/utils/loguru_logger.py new file mode 100644 index 00000000..b94ff33f --- /dev/null +++ b/swarms/utils/loguru_logger.py @@ -0,0 +1,10 @@ +from loguru import logger + +logger = logger.add( + "MessagePool.log", + level="INFO", + colorize=True, + format="{time} {message}", + backtrace=True, + diagnose=True, +) diff --git a/swarms/utils/main.py b/swarms/utils/main.py index b94fae11..9dbd47fd 100644 --- a/swarms/utils/main.py +++ b/swarms/utils/main.py @@ -108,7 +108,7 @@ class Code: self.value = value def __str__(self): - return "%d" % self.value + return f"{int(self.value)}" class Color(Code): diff --git a/tests/structs/test_message_pool.py b/tests/structs/test_message_pool.py new file mode 100644 index 00000000..91d0c28b --- /dev/null +++ b/tests/structs/test_message_pool.py @@ -0,0 +1,117 @@ +from swarms.structs.agent import Agent +from swarms.structs.message_pool import MessagePool +from swarms import OpenAIChat + + +def test_message_pool_initialization(): + agent1 = Agent(llm=OpenAIChat(), agent_name="agent1") + agent2 = Agent(llm=OpenAIChat(), agent_name="agent1") + moderator = Agent(llm=OpenAIChat(), agent_name="agent1") + agents = [agent1, agent2] + message_pool = MessagePool( + agents=agents, moderator=moderator, turns=5 + ) + + assert message_pool.agent == agents + assert message_pool.moderator == moderator + assert message_pool.turns == 5 + assert message_pool.messages == [] + + +def test_message_pool_add(): + agent1 = Agent(llm=OpenAIChat(), agent_name="agent1") + message_pool = MessagePool( + agents=[agent1], moderator=agent1, turns=5 + ) + message_pool.add(agent=agent1, content="Hello, world!", turn=1) + + assert message_pool.messages == [ + { + "agent": agent1, + "content": "Hello, world!", + "turn": 1, + "visible_to": "all", + "logged": True, + } + ] + + +def test_message_pool_reset(): + agent1 = Agent(llm=OpenAIChat(), agent_name="agent1") + message_pool = MessagePool( + agents=[agent1], moderator=agent1, turns=5 + ) + message_pool.add(agent=agent1, content="Hello, world!", turn=1) + message_pool.reset() + + assert message_pool.messages == [] + + +def test_message_pool_last_turn(): + agent1 = Agent(llm=OpenAIChat(), agent_name="agent1") + message_pool = MessagePool( + agents=[agent1], moderator=agent1, turns=5 + ) + message_pool.add(agent=agent1, content="Hello, world!", turn=1) + + assert message_pool.last_turn() == 1 + + +def test_message_pool_last_message(): + agent1 = Agent(llm=OpenAIChat(), agent_name="agent1") + message_pool = MessagePool( + agents=[agent1], moderator=agent1, turns=5 + ) + message_pool.add(agent=agent1, content="Hello, world!", turn=1) + + assert message_pool.last_message == { + "agent": agent1, + "content": "Hello, world!", + "turn": 1, + "visible_to": "all", + "logged": True, + } + + +def test_message_pool_get_all_messages(): + agent1 = Agent(llm=OpenAIChat(), agent_name="agent1") + message_pool = MessagePool( + agents=[agent1], moderator=agent1, turns=5 + ) + message_pool.add(agent=agent1, content="Hello, world!", turn=1) + + assert message_pool.get_all_messages() == [ + { + "agent": agent1, + "content": "Hello, world!", + "turn": 1, + "visible_to": "all", + "logged": True, + } + ] + + +def test_message_pool_get_visible_messages(): + agent1 = Agent(llm=OpenAIChat(), agent_name="agent1") + agent2 = Agent(agent_name="agent2") + message_pool = MessagePool( + agents=[agent1, agent2], moderator=agent1, turns=5 + ) + message_pool.add( + agent=agent1, + content="Hello, agent2!", + turn=1, + visible_to=[agent2.agent_name], + ) + + assert message_pool.get_visible_messages( + agent=agent2, turn=2 + ) == [ + { + "agent": agent1, + "content": "Hello, agent2!", + "turn": 1, + "visible_to": [agent2.agent_name], + "logged": True, + } + ] From be46bcf48f0b07e92521e164e3f793b61aa35bb9 Mon Sep 17 00:00:00 2001 From: Kye Date: Sat, 24 Feb 2024 22:53:43 -0800 Subject: [PATCH 6/9] [CLEANUP] --- playground/agents/multion_agent.py | 26 ++++++--- playground/structs/kyle_hackathon.py | 4 +- pyproject.toml | 2 +- runtime/multi_threading.rs | 83 ++++++++++++++-------------- tests/agents/test_multion.py | 57 +++++++++++++++++++ 5 files changed, 121 insertions(+), 51 deletions(-) create mode 100644 tests/agents/test_multion.py diff --git a/playground/agents/multion_agent.py b/playground/agents/multion_agent.py index 88e383b3..a6f2ce24 100644 --- a/playground/agents/multion_agent.py +++ b/playground/agents/multion_agent.py @@ -1,12 +1,10 @@ from swarms.agents.multion_agent import MultiOnAgent -from swarms.structs.agent import Agent -from swarms.structs.concurrent_workflow import ConcurrentWorkflow -from swarms.structs.task import Task +import timeit +from swarms import Agent, ConcurrentWorkflow, Task +from swarms.utils.loguru_logger import logger # model -model = MultiOnAgent( - multion_api_key="" -) +model = MultiOnAgent(multion_api_key="") # out = model.run("search for a recipe") agent = Agent( @@ -17,6 +15,7 @@ agent = Agent( system_prompt=None, ) +logger.info("[Agent][ID][MultiOnAgent][Initialized][Successfully") # Task task = Task( @@ -27,16 +26,25 @@ task = Task( ), ) - # Swarm +logger.info( + f"Running concurrent workflow with task: {task.description}" +) + +# Measure execution time +start_time = timeit.default_timer() + workflow = ConcurrentWorkflow( - max_workers=21, + max_workers=1, autosave=True, print_results=True, return_results=True, ) - # Add task to workflow workflow.add(task) workflow.run() + +# Calculate execution time +execution_time = timeit.default_timer() - start_time +logger.info(f"Execution time: {execution_time} seconds") diff --git a/playground/structs/kyle_hackathon.py b/playground/structs/kyle_hackathon.py index c66de68b..48c15b39 100644 --- a/playground/structs/kyle_hackathon.py +++ b/playground/structs/kyle_hackathon.py @@ -37,7 +37,9 @@ def multion_tool( # Execute the interpreter tool @tool -def execute_interpreter_tool(code: str,): +def execute_interpreter_tool( + code: str, +): """ Executes a single command using the interpreter. diff --git a/pyproject.toml b/pyproject.toml index e9af9b9b..00b4ae20 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "swarms" -version = "4.1.8" +version = "4.1.9" description = "Swarms - Pytorch" license = "MIT" authors = ["Kye Gomez "] diff --git a/runtime/multi_threading.rs b/runtime/multi_threading.rs index 99346ed2..0e75606f 100644 --- a/runtime/multi_threading.rs +++ b/runtime/multi_threading.rs @@ -29,48 +29,51 @@ fn my_module(py: Python, m: &PyModule) -> PyResult<()> { Ok(()) } + + +/// The function returns `Ok(())` if all modules are processed successfully. +/// Note: This code assumes that the necessary dependencies (`pyo3`, `rayon`, `log`) are already imported and initialized. +/// +/// # Arguments +/// +/// * `modules` - A vector of `PythonModule` structs representing the Python modules and functions to execute. +/// * `num_threads` - The number of threads to use for parallel processing. +/// +/// # Examples +/// +/// ``` +/// use pyo3::types::PyModule; +/// use pyo3::types::PyResult; +/// use pyo3::prelude::*; +/// +/// struct PythonModule<'a> { +/// name: &'a str, +/// function: &'a str, +/// args: Vec<&'a str>, +/// } +/// +/// #[pymodule] +/// fn multithreading_processor(modules: Vec, num_threads: usize) -> Result<(), PythonError> { +/// // Function implementation +/// Ok(()) +/// } +/// ``` +/// +/// # Errors +/// +/// Returns a `PythonError` if an import error or a function call error occurs. +/// +/// # Panics +/// +/// This function does not panic. +/// +/// # Safety +/// +/// This function is safe to call, but it assumes that the necessary dependencies (`pyo3`, `rayon`, `log`) are already imported and initialized. +// Initialize Python interpreter #[pyfunction] fn process_python_modules(modules: Vec, num_threads: usize) -> Result<(), PythonError> { - /// The function returns `Ok(())` if all modules are processed successfully. - /// Note: This code assumes that the necessary dependencies (`pyo3`, `rayon`, `log`) are already imported and initialized. - /// - /// # Arguments - /// - /// * `modules` - A vector of `PythonModule` structs representing the Python modules and functions to execute. - /// * `num_threads` - The number of threads to use for parallel processing. - /// - /// # Examples - /// - /// ``` - /// use pyo3::types::PyModule; - /// use pyo3::types::PyResult; - /// use pyo3::prelude::*; - /// - /// struct PythonModule<'a> { - /// name: &'a str, - /// function: &'a str, - /// args: Vec<&'a str>, - /// } - /// - /// #[pymodule] - /// fn multithreading_processor(modules: Vec, num_threads: usize) -> Result<(), PythonError> { - /// // Function implementation - /// Ok(()) - /// } - /// ``` - /// - /// # Errors - /// - /// Returns a `PythonError` if an import error or a function call error occurs. - /// - /// # Panics - /// - /// This function does not panic. - /// - /// # Safety - /// - /// This function is safe to call, but it assumes that the necessary dependencies (`pyo3`, `rayon`, `log`) are already imported and initialized. - // Initialize Python interpreter + let gil = Python::acquire_gil(); let py = gil.python(); diff --git a/tests/agents/test_multion.py b/tests/agents/test_multion.py new file mode 100644 index 00000000..8da68e23 --- /dev/null +++ b/tests/agents/test_multion.py @@ -0,0 +1,57 @@ +import pytest +from unittest.mock import patch, MagicMock +from swarms.agents.multion_agent import MultiOnAgent + + +@patch("swarms.agents.multion_agent.multion") +def test_multion_agent_run(mock_multion): + mock_response = MagicMock() + mock_response.result = "result" + mock_response.status = "status" + mock_response.lastUrl = "lastUrl" + mock_multion.browse.return_value = mock_response + + agent = MultiOnAgent( + multion_api_key="test_key", + max_steps=5, + starting_url="https://www.example.com", + ) + result, status, last_url = agent.run("task") + + assert result == "result" + assert status == "status" + assert last_url == "lastUrl" + mock_multion.browse.assert_called_once_with( + { + "cmd": "task", + "url": "https://www.example.com", + "maxSteps": 5, + } + ) + + +# Additional tests for different tasks +@pytest.mark.parametrize( + "task", ["task1", "task2", "task3", "task4", "task5"] +) +@patch("swarms.agents.multion_agent.multion") +def test_multion_agent_run_different_tasks(mock_multion, task): + mock_response = MagicMock() + mock_response.result = "result" + mock_response.status = "status" + mock_response.lastUrl = "lastUrl" + mock_multion.browse.return_value = mock_response + + agent = MultiOnAgent( + multion_api_key="test_key", + max_steps=5, + starting_url="https://www.example.com", + ) + result, status, last_url = agent.run(task) + + assert result == "result" + assert status == "status" + assert last_url == "lastUrl" + mock_multion.browse.assert_called_once_with( + {"cmd": task, "url": "https://www.example.com", "maxSteps": 5} + ) From db43acbf7db79df02768cbc907d9ed548057a440 Mon Sep 17 00:00:00 2001 From: Kye Date: Sun, 25 Feb 2024 00:22:26 -0800 Subject: [PATCH 7/9] [PLAYGROUND][Cleanup] --- .../multion_agent.py => multion_agent.py | 19 ++++++------- playground/diy/hierchical_example.py | 28 ------------------- playground/memory/chroma_usage_example.py | 18 ++++++++---- ...ow_example.py => agent_basic_customize.py} | 0 ...gterm.py => agent_with_longterm_memory.py} | 2 +- playground/structs/chat_example.py | 11 -------- playground/structs/company_example.py | 1 - .../structs/dialogue_simulator_example.py | 3 +- playground/structs/easy_example.py | 19 +++++++++---- playground/structs/godmode_example.py | 2 +- playground/structs/orchestrator_example.py | 19 ------------- playground/structs/social_app_example.py | 19 ------------- .../structs/stackoverflow_swarm_example.py | 0 playground/structs/swarms_example.py | 16 ----------- playground/structs/todo_app_example.py | 19 ------------- playground/structs/tool_utils_example.py | 19 ------------- playground/structs/workflow_example.py | 7 ----- playground/tools/agent_with_tools_example.py | 24 ++++++++-------- .../tools/tool_prompt_scaper_example.py | 22 --------------- playground/workflow_example_example.py | 10 ------- swarms/agents/multion_agent.py | 16 ++++++----- 21 files changed, 58 insertions(+), 216 deletions(-) rename playground/agents/multion_agent.py => multion_agent.py (63%) delete mode 100644 playground/diy/hierchical_example.py rename playground/structs/{flow_example.py => agent_basic_customize.py} (100%) rename playground/structs/{agent_with_longterm.py => agent_with_longterm_memory.py} (97%) delete mode 100644 playground/structs/chat_example.py delete mode 100644 playground/structs/orchestrator_example.py delete mode 100644 playground/structs/social_app_example.py delete mode 100644 playground/structs/stackoverflow_swarm_example.py delete mode 100644 playground/structs/swarms_example.py delete mode 100644 playground/structs/todo_app_example.py delete mode 100644 playground/structs/tool_utils_example.py delete mode 100644 playground/structs/workflow_example.py delete mode 100644 playground/tools/tool_prompt_scaper_example.py delete mode 100644 playground/workflow_example_example.py diff --git a/playground/agents/multion_agent.py b/multion_agent.py similarity index 63% rename from playground/agents/multion_agent.py rename to multion_agent.py index a6f2ce24..6830e973 100644 --- a/playground/agents/multion_agent.py +++ b/multion_agent.py @@ -1,10 +1,9 @@ from swarms.agents.multion_agent import MultiOnAgent import timeit from swarms import Agent, ConcurrentWorkflow, Task -from swarms.utils.loguru_logger import logger # model -model = MultiOnAgent(multion_api_key="") +model = MultiOnAgent(multion_api_key="535ae401948b4c59bc1b2c61eec90fe6") # out = model.run("search for a recipe") agent = Agent( @@ -15,27 +14,26 @@ agent = Agent( system_prompt=None, ) -logger.info("[Agent][ID][MultiOnAgent][Initialized][Successfully") +# logger.info("[Agent][ID][MultiOnAgent][Initialized][Successfully") # Task task = Task( agent=agent, description=( - "send an email to vyom on superhuman for a partnership with" - " multion" + "Download https://www.coachcamel.com/" ), ) # Swarm -logger.info( - f"Running concurrent workflow with task: {task.description}" -) +# logger.info( +# f"Running concurrent workflow with task: {task.description}" +# ) # Measure execution time start_time = timeit.default_timer() workflow = ConcurrentWorkflow( - max_workers=1, + max_workers=20, autosave=True, print_results=True, return_results=True, @@ -47,4 +45,5 @@ workflow.run() # Calculate execution time execution_time = timeit.default_timer() - start_time -logger.info(f"Execution time: {execution_time} seconds") +# logger.info(f"Execution time: {execution_time} seconds") +print(f"Execution time: {execution_time} seconds") \ No newline at end of file diff --git a/playground/diy/hierchical_example.py b/playground/diy/hierchical_example.py deleted file mode 100644 index 73b58f45..00000000 --- a/playground/diy/hierchical_example.py +++ /dev/null @@ -1,28 +0,0 @@ -from swarms import HierarchicalSwarm - -swarm = HierarchicalSwarm( - openai_api_key="key", - model_type="openai", - model_id="gpt-4", - use_vectorstore=False, - use_async=False, - human_in_the_loop=False, - logging_enabled=False, -) - -# run the swarm with an objective -result = swarm.run("Design a new car") - -# or huggingface -swarm = HierarchicalSwarm( - model_type="huggingface", - model_id="tiaueu/falcon", - use_vectorstore=True, - embedding_size=768, - use_async=False, - human_in_the_loop=True, - logging_enabled=False, -) - -# Run the swarm with a particular objective -result = swarm.run("Write a sci-fi short story") diff --git a/playground/memory/chroma_usage_example.py b/playground/memory/chroma_usage_example.py index c17efa3a..d00822b0 100644 --- a/playground/memory/chroma_usage_example.py +++ b/playground/memory/chroma_usage_example.py @@ -1,11 +1,17 @@ -from swarms.memory import chroma +from swarms.memory import ChromaDB -chromadbcl = chroma.ChromaClient() -chromadbcl.add_vectors( - ["This is a document", "BONSAIIIIIII", "the walking dead"] +# Initialize the memory +chroma = ChromaDB( + metric="cosine", + limit_tokens=1000, + verbose=True, ) -results = chromadbcl.search_vectors("zombie", limit=1) +# Add text +text = "This is a test" +chroma.add(text) + +# Search for similar text +similar_text = chroma.query(text) -print(results) diff --git a/playground/structs/flow_example.py b/playground/structs/agent_basic_customize.py similarity index 100% rename from playground/structs/flow_example.py rename to playground/structs/agent_basic_customize.py diff --git a/playground/structs/agent_with_longterm.py b/playground/structs/agent_with_longterm_memory.py similarity index 97% rename from playground/structs/agent_with_longterm.py rename to playground/structs/agent_with_longterm_memory.py index 588d6546..2dcc30ac 100644 --- a/playground/structs/agent_with_longterm.py +++ b/playground/structs/agent_with_longterm_memory.py @@ -14,7 +14,7 @@ api_key = os.environ.get("OPENAI_API_KEY") # Initilaize the chromadb client chromadb = ChromaDB( - metric="cosine", + metric="cosine",g output="results", ) diff --git a/playground/structs/chat_example.py b/playground/structs/chat_example.py deleted file mode 100644 index 08783068..00000000 --- a/playground/structs/chat_example.py +++ /dev/null @@ -1,11 +0,0 @@ -from swarms import Orchestrator, Worker - -# Instantiate the Orchestrator with 10 agents -orchestrator = Orchestrator( - Worker, agent_list=[Worker] * 10, task_queue=[] -) - -# Agent 1 sends a message to Agent 2 -orchestrator.chat( - sender_id=1, receiver_id=2, message="Hello, Agent 2!" -) diff --git a/playground/structs/company_example.py b/playground/structs/company_example.py index 72396c61..1f008e77 100644 --- a/playground/structs/company_example.py +++ b/playground/structs/company_example.py @@ -1,4 +1,3 @@ -# Example import os diff --git a/playground/structs/dialogue_simulator_example.py b/playground/structs/dialogue_simulator_example.py index 14c35b7e..b83e13ef 100644 --- a/playground/structs/dialogue_simulator_example.py +++ b/playground/structs/dialogue_simulator_example.py @@ -1,6 +1,5 @@ from swarms.models import OpenAIChat -from swarms.swarms import DialogueSimulator -from swarms.workers.worker import Worker +from swarms import DialogueSimulator, Worker llm = OpenAIChat( model_name="gpt-4", openai_api_key="api-key", temperature=0.5 diff --git a/playground/structs/easy_example.py b/playground/structs/easy_example.py index 2a537c10..bebdb11a 100644 --- a/playground/structs/easy_example.py +++ b/playground/structs/easy_example.py @@ -1,7 +1,14 @@ -from swarms import swarm +from swarms import Agent, OpenAIChat -# Use the function -api_key = "APIKEY" -objective = "What is the capital of the UK?" -result = swarm(api_key, objective) -print(result) # Prints: "The capital of the UK is London." +## Initialize the workflow +agent = Agent( + llm=OpenAIChat(), + max_loops=1, + autosave=True, + dashboard=False, + streaming_on=True, + verbose=True, +) + +# Run the workflow on a task +agent("Find a chick fil a equivalent in hayes valley") diff --git a/playground/structs/godmode_example.py b/playground/structs/godmode_example.py index 46f71393..5d3cef83 100644 --- a/playground/structs/godmode_example.py +++ b/playground/structs/godmode_example.py @@ -3,7 +3,7 @@ import os from dotenv import load_dotenv from swarms.models import Anthropic, Gemini, Mixtral, OpenAIChat -from swarms.swarms import ModelParallelizer +from swarms import ModelParallelizer load_dotenv() diff --git a/playground/structs/orchestrator_example.py b/playground/structs/orchestrator_example.py deleted file mode 100644 index 6b91b74f..00000000 --- a/playground/structs/orchestrator_example.py +++ /dev/null @@ -1,19 +0,0 @@ -from swarms import Orchestrator, Worker - -node = Worker( - openai_api_key="", - ai_name="Optimus Prime", -) - - -# Instantiate the Orchestrator with 10 agents -orchestrator = Orchestrator( - node, agent_list=[node] * 10, task_queue=[] -) - -# Agent 7 sends a message to Agent 9 -orchestrator.chat( - sender_id=7, - receiver_id=9, - message="Can you help me with this task?", -) diff --git a/playground/structs/social_app_example.py b/playground/structs/social_app_example.py deleted file mode 100644 index 8bf90bf5..00000000 --- a/playground/structs/social_app_example.py +++ /dev/null @@ -1,19 +0,0 @@ -from ..swarms import HierarchicalSwarm - -# Retrieve your API key from the environment or replace with your actual key -api_key = "sksdsds" - -# Initialize HierarchicalSwarm with your API key -swarm = HierarchicalSwarm(openai_api_key=api_key) - -# Define an objective -objective = """ -Please develop and serve a simple community web service. -People can signup, login, post, comment. -Post and comment should be visible at once. -I want it to have neumorphism-style. -The ports you can use are 4500 and 6500. -""" - -# Run HierarchicalSwarm -swarm.run(objective) diff --git a/playground/structs/stackoverflow_swarm_example.py b/playground/structs/stackoverflow_swarm_example.py deleted file mode 100644 index e69de29b..00000000 diff --git a/playground/structs/swarms_example.py b/playground/structs/swarms_example.py deleted file mode 100644 index 9f015807..00000000 --- a/playground/structs/swarms_example.py +++ /dev/null @@ -1,16 +0,0 @@ -from swarms import HierarchicalSwarm - -# Retrieve your API key from the environment or replace with your actual key -api_key = "" - -# Initialize HierarchicalSwarm with your API key -swarm = HierarchicalSwarm(api_key) - -# Define an objective -objective = ( - "Find 20 potential customers for a HierarchicalSwarm based AI" - " Agent automation infrastructure" -) - -# Run HierarchicalSwarm -swarm.run(objective) diff --git a/playground/structs/todo_app_example.py b/playground/structs/todo_app_example.py deleted file mode 100644 index 981bf499..00000000 --- a/playground/structs/todo_app_example.py +++ /dev/null @@ -1,19 +0,0 @@ -from swarms import HierarchicalSwarm - -# Retrieve your API key from the environment or replace with your actual key -api_key = "sksdsds" - -# Initialize HierarchicalSwarm with your API key -swarm = HierarchicalSwarm(openai_api_key=api_key) - -# Define an objective -objective = """ -Please develop and serve a simple web TODO app. -The user can list all TODO items and add or delete each TODO item. -I want it to have neumorphism-style. -The ports you can use are 4500 and 6500. - -""" - -# Run HierarchicalSwarm -swarm.run(objective) diff --git a/playground/structs/tool_utils_example.py b/playground/structs/tool_utils_example.py deleted file mode 100644 index ff7e17c2..00000000 --- a/playground/structs/tool_utils_example.py +++ /dev/null @@ -1,19 +0,0 @@ -from swarms.tools.tool import tool -from swarms.tools.tool_func_doc_scraper import scrape_tool_func_docs - - -@tool -def search_api(query: str) -> str: - """Search API - - Args: - query (str): _description_ - - Returns: - str: _description_ - """ - print(f"Searching API for {query}") - - -tool_docs = scrape_tool_func_docs(search_api) -print(tool_docs) diff --git a/playground/structs/workflow_example.py b/playground/structs/workflow_example.py deleted file mode 100644 index 0d9f18c4..00000000 --- a/playground/structs/workflow_example.py +++ /dev/null @@ -1,7 +0,0 @@ -from swarms.models import OpenAIChat -from swarms.structs.workflow import Workflow - -llm = OpenAIChat() - - -workflow = Workflow(llm) diff --git a/playground/tools/agent_with_tools_example.py b/playground/tools/agent_with_tools_example.py index 0d736a16..8ebcde69 100644 --- a/playground/tools/agent_with_tools_example.py +++ b/playground/tools/agent_with_tools_example.py @@ -2,8 +2,8 @@ import os from dotenv import load_dotenv -from swarms.models import OpenAIChat -from swarms.structs import Agent +from swarms import OpenAIChat, Agent +from swarms.tools.tool import tool load_dotenv() @@ -12,24 +12,24 @@ api_key = os.environ.get("OPENAI_API_KEY") llm = OpenAIChat(api_key=api_key) -# @tool -# def search_api(query: str) -> str: -# """Search API +@tool +def search_api(query: str) -> str: + """Search API -# Args: -# query (str): _description_ + Args: + query (str): _description_ -# Returns: -# str: _description_ -# """ -# print(f"Searching API for {query}") + Returns: + str: _description_ + """ + print(f"Searching API for {query}") ## Initialize the workflow agent = Agent( llm=llm, max_loops=5, - # tools=[search_api], + tools=[search_api], dashboard=True, ) diff --git a/playground/tools/tool_prompt_scaper_example.py b/playground/tools/tool_prompt_scaper_example.py deleted file mode 100644 index 2c0434d6..00000000 --- a/playground/tools/tool_prompt_scaper_example.py +++ /dev/null @@ -1,22 +0,0 @@ -from swarms.tools.tool import tool -from swarms.tools.tool_func_doc_scraper import scrape_tool_func_docs - -# Define a tool by decorating a function with the tool decorator and providing a docstring - - -@tool(return_direct=True) -def search_api(query: str): - """Search the web for the query - - Args: - query (str): _description_ - - Returns: - _type_: _description_ - """ - return f"Search results for {query}" - - -# Scrape the tool func docs to prepare for injection into the agent prompt -out = scrape_tool_func_docs(search_api) -print(out) diff --git a/playground/workflow_example_example.py b/playground/workflow_example_example.py deleted file mode 100644 index 78909dc7..00000000 --- a/playground/workflow_example_example.py +++ /dev/null @@ -1,10 +0,0 @@ -from swarms import Workflow -from swarms.models import ChatOpenAI - -workflow = Workflow(ChatOpenAI) - -workflow.add("What's the weather in miami") -workflow.add("Provide details for {{ parent_output }}") -workflow.add("Summarize the above information: {{ parent_output}}") - -workflow.run() diff --git a/swarms/agents/multion_agent.py b/swarms/agents/multion_agent.py index 760f3251..ecc36cb8 100644 --- a/swarms/agents/multion_agent.py +++ b/swarms/agents/multion_agent.py @@ -37,12 +37,7 @@ class MultiOnAgent(AbstractLLM): self.max_steps = max_steps self.starting_url = starting_url - self.multion = multion.login( - use_api=True, - multion_api_key=str(multion_api_key), - *args, - **kwargs, - ) + def run(self, task: str, *args, **kwargs): """ @@ -56,7 +51,14 @@ class MultiOnAgent(AbstractLLM): Returns: dict: The response from the browsing task. """ - response = self.multion.browse( + multion.login( + use_api=True, + multion_api_key=str(self.multion_api_key), + *args, + **kwargs, + ) + + response = multion.browse( { "cmd": task, "url": self.starting_url, From ea98cbdae86e91c1d719f0d648ac40fa0eedd5ca Mon Sep 17 00:00:00 2001 From: Kye Date: Sun, 25 Feb 2024 00:27:46 -0800 Subject: [PATCH 8/9] [CLEANUP] --- playground/agents/multi_modal_auto_agent_example.py | 3 +-- multion_agent.py => playground/agents/multion_agent.py | 2 +- playground/structs/agent_with_longterm_memory.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) rename multion_agent.py => playground/agents/multion_agent.py (89%) diff --git a/playground/agents/multi_modal_auto_agent_example.py b/playground/agents/multi_modal_auto_agent_example.py index 4204f48c..a21aebb8 100644 --- a/playground/agents/multi_modal_auto_agent_example.py +++ b/playground/agents/multi_modal_auto_agent_example.py @@ -3,8 +3,7 @@ import os from dotenv import load_dotenv -from swarms.models.gpt4_vision_api import GPT4VisionAPI -from swarms.structs import Agent +from swarms import GPT4VisionAPI, Agent # Load the environment variables load_dotenv() diff --git a/multion_agent.py b/playground/agents/multion_agent.py similarity index 89% rename from multion_agent.py rename to playground/agents/multion_agent.py index 6830e973..b4352e81 100644 --- a/multion_agent.py +++ b/playground/agents/multion_agent.py @@ -3,7 +3,7 @@ import timeit from swarms import Agent, ConcurrentWorkflow, Task # model -model = MultiOnAgent(multion_api_key="535ae401948b4c59bc1b2c61eec90fe6") +model = MultiOnAgent(multion_api_key="api-key") # out = model.run("search for a recipe") agent = Agent( diff --git a/playground/structs/agent_with_longterm_memory.py b/playground/structs/agent_with_longterm_memory.py index 2dcc30ac..588d6546 100644 --- a/playground/structs/agent_with_longterm_memory.py +++ b/playground/structs/agent_with_longterm_memory.py @@ -14,7 +14,7 @@ api_key = os.environ.get("OPENAI_API_KEY") # Initilaize the chromadb client chromadb = ChromaDB( - metric="cosine",g + metric="cosine", output="results", ) From 2c9bad43fbdc124658099ffa754d2dbd758962f4 Mon Sep 17 00:00:00 2001 From: Kye Date: Sun, 25 Feb 2024 21:01:16 -0800 Subject: [PATCH 9/9] CLEANUP --- playground/agents/multion_agent.py | 6 +- playground/memory/chroma_usage_example.py | 1 - playground/structs/company_example.py | 1 - playground/tools/agent_with_tools_example.py | 1 + swarms/agents/multion_agent.py | 4 +- swarms/loaders/pdf_loader.py | 77 -------------------- 6 files changed, 4 insertions(+), 86 deletions(-) delete mode 100644 swarms/loaders/pdf_loader.py diff --git a/playground/agents/multion_agent.py b/playground/agents/multion_agent.py index b4352e81..5534ddce 100644 --- a/playground/agents/multion_agent.py +++ b/playground/agents/multion_agent.py @@ -19,9 +19,7 @@ agent = Agent( # Task task = Task( agent=agent, - description=( - "Download https://www.coachcamel.com/" - ), + description="Download https://www.coachcamel.com/", ) # Swarm @@ -46,4 +44,4 @@ workflow.run() # Calculate execution time execution_time = timeit.default_timer() - start_time # logger.info(f"Execution time: {execution_time} seconds") -print(f"Execution time: {execution_time} seconds") \ No newline at end of file +print(f"Execution time: {execution_time} seconds") diff --git a/playground/memory/chroma_usage_example.py b/playground/memory/chroma_usage_example.py index d00822b0..4f45117e 100644 --- a/playground/memory/chroma_usage_example.py +++ b/playground/memory/chroma_usage_example.py @@ -14,4 +14,3 @@ chroma.add(text) # Search for similar text similar_text = chroma.query(text) - diff --git a/playground/structs/company_example.py b/playground/structs/company_example.py index 1f008e77..abdee607 100644 --- a/playground/structs/company_example.py +++ b/playground/structs/company_example.py @@ -1,4 +1,3 @@ - import os from dotenv import load_dotenv diff --git a/playground/tools/agent_with_tools_example.py b/playground/tools/agent_with_tools_example.py index 8ebcde69..4524edf1 100644 --- a/playground/tools/agent_with_tools_example.py +++ b/playground/tools/agent_with_tools_example.py @@ -12,6 +12,7 @@ api_key = os.environ.get("OPENAI_API_KEY") llm = OpenAIChat(api_key=api_key) + @tool def search_api(query: str) -> str: """Search API diff --git a/swarms/agents/multion_agent.py b/swarms/agents/multion_agent.py index ecc36cb8..2ef66b47 100644 --- a/swarms/agents/multion_agent.py +++ b/swarms/agents/multion_agent.py @@ -37,8 +37,6 @@ class MultiOnAgent(AbstractLLM): self.max_steps = max_steps self.starting_url = starting_url - - def run(self, task: str, *args, **kwargs): """ Runs a browsing task. @@ -57,7 +55,7 @@ class MultiOnAgent(AbstractLLM): *args, **kwargs, ) - + response = multion.browse( { "cmd": task, diff --git a/swarms/loaders/pdf_loader.py b/swarms/loaders/pdf_loader.py deleted file mode 100644 index 34085efb..00000000 --- a/swarms/loaders/pdf_loader.py +++ /dev/null @@ -1,77 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -from pathlib import Path -from typing import IO - -from pypdf import PdfReader - -from swarms.utils.hash import str_to_hash - - -@dataclass -class TextArtifact: - text: str - - -@dataclass -class PDFLoader: - """ - A class for loading PDF files and extracting text artifacts. - - Args: - tokenizer (str): The tokenizer to use for chunking the text. - max_tokens (int): The maximum number of tokens per chunk. - - Methods: - load(source, password=None, *args, **kwargs): - Load a single PDF file and extract text artifacts. - - load_collection(sources, password=None, *args, **kwargs): - Load a collection of PDF files and extract text artifacts. - - Private Methods: - _load_pdf(stream, password=None): - Load a PDF file and extract text artifacts. - - Attributes: - tokenizer (str): The tokenizer used for chunking the text. - max_tokens (int): The maximum number of tokens per chunk. - """ - - tokenizer: str - max_tokens: int - - def __post_init__(self): - self.chunker = PdfChunker( - tokenizer=self.tokenizer, max_tokens=self.max_tokens - ) - - def load( - self, - source: str | IO | Path, - password: str | None = None, - *args, - **kwargs, - ) -> list[TextArtifact]: - return self._load_pdf(source, password) - - def load_collection( - self, - sources: list[str | IO | Path], - password: str | None = None, - *args, - **kwargs, - ) -> dict[str, list[TextArtifact]]: - return { - str_to_hash(str(s)): self._load_pdf(s, password) - for s in sources - } - - def _load_pdf( - self, stream: str | IO | Path, password: str | None - ) -> list[TextArtifact]: - reader = PdfReader(stream, strict=True, password=password) - return [ - TextArtifact(text=p.extract_text()) for p in reader.pages - ]