From 498ff905b0b2ae839b0d956f946da31fd317905c Mon Sep 17 00:00:00 2001 From: Kye Date: Thu, 23 Nov 2023 23:27:40 -0800 Subject: [PATCH] code quality fixes: line length = 80 --- playground/agents/mm_agent_example.py | 5 + playground/agents/revgpt_agent.py | 28 + .../demos/accountant_team/accountant_team.py | 120 +++ playground/demos/ai_research_team/main.py | 5 + playground/demos/autobloggen.py | 4 + playground/demos/autotemp/autotemp.py | 18 +- playground/demos/blog_gen/blog_gen.py | 38 +- .../multi_modal_auto_agent.py | 5 +- playground/demos/nutrition/nutrition.py | 32 +- playground/demos/positive_med/positive_med.py | 9 + playground/structs/flow_tools.py | 65 ++ playground/swarms/debate.py | 21 + playground/swarms/multi_agent_debate.py | 6 + playground/swarms/orchestrate.py | 4 + playground/swarms/orchestrator.py | 4 + playground/swarms/swarms_example.py | 5 + pyproject.toml | 10 + swarms/memory/base.py | 123 +++ swarms/memory/chroma.py | 723 ++++++++++++++++++ swarms/memory/cosine_similarity.py | 11 + swarms/memory/db.py | 177 +++++ swarms/memory/ocean.py | 157 ++++ swarms/memory/pg.py | 5 + swarms/memory/pinecone.py | 8 + swarms/memory/schemas.py | 10 + swarms/models/__init__.py | 4 + swarms/models/anthropic.py | 15 +- swarms/models/bioclip.py | 10 + swarms/models/dalle3.py | 23 + swarms/models/distilled_whisperx.py | 18 + swarms/models/fuyu.py | 13 + swarms/models/gpt4v.py | 261 +++++++ swarms/models/huggingface.py | 25 + swarms/models/idefics.py | 15 + swarms/models/jina_embeds.py | 4 + swarms/models/kosmos2.py | 4 + swarms/models/kosmos_two.py | 14 + swarms/models/mistral.py | 16 + swarms/models/mpt.py | 4 + swarms/models/openai_embeddings.py | 26 +- swarms/models/openai_models.py | 52 ++ swarms/models/palm.py | 12 + swarms/models/simple_ada.py | 6 + swarms/models/speecht5.py | 6 + swarms/models/ssd_1b.py | 17 + swarms/models/wizard_storytelling.py | 4 + swarms/models/yarn_mistral.py | 4 + swarms/prompts/agent_prompt.py | 10 + swarms/prompts/agent_prompts.py | 78 ++ swarms/prompts/multi_modal_prompts.py | 5 + swarms/prompts/python.py | 54 ++ swarms/prompts/sales.py | 30 + swarms/prompts/sales_prompts.py | 30 + swarms/structs/autoscaler.py | 17 + swarms/structs/flow.py | 30 +- swarms/structs/non_linear_workflow.py | 15 +- swarms/structs/sequential_workflow.py | 32 + swarms/swarms/base.py | 8 + swarms/swarms/dialogue_simulator.py | 93 +++ swarms/swarms/god_mode.py | 17 + swarms/swarms/groupchat.py | 13 + swarms/swarms/multi_agent_collab.py | 13 +- swarms/swarms/orchestrate.py | 287 +++++++ swarms/tools/autogpt.py | 200 +++++ swarms/tools/mm_models.py | 284 +++++++ swarms/tools/tool.py | 38 + swarms/utils/code_interpreter.py | 8 + swarms/utils/decorators.py | 8 + swarms/utils/futures.py | 12 + swarms/utils/loggers.py | 11 + swarms/utils/parse_code.py | 8 + tests/agents/omni_modal.py | 40 + tests/memory/oceandb.py | 97 +++ tests/models/bingchat.py | 61 ++ tests/models/gpt4v.py | 414 ++++++++++ tests/models/revgptv1.py | 90 +++ tests/models/test_biogpt.py | 5 + tests/models/test_cohere.py | 20 + tests/models/test_dalle3.py | 59 +- tests/models/test_distill_whisper.py | 18 + tests/models/test_elevenlab.py | 4 + tests/models/test_fuyu.py | 6 + tests/models/test_idefics.py | 10 + tests/models/test_kosmos2.py | 3 + tests/models/test_nougat.py | 4 + tests/models/test_speech_t5.py | 4 + tests/models/test_vilt.py | 4 + tests/models/test_yi_200k.py | 4 + tests/structs/test_agent.py | 33 + tests/swarms/multi_agent_debate.py | 66 ++ tests/tools/test_base.py | 4 + .../utils/test_subprocess_code_interpreter.py | 8 + 92 files changed, 4354 insertions(+), 47 deletions(-) create mode 100644 playground/agents/revgpt_agent.py create mode 100644 playground/demos/accountant_team/accountant_team.py create mode 100644 playground/structs/flow_tools.py create mode 100644 swarms/memory/base.py create mode 100644 swarms/memory/db.py create mode 100644 swarms/memory/ocean.py create mode 100644 swarms/models/gpt4v.py create mode 100644 swarms/swarms/dialogue_simulator.py create mode 100644 swarms/swarms/orchestrate.py create mode 100644 swarms/tools/autogpt.py create mode 100644 swarms/tools/mm_models.py create mode 100644 swarms/utils/futures.py create mode 100644 tests/agents/omni_modal.py create mode 100644 tests/memory/oceandb.py create mode 100644 tests/models/bingchat.py create mode 100644 tests/models/gpt4v.py create mode 100644 tests/models/revgptv1.py create mode 100644 tests/swarms/multi_agent_debate.py diff --git a/playground/agents/mm_agent_example.py b/playground/agents/mm_agent_example.py index 6cedcb29..bc580b19 100644 --- a/playground/agents/mm_agent_example.py +++ b/playground/agents/mm_agent_example.py @@ -12,8 +12,13 @@ img = node.run_img("/image1", "What is this image about?") chat = node.chat( ( +<<<<<<< HEAD "What is your name? Generate a picture of yourself. What is" " this image about?" +======= + "What is your name? Generate a picture of yourself. What is this image" + " about?" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ), streaming=True, ) diff --git a/playground/agents/revgpt_agent.py b/playground/agents/revgpt_agent.py new file mode 100644 index 00000000..16a720e8 --- /dev/null +++ b/playground/agents/revgpt_agent.py @@ -0,0 +1,28 @@ +import os +from dotenv import load_dotenv +from swarms.models.revgptV4 import RevChatGPTModel +from swarms.workers.worker import Worker + +load_dotenv() + +config = { + "model": os.getenv("REVGPT_MODEL"), + "plugin_ids": [os.getenv("REVGPT_PLUGIN_IDS")], + "disable_history": os.getenv("REVGPT_DISABLE_HISTORY") == "True", + "PUID": os.getenv("REVGPT_PUID"), + "unverified_plugin_domains": [ + os.getenv("REVGPT_UNVERIFIED_PLUGIN_DOMAINS") + ], +} + +llm = RevChatGPTModel(access_token=os.getenv("ACCESS_TOKEN"), **config) + +worker = Worker(ai_name="Optimus Prime", llm=llm) + +task = ( + "What were the winning boston marathon times for the past 5 years (ending" + " in 2022)? Generate a table of the year, name, country of origin, and" + " times." +) +response = worker.run(task) +print(response) diff --git a/playground/demos/accountant_team/accountant_team.py b/playground/demos/accountant_team/accountant_team.py new file mode 100644 index 00000000..d9edc2f6 --- /dev/null +++ b/playground/demos/accountant_team/accountant_team.py @@ -0,0 +1,120 @@ +import os +from typing import List + +from dotenv import load_dotenv + +from swarms.models import Anthropic, OpenAIChat +from swarms.prompts.accountant_swarm_prompts import ( + DECISION_MAKING_PROMPT, + DOC_ANALYZER_AGENT_PROMPT, + FRAUD_DETECTION_AGENT_PROMPT, + SUMMARY_GENERATOR_AGENT_PROMPT, +) +from swarms.structs import Flow +from swarms.utils.pdf_to_text import pdf_to_text + +# Environment variables +load_dotenv() +anthropic_api_key = os.getenv("ANTHROPIC_API_KEY") +openai_api_key = os.getenv("OPENAI_API_KEY") + + +# Base llms +llm1 = OpenAIChat( + openai_api_key=openai_api_key, +) + +llm2 = Anthropic( + anthropic_api_key=anthropic_api_key, +) + + +# Agents +doc_analyzer_agent = Flow( + llm=llm1, + sop=DOC_ANALYZER_AGENT_PROMPT, +) +summary_generator_agent = Flow( + llm=llm2, + sop=SUMMARY_GENERATOR_AGENT_PROMPT, +) +decision_making_support_agent = Flow( + llm=llm2, + sop=DECISION_MAKING_PROMPT, +) + + +class AccountantSwarms: + """ + Accountant Swarms is a collection of agents that work together to help + accountants with their work. + + Flow: analyze doc -> detect fraud -> generate summary -> decision making support + + The agents are: + - User Consultant: Asks the user many questions + - Document Analyzer: Extracts text from the image of the financial document + - Fraud Detection: Detects fraud in the document + - Summary Agent: Generates an actionable summary of the document + - Decision Making Support: Provides decision making support to the accountant + + The agents are connected together in a workflow that is defined in the + run method. + + The workflow is as follows: + 1. The Document Analyzer agent extracts text from the image of the + financial document. + 2. The Fraud Detection agent detects fraud in the document. + 3. The Summary Agent generates an actionable summary of the document. + 4. The Decision Making Support agent provides decision making support + + """ + + def __init__( + self, + pdf_path: str, + list_pdfs: List[str] = None, + fraud_detection_instructions: str = None, + summary_agent_instructions: str = None, + decision_making_support_agent_instructions: str = None, + ): + super().__init__() + self.pdf_path = pdf_path + self.list_pdfs = list_pdfs + self.fraud_detection_instructions = fraud_detection_instructions + self.summary_agent_instructions = summary_agent_instructions + self.decision_making_support_agent_instructions = ( + decision_making_support_agent_instructions + ) + + def run(self): + # Transform the pdf to text + pdf_text = pdf_to_text(self.pdf_path) + + # Detect fraud in the document + fraud_detection_agent_output = doc_analyzer_agent.run( + f"{self.fraud_detection_instructions}: {pdf_text}" + ) + + # Generate an actionable summary of the document + summary_agent_output = summary_generator_agent.run( + f"{self.summary_agent_instructions}: {fraud_detection_agent_output}" + ) + + # Provide decision making support to the accountant + decision_making_support_agent_output = decision_making_support_agent.run( + f"{self.decision_making_support_agent_instructions}:" + f" {summary_agent_output}" + ) + + return decision_making_support_agent_output + + +swarm = AccountantSwarms( + pdf_path="tesla.pdf", + fraud_detection_instructions="Detect fraud in the document", + summary_agent_instructions="Generate an actionable summary of the document", + decision_making_support_agent_instructions=( + "Provide decision making support to the business owner:" + ), +) diff --git a/playground/demos/ai_research_team/main.py b/playground/demos/ai_research_team/main.py index bda9e0de..9b196260 100644 --- a/playground/demos/ai_research_team/main.py +++ b/playground/demos/ai_research_team/main.py @@ -48,9 +48,14 @@ paper_implementor_agent = Agent( paper = pdf_to_text(PDF_PATH) algorithmic_psuedocode_agent = paper_summarizer_agent.run( +<<<<<<< HEAD "Focus on creating the algorithmic pseudocode for the novel" f" method in this paper: {paper}" ) pytorch_code = paper_implementor_agent.run( algorithmic_psuedocode_agent +======= + "Focus on creating the algorithmic pseudocode for the novel method in this" + f" paper: {paper}" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) diff --git a/playground/demos/autobloggen.py b/playground/demos/autobloggen.py index 09b02674..536e649a 100644 --- a/playground/demos/autobloggen.py +++ b/playground/demos/autobloggen.py @@ -126,11 +126,15 @@ class AutoBlogGenSwarm: except Exception as error: print( colored( +<<<<<<< HEAD:playground/demos/autobloggen.py ( "Error while running AutoBlogGenSwarm" f" {error}" ), "red", +======= + f"Error while running AutoBlogGenSwarm {error}", "red" +>>>>>>> 49c7b97c (code quality fixes: line length = 80):swarms/swarms/autobloggen.py ) ) if attempt == self.retry_attempts - 1: diff --git a/playground/demos/autotemp/autotemp.py b/playground/demos/autotemp/autotemp.py index ab521606..b136bad7 100644 --- a/playground/demos/autotemp/autotemp.py +++ b/playground/demos/autotemp/autotemp.py @@ -9,11 +9,18 @@ class AutoTemp: """ def __init__( - self, api_key, default_temp=0.0, alt_temps=None, auto_select=True, max_workers=6 + self, + api_key, + default_temp=0.0, + alt_temps=None, + auto_select=True, + max_workers=6, ): self.api_key = api_key self.default_temp = default_temp - self.alt_temps = alt_temps if alt_temps else [0.4, 0.6, 0.8, 1.0, 1.2, 1.4] + self.alt_temps = ( + alt_temps if alt_temps else [0.4, 0.6, 0.8, 1.0, 1.2, 1.4] + ) self.auto_select = auto_select self.max_workers = max_workers self.llm = OpenAIChat( @@ -62,12 +69,15 @@ class AutoTemp: if not scores: return "No valid outputs generated.", None - sorted_scores = sorted(scores.items(), key=lambda item: item[1], reverse=True) + sorted_scores = sorted( + scores.items(), key=lambda item: item[1], reverse=True + ) best_temp, best_score = sorted_scores[0] best_output = outputs[best_temp] return ( - f"Best AutoTemp Output (Temp {best_temp} | Score: {best_score}):\n{best_output}" + f"Best AutoTemp Output (Temp {best_temp} | Score:" + f" {best_score}):\n{best_output}" if self.auto_select else "\n".join( f"Temp {temp} | Score: {score}:\n{outputs[temp]}" diff --git a/playground/demos/blog_gen/blog_gen.py b/playground/demos/blog_gen/blog_gen.py index 3781d895..84ab240d 100644 --- a/playground/demos/blog_gen/blog_gen.py +++ b/playground/demos/blog_gen/blog_gen.py @@ -7,7 +7,10 @@ from swarms.structs import SequentialWorkflow class BlogGen: def __init__( - self, api_key, blog_topic, temperature_range: str = "0.4,0.6,0.8,1.0,1.2" + self, + api_key, + blog_topic, + temperature_range: str = "0.4,0.6,0.8,1.0,1.2", ): # Add blog_topic as an argument self.openai_chat = OpenAIChat(openai_api_key=api_key, temperature=0.8) self.auto_temp = AutoTemp(api_key) @@ -40,7 +43,10 @@ class BlogGen: topic_output = topic_result.generations[0][0].text print( colored( - f"\nTopic Selection Task Output:\n----------------------------\n{topic_output}\n", + ( + "\nTopic Selection Task" + f" Output:\n----------------------------\n{topic_output}\n" + ), "white", ) ) @@ -58,7 +64,10 @@ class BlogGen: initial_draft_output = auto_temp_output # Assuming AutoTemp.run returns the best output directly print( colored( - f"\nInitial Draft Output:\n----------------------------\n{initial_draft_output}\n", + ( + "\nInitial Draft" + f" Output:\n----------------------------\n{initial_draft_output}\n" + ), "white", ) ) @@ -71,7 +80,10 @@ class BlogGen: review_output = review_result.generations[0][0].text print( colored( - f"\nReview Output:\n----------------------------\n{review_output}\n", + ( + "\nReview" + f" Output:\n----------------------------\n{review_output}\n" + ), "white", ) ) @@ -80,22 +92,28 @@ class BlogGen: distribution_prompt = self.DISTRIBUTION_AGENT_SYSTEM_PROMPT.replace( "{{ARTICLE_TOPIC}}", chosen_topic ) - distribution_result = self.openai_chat.generate([distribution_prompt]) + distribution_result = self.openai_chat.generate( + [distribution_prompt] + ) distribution_output = distribution_result.generations[0][0].text print( colored( - f"\nDistribution Output:\n----------------------------\n{distribution_output}\n", + ( + "\nDistribution" + f" Output:\n----------------------------\n{distribution_output}\n" + ), "white", ) ) # Final compilation of the blog - final_blog_content = ( - f"{initial_draft_output}\n\n{review_output}\n\n{distribution_output}" - ) + final_blog_content = f"{initial_draft_output}\n\n{review_output}\n\n{distribution_output}" print( colored( - f"\nFinal Blog Content:\n----------------------------\n{final_blog_content}\n", + ( + "\nFinal Blog" + f" Content:\n----------------------------\n{final_blog_content}\n" + ), "green", ) ) diff --git a/playground/demos/multi_modal_autonomous_agents/multi_modal_auto_agent.py b/playground/demos/multi_modal_autonomous_agents/multi_modal_auto_agent.py index 3e201063..5878a1b8 100644 --- a/playground/demos/multi_modal_autonomous_agents/multi_modal_auto_agent.py +++ b/playground/demos/multi_modal_autonomous_agents/multi_modal_auto_agent.py @@ -23,7 +23,10 @@ from swarms.models import Idefics # Multi Modality Auto Agent llm = Idefics(max_length=2000) -task = "User: What is in this image? https://upload.wikimedia.org/wikipedia/commons/8/86/Id%C3%A9fix.JPG" +task = ( + "User: What is in this image?" + " https://upload.wikimedia.org/wikipedia/commons/8/86/Id%C3%A9fix.JPG" +) ## Initialize the workflow flow = Flow( diff --git a/playground/demos/nutrition/nutrition.py b/playground/demos/nutrition/nutrition.py index c263f2cd..ffdafd7c 100644 --- a/playground/demos/nutrition/nutrition.py +++ b/playground/demos/nutrition/nutrition.py @@ -10,9 +10,16 @@ load_dotenv() openai_api_key = os.getenv("OPENAI_API_KEY") # Define prompts for various tasks -MEAL_PLAN_PROMPT = "Based on the following user preferences: dietary restrictions as vegetarian, preferred cuisines as Italian and Indian, a total caloric intake of around 2000 calories per day, and an exclusion of legumes, create a detailed weekly meal plan. Include a variety of meals for breakfast, lunch, dinner, and optional snacks." +MEAL_PLAN_PROMPT = ( + "Based on the following user preferences: dietary restrictions as" + " vegetarian, preferred cuisines as Italian and Indian, a total caloric" + " intake of around 2000 calories per day, and an exclusion of legumes," + " create a detailed weekly meal plan. Include a variety of meals for" + " breakfast, lunch, dinner, and optional snacks." +) IMAGE_ANALYSIS_PROMPT = ( - "Identify the items in this fridge, including their quantities and condition." + "Identify the items in this fridge, including their quantities and" + " condition." ) @@ -45,7 +52,9 @@ def create_vision_agent(image_path): {"type": "text", "text": IMAGE_ANALYSIS_PROMPT}, { "type": "image_url", - "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}, + "image_url": { + "url": f"data:image/jpeg;base64,{base64_image}" + }, }, ], } @@ -53,7 +62,9 @@ def create_vision_agent(image_path): "max_tokens": 300, } response = requests.post( - "https://api.openai.com/v1/chat/completions", headers=headers, json=payload + "https://api.openai.com/v1/chat/completions", + headers=headers, + json=payload, ) return response.json() @@ -65,10 +76,11 @@ def generate_integrated_shopping_list( # Prepare the prompt for the LLM fridge_contents = image_analysis["choices"][0]["message"]["content"] prompt = ( - f"Based on this meal plan: {meal_plan_output}, " - f"and the following items in the fridge: {fridge_contents}, " - f"considering dietary preferences as vegetarian with a preference for Italian and Indian cuisines, " - f"generate a comprehensive shopping list that includes only the items needed." + f"Based on this meal plan: {meal_plan_output}, and the following items" + f" in the fridge: {fridge_contents}, considering dietary preferences as" + " vegetarian with a preference for Italian and Indian cuisines," + " generate a comprehensive shopping list that includes only the items" + " needed." ) # Send the prompt to the LLM and return the response @@ -94,7 +106,9 @@ user_preferences = { } # Generate Meal Plan -meal_plan_output = meal_plan_agent.run(f"Generate a meal plan: {user_preferences}") +meal_plan_output = meal_plan_agent.run( + f"Generate a meal plan: {user_preferences}" +) # Vision Agent - Analyze an Image image_analysis_output = create_vision_agent("full_fridge.jpg") diff --git a/playground/demos/positive_med/positive_med.py b/playground/demos/positive_med/positive_med.py index b92b9586..77416972 100644 --- a/playground/demos/positive_med/positive_med.py +++ b/playground/demos/positive_med/positive_med.py @@ -41,9 +41,13 @@ def get_review_prompt(article): return prompt +<<<<<<< HEAD def social_media_prompt( article: str, goal: str = "Clicks and engagement" ): +======= +def social_media_prompt(article: str, goal: str = "Clicks and engagement"): +>>>>>>> 49c7b97c (code quality fixes: line length = 80) prompt = SOCIAL_MEDIA_SYSTEM_PROMPT_AGENT.replace( "{{ARTICLE}}", article ).replace("{{GOAL}}", goal) @@ -56,8 +60,13 @@ topic_selection_task = ( " practices" ) topics = llm( +<<<<<<< HEAD f"Your System Instructions: {TOPIC_GENERATOR_SYSTEM_PROMPT}, Your" f" current task: {topic_selection_task}" +======= + f"Your System Instructions: {TOPIC_GENERATOR}, Your current task:" + f" {topic_selection_task}" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) dashboard = print( diff --git a/playground/structs/flow_tools.py b/playground/structs/flow_tools.py new file mode 100644 index 00000000..42ec0f72 --- /dev/null +++ b/playground/structs/flow_tools.py @@ -0,0 +1,65 @@ +from swarms.models import Anthropic +from swarms.structs import Flow +from swarms.tools.tool import tool + +import asyncio + + +llm = Anthropic( + anthropic_api_key="", +) + + +async def async_load_playwright(url: str) -> str: + """Load the specified URLs using Playwright and parse using BeautifulSoup.""" + from bs4 import BeautifulSoup + from playwright.async_api import async_playwright + + results = "" + async with async_playwright() as p: + browser = await p.chromium.launch(headless=True) + try: + page = await browser.new_page() + await page.goto(url) + + page_source = await page.content() + soup = BeautifulSoup(page_source, "html.parser") + + for script in soup(["script", "style"]): + script.extract() + + text = soup.get_text() + lines = (line.strip() for line in text.splitlines()) + chunks = ( + phrase.strip() for line in lines for phrase in line.split(" ") + ) + results = "\n".join(chunk for chunk in chunks if chunk) + except Exception as e: + results = f"Error: {e}" + await browser.close() + return results + + +def run_async(coro): + event_loop = asyncio.get_event_loop() + return event_loop.run_until_complete(coro) + + +@tool +def browse_web_page(url: str) -> str: + """Verbose way to scrape a whole webpage. Likely to cause issues parsing.""" + return run_async(async_load_playwright(url)) + + +## Initialize the workflow +flow = Flow( + llm=llm, + max_loops=5, + tools=[browse_web_page], + dashboard=True, +) + +out = flow.run( + "Generate a 10,000 word blog on mental clarity and the benefits of" + " meditation." +) diff --git a/playground/swarms/debate.py b/playground/swarms/debate.py index 5108d527..16a2cae4 100644 --- a/playground/swarms/debate.py +++ b/playground/swarms/debate.py @@ -37,9 +37,13 @@ class DialogueAgent: [ self.system_message, HumanMessage( +<<<<<<< HEAD content="\n".join( self.message_history + [self.prefix] ) +======= + content="\n".join(self.message_history + [self.prefix]) +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ), ] ) @@ -133,8 +137,12 @@ The presidential candidates are: {', '.join(character_names)}.""" player_descriptor_system_message = SystemMessage( content=( +<<<<<<< HEAD "You can add detail to the description of each presidential" " candidate." +======= + "You can add detail to the description of each presidential candidate." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) ) @@ -163,9 +171,13 @@ Your goal is to be as creative as possible and make the voters think you are the """ +<<<<<<< HEAD def generate_character_system_message( character_name, character_header ): +======= +def generate_character_system_message(character_name, character_header): +>>>>>>> 49c7b97c (code quality fixes: line length = 80) return SystemMessage(content=f"""{character_header} You will speak in the style of {character_name}, and exaggerate their personality. You will come up with creative ideas related to {topic}. @@ -192,9 +204,13 @@ character_headers = [ ) ] character_system_messages = [ +<<<<<<< HEAD generate_character_system_message( character_name, character_headers ) +======= + generate_character_system_message(character_name, character_headers) +>>>>>>> 49c7b97c (code quality fixes: line length = 80) for character_name, character_headers in zip( character_names, character_headers ) @@ -220,8 +236,13 @@ for ( class BidOutputParser(RegexParser): def get_format_instructions(self) -> str: return ( +<<<<<<< HEAD "Your response should be an integer delimited by angled" " brackets, like this: ." +======= + "Your response should be an integer delimited by angled brackets," + " like this: ." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) diff --git a/playground/swarms/multi_agent_debate.py b/playground/swarms/multi_agent_debate.py index 6124a21c..4d4e0ce0 100644 --- a/playground/swarms/multi_agent_debate.py +++ b/playground/swarms/multi_agent_debate.py @@ -40,9 +40,15 @@ debate = MultiAgentDebate(agents, select_speaker) # Run task task = ( +<<<<<<< HEAD "What were the winning boston marathon times for the past 5 years" " (ending in 2022)? Generate a table of the year, name, country" " of origin, and times." +======= + "What were the winning boston marathon times for the past 5 years (ending" + " in 2022)? Generate a table of the year, name, country of origin, and" + " times." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) results = debate.run(task, max_iters=4) diff --git a/playground/swarms/orchestrate.py b/playground/swarms/orchestrate.py index b0e17588..dd59d9e2 100644 --- a/playground/swarms/orchestrate.py +++ b/playground/swarms/orchestrate.py @@ -13,7 +13,11 @@ orchestrator = Orchestrator( # Agent 7 sends a message to Agent 9 orchestrator.chat( +<<<<<<< HEAD sender_id=7, receiver_id=9, message="Can you help me with this task?", +======= + sender_id=7, receiver_id=9, message="Can you help me with this task?" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) diff --git a/playground/swarms/orchestrator.py b/playground/swarms/orchestrator.py index b0e17588..dd59d9e2 100644 --- a/playground/swarms/orchestrator.py +++ b/playground/swarms/orchestrator.py @@ -13,7 +13,11 @@ orchestrator = Orchestrator( # Agent 7 sends a message to Agent 9 orchestrator.chat( +<<<<<<< HEAD sender_id=7, receiver_id=9, message="Can you help me with this task?", +======= + sender_id=7, receiver_id=9, message="Can you help me with this task?" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) diff --git a/playground/swarms/swarms_example.py b/playground/swarms/swarms_example.py index 9f015807..87d9b3c8 100644 --- a/playground/swarms/swarms_example.py +++ b/playground/swarms/swarms_example.py @@ -8,8 +8,13 @@ swarm = HierarchicalSwarm(api_key) # Define an objective objective = ( +<<<<<<< HEAD "Find 20 potential customers for a HierarchicalSwarm based AI" " Agent automation infrastructure" +======= + "Find 20 potential customers for a HierarchicalSwarm based AI Agent" + " automation infrastructure" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) # Run HierarchicalSwarm diff --git a/pyproject.toml b/pyproject.toml index 17f789aa..fd97c4ad 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -85,3 +85,13 @@ aggressive = 3 [tool.ruff] line-length = 80 +<<<<<<< HEAD +======= + +[tool.black] +line-length = 80 +target-version = ['py38'] +preview = true + + +>>>>>>> 49c7b97c (code quality fixes: line length = 80) diff --git a/swarms/memory/base.py b/swarms/memory/base.py new file mode 100644 index 00000000..3ca49617 --- /dev/null +++ b/swarms/memory/base.py @@ -0,0 +1,123 @@ +from abc import ABC, abstractmethod +from concurrent import futures +from dataclasses import dataclass +from typing import Optional, Any +from attr import define, field, Factory +from swarms.utils.futures import execute_futures_dict +from griptape.artifacts import TextArtifact + + +@define +class BaseVectorStore(ABC): + """ """ + + DEFAULT_QUERY_COUNT = 5 + + @dataclass + class QueryResult: + id: str + vector: list[float] + score: float + meta: Optional[dict] = None + namespace: Optional[str] = None + + @dataclass + class Entry: + id: str + vector: list[float] + meta: Optional[dict] = None + namespace: Optional[str] = None + + embedding_driver: Any + futures_executor: futures.Executor = field( + default=Factory(lambda: futures.ThreadPoolExecutor()), kw_only=True + ) + + def upsert_text_artifacts( + self, + artifacts: dict[str, list[TextArtifact]], + meta: Optional[dict] = None, + **kwargs, + ) -> None: + execute_futures_dict( + { + namespace: self.futures_executor.submit( + self.upsert_text_artifact, a, namespace, meta, **kwargs + ) + for namespace, artifact_list in artifacts.items() + for a in artifact_list + } + ) + + def upsert_text_artifact( + self, + artifact: TextArtifact, + namespace: Optional[str] = None, + meta: Optional[dict] = None, + **kwargs, + ) -> str: + if not meta: + meta = {} + + meta["artifact"] = artifact.to_json() + + if artifact.embedding: + vector = artifact.embedding + else: + vector = artifact.generate_embedding(self.embedding_driver) + + return self.upsert_vector( + vector, + vector_id=artifact.id, + namespace=namespace, + meta=meta, + **kwargs, + ) + + def upsert_text( + self, + string: str, + vector_id: Optional[str] = None, + namespace: Optional[str] = None, + meta: Optional[dict] = None, + **kwargs, + ) -> str: + return self.upsert_vector( + self.embedding_driver.embed_string(string), + vector_id=vector_id, + namespace=namespace, + meta=meta if meta else {}, + **kwargs, + ) + + @abstractmethod + def upsert_vector( + self, + vector: list[float], + vector_id: Optional[str] = None, + namespace: Optional[str] = None, + meta: Optional[dict] = None, + **kwargs, + ) -> str: + ... + + @abstractmethod + def load_entry( + self, vector_id: str, namespace: Optional[str] = None + ) -> Entry: + ... + + @abstractmethod + def load_entries(self, namespace: Optional[str] = None) -> list[Entry]: + ... + + @abstractmethod + def query( + self, + query: str, + count: Optional[int] = None, + namespace: Optional[str] = None, + include_vectors: bool = False, + **kwargs, + ) -> list[QueryResult]: + ... diff --git a/swarms/memory/chroma.py b/swarms/memory/chroma.py index e69de29b..08de83d9 100644 --- a/swarms/memory/chroma.py +++ b/swarms/memory/chroma.py @@ -0,0 +1,723 @@ +<<<<<<< HEAD +======= +from __future__ import annotations + +import logging +import uuid +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Dict, + Iterable, + List, + Optional, + Tuple, + Type, +) + +import numpy as np + +from swarms.structs.document import Document +from swarms.models.embeddings_base import Embeddings +from langchain.schema.vectorstore import VectorStore +from langchain.utils import xor_args +from langchain.vectorstores.utils import maximal_marginal_relevance + +if TYPE_CHECKING: + import chromadb + import chromadb.config + from chromadb.api.types import ID, OneOrMany, Where, WhereDocument + +logger = logging.getLogger() +DEFAULT_K = 4 # Number of Documents to return. + + +def _results_to_docs(results: Any) -> List[Document]: + return [doc for doc, _ in _results_to_docs_and_scores(results)] + + +def _results_to_docs_and_scores(results: Any) -> List[Tuple[Document, float]]: + return [ + # TODO: Chroma can do batch querying, + # we shouldn't hard code to the 1st result + (Document(page_content=result[0], metadata=result[1] or {}), result[2]) + for result in zip( + results["documents"][0], + results["metadatas"][0], + results["distances"][0], + ) + ] + + +class Chroma(VectorStore): + """`ChromaDB` vector store. + + To use, you should have the ``chromadb`` python package installed. + + Example: + .. code-block:: python + + from langchain.vectorstores import Chroma + from langchain.embeddings.openai import OpenAIEmbeddings + + embeddings = OpenAIEmbeddings() + vectorstore = Chroma("langchain_store", embeddings) + """ + + _LANGCHAIN_DEFAULT_COLLECTION_NAME = "langchain" + + def __init__( + self, + collection_name: str = _LANGCHAIN_DEFAULT_COLLECTION_NAME, + embedding_function: Optional[Embeddings] = None, + persist_directory: Optional[str] = None, + client_settings: Optional[chromadb.config.Settings] = None, + collection_metadata: Optional[Dict] = None, + client: Optional[chromadb.Client] = None, + relevance_score_fn: Optional[Callable[[float], float]] = None, + ) -> None: + """Initialize with a Chroma client.""" + try: + import chromadb + import chromadb.config + except ImportError: + raise ImportError( + "Could not import chromadb python package. " + "Please install it with `pip install chromadb`." + ) + + if client is not None: + self._client_settings = client_settings + self._client = client + self._persist_directory = persist_directory + else: + if client_settings: + # If client_settings is provided with persist_directory specified, + # then it is "in-memory and persisting to disk" mode. + client_settings.persist_directory = ( + persist_directory or client_settings.persist_directory + ) + if client_settings.persist_directory is not None: + # Maintain backwards compatibility with chromadb < 0.4.0 + major, minor, _ = chromadb.__version__.split(".") + if int(major) == 0 and int(minor) < 4: + client_settings.chroma_db_impl = "duckdb+parquet" + + _client_settings = client_settings + elif persist_directory: + # Maintain backwards compatibility with chromadb < 0.4.0 + major, minor, _ = chromadb.__version__.split(".") + if int(major) == 0 and int(minor) < 4: + _client_settings = chromadb.config.Settings( + chroma_db_impl="duckdb+parquet", + ) + else: + _client_settings = chromadb.config.Settings( + is_persistent=True + ) + _client_settings.persist_directory = persist_directory + else: + _client_settings = chromadb.config.Settings() + self._client_settings = _client_settings + self._client = chromadb.Client(_client_settings) + self._persist_directory = ( + _client_settings.persist_directory or persist_directory + ) + + self._embedding_function = embedding_function + self._collection = self._client.get_or_create_collection( + name=collection_name, + embedding_function=( + self._embedding_function.embed_documents + if self._embedding_function is not None + else None + ), + metadata=collection_metadata, + ) + self.override_relevance_score_fn = relevance_score_fn + + @property + def embeddings(self) -> Optional[Embeddings]: + return self._embedding_function + + @xor_args(("query_texts", "query_embeddings")) + def __query_collection( + self, + query_texts: Optional[List[str]] = None, + query_embeddings: Optional[List[List[float]]] = None, + n_results: int = 4, + where: Optional[Dict[str, str]] = None, + where_document: Optional[Dict[str, str]] = None, + **kwargs: Any, + ) -> List[Document]: + """Query the chroma collection.""" + try: + import chromadb # noqa: F401 + except ImportError: + raise ValueError( + "Could not import chromadb python package. " + "Please install it with `pip install chromadb`." + ) + return self._collection.query( + query_texts=query_texts, + query_embeddings=query_embeddings, + n_results=n_results, + where=where, + where_document=where_document, + **kwargs, + ) + + def add_texts( + self, + texts: Iterable[str], + metadatas: Optional[List[dict]] = None, + ids: Optional[List[str]] = None, + **kwargs: Any, + ) -> List[str]: + """Run more texts through the embeddings and add to the vectorstore. + + Args: + texts (Iterable[str]): Texts to add to the vectorstore. + metadatas (Optional[List[dict]], optional): Optional list of metadatas. + ids (Optional[List[str]], optional): Optional list of IDs. + + Returns: + List[str]: List of IDs of the added texts. + """ + # TODO: Handle the case where the user doesn't provide ids on the Collection + if ids is None: + ids = [str(uuid.uuid1()) for _ in texts] + embeddings = None + texts = list(texts) + if self._embedding_function is not None: + embeddings = self._embedding_function.embed_documents(texts) + if metadatas: + # fill metadatas with empty dicts if somebody + # did not specify metadata for all texts + length_diff = len(texts) - len(metadatas) + if length_diff: + metadatas = metadatas + [{}] * length_diff + empty_ids = [] + non_empty_ids = [] + for idx, m in enumerate(metadatas): + if m: + non_empty_ids.append(idx) + else: + empty_ids.append(idx) + if non_empty_ids: + metadatas = [metadatas[idx] for idx in non_empty_ids] + texts_with_metadatas = [texts[idx] for idx in non_empty_ids] + embeddings_with_metadatas = ( + [embeddings[idx] for idx in non_empty_ids] + if embeddings + else None + ) + ids_with_metadata = [ids[idx] for idx in non_empty_ids] + try: + self._collection.upsert( + metadatas=metadatas, + embeddings=embeddings_with_metadatas, + documents=texts_with_metadatas, + ids=ids_with_metadata, + ) + except ValueError as e: + if "Expected metadata value to be" in str(e): + msg = ( + "Try filtering complex metadata from the document" + " using " + "langchain.vectorstores.utils.filter_complex_metadata." + ) + raise ValueError(e.args[0] + "\n\n" + msg) + else: + raise e + if empty_ids: + texts_without_metadatas = [texts[j] for j in empty_ids] + embeddings_without_metadatas = ( + [embeddings[j] for j in empty_ids] if embeddings else None + ) + ids_without_metadatas = [ids[j] for j in empty_ids] + self._collection.upsert( + embeddings=embeddings_without_metadatas, + documents=texts_without_metadatas, + ids=ids_without_metadatas, + ) + else: + self._collection.upsert( + embeddings=embeddings, + documents=texts, + ids=ids, + ) + return ids + + def similarity_search( + self, + query: str, + k: int = DEFAULT_K, + filter: Optional[Dict[str, str]] = None, + **kwargs: Any, + ) -> List[Document]: + """Run similarity search with Chroma. + + Args: + query (str): Query text to search for. + k (int): Number of results to return. Defaults to 4. + filter (Optional[Dict[str, str]]): Filter by metadata. Defaults to None. + + Returns: + List[Document]: List of documents most similar to the query text. + """ + docs_and_scores = self.similarity_search_with_score( + query, k, filter=filter + ) + return [doc for doc, _ in docs_and_scores] + + def similarity_search_by_vector( + self, + embedding: List[float], + k: int = DEFAULT_K, + filter: Optional[Dict[str, str]] = None, + where_document: Optional[Dict[str, str]] = None, + **kwargs: Any, + ) -> List[Document]: + """Return docs most similar to embedding vector. + Args: + embedding (List[float]): Embedding to look up documents similar to. + k (int): Number of Documents to return. Defaults to 4. + filter (Optional[Dict[str, str]]): Filter by metadata. Defaults to None. + Returns: + List of Documents most similar to the query vector. + """ + results = self.__query_collection( + query_embeddings=embedding, + n_results=k, + where=filter, + where_document=where_document, + ) + return _results_to_docs(results) + + def similarity_search_by_vector_with_relevance_scores( + self, + embedding: List[float], + k: int = DEFAULT_K, + filter: Optional[Dict[str, str]] = None, + where_document: Optional[Dict[str, str]] = None, + **kwargs: Any, + ) -> List[Tuple[Document, float]]: + """ + Return docs most similar to embedding vector and similarity score. + + Args: + embedding (List[float]): Embedding to look up documents similar to. + k (int): Number of Documents to return. Defaults to 4. + filter (Optional[Dict[str, str]]): Filter by metadata. Defaults to None. + + Returns: + List[Tuple[Document, float]]: List of documents most similar to + the query text and cosine distance in float for each. + Lower score represents more similarity. + """ + results = self.__query_collection( + query_embeddings=embedding, + n_results=k, + where=filter, + where_document=where_document, + ) + return _results_to_docs_and_scores(results) + + def similarity_search_with_score( + self, + query: str, + k: int = DEFAULT_K, + filter: Optional[Dict[str, str]] = None, + where_document: Optional[Dict[str, str]] = None, + **kwargs: Any, + ) -> List[Tuple[Document, float]]: + """Run similarity search with Chroma with distance. + + Args: + query (str): Query text to search for. + k (int): Number of results to return. Defaults to 4. + filter (Optional[Dict[str, str]]): Filter by metadata. Defaults to None. + + Returns: + List[Tuple[Document, float]]: List of documents most similar to + the query text and cosine distance in float for each. + Lower score represents more similarity. + """ + if self._embedding_function is None: + results = self.__query_collection( + query_texts=[query], + n_results=k, + where=filter, + where_document=where_document, + ) + else: + query_embedding = self._embedding_function.embed_query(query) + results = self.__query_collection( + query_embeddings=[query_embedding], + n_results=k, + where=filter, + where_document=where_document, + ) + + return _results_to_docs_and_scores(results) + + def _select_relevance_score_fn(self) -> Callable[[float], float]: + """ + The 'correct' relevance function + may differ depending on a few things, including: + - the distance / similarity metric used by the VectorStore + - the scale of your embeddings (OpenAI's are unit normed. Many others are not!) + - embedding dimensionality + - etc. + """ + if self.override_relevance_score_fn: + return self.override_relevance_score_fn + + distance = "l2" + distance_key = "hnsw:space" + metadata = self._collection.metadata + + if metadata and distance_key in metadata: + distance = metadata[distance_key] + + if distance == "cosine": + return self._cosine_relevance_score_fn + elif distance == "l2": + return self._euclidean_relevance_score_fn + elif distance == "ip": + return self._max_inner_product_relevance_score_fn + else: + raise ValueError( + "No supported normalization function" + f" for distance metric of type: {distance}." + "Consider providing relevance_score_fn to Chroma constructor." + ) + + def max_marginal_relevance_search_by_vector( + self, + embedding: List[float], + k: int = DEFAULT_K, + fetch_k: int = 20, + lambda_mult: float = 0.5, + filter: Optional[Dict[str, str]] = None, + where_document: Optional[Dict[str, str]] = None, + **kwargs: Any, + ) -> List[Document]: + """Return docs selected using the maximal marginal relevance. + Maximal marginal relevance optimizes for similarity to query AND diversity + among selected documents. + + Args: + embedding: Embedding to look up documents similar to. + k: Number of Documents to return. Defaults to 4. + fetch_k: Number of Documents to fetch to pass to MMR algorithm. + lambda_mult: Number between 0 and 1 that determines the degree + of diversity among the results with 0 corresponding + to maximum diversity and 1 to minimum diversity. + Defaults to 0.5. + filter (Optional[Dict[str, str]]): Filter by metadata. Defaults to None. + + Returns: + List of Documents selected by maximal marginal relevance. + """ + + results = self.__query_collection( + query_embeddings=embedding, + n_results=fetch_k, + where=filter, + where_document=where_document, + include=["metadatas", "documents", "distances", "embeddings"], + ) + mmr_selected = maximal_marginal_relevance( + np.array(embedding, dtype=np.float32), + results["embeddings"][0], + k=k, + lambda_mult=lambda_mult, + ) + + candidates = _results_to_docs(results) + + selected_results = [ + r for i, r in enumerate(candidates) if i in mmr_selected + ] + return selected_results + + def max_marginal_relevance_search( + self, + query: str, + k: int = DEFAULT_K, + fetch_k: int = 20, + lambda_mult: float = 0.5, + filter: Optional[Dict[str, str]] = None, + where_document: Optional[Dict[str, str]] = None, + **kwargs: Any, + ) -> List[Document]: + """Return docs selected using the maximal marginal relevance. + Maximal marginal relevance optimizes for similarity to query AND diversity + among selected documents. + + Args: + query: Text to look up documents similar to. + k: Number of Documents to return. Defaults to 4. + fetch_k: Number of Documents to fetch to pass to MMR algorithm. + lambda_mult: Number between 0 and 1 that determines the degree + of diversity among the results with 0 corresponding + to maximum diversity and 1 to minimum diversity. + Defaults to 0.5. + filter (Optional[Dict[str, str]]): Filter by metadata. Defaults to None. + + Returns: + List of Documents selected by maximal marginal relevance. + """ + if self._embedding_function is None: + raise ValueError( + "For MMR search, you must specify an embedding function" + " oncreation." + ) + + embedding = self._embedding_function.embed_query(query) + docs = self.max_marginal_relevance_search_by_vector( + embedding, + k, + fetch_k, + lambda_mult=lambda_mult, + filter=filter, + where_document=where_document, + ) + return docs + + def delete_collection(self) -> None: + """Delete the collection.""" + self._client.delete_collection(self._collection.name) + + def get( + self, + ids: Optional[OneOrMany[ID]] = None, + where: Optional[Where] = None, + limit: Optional[int] = None, + offset: Optional[int] = None, + where_document: Optional[WhereDocument] = None, + include: Optional[List[str]] = None, + ) -> Dict[str, Any]: + """Gets the collection. + + Args: + ids: The ids of the embeddings to get. Optional. + where: A Where type dict used to filter results by. + E.g. `{"color" : "red", "price": 4.20}`. Optional. + limit: The number of documents to return. Optional. + offset: The offset to start returning results from. + Useful for paging results with limit. Optional. + where_document: A WhereDocument type dict used to filter by the documents. + E.g. `{$contains: "hello"}`. Optional. + include: A list of what to include in the results. + Can contain `"embeddings"`, `"metadatas"`, `"documents"`. + Ids are always included. + Defaults to `["metadatas", "documents"]`. Optional. + """ + kwargs = { + "ids": ids, + "where": where, + "limit": limit, + "offset": offset, + "where_document": where_document, + } + + if include is not None: + kwargs["include"] = include + + return self._collection.get(**kwargs) + + def persist(self) -> None: + """Persist the collection. + + This can be used to explicitly persist the data to disk. + It will also be called automatically when the object is destroyed. + """ + if self._persist_directory is None: + raise ValueError( + "You must specify a persist_directory on" + "creation to persist the collection." + ) + import chromadb + + # Maintain backwards compatibility with chromadb < 0.4.0 + major, minor, _ = chromadb.__version__.split(".") + if int(major) == 0 and int(minor) < 4: + self._client.persist() + + def update_document(self, document_id: str, document: Document) -> None: + """Update a document in the collection. + + Args: + document_id (str): ID of the document to update. + document (Document): Document to update. + """ + return self.update_documents([document_id], [document]) + + def update_documents( + self, ids: List[str], documents: List[Document] + ) -> None: + """Update a document in the collection. + + Args: + ids (List[str]): List of ids of the document to update. + documents (List[Document]): List of documents to update. + """ + text = [document.page_content for document in documents] + metadata = [document.metadata for document in documents] + if self._embedding_function is None: + raise ValueError( + "For update, you must specify an embedding function on" + " creation." + ) + embeddings = self._embedding_function.embed_documents(text) + + if hasattr( + self._collection._client, "max_batch_size" + ): # for Chroma 0.4.10 and above + from chromadb.utils.batch_utils import create_batches + + for batch in create_batches( + api=self._collection._client, + ids=ids, + metadatas=metadata, + documents=text, + embeddings=embeddings, + ): + self._collection.update( + ids=batch[0], + embeddings=batch[1], + documents=batch[3], + metadatas=batch[2], + ) + else: + self._collection.update( + ids=ids, + embeddings=embeddings, + documents=text, + metadatas=metadata, + ) + + @classmethod + def from_texts( + cls: Type[Chroma], + texts: List[str], + embedding: Optional[Embeddings] = None, + metadatas: Optional[List[dict]] = None, + ids: Optional[List[str]] = None, + collection_name: str = _LANGCHAIN_DEFAULT_COLLECTION_NAME, + persist_directory: Optional[str] = None, + client_settings: Optional[chromadb.config.Settings] = None, + client: Optional[chromadb.Client] = None, + collection_metadata: Optional[Dict] = None, + **kwargs: Any, + ) -> Chroma: + """Create a Chroma vectorstore from a raw documents. + + If a persist_directory is specified, the collection will be persisted there. + Otherwise, the data will be ephemeral in-memory. + + Args: + texts (List[str]): List of texts to add to the collection. + collection_name (str): Name of the collection to create. + persist_directory (Optional[str]): Directory to persist the collection. + embedding (Optional[Embeddings]): Embedding function. Defaults to None. + metadatas (Optional[List[dict]]): List of metadatas. Defaults to None. + ids (Optional[List[str]]): List of document IDs. Defaults to None. + client_settings (Optional[chromadb.config.Settings]): Chroma client settings + collection_metadata (Optional[Dict]): Collection configurations. + Defaults to None. + + Returns: + Chroma: Chroma vectorstore. + """ + chroma_collection = cls( + collection_name=collection_name, + embedding_function=embedding, + persist_directory=persist_directory, + client_settings=client_settings, + client=client, + collection_metadata=collection_metadata, + **kwargs, + ) + if ids is None: + ids = [str(uuid.uuid1()) for _ in texts] + if hasattr( + chroma_collection._client, "max_batch_size" + ): # for Chroma 0.4.10 and above + from chromadb.utils.batch_utils import create_batches + + for batch in create_batches( + api=chroma_collection._client, + ids=ids, + metadatas=metadatas, + documents=texts, + ): + chroma_collection.add_texts( + texts=batch[3] if batch[3] else [], + metadatas=batch[2] if batch[2] else None, + ids=batch[0], + ) + else: + chroma_collection.add_texts( + texts=texts, metadatas=metadatas, ids=ids + ) + return chroma_collection + + @classmethod + def from_documents( + cls: Type[Chroma], + documents: List[Document], + embedding: Optional[Embeddings] = None, + ids: Optional[List[str]] = None, + collection_name: str = _LANGCHAIN_DEFAULT_COLLECTION_NAME, + persist_directory: Optional[str] = None, + client_settings: Optional[chromadb.config.Settings] = None, + client: Optional[chromadb.Client] = None, # Add this line + collection_metadata: Optional[Dict] = None, + **kwargs: Any, + ) -> Chroma: + """Create a Chroma vectorstore from a list of documents. + + If a persist_directory is specified, the collection will be persisted there. + Otherwise, the data will be ephemeral in-memory. + + Args: + collection_name (str): Name of the collection to create. + persist_directory (Optional[str]): Directory to persist the collection. + ids (Optional[List[str]]): List of document IDs. Defaults to None. + documents (List[Document]): List of documents to add to the vectorstore. + embedding (Optional[Embeddings]): Embedding function. Defaults to None. + client_settings (Optional[chromadb.config.Settings]): Chroma client settings + collection_metadata (Optional[Dict]): Collection configurations. + Defaults to None. + + Returns: + Chroma: Chroma vectorstore. + """ + texts = [doc.page_content for doc in documents] + metadatas = [doc.metadata for doc in documents] + return cls.from_texts( + texts=texts, + embedding=embedding, + metadatas=metadatas, + ids=ids, + collection_name=collection_name, + persist_directory=persist_directory, + client_settings=client_settings, + client=client, + collection_metadata=collection_metadata, + **kwargs, + ) + + def delete(self, ids: Optional[List[str]] = None, **kwargs: Any) -> None: + """Delete by vector IDs. + + Args: + ids: List of ids to delete. + """ + self._collection.delete(ids=ids) +>>>>>>> 49c7b97c (code quality fixes: line length = 80) diff --git a/swarms/memory/cosine_similarity.py b/swarms/memory/cosine_similarity.py index 6e7b1df3..f901154b 100644 --- a/swarms/memory/cosine_similarity.py +++ b/swarms/memory/cosine_similarity.py @@ -18,8 +18,13 @@ def cosine_similarity(X: Matrix, Y: Matrix) -> np.ndarray: Y = np.array(Y) if X.shape[1] != Y.shape[1]: raise ValueError( +<<<<<<< HEAD "Number of columns in X and Y must be the same. X has" f" shape {X.shape} and Y has shape {Y.shape}." +======= + "Number of columns in X and Y must be the same. X has shape" + f" {X.shape} and Y has shape {Y.shape}." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) try: import simsimd as simd @@ -32,9 +37,15 @@ def cosine_similarity(X: Matrix, Y: Matrix) -> np.ndarray: return Z except ImportError: logger.info( +<<<<<<< HEAD "Unable to import simsimd, defaulting to NumPy" " implementation. If you want to use simsimd please" " install with `pip install simsimd`." +======= + "Unable to import simsimd, defaulting to NumPy implementation. If" + " you want to use simsimd please install with `pip install" + " simsimd`." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) X_norm = np.linalg.norm(X, axis=1) Y_norm = np.linalg.norm(Y, axis=1) diff --git a/swarms/memory/db.py b/swarms/memory/db.py new file mode 100644 index 00000000..4ffec16f --- /dev/null +++ b/swarms/memory/db.py @@ -0,0 +1,177 @@ +import uuid +from abc import ABC +from typing import Any, Dict, List, Optional + +from swarms.memory.schemas import Artifact, Status +from swarms.memory.schemas import Step as APIStep +from swarms.memory.schemas import Task as APITask + + +class Step(APIStep): + additional_properties: Optional[Dict[str, str]] = None + + +class Task(APITask): + steps: List[Step] = [] + + +class NotFoundException(Exception): + """ + Exception raised when a resource is not found. + """ + + def __init__(self, item_name: str, item_id: str): + self.item_name = item_name + self.item_id = item_id + super().__init__(f"{item_name} with {item_id} not found.") + + +class TaskDB(ABC): + async def create_task( + self, + input: Optional[str], + additional_input: Any = None, + artifacts: Optional[List[Artifact]] = None, + steps: Optional[List[Step]] = None, + ) -> Task: + raise NotImplementedError + + async def create_step( + self, + task_id: str, + name: Optional[str] = None, + input: Optional[str] = None, + is_last: bool = False, + additional_properties: Optional[Dict[str, str]] = None, + ) -> Step: + raise NotImplementedError + + async def create_artifact( + self, + task_id: str, + file_name: str, + relative_path: Optional[str] = None, + step_id: Optional[str] = None, + ) -> Artifact: + raise NotImplementedError + + async def get_task(self, task_id: str) -> Task: + raise NotImplementedError + + async def get_step(self, task_id: str, step_id: str) -> Step: + raise NotImplementedError + + async def get_artifact(self, task_id: str, artifact_id: str) -> Artifact: + raise NotImplementedError + + async def list_tasks(self) -> List[Task]: + raise NotImplementedError + + async def list_steps( + self, task_id: str, status: Optional[Status] = None + ) -> List[Step]: + raise NotImplementedError + + +class InMemoryTaskDB(TaskDB): + _tasks: Dict[str, Task] = {} + + async def create_task( + self, + input: Optional[str], + additional_input: Any = None, + artifacts: Optional[List[Artifact]] = None, + steps: Optional[List[Step]] = None, + ) -> Task: + if not steps: + steps = [] + if not artifacts: + artifacts = [] + task_id = str(uuid.uuid4()) + task = Task( + task_id=task_id, + input=input, + steps=steps, + artifacts=artifacts, + additional_input=additional_input, + ) + self._tasks[task_id] = task + return task + + async def create_step( + self, + task_id: str, + name: Optional[str] = None, + input: Optional[str] = None, + is_last=False, + additional_properties: Optional[Dict[str, Any]] = None, + ) -> Step: + step_id = str(uuid.uuid4()) + step = Step( + task_id=task_id, + step_id=step_id, + name=name, + input=input, + status=Status.created, + is_last=is_last, + additional_properties=additional_properties, + ) + task = await self.get_task(task_id) + task.steps.append(step) + return step + + async def get_task(self, task_id: str) -> Task: + task = self._tasks.get(task_id, None) + if not task: + raise NotFoundException("Task", task_id) + return task + + async def get_step(self, task_id: str, step_id: str) -> Step: + task = await self.get_task(task_id) + step = next(filter(lambda s: s.task_id == task_id, task.steps), None) + if not step: + raise NotFoundException("Step", step_id) + return step + + async def get_artifact(self, task_id: str, artifact_id: str) -> Artifact: + task = await self.get_task(task_id) + artifact = next( + filter(lambda a: a.artifact_id == artifact_id, task.artifacts), None + ) + if not artifact: + raise NotFoundException("Artifact", artifact_id) + return artifact + + async def create_artifact( + self, + task_id: str, + file_name: str, + relative_path: Optional[str] = None, + step_id: Optional[str] = None, + ) -> Artifact: + artifact_id = str(uuid.uuid4()) + artifact = Artifact( + artifact_id=artifact_id, + file_name=file_name, + relative_path=relative_path, + ) + task = await self.get_task(task_id) + task.artifacts.append(artifact) + + if step_id: + step = await self.get_step(task_id, step_id) + step.artifacts.append(artifact) + + return artifact + + async def list_tasks(self) -> List[Task]: + return [task for task in self._tasks.values()] + + async def list_steps( + self, task_id: str, status: Optional[Status] = None + ) -> List[Step]: + task = await self.get_task(task_id) + steps = task.steps + if status: + steps = list(filter(lambda s: s.status == status, steps)) + return steps diff --git a/swarms/memory/ocean.py b/swarms/memory/ocean.py new file mode 100644 index 00000000..fb0873af --- /dev/null +++ b/swarms/memory/ocean.py @@ -0,0 +1,157 @@ +import logging +from typing import List + +import oceandb +from oceandb.utils.embedding_function import MultiModalEmbeddingFunction + + +class OceanDB: + """ + A class to interact with OceanDB. + + ... + + Attributes + ---------- + client : oceandb.Client + a client to interact with OceanDB + + Methods + ------- + create_collection(collection_name: str, modality: str): + Creates a new collection in OceanDB. + append_document(collection, document: str, id: str): + Appends a document to a collection in OceanDB. + add_documents(collection, documents: List[str], ids: List[str]): + Adds multiple documents to a collection in OceanDB. + query(collection, query_texts: list[str], n_results: int): + Queries a collection in OceanDB. + """ + + def __init__(self, client: oceandb.Client = None): + """ + Constructs all the necessary attributes for the OceanDB object. + + Parameters + ---------- + client : oceandb.Client, optional + a client to interact with OceanDB (default is None, which creates a new client) + """ + try: + self.client = client if client else oceandb.Client() + print(self.client.heartbeat()) + except Exception as e: + logging.error(f"Failed to initialize OceanDB client. Error: {e}") + raise + + def create_collection(self, collection_name: str, modality: str): + """ + Creates a new collection in OceanDB. + + Parameters + ---------- + collection_name : str + the name of the new collection + modality : str + the modality of the new collection + + Returns + ------- + collection + the created collection + """ + try: + embedding_function = MultiModalEmbeddingFunction(modality=modality) + collection = self.client.create_collection( + collection_name, embedding_function=embedding_function + ) + return collection + except Exception as e: + logging.error(f"Failed to create collection. Error {e}") + raise + + def append_document(self, collection, document: str, id: str): + """ + Appends a document to a collection in OceanDB. + + Parameters + ---------- + collection + the collection to append the document to + document : str + the document to append + id : str + the id of the document + + Returns + ------- + result + the result of the append operation + """ + try: + return collection.add(documents=[document], ids=[id]) + except Exception as e: + logging.error( + f"Failed to append document to the collection. Error {e}" + ) + raise + + def add_documents(self, collection, documents: List[str], ids: List[str]): + """ + Adds multiple documents to a collection in OceanDB. + + Parameters + ---------- + collection + the collection to add the documents to + documents : List[str] + the documents to add + ids : List[str] + the ids of the documents + + Returns + ------- + result + the result of the add operation + """ + try: + return collection.add(documents=documents, ids=ids) + except Exception as e: + logging.error(f"Failed to add documents to collection. Error: {e}") + raise + + def query(self, collection, query_texts: list[str], n_results: int): + """ + Queries a collection in OceanDB. + + Parameters + ---------- + collection + the collection to query + query_texts : list[str] + the texts to query + n_results : int + the number of results to return + + Returns + ------- + results + the results of the query + """ + try: + results = collection.query( + query_texts=query_texts, n_results=n_results + ) + return results + except Exception as e: + logging.error(f"Failed to query the collection. Error {e}") + raise + + +# Example +# ocean = OceanDB() +# collection = ocean.create_collection("test", "text") +# ocean.append_document(collection, "hello world", "1") +# ocean.add_documents(collection, ["hello world", "hello world"], ["2", "3"]) +# results = ocean.query(collection, ["hello world"], 3) +# print(results) diff --git a/swarms/memory/pg.py b/swarms/memory/pg.py index 50972d98..e6fa00b9 100644 --- a/swarms/memory/pg.py +++ b/swarms/memory/pg.py @@ -127,8 +127,13 @@ class PgVectorVectorStore(BaseVectorStore): if not connection_string.startswith("postgresql://"): raise ValueError( +<<<<<<< HEAD "The connection string must describe a Postgres" " database connection" +======= + "The connection string must describe a Postgres database" + " connection" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) @engine.validator diff --git a/swarms/memory/pinecone.py b/swarms/memory/pinecone.py index ee26bccb..36f53edc 100644 --- a/swarms/memory/pinecone.py +++ b/swarms/memory/pinecone.py @@ -144,7 +144,11 @@ class PineconeVectorStoreStore(BaseVectorStore): def load_entries( self, namespace: Optional[str] = None +<<<<<<< HEAD ) -> list[BaseVectorStore.Entry]: +======= + ) -> list[BaseVector.Entry]: +>>>>>>> 49c7b97c (code quality fixes: line length = 80) """Load entries""" # This is a hacky way to query up to 10,000 values from Pinecone. Waiting on an official API for fetching # all values from a namespace: @@ -176,7 +180,11 @@ class PineconeVectorStoreStore(BaseVectorStore): # PineconDBStorageDriver-specific params: include_metadata=True, **kwargs, +<<<<<<< HEAD ) -> list[BaseVectorStore.QueryResult]: +======= + ) -> list[BaseVector.QueryResult]: +>>>>>>> 49c7b97c (code quality fixes: line length = 80) """Query vectors""" vector = self.embedding_driver.embed_string(query) diff --git a/swarms/memory/schemas.py b/swarms/memory/schemas.py index 9147a909..b19e6ad1 100644 --- a/swarms/memory/schemas.py +++ b/swarms/memory/schemas.py @@ -60,8 +60,12 @@ class StepOutput(BaseModel): __root__: Any = Field( ..., description=( +<<<<<<< HEAD "Output that the task step has produced. Any value is" " allowed." +======= + "Output that the task step has produced. Any value is allowed." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ), example='{\n"tokens": 7894,\n"estimated_cost": "0,24$"\n}', ) @@ -132,9 +136,15 @@ class Step(StepRequestBody): None, description="Output of the task step.", example=( +<<<<<<< HEAD "I am going to use the write_to_file command and write" " Washington to a file called output.txt" " >>>>>> 49c7b97c (code quality fixes: line length = 80) ), ) additional_output: Optional[StepOutput] = None diff --git a/swarms/models/__init__.py b/swarms/models/__init__.py index 09e2170d..57a854f7 100644 --- a/swarms/models/__init__.py +++ b/swarms/models/__init__.py @@ -8,10 +8,14 @@ from swarms.models.openai_models import ( AzureOpenAI, OpenAIChat, ) # noqa: E402 +<<<<<<< HEAD # from swarms.models.vllm import vLLM # noqa: E402 # from swarms.models.zephyr import Zephyr # noqa: E402 +======= +from swarms.models.zephyr import Zephyr # noqa: E402 +>>>>>>> 49c7b97c (code quality fixes: line length = 80) from swarms.models.biogpt import BioGPT # noqa: E402 from swarms.models.huggingface import HuggingfaceLLM # noqa: E402 from swarms.models.wizard_storytelling import ( diff --git a/swarms/models/anthropic.py b/swarms/models/anthropic.py index adffe49d..679a46d4 100644 --- a/swarms/models/anthropic.py +++ b/swarms/models/anthropic.py @@ -200,11 +200,9 @@ def build_extra_kwargs( if field_name in extra_kwargs: raise ValueError(f"Found {field_name} supplied twice.") if field_name not in all_required_field_names: - warnings.warn( - f"""WARNING! {field_name} is not default parameter. + warnings.warn(f"""WARNING! {field_name} is not default parameter. {field_name} was transferred to model_kwargs. - Please confirm that {field_name} is what you intended.""" - ) + Please confirm that {field_name} is what you intended.""") extra_kwargs[field_name] = values.pop(field_name) invalid_model_kwargs = all_required_field_names.intersection( @@ -212,9 +210,14 @@ def build_extra_kwargs( ) if invalid_model_kwargs: raise ValueError( +<<<<<<< HEAD f"Parameters {invalid_model_kwargs} should be specified" " explicitly. Instead they were passed in as part of" " `model_kwargs` parameter." +======= + f"Parameters {invalid_model_kwargs} should be specified explicitly." + " Instead they were passed in as part of `model_kwargs` parameter." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) return extra_kwargs @@ -415,8 +418,12 @@ class Anthropic(LLM, _AnthropicCommon): # As a last resort, wrap the prompt ourselves to emulate instruct-style. return ( +<<<<<<< HEAD f"{self.HUMAN_PROMPT} {prompt}{self.AI_PROMPT} Sure, here" " you go:\n" +======= + f"{self.HUMAN_PROMPT} {prompt}{self.AI_PROMPT} Sure, here you go:\n" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) def _call( diff --git a/swarms/models/bioclip.py b/swarms/models/bioclip.py index e2d070af..bb056536 100644 --- a/swarms/models/bioclip.py +++ b/swarms/models/bioclip.py @@ -112,9 +112,15 @@ class BioClip: template: str = "this is a photo of ", context_length: int = 256, ): +<<<<<<< HEAD image = torch.stack( [self.preprocess_val(Image.open(img_path))] ).to(self.device) +======= + image = torch.stack([self.preprocess_val(Image.open(img_path))]).to( + self.device + ) +>>>>>>> 49c7b97c (code quality fixes: line length = 80) texts = self.tokenizer( [template + l for l in labels], context_length=context_length, @@ -152,10 +158,14 @@ class BioClip: metadata["filename"] + "\n" + "\n".join( +<<<<<<< HEAD [ f"{k}: {v*100:.1f}" for k, v in metadata["top_probs"].items() ] +======= + [f"{k}: {v*100:.1f}" for k, v in metadata["top_probs"].items()] +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) ) ax.set_title(title, fontsize=14) diff --git a/swarms/models/dalle3.py b/swarms/models/dalle3.py index 40f63418..42e7f87a 100644 --- a/swarms/models/dalle3.py +++ b/swarms/models/dalle3.py @@ -171,8 +171,13 @@ class Dalle3: print( colored( ( +<<<<<<< HEAD f"Error running Dalle3: {error} try" " optimizing your api key and or try again" +======= + f"Error running Dalle3: {error} try optimizing your api" + " key and or try again" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ), "red", ) @@ -238,8 +243,13 @@ class Dalle3: print( colored( ( +<<<<<<< HEAD f"Error running Dalle3: {error} try" " optimizing your api key and or try again" +======= + f"Error running Dalle3: {error} try optimizing your api" + " key and or try again" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ), "red", ) @@ -324,15 +334,21 @@ class Dalle3: print( colored( ( +<<<<<<< HEAD f"Error running Dalle3: {error} try" " optimizing your api key and or try" " again" +======= + f"Error running Dalle3: {error} try optimizing" + " your api key and or try again" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ), "red", ) ) print( colored( +<<<<<<< HEAD ( "Error running Dalle3:" f" {error.http_status}" @@ -345,6 +361,13 @@ class Dalle3: f"Error running Dalle3: {error.error}", "red", ) +======= + f"Error running Dalle3: {error.http_status}", "red" + ) + ) + print( + colored(f"Error running Dalle3: {error.error}", "red") +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) raise error diff --git a/swarms/models/distilled_whisperx.py b/swarms/models/distilled_whisperx.py index 951dcd10..da324dee 100644 --- a/swarms/models/distilled_whisperx.py +++ b/swarms/models/distilled_whisperx.py @@ -33,8 +33,13 @@ def async_retry(max_retries=3, exceptions=(Exception,), delay=1): if retries <= 0: raise print( +<<<<<<< HEAD f"Retry after exception: {e}, Attempts" f" remaining: {retries}" +======= + f"Retry after exception: {e}, Attempts remaining:" + f" {retries}" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) await asyncio.sleep(delay) @@ -70,9 +75,13 @@ class DistilWhisperModel: def __init__(self, model_id="distil-whisper/distil-large-v2"): self.device = "cuda:0" if torch.cuda.is_available() else "cpu" self.torch_dtype = ( +<<<<<<< HEAD torch.float16 if torch.cuda.is_available() else torch.float32 +======= + torch.float16 if torch.cuda.is_available() else torch.float32 +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) self.model_id = model_id self.model = AutoModelForSpeechSeq2Seq.from_pretrained( @@ -162,8 +171,13 @@ class DistilWhisperModel: return_tensors="pt", padding=True, ) +<<<<<<< HEAD processed_inputs = ( processed_inputs.input_values.to(self.device) +======= + processed_inputs = processed_inputs.input_values.to( + self.device +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) # Generate transcription for the chunk @@ -185,8 +199,12 @@ class DistilWhisperModel: except Exception as e: print( +<<<<<<< HEAD colored( f"An error occurred during transcription: {e}", "red", ) +======= + colored(f"An error occurred during transcription: {e}", "red") +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) diff --git a/swarms/models/fuyu.py b/swarms/models/fuyu.py index f722bbb6..f16ffb39 100644 --- a/swarms/models/fuyu.py +++ b/swarms/models/fuyu.py @@ -69,9 +69,22 @@ class Fuyu(BaseMultiModalModel): text (str): _description_ img (str): _description_ +<<<<<<< HEAD Returns: _type_: _description_ """ +======= + output = self.model.generate( + **model_inputs, max_new_tokens=self.max_new_tokens + ) + text = self.processor.batch_decode( + output[:, -7:], skip_special_tokens=True + ) + return print(str(text)) + + def get_img_from_web(self, img_url: str): + """Get the image from the web""" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) try: img = self.get_img(img) model_inputs = self.processor( diff --git a/swarms/models/gpt4v.py b/swarms/models/gpt4v.py new file mode 100644 index 00000000..8f2683e0 --- /dev/null +++ b/swarms/models/gpt4v.py @@ -0,0 +1,261 @@ +import os +import asyncio +import base64 +import concurrent.futures +import re +from dataclasses import dataclass +from typing import List, Optional, Tuple + +import openai +import requests +from cachetools import TTLCache +from dotenv import load_dotenv +from openai import OpenAI +from ratelimit import limits, sleep_and_retry +from termcolor import colored + +# ENV +load_dotenv() + + +@dataclass +class GPT4VisionResponse: + """A response structure for GPT-4""" + + answer: str + + +@dataclass +class GPT4Vision: + """ + GPT4Vision model class + + Attributes: + ----------- + max_retries: int + The maximum number of retries to make to the API + backoff_factor: float + The backoff factor to use for exponential backoff + timeout_seconds: int + The timeout in seconds for the API request + api_key: str + The API key to use for the API request + quality: str + The quality of the image to generate + max_tokens: int + The maximum number of tokens to use for the API request + + Methods: + -------- + process_img(self, img_path: str) -> str: + Processes the image to be used for the API request + run(self, img: Union[str, List[str]], tasks: List[str]) -> GPT4VisionResponse: + Makes a call to the GPT-4 Vision API and returns the image url + + Example: + >>> gpt4vision = GPT4Vision() + >>> img = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png" + >>> tasks = ["A painting of a dog"] + >>> answer = gpt4vision(img, tasks) + >>> print(answer) + + """ + + max_retries: int = 3 + model: str = "gpt-4-vision-preview" + backoff_factor: float = 2.0 + timeout_seconds: int = 10 + openai_api_key: Optional[str] = None or os.getenv("OPENAI_API_KEY") + # 'Low' or 'High' for respesctively fast or high quality, but high more token usage + quality: str = "low" + # Max tokens to use for the API request, the maximum might be 3,000 but we don't know + max_tokens: int = 200 + client = OpenAI( + api_key=openai_api_key, + ) + dashboard: bool = True + call_limit: int = 1 + period_seconds: int = 60 + + # Cache for storing API Responses + cache = TTLCache(maxsize=100, ttl=600) # Cache for 10 minutes + + class Config: + """Config class for the GPT4Vision model""" + + arbitary_types_allowed = True + + def process_img(self, img: str) -> str: + """Processes the image to be used for the API request""" + with open(img, "rb") as image_file: + return base64.b64encode(image_file.read()).decode("utf-8") + + @sleep_and_retry + @limits( + calls=call_limit, period=period_seconds + ) # Rate limit of 10 calls per minute + def run(self, task: str, img: str): + """ + Run the GPT-4 Vision model + + Task: str + The task to run + Img: str + The image to run the task on + + """ + if self.dashboard: + self.print_dashboard() + try: + response = self.client.chat.completions.create( + model="gpt-4-vision-preview", + messages=[ + { + "role": "user", + "content": [ + {"type": "text", "text": task}, + { + "type": "image_url", + "image_url": { + "url": str(img), + }, + }, + ], + } + ], + max_tokens=self.max_tokens, + ) + + out = print(response.choices[0]) + # out = self.clean_output(out) + return out + except openai.OpenAIError as e: + # logger.error(f"OpenAI API error: {e}") + return f"OpenAI API error: Could not process the image. {e}" + except Exception as e: + return f"Unexpected error occurred while processing the image. {e}" + + def clean_output(self, output: str): + # Regex pattern to find the Choice object representation in the output + pattern = r"Choice\(.*?\(content=\"(.*?)\".*?\)\)" + match = re.search(pattern, output, re.DOTALL) + + if match: + # Extract the content from the matched pattern + content = match.group(1) + # Replace escaped quotes to get the clean content + content = content.replace(r"\"", '"') + print(content) + else: + print("No content found in the output.") + + async def arun(self, task: str, img: str): + """ + Arun is an async version of run + + Task: str + The task to run + Img: str + The image to run the task on + + """ + try: + response = await self.client.chat.completions.create( + model="gpt-4-vision-preview", + messages=[ + { + "role": "user", + "content": [ + {"type": "text", "text": task}, + { + "type": "image_url", + "image_url": { + "url": img, + }, + }, + ], + } + ], + max_tokens=self.max_tokens, + ) + + return print(response.choices[0]) + except openai.OpenAIError as e: + # logger.error(f"OpenAI API error: {e}") + return f"OpenAI API error: Could not process the image. {e}" + except Exception as e: + return f"Unexpected error occurred while processing the image. {e}" + + def run_batch(self, tasks_images: List[Tuple[str, str]]) -> List[str]: + """Process a batch of tasks and images""" + with concurrent.futures.ThreadPoolExecutor() as executor: + futures = [ + executor.submit(self.run, task, img) + for task, img in tasks_images + ] + results = [future.result() for future in futures] + return results + + async def run_batch_async( + self, tasks_images: List[Tuple[str, str]] + ) -> List[str]: + """Process a batch of tasks and images asynchronously""" + loop = asyncio.get_event_loop() + futures = [ + loop.run_in_executor(None, self.run, task, img) + for task, img in tasks_images + ] + return await asyncio.gather(*futures) + + async def run_batch_async_with_retries( + self, tasks_images: List[Tuple[str, str]] + ) -> List[str]: + """Process a batch of tasks and images asynchronously with retries""" + loop = asyncio.get_event_loop() + futures = [ + loop.run_in_executor(None, self.run_with_retries, task, img) + for task, img in tasks_images + ] + return await asyncio.gather(*futures) + + def print_dashboard(self): + dashboard = print( + colored( + f""" + GPT4Vision Dashboard + ------------------- + Max Retries: {self.max_retries} + Model: {self.model} + Backoff Factor: {self.backoff_factor} + Timeout Seconds: {self.timeout_seconds} + Image Quality: {self.quality} + Max Tokens: {self.max_tokens} + + """, + "green", + ) + ) + return dashboard + + def health_check(self): + """Health check for the GPT4Vision model""" + try: + response = requests.get("https://api.openai.com/v1/engines") + return response.status_code == 200 + except requests.RequestException as error: + print(f"Health check failed: {error}") + return False + + def sanitize_input(self, text: str) -> str: + """ + Sanitize input to prevent injection attacks. + + Parameters: + text: str - The input text to be sanitized. + + Returns: + The sanitized text. + """ + # Example of simple sanitization, this should be expanded based on the context and usage + sanitized_text = re.sub(r"[^\w\s]", "", text) + return sanitized_text diff --git a/swarms/models/huggingface.py b/swarms/models/huggingface.py index bbb39223..231c3b2b 100644 --- a/swarms/models/huggingface.py +++ b/swarms/models/huggingface.py @@ -189,11 +189,15 @@ class HuggingfaceLLM: # raise print( colored( +<<<<<<< HEAD ( "Failed to load the model and or the" f" tokenizer: {e}" ), "red", +======= + f"Failed to load the model and or the tokenizer: {e}", "red" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) ) @@ -227,8 +231,12 @@ class HuggingfaceLLM: self.model = DDP(self.model) except Exception as error: self.logger.error( +<<<<<<< HEAD "Failed to load the model or the tokenizer:" f" {error}" +======= + f"Failed to load the model or the tokenizer: {error}" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) raise @@ -270,7 +278,13 @@ class HuggingfaceLLM: self.print_dashboard(task) try: +<<<<<<< HEAD inputs = self.tokenizer.encode(task, return_tensors="pt") +======= + inputs = self.tokenizer.encode(task, return_tensors="pt").to( + self.device + ) +>>>>>>> 49c7b97c (code quality fixes: line length = 80) # self.log.start() @@ -311,9 +325,14 @@ class HuggingfaceLLM: print( colored( ( +<<<<<<< HEAD "HuggingfaceLLM could not generate text" f" because of error: {e}, try optimizing your" " arguments" +======= + "HuggingfaceLLM could not generate text because of" + f" error: {e}, try optimizing your arguments" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ), "red", ) @@ -338,9 +357,15 @@ class HuggingfaceLLM: self.print_dashboard(task) try: +<<<<<<< HEAD inputs = self.tokenizer.encode( task, return_tensors="pt" ).to(self.device) +======= + inputs = self.tokenizer.encode(task, return_tensors="pt").to( + self.device + ) +>>>>>>> 49c7b97c (code quality fixes: line length = 80) # self.log.start() diff --git a/swarms/models/idefics.py b/swarms/models/idefics.py index b014fbce..c6b85f01 100644 --- a/swarms/models/idefics.py +++ b/swarms/models/idefics.py @@ -85,10 +85,25 @@ class Idefics(BaseMultiModalModel): *args, **kwargs, ): +<<<<<<< HEAD # Initialize the parent class super().__init__(*args, **kwargs) self.model_name = model_name self.device = device +======= + self.device = ( + device + if device + else ("cuda" if torch.cuda.is_available() else "cpu") + ) + self.model = IdeficsForVisionText2Text.from_pretrained( + checkpoint, + torch_dtype=torch_dtype, + ).to(self.device) + + self.processor = AutoProcessor.from_pretrained(checkpoint) + +>>>>>>> 49c7b97c (code quality fixes: line length = 80) self.max_length = max_length self.batched_mode = batched_mode diff --git a/swarms/models/jina_embeds.py b/swarms/models/jina_embeds.py index c294f764..92166baf 100644 --- a/swarms/models/jina_embeds.py +++ b/swarms/models/jina_embeds.py @@ -125,8 +125,12 @@ class JinaEmbeddings: self.model = DDP(self.model) except Exception as error: self.logger.error( +<<<<<<< HEAD "Failed to load the model or the tokenizer:" f" {error}" +======= + f"Failed to load the model or the tokenizer: {error}" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) raise diff --git a/swarms/models/kosmos2.py b/swarms/models/kosmos2.py index 9a9a0de3..a6be1ce3 100644 --- a/swarms/models/kosmos2.py +++ b/swarms/models/kosmos2.py @@ -71,9 +71,13 @@ class Kosmos2(BaseModel): image = Image.open(img) prompt = "An image of" +<<<<<<< HEAD inputs = self.processor( text=prompt, images=image, return_tensors="pt" ) +======= + inputs = self.processor(text=prompt, images=image, return_tensors="pt") +>>>>>>> 49c7b97c (code quality fixes: line length = 80) outputs = self.model.generate( **inputs, use_cache=True, max_new_tokens=64 ) diff --git a/swarms/models/kosmos_two.py b/swarms/models/kosmos_two.py index 3b1d4233..f92e0cfc 100644 --- a/swarms/models/kosmos_two.py +++ b/swarms/models/kosmos_two.py @@ -150,9 +150,13 @@ class Kosmos: reverse_norm_std = torch.tensor( [0.26862954, 0.26130258, 0.27577711] )[:, None, None] +<<<<<<< HEAD image_tensor = ( image_tensor * reverse_norm_std + reverse_norm_mean ) +======= + image_tensor = image_tensor * reverse_norm_std + reverse_norm_mean +>>>>>>> 49c7b97c (code quality fixes: line length = 80) pil_img = T.ToPILImage()(image_tensor) image_h = pil_img.height image_w = pil_img.width @@ -244,12 +248,16 @@ class Kosmos: for prev_bbox in previous_bboxes: while is_overlapping( +<<<<<<< HEAD ( text_bg_x1, text_bg_y1, text_bg_x2, text_bg_y2, ), +======= + (text_bg_x1, text_bg_y1, text_bg_x2, text_bg_y2), +>>>>>>> 49c7b97c (code quality fixes: line length = 80) prev_bbox, ): text_bg_y1 += ( @@ -267,6 +275,12 @@ class Kosmos: + text_offset_original + 2 * text_spaces ) +<<<<<<< HEAD +======= + y1 += ( + text_height + text_offset_original + 2 * text_spaces + ) +>>>>>>> 49c7b97c (code quality fixes: line length = 80) if text_bg_y2 >= image_h: text_bg_y1 = max( diff --git a/swarms/models/mistral.py b/swarms/models/mistral.py index 297ecf12..1f7b033b 100644 --- a/swarms/models/mistral.py +++ b/swarms/models/mistral.py @@ -50,8 +50,12 @@ class Mistral: # Check if the specified device is available if not torch.cuda.is_available() and device == "cuda": raise ValueError( +<<<<<<< HEAD "CUDA is not available. Please choose a different" " device." +======= + "CUDA is not available. Please choose a different device." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) # Load the model and tokenizer @@ -79,9 +83,15 @@ class Mistral: """Run the model on a given task.""" try: +<<<<<<< HEAD model_inputs = self.tokenizer( [task], return_tensors="pt" ).to(self.device) +======= + model_inputs = self.tokenizer([task], return_tensors="pt").to( + self.device + ) +>>>>>>> 49c7b97c (code quality fixes: line length = 80) generated_ids = self.model.generate( **model_inputs, max_length=self.max_length, @@ -100,9 +110,15 @@ class Mistral: """Run the model on a given task.""" try: +<<<<<<< HEAD model_inputs = self.tokenizer( [task], return_tensors="pt" ).to(self.device) +======= + model_inputs = self.tokenizer([task], return_tensors="pt").to( + self.device + ) +>>>>>>> 49c7b97c (code quality fixes: line length = 80) generated_ids = self.model.generate( **model_inputs, max_length=self.max_length, diff --git a/swarms/models/mpt.py b/swarms/models/mpt.py index 56f1bbdb..8e007f6c 100644 --- a/swarms/models/mpt.py +++ b/swarms/models/mpt.py @@ -30,10 +30,14 @@ class MPT7B: """ def __init__( +<<<<<<< HEAD self, model_name: str, tokenizer_name: str, max_tokens: int = 100, +======= + self, model_name: str, tokenizer_name: str, max_tokens: int = 100 +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ): # Loading model and tokenizer details self.model_name = model_name diff --git a/swarms/models/openai_embeddings.py b/swarms/models/openai_embeddings.py index 0cbbdbee..4f3cf611 100644 --- a/swarms/models/openai_embeddings.py +++ b/swarms/models/openai_embeddings.py @@ -188,7 +188,9 @@ class OpenAIEmbeddings(BaseModel, Embeddings): client: Any #: :meta private: model: str = "text-embedding-ada-002" - deployment: str = model # to support Azure OpenAI Service custom deployment names + deployment: str = ( + model # to support Azure OpenAI Service custom deployment names + ) openai_api_version: Optional[str] = None # to support Azure OpenAI Service custom endpoints openai_api_base: Optional[str] = None @@ -256,9 +258,15 @@ class OpenAIEmbeddings(BaseModel, Embeddings): ) if invalid_model_kwargs: raise ValueError( +<<<<<<< HEAD f"Parameters {invalid_model_kwargs} should be" " specified explicitly. Instead they were passed in" " as part of `model_kwargs` parameter." +======= + f"Parameters {invalid_model_kwargs} should be specified" + " explicitly. Instead they were passed in as part of" + " `model_kwargs` parameter." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) values["model_kwargs"] = extra @@ -369,8 +377,12 @@ class OpenAIEmbeddings(BaseModel, Embeddings): encoding = tiktoken.encoding_for_model(model_name) except KeyError: logger.warning( +<<<<<<< HEAD "Warning: model not found. Using cl100k_base" " encoding." +======= + "Warning: model not found. Using cl100k_base encoding." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) model = "cl100k_base" encoding = tiktoken.get_encoding(model) @@ -435,9 +447,13 @@ class OpenAIEmbeddings(BaseModel, Embeddings): average = np.average( _result, axis=0, weights=num_tokens_in_batch[i] ) +<<<<<<< HEAD embeddings[i] = ( average / np.linalg.norm(average) ).tolist() +======= + embeddings[i] = (average / np.linalg.norm(average)).tolist() +>>>>>>> 49c7b97c (code quality fixes: line length = 80) return embeddings @@ -469,8 +485,12 @@ class OpenAIEmbeddings(BaseModel, Embeddings): encoding = tiktoken.encoding_for_model(model_name) except KeyError: logger.warning( +<<<<<<< HEAD "Warning: model not found. Using cl100k_base" " encoding." +======= + "Warning: model not found. Using cl100k_base encoding." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) model = "cl100k_base" encoding = tiktoken.get_encoding(model) @@ -526,9 +546,13 @@ class OpenAIEmbeddings(BaseModel, Embeddings): average = np.average( _result, axis=0, weights=num_tokens_in_batch[i] ) +<<<<<<< HEAD embeddings[i] = ( average / np.linalg.norm(average) ).tolist() +======= + embeddings[i] = (average / np.linalg.norm(average)).tolist() +>>>>>>> 49c7b97c (code quality fixes: line length = 80) return embeddings diff --git a/swarms/models/openai_models.py b/swarms/models/openai_models.py index 14332ff2..a5c80b9a 100644 --- a/swarms/models/openai_models.py +++ b/swarms/models/openai_models.py @@ -70,9 +70,13 @@ def _stream_response_to_generation_chunk( finish_reason=stream_response["choices"][0].get( "finish_reason", None ), +<<<<<<< HEAD logprobs=stream_response["choices"][0].get( "logprobs", None ), +======= + logprobs=stream_response["choices"][0].get("logprobs", None), +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ), ) @@ -81,6 +85,7 @@ def _update_response( response: Dict[str, Any], stream_response: Dict[str, Any] ) -> None: """Update response from the stream response.""" +<<<<<<< HEAD response["choices"][0]["text"] += stream_response["choices"][0][ "text" ] @@ -90,6 +95,15 @@ def _update_response( response["choices"][0]["logprobs"] = stream_response["choices"][ 0 ]["logprobs"] +======= + response["choices"][0]["text"] += stream_response["choices"][0]["text"] + response["choices"][0]["finish_reason"] = stream_response["choices"][0].get( + "finish_reason", None + ) + response["choices"][0]["logprobs"] = stream_response["choices"][0][ + "logprobs" + ] +>>>>>>> 49c7b97c (code quality fixes: line length = 80) def _streaming_response_template() -> Dict[str, Any]: @@ -431,9 +445,13 @@ class BaseOpenAI(BaseLLM): { "text": generation.text, "finish_reason": ( +<<<<<<< HEAD generation.generation_info.get( "finish_reason" ) +======= + generation.generation_info.get("finish_reason") +>>>>>>> 49c7b97c (code quality fixes: line length = 80) if generation.generation_info else None ), @@ -491,9 +509,13 @@ class BaseOpenAI(BaseLLM): { "text": generation.text, "finish_reason": ( +<<<<<<< HEAD generation.generation_info.get( "finish_reason" ) +======= + generation.generation_info.get("finish_reason") +>>>>>>> 49c7b97c (code quality fixes: line length = 80) if generation.generation_info else None ), @@ -525,8 +547,12 @@ class BaseOpenAI(BaseLLM): if stop is not None: if "stop" in params: raise ValueError( +<<<<<<< HEAD "`stop` found in both the input and default" " params." +======= + "`stop` found in both the input and default params." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) params["stop"] = stop if params["max_tokens"] == -1: @@ -620,8 +646,12 @@ class BaseOpenAI(BaseLLM): enc = tiktoken.encoding_for_model(model_name) except KeyError: logger.warning( +<<<<<<< HEAD "Warning: model not found. Using cl100k_base" " encoding." +======= + "Warning: model not found. Using cl100k_base encoding." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) model = "cl100k_base" enc = tiktoken.get_encoding(model) @@ -683,8 +713,13 @@ class BaseOpenAI(BaseLLM): if context_size is None: raise ValueError( +<<<<<<< HEAD f"Unknown model: {modelname}. Please provide a valid" " OpenAI model name.Known models are: " +======= + f"Unknown model: {modelname}. Please provide a valid OpenAI" + " model name.Known models are: " +>>>>>>> 49c7b97c (code quality fixes: line length = 80) + ", ".join(model_token_mapping.keys()) ) @@ -925,8 +960,13 @@ class OpenAIChat(BaseLLM): ) -> Tuple: if len(prompts) > 1: raise ValueError( +<<<<<<< HEAD "OpenAIChat currently only supports single prompt," f" got {prompts}" +======= + "OpenAIChat currently only supports single prompt, got" + f" {prompts}" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) messages = self.prefix_messages + [ {"role": "user", "content": prompts[0]} @@ -938,8 +978,12 @@ class OpenAIChat(BaseLLM): if stop is not None: if "stop" in params: raise ValueError( +<<<<<<< HEAD "`stop` found in both the input and default" " params." +======= + "`stop` found in both the input and default params." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) params["stop"] = stop if params.get("max_tokens") == -1: @@ -1019,9 +1063,13 @@ class OpenAIChat(BaseLLM): generations=[ [ Generation( +<<<<<<< HEAD text=full_response["choices"][0]["message"][ "content" ] +======= + text=full_response["choices"][0]["message"]["content"] +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) ] ], @@ -1060,9 +1108,13 @@ class OpenAIChat(BaseLLM): generations=[ [ Generation( +<<<<<<< HEAD text=full_response["choices"][0]["message"][ "content" ] +======= + text=full_response["choices"][0]["message"]["content"] +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) ] ], diff --git a/swarms/models/palm.py b/swarms/models/palm.py index d61d4856..d2a773f9 100644 --- a/swarms/models/palm.py +++ b/swarms/models/palm.py @@ -47,9 +47,13 @@ def _create_retry_decorator() -> Callable[[Any], Any]: | retry_if_exception_type( google.api_core.exceptions.ServiceUnavailable ) +<<<<<<< HEAD | retry_if_exception_type( google.api_core.exceptions.GoogleAPIError ) +======= + | retry_if_exception_type(google.api_core.exceptions.GoogleAPIError) +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ), before_sleep=before_sleep_log(logger, logging.WARNING), ) @@ -127,9 +131,13 @@ class GooglePalm(BaseLLM, BaseModel): values["temperature"] is not None and not 0 <= values["temperature"] <= 1 ): +<<<<<<< HEAD raise ValueError( "temperature must be in the range [0.0, 1.0]" ) +======= + raise ValueError("temperature must be in the range [0.0, 1.0]") +>>>>>>> 49c7b97c (code quality fixes: line length = 80) if ( values["top_p"] is not None @@ -144,9 +152,13 @@ class GooglePalm(BaseLLM, BaseModel): values["max_output_tokens"] is not None and values["max_output_tokens"] <= 0 ): +<<<<<<< HEAD raise ValueError( "max_output_tokens must be greater than zero" ) +======= + raise ValueError("max_output_tokens must be greater than zero") +>>>>>>> 49c7b97c (code quality fixes: line length = 80) return values diff --git a/swarms/models/simple_ada.py b/swarms/models/simple_ada.py index e9a599d0..0f9a581d 100644 --- a/swarms/models/simple_ada.py +++ b/swarms/models/simple_ada.py @@ -18,6 +18,12 @@ def get_ada_embeddings( text = text.replace("\n", " ") +<<<<<<< HEAD return client.embeddings.create(input=[text], model=model)[ "data" ][0]["embedding"] +======= + return client.embeddings.create(input=[text], model=model)["data"][0][ + "embedding" + ] +>>>>>>> 49c7b97c (code quality fixes: line length = 80) diff --git a/swarms/models/speecht5.py b/swarms/models/speecht5.py index cc6ef931..bb4b9094 100644 --- a/swarms/models/speecht5.py +++ b/swarms/models/speecht5.py @@ -87,6 +87,7 @@ class SpeechT5: self.model_name = model_name self.vocoder_name = vocoder_name self.dataset_name = dataset_name +<<<<<<< HEAD self.processor = SpeechT5Processor.from_pretrained( self.model_name ) @@ -96,6 +97,11 @@ class SpeechT5: self.vocoder = SpeechT5HifiGan.from_pretrained( self.vocoder_name ) +======= + self.processor = SpeechT5Processor.from_pretrained(self.model_name) + self.model = SpeechT5ForTextToSpeech.from_pretrained(self.model_name) + self.vocoder = SpeechT5HifiGan.from_pretrained(self.vocoder_name) +>>>>>>> 49c7b97c (code quality fixes: line length = 80) self.embeddings_dataset = load_dataset( self.dataset_name, split="validation" ) diff --git a/swarms/models/ssd_1b.py b/swarms/models/ssd_1b.py index d3b9086b..f9b0bb4a 100644 --- a/swarms/models/ssd_1b.py +++ b/swarms/models/ssd_1b.py @@ -145,8 +145,13 @@ class SSD1B: print( colored( ( +<<<<<<< HEAD f"Error running SSD1B: {error} try optimizing" " your api key and or try again" +======= + f"Error running SSD1B: {error} try optimizing your api" + " key and or try again" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ), "red", ) @@ -235,15 +240,21 @@ class SSD1B: print( colored( ( +<<<<<<< HEAD f"Error running SSD1B: {error} try" " optimizing your api key and or try" " again" +======= + f"Error running SSD1B: {error} try optimizing" + " your api key and or try again" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ), "red", ) ) print( colored( +<<<<<<< HEAD ( "Error running SSD1B:" f" {error.http_status}" @@ -257,6 +268,12 @@ class SSD1B: "red", ) ) +======= + f"Error running SSD1B: {error.http_status}", "red" + ) + ) + print(colored(f"Error running SSD1B: {error.error}", "red")) +>>>>>>> 49c7b97c (code quality fixes: line length = 80) raise error def _generate_uuid(self): diff --git a/swarms/models/wizard_storytelling.py b/swarms/models/wizard_storytelling.py index 0dd6c1a1..06b0d775 100644 --- a/swarms/models/wizard_storytelling.py +++ b/swarms/models/wizard_storytelling.py @@ -114,8 +114,12 @@ class WizardLLMStoryTeller: self.model = DDP(self.model) except Exception as error: self.logger.error( +<<<<<<< HEAD "Failed to load the model or the tokenizer:" f" {error}" +======= + f"Failed to load the model or the tokenizer: {error}" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) raise diff --git a/swarms/models/yarn_mistral.py b/swarms/models/yarn_mistral.py index ff65b856..6613d70f 100644 --- a/swarms/models/yarn_mistral.py +++ b/swarms/models/yarn_mistral.py @@ -119,8 +119,12 @@ class YarnMistral128: self.model = DDP(self.model) except Exception as error: self.logger.error( +<<<<<<< HEAD "Failed to load the model or the tokenizer:" f" {error}" +======= + f"Failed to load the model or the tokenizer: {error}" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) raise diff --git a/swarms/prompts/agent_prompt.py b/swarms/prompts/agent_prompt.py index 62f921e2..e4d67929 100644 --- a/swarms/prompts/agent_prompt.py +++ b/swarms/prompts/agent_prompt.py @@ -16,8 +16,12 @@ class PromptGenerator: "text": "thought", "reasoning": "reasoning", "plan": ( +<<<<<<< HEAD "- short bulleted\n- list that conveys\n-" " long-term plan" +======= + "- short bulleted\n- list that conveys\n- long-term plan" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ), "criticism": "constructive self-criticism", "speak": "thoughts summary to say to user", @@ -76,10 +80,16 @@ class PromptGenerator: prompt_string = ( f"Constraints:\n{''.join(self.constraints)}\n\nCommands:\n{''.join(self.commands)}\n\nResources:\n{''.join(self.resources)}\n\nPerformance" f" Evaluation:\n{''.join(self.performance_evaluation)}\n\nYou" +<<<<<<< HEAD " should only respond in JSON format as described below" " \nResponse Format:" f" \n{formatted_response_format} \nEnsure the response" " can be parsed by Python json.loads" +======= + " should only respond in JSON format as described below \nResponse" + f" Format: \n{formatted_response_format} \nEnsure the response can" + " be parsed by Python json.loads" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) return prompt_string diff --git a/swarms/prompts/agent_prompts.py b/swarms/prompts/agent_prompts.py index 88853b09..543c7008 100644 --- a/swarms/prompts/agent_prompts.py +++ b/swarms/prompts/agent_prompts.py @@ -5,6 +5,7 @@ def generate_agent_role_prompt(agent): """ prompts = { "Finance Agent": ( +<<<<<<< HEAD "You are a seasoned finance analyst AI assistant. Your" " primary goal is to compose comprehensive, astute," " impartial, and methodically arranged financial reports" @@ -29,6 +30,28 @@ def generate_agent_role_prompt(agent): " sole purpose is to write well written, critically" " acclaimed, objective and structured reports on given" " text." +======= + "You are a seasoned finance analyst AI assistant. Your primary goal" + " is to compose comprehensive, astute, impartial, and methodically" + " arranged financial reports based on provided data and trends." + ), + "Travel Agent": ( + "You are a world-travelled AI tour guide assistant. Your main" + " purpose is to draft engaging, insightful, unbiased, and" + " well-structured travel reports on given locations, including" + " history, attractions, and cultural insights." + ), + "Academic Research Agent": ( + "You are an AI academic research assistant. Your primary" + " responsibility is to create thorough, academically rigorous," + " unbiased, and systematically organized reports on a given" + " research topic, following the standards of scholarly work." + ), + "Default Agent": ( + "You are an AI critical thinker research assistant. Your sole" + " purpose is to write well written, critically acclaimed, objective" + " and structured reports on given text." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ), } @@ -43,6 +66,7 @@ def generate_report_prompt(question, research_summary): """ return ( +<<<<<<< HEAD f'"""{research_summary}""" Using the above information,' f' answer the following question or topic: "{question}" in a' " detailed report -- The report should focus on the answer" @@ -51,6 +75,14 @@ def generate_report_prompt(question, research_summary): " of 1,200 words and with markdown syntax and apa format." " Write all source urls at the end of the report in apa" " format" +======= + f'"""{research_summary}""" Using the above information, answer the' + f' following question or topic: "{question}" in a detailed report --' + " The report should focus on the answer to the question, should be" + " well structured, informative, in depth, with facts and numbers if" + " available, a minimum of 1,200 words and with markdown syntax and apa" + " format. Write all source urls at the end of the report in apa format" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) @@ -61,10 +93,17 @@ def generate_search_queries_prompt(question): """ return ( +<<<<<<< HEAD "Write 4 google search queries to search online that form an" f' objective opinion from the following: "{question}"You must' " respond with a list of strings in the following format:" ' ["query 1", "query 2", "query 3", "query 4"]' +======= + "Write 4 google search queries to search online that form an objective" + f' opinion from the following: "{question}"You must respond with a list' + ' of strings in the following format: ["query 1", "query 2", "query' + ' 3", "query 4"]' +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) @@ -79,6 +118,7 @@ def generate_resource_report_prompt(question, research_summary): str: The resource report prompt for the given question and research summary. """ return ( +<<<<<<< HEAD f'"""{research_summary}""" Based on the above information,' " generate a bibliography recommendation report for the" f' following question or topic: "{question}". The report' @@ -90,6 +130,18 @@ def generate_resource_report_prompt(question, research_summary): " in-depth, and follows Markdown syntax. Include relevant" " facts, figures, and numbers whenever available. The report" " should have a minimum length of 1,200 words." +======= + f'"""{research_summary}""" Based on the above information, generate a' + " bibliography recommendation report for the following question or" + f' topic: "{question}". The report should provide a detailed analysis' + " of each recommended resource, explaining how each source can" + " contribute to finding answers to the research question. Focus on the" + " relevance, reliability, and significance of each source. Ensure that" + " the report is well-structured, informative, in-depth, and follows" + " Markdown syntax. Include relevant facts, figures, and numbers" + " whenever available. The report should have a minimum length of 1,200" + " words." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) @@ -101,6 +153,7 @@ def generate_outline_report_prompt(question, research_summary): """ return ( +<<<<<<< HEAD f'"""{research_summary}""" Using the above information,' " generate an outline for a research report in Markdown" f' syntax for the following question or topic: "{question}".' @@ -110,6 +163,16 @@ def generate_outline_report_prompt(question, research_summary): " report should be detailed, informative, in-depth, and a" " minimum of 1,200 words. Use appropriate Markdown syntax to" " format the outline and ensure readability." +======= + f'"""{research_summary}""" Using the above information, generate an' + " outline for a research report in Markdown syntax for the following" + f' question or topic: "{question}". The outline should provide a' + " well-structured framework for the research report, including the" + " main sections, subsections, and key points to be covered. The" + " research report should be detailed, informative, in-depth, and a" + " minimum of 1,200 words. Use appropriate Markdown syntax to format" + " the outline and ensure readability." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) @@ -121,12 +184,20 @@ def generate_concepts_prompt(question, research_summary): """ return ( +<<<<<<< HEAD f'"""{research_summary}""" Using the above information,' " generate a list of 5 main concepts to learn for a research" f' report on the following question or topic: "{question}".' " The outline should provide a well-structured frameworkYou" " must respond with a list of strings in the following" ' format: ["concepts 1", "concepts 2", "concepts 3",' +======= + f'"""{research_summary}""" Using the above information, generate a list' + " of 5 main concepts to learn for a research report on the following" + f' question or topic: "{question}". The outline should provide a' + " well-structured frameworkYou must respond with a list of strings in" + ' the following format: ["concepts 1", "concepts 2", "concepts 3",' +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ' "concepts 4, concepts 5"]' ) @@ -141,11 +212,18 @@ def generate_lesson_prompt(concept): """ prompt = ( +<<<<<<< HEAD f"generate a comprehensive lesson about {concept} in Markdown" f" syntax. This should include the definitionof {concept}," " its historical background and development, its" " applications or uses in differentfields, and notable" f" events or facts related to {concept}." +======= + f"generate a comprehensive lesson about {concept} in Markdown syntax." + f" This should include the definitionof {concept}, its historical" + " background and development, its applications or uses in" + f" differentfields, and notable events or facts related to {concept}." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) return prompt diff --git a/swarms/prompts/multi_modal_prompts.py b/swarms/prompts/multi_modal_prompts.py index 83e9800c..526212d3 100644 --- a/swarms/prompts/multi_modal_prompts.py +++ b/swarms/prompts/multi_modal_prompts.py @@ -1,6 +1,11 @@ ERROR_PROMPT = ( +<<<<<<< HEAD "An error has occurred for the following text: \n{promptedQuery}" " Please explain this error.\n {e}" +======= + "An error has occurred for the following text: \n{promptedQuery} Please" + " explain this error.\n {e}" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) IMAGE_PROMPT = """ diff --git a/swarms/prompts/python.py b/swarms/prompts/python.py index a6210024..add209cd 100644 --- a/swarms/prompts/python.py +++ b/swarms/prompts/python.py @@ -2,6 +2,7 @@ PY_SIMPLE_COMPLETION_INSTRUCTION = ( "# Write the body of this function only." ) PY_REFLEXION_COMPLETION_INSTRUCTION = ( +<<<<<<< HEAD "You are a Python writing assistant. You will be given your past" " function implementation, a series of unit tests, and a hint to" " change the implementation appropriately. Write your full" @@ -14,6 +15,20 @@ PY_SELF_REFLECTION_COMPLETION_INSTRUCTION = ( " wrong as indicated by the tests. You will need this as a hint" " when you try again later. Only provide the few sentence" " description in your answer, not the implementation.\n\n-----" +======= + "You are a Python writing assistant. You will be given your past function" + " implementation, a series of unit tests, and a hint to change the" + " implementation appropriately. Write your full implementation (restate the" + " function signature).\n\n-----" +) +PY_SELF_REFLECTION_COMPLETION_INSTRUCTION = ( + "You are a Python writing assistant. You will be given a function" + " implementation and a series of unit tests. Your goal is to write a few" + " sentences to explain why your implementation is wrong as indicated by the" + " tests. You will need this as a hint when you try again later. Only" + " provide the few sentence description in your answer, not the" + " implementation.\n\n-----" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) USE_PYTHON_CODEBLOCK_INSTRUCTION = ( "Use a Python code block to write your response. For" @@ -21,6 +36,7 @@ USE_PYTHON_CODEBLOCK_INSTRUCTION = ( ) PY_SIMPLE_CHAT_INSTRUCTION = ( +<<<<<<< HEAD "You are an AI that only responds with python code, NOT ENGLISH." " You will be given a function signature and its docstring by the" " user. Write your full implementation (restate the function" @@ -43,6 +59,28 @@ PY_REFLEXION_CHAT_INSTRUCTION_V2 = ( " implementation of a function, a series of unit tests results," " and your self-reflection on your previous implementation. Write" " your full implementation (restate the function signature)." +======= + "You are an AI that only responds with python code, NOT ENGLISH. You will" + " be given a function signature and its docstring by the user. Write your" + " full implementation (restate the function signature)." +) +PY_SIMPLE_CHAT_INSTRUCTION_V2 = ( + "You are an AI that only responds with only python code. You will be given" + " a function signature and its docstring by the user. Write your full" + " implementation (restate the function signature)." +) +PY_REFLEXION_CHAT_INSTRUCTION = ( + "You are an AI Python assistant. You will be given your past function" + " implementation, a series of unit tests, and a hint to change the" + " implementation appropriately. Write your full implementation (restate the" + " function signature)." +) +PY_REFLEXION_CHAT_INSTRUCTION_V2 = ( + "You are an AI Python assistant. You will be given your previous" + " implementation of a function, a series of unit tests results, and your" + " self-reflection on your previous implementation. Write your full" + " implementation (restate the function signature)." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) PY_REFLEXION_FEW_SHOT_ADD = '''Example 1: [previous impl]: @@ -177,6 +215,7 @@ END EXAMPLES ''' PY_SELF_REFLECTION_CHAT_INSTRUCTION = ( +<<<<<<< HEAD "You are a Python programming assistant. You will be given a" " function implementation and a series of unit tests. Your goal" " is to write a few sentences to explain why your implementation" @@ -191,6 +230,21 @@ PY_SELF_REFLECTION_CHAT_INSTRUCTION_V2 = ( " implementation is wrong as indicated by the tests. You will" " need this as guidance when you try again later. Only provide" " the few sentence description in your answer, not the" +======= + "You are a Python programming assistant. You will be given a function" + " implementation and a series of unit tests. Your goal is to write a few" + " sentences to explain why your implementation is wrong as indicated by the" + " tests. You will need this as a hint when you try again later. Only" + " provide the few sentence description in your answer, not the" + " implementation." +) +PY_SELF_REFLECTION_CHAT_INSTRUCTION_V2 = ( + "You are a Python programming assistant. You will be given a function" + " implementation and a series of unit test results. Your goal is to write a" + " few sentences to explain why your implementation is wrong as indicated by" + " the tests. You will need this as guidance when you try again later. Only" + " provide the few sentence description in your answer, not the" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) " implementation. You will be given a few examples by the user." ) PY_SELF_REFLECTION_FEW_SHOT = """Example 1: diff --git a/swarms/prompts/sales.py b/swarms/prompts/sales.py index d69f9086..f9e89069 100644 --- a/swarms/prompts/sales.py +++ b/swarms/prompts/sales.py @@ -1,5 +1,6 @@ conversation_stages = { "1": ( +<<<<<<< HEAD "Introduction: Start the conversation by introducing yourself" " and your company. Be polite and respectful while keeping" " the tone of the conversation professional. Your greeting" @@ -22,6 +23,29 @@ conversation_stages = { "Needs analysis: Ask open-ended questions to uncover the" " prospect's needs and pain points. Listen carefully to their" " responses and take notes." +======= + "Introduction: Start the conversation by introducing yourself and your" + " company. Be polite and respectful while keeping the tone of the" + " conversation professional. Your greeting should be welcoming. Always" + " clarify in your greeting the reason why you are contacting the" + " prospect." + ), + "2": ( + "Qualification: Qualify the prospect by confirming if they are the" + " right person to talk to regarding your product/service. Ensure that" + " they have the authority to make purchasing decisions." + ), + "3": ( + "Value proposition: Briefly explain how your product/service can" + " benefit the prospect. Focus on the unique selling points and value" + " proposition of your product/service that sets it apart from" + " competitors." + ), + "4": ( + "Needs analysis: Ask open-ended questions to uncover the prospect's" + " needs and pain points. Listen carefully to their responses and take" + " notes." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ), "5": ( "Solution presentation: Based on the prospect's needs," @@ -34,10 +58,16 @@ conversation_stages = { " provide evidence or testimonials to support your claims." ), "7": ( +<<<<<<< HEAD "Close: Ask for the sale by proposing a next step. This could" " be a demo, a trial or a meeting with decision-makers." " Ensure to summarize what has been discussed and reiterate" " the benefits." +======= + "Close: Ask for the sale by proposing a next step. This could be a" + " demo, a trial or a meeting with decision-makers. Ensure to summarize" + " what has been discussed and reiterate the benefits." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ), } diff --git a/swarms/prompts/sales_prompts.py b/swarms/prompts/sales_prompts.py index dbc2b40e..fa8e4931 100644 --- a/swarms/prompts/sales_prompts.py +++ b/swarms/prompts/sales_prompts.py @@ -46,6 +46,7 @@ Conversation history: conversation_stages = { "1": ( +<<<<<<< HEAD "Introduction: Start the conversation by introducing yourself" " and your company. Be polite and respectful while keeping" " the tone of the conversation professional. Your greeting" @@ -68,6 +69,29 @@ conversation_stages = { "Needs analysis: Ask open-ended questions to uncover the" " prospect's needs and pain points. Listen carefully to their" " responses and take notes." +======= + "Introduction: Start the conversation by introducing yourself and your" + " company. Be polite and respectful while keeping the tone of the" + " conversation professional. Your greeting should be welcoming. Always" + " clarify in your greeting the reason why you are contacting the" + " prospect." + ), + "2": ( + "Qualification: Qualify the prospect by confirming if they are the" + " right person to talk to regarding your product/service. Ensure that" + " they have the authority to make purchasing decisions." + ), + "3": ( + "Value proposition: Briefly explain how your product/service can" + " benefit the prospect. Focus on the unique selling points and value" + " proposition of your product/service that sets it apart from" + " competitors." + ), + "4": ( + "Needs analysis: Ask open-ended questions to uncover the prospect's" + " needs and pain points. Listen carefully to their responses and take" + " notes." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ), "5": ( "Solution presentation: Based on the prospect's needs," @@ -80,9 +104,15 @@ conversation_stages = { " provide evidence or testimonials to support your claims." ), "7": ( +<<<<<<< HEAD "Close: Ask for the sale by proposing a next step. This could" " be a demo, a trial or a meeting with decision-makers." " Ensure to summarize what has been discussed and reiterate" " the benefits." +======= + "Close: Ask for the sale by proposing a next step. This could be a" + " demo, a trial or a meeting with decision-makers. Ensure to summarize" + " what has been discussed and reiterate the benefits." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ), } diff --git a/swarms/structs/autoscaler.py b/swarms/structs/autoscaler.py index 1cb31333..2256279b 100644 --- a/swarms/structs/autoscaler.py +++ b/swarms/structs/autoscaler.py @@ -8,7 +8,11 @@ import concurrent.futures from termcolor import colored +<<<<<<< HEAD from swarms.structs.agent import Agent +======= +from swarms.structs.flow import Flow +>>>>>>> 49c7b97c (code quality fixes: line length = 80) from swarms.utils.decorators import ( error_decorator, log_decorator, @@ -139,8 +143,12 @@ class AutoScaler: self.task_queue.put(task) except Exception as error: print( +<<<<<<< HEAD f"Error adding task to queue: {error} try again with" " a new task" +======= + f"Error adding task to queue: {error} try again with a new task" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) @log_decorator @@ -217,14 +225,23 @@ class AutoScaler: ): self.scale_up() elif ( +<<<<<<< HEAD active_agents / len(self.agents_pool) < self.idle_threshold +======= + active_agents / len(self.agents_pool) < self.idle_threshold +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ): self.scale_down() except Exception as error: print( +<<<<<<< HEAD f"Error monitoring and scaling: {error} try again" " with a new task" +======= + f"Error monitoring and scaling: {error} try again with a new" + " task" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) @log_decorator diff --git a/swarms/structs/flow.py b/swarms/structs/flow.py index aa0060b4..166d619e 100644 --- a/swarms/structs/flow.py +++ b/swarms/structs/flow.py @@ -348,7 +348,8 @@ class Flow: return "\n".join(tool_descriptions) except Exception as error: print( - f"Error getting tool description: {error} try adding a description to the tool or removing the tool" + f"Error getting tool description: {error} try adding a" + " description to the tool or removing the tool" ) else: return "No tools available" @@ -479,8 +480,12 @@ class Flow: print(colored("Initializing Autonomous Agent...", "yellow")) # print(colored("Loading modules...", "yellow")) # print(colored("Modules loaded successfully.", "green")) - print(colored("Autonomous Agent Activated.", "cyan", attrs=["bold"])) - print(colored("All systems operational. Executing task...", "green")) + print( + colored("Autonomous Agent Activated.", "cyan", attrs=["bold"]) + ) + print( + colored("All systems operational. Executing task...", "green") + ) except Exception as error: print( colored( @@ -525,14 +530,16 @@ class Flow: loop_count = 0 while self.max_loops == "auto" or loop_count < self.max_loops: loop_count += 1 - print(colored(f"\nLoop {loop_count} of {self.max_loops}", "blue")) + print( + colored(f"\nLoop {loop_count} of {self.max_loops}", "blue") + ) print("\n") # Check to see if stopping token is in the output to stop the loop if self.stopping_token: - if self._check_stopping_condition(response) or parse_done_token( + if self._check_stopping_condition( response - ): + ) or parse_done_token(response): break # Adjust temperature, comment if no work @@ -629,7 +636,9 @@ class Flow: print(colored(f"\nLoop {loop_count} of {self.max_loops}", "blue")) print("\n") - if self._check_stopping_condition(response) or parse_done_token(response): + if self._check_stopping_condition(response) or parse_done_token( + response + ): break # Adjust temperature, comment if no work @@ -949,7 +958,8 @@ class Flow: if hasattr(self.llm, name): value = getattr(self.llm, name) if isinstance( - value, (str, int, float, bool, list, dict, tuple, type(None)) + value, + (str, int, float, bool, list, dict, tuple, type(None)), ): llm_params[name] = value else: @@ -1010,7 +1020,9 @@ class Flow: print(f"Flow state loaded from {file_path}") - def retry_on_failure(self, function, retries: int = 3, retry_delay: int = 1): + def retry_on_failure( + self, function, retries: int = 3, retry_delay: int = 1 + ): """Retry wrapper for LLM calls.""" attempt = 0 while attempt < retries: diff --git a/swarms/structs/non_linear_workflow.py b/swarms/structs/non_linear_workflow.py index 22cef91e..79bc0af7 100644 --- a/swarms/structs/non_linear_workflow.py +++ b/swarms/structs/non_linear_workflow.py @@ -7,7 +7,11 @@ from typing import Callable, List, Dict, Any, Sequence class Task: def __init__( - self, id: str, task: str, flows: Sequence[Flow], dependencies: List[str] = [] + self, + id: str, + task: str, + flows: Sequence[Flow], + dependencies: List[str] = [], ): self.id = id self.task = task @@ -20,7 +24,9 @@ class Task: for flow in self.flows: result = flow.run(self.task, *args) self.results.append(result) - args = [result] # The output of one flow becomes the input to the next + args = [ + result + ] # The output of one flow becomes the input to the next class Workflow: @@ -41,7 +47,10 @@ class Workflow: ): future = self.executor.submit( task.execute, - {dep: self.tasks[dep].results for dep in task.dependencies}, + { + dep: self.tasks[dep].results + for dep in task.dependencies + }, ) futures.append((future, task.id)) diff --git a/swarms/structs/sequential_workflow.py b/swarms/structs/sequential_workflow.py index 49c529cb..4e50a75b 100644 --- a/swarms/structs/sequential_workflow.py +++ b/swarms/structs/sequential_workflow.py @@ -117,11 +117,15 @@ class SequentialWorkflow: dashboard: bool = False def add( +<<<<<<< HEAD self, agent: Union[Callable, Agent], task: Optional[str] = None, *args, **kwargs, +======= + self, task: str, flow: Union[Callable, Flow], *args, **kwargs +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) -> None: """ Add a task to the workflow. @@ -511,9 +515,15 @@ class SequentialWorkflow: # Ensure that 'task' is provided in the kwargs if "task" not in task.kwargs: raise ValueError( +<<<<<<< HEAD "The 'task' argument is required" " for the Agent agent execution" f" in '{task.description}'" +======= + "The 'task' argument is required for the" + " Flow flow execution in" + f" '{task.description}'" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) # Separate the 'task' argument from other kwargs flow_task_arg = task.kwargs.pop("task") @@ -565,6 +575,7 @@ class SequentialWorkflow: ValueError: If a Agent instance is used as a task and the 'task' argument is not provided. """ +<<<<<<< HEAD try: for _ in range(self.max_loops): for task in self.tasks: @@ -585,6 +596,19 @@ class SequentialWorkflow: flow_task_arg, *task.args, **task.kwargs, +======= + for _ in range(self.max_loops): + for task in self.tasks: + # Check if the current task can be executed + if task.result is None: + # Check if the flow is a Flow and a 'task' argument is needed + if isinstance(task.flow, Flow): + # Ensure that 'task' is provided in the kwargs + if "task" not in task.kwargs: + raise ValueError( + "The 'task' argument is required for the Flow" + f" flow execution in '{task.description}'" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) else: # If it's not a Agent instance, call the agent directly @@ -592,6 +616,7 @@ class SequentialWorkflow: *task.args, **task.kwargs ) +<<<<<<< HEAD # Pass the result as an argument to the next task if it exists next_task_index = self.tasks.index(task) + 1 if next_task_index < len(self.tasks): @@ -620,3 +645,10 @@ class SequentialWorkflow: attrs=["bold", "underline"], ) ) +======= + # Autosave the workflow state + if self.autosave: + self.save_workflow_state( + "sequential_workflow_state.json" + ) +>>>>>>> 49c7b97c (code quality fixes: line length = 80) diff --git a/swarms/swarms/base.py b/swarms/swarms/base.py index fe81df4e..f674faf1 100644 --- a/swarms/swarms/base.py +++ b/swarms/swarms/base.py @@ -101,11 +101,19 @@ class AbstractSwarm(ABC): """Remove a agent from the swarm""" pass +<<<<<<< HEAD # @abstractmethod def broadcast( self, message: str, sender: Optional["Agent"] = None ): """Broadcast a message to all agents""" +======= + @abstractmethod + def broadcast( + self, message: str, sender: Optional["AbstractWorker"] = None + ): + """Broadcast a message to all workers""" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) pass # @abstractmethod diff --git a/swarms/swarms/dialogue_simulator.py b/swarms/swarms/dialogue_simulator.py new file mode 100644 index 00000000..2775daf0 --- /dev/null +++ b/swarms/swarms/dialogue_simulator.py @@ -0,0 +1,93 @@ +import os +from typing import Callable, List + + +class DialogueSimulator: + """ + Dialogue Simulator + ------------------ + + Args: + ------ + agents: List[Callable] + max_iters: int + name: str + + Usage: + ------ + >>> from swarms import DialogueSimulator + >>> from swarms.structs.flow import Flow + >>> agents = Flow() + >>> agents1 = Flow() + >>> model = DialogueSimulator([agents, agents1], max_iters=10, name="test") + >>> model.run("test") + """ + + def __init__( + self, agents: List[Callable], max_iters: int = 10, name: str = None + ): + self.agents = agents + self.max_iters = max_iters + self.name = name + + def run(self, message: str = None): + """Run the dialogue simulator""" + try: + step = 0 + if self.name and message: + prompt = f"Name {self.name} and message: {message}" + for agent in self.agents: + agent.run(prompt) + step += 1 + + while step < self.max_iters: + speaker_idx = step % len(self.agents) + speaker = self.agents[speaker_idx] + speaker_message = speaker.run(prompt) + + for receiver in self.agents: + message_history = ( + f"Speaker Name: {speaker.name} and message:" + f" {speaker_message}" + ) + receiver.run(message_history) + + print(f"({speaker.name}): {speaker_message}") + print("\n") + step += 1 + except Exception as error: + print(f"Error running dialogue simulator: {error}") + + def __repr__(self): + return ( + f"DialogueSimulator({self.agents}, {self.max_iters}, {self.name})" + ) + + def save_state(self): + """Save the state of the dialogue simulator""" + try: + if self.name: + filename = f"{self.name}.txt" + with open(filename, "w") as file: + file.write(str(self)) + except Exception as error: + print(f"Error saving state: {error}") + + def load_state(self): + """Load the state of the dialogue simulator""" + try: + if self.name: + filename = f"{self.name}.txt" + with open(filename, "r") as file: + return file.read() + except Exception as error: + print(f"Error loading state: {error}") + + def delete_state(self): + """Delete the state of the dialogue simulator""" + try: + if self.name: + filename = f"{self.name}.txt" + os.remove(filename) + except Exception as error: + print(f"Error deleting state: {error}") diff --git a/swarms/swarms/god_mode.py b/swarms/swarms/god_mode.py index 29178b2c..3824f3dd 100644 --- a/swarms/swarms/god_mode.py +++ b/swarms/swarms/god_mode.py @@ -64,11 +64,15 @@ class GodMode: table.append([f"LLM {i+1}", response]) print( colored( +<<<<<<< HEAD tabulate( table, headers=["LLM", "Response"], tablefmt="pretty", ), +======= + tabulate(table, headers=["LLM", "Response"], tablefmt="pretty"), +>>>>>>> 49c7b97c (code quality fixes: line length = 80) "cyan", ) ) @@ -88,11 +92,15 @@ class GodMode: table.append([f"LLM {i+1}", response]) print( colored( +<<<<<<< HEAD tabulate( table, headers=["LLM", "Response"], tablefmt="pretty", ), +======= + tabulate(table, headers=["LLM", "Response"], tablefmt="pretty"), +>>>>>>> 49c7b97c (code quality fixes: line length = 80) "cyan", ) ) @@ -130,11 +138,15 @@ class GodMode: ] print( colored( +<<<<<<< HEAD tabulate( table, headers=["LLM", "Response"], tablefmt="pretty", ), +======= + tabulate(table, headers=["LLM", "Response"], tablefmt="pretty"), +>>>>>>> 49c7b97c (code quality fixes: line length = 80) "cyan", ) ) @@ -171,8 +183,13 @@ class GodMode: responses.append(future.result()) except Exception as error: print( +<<<<<<< HEAD f"{future_to_llm[future]} generated an" f" exception: {error}" +======= + f"{future_to_llm[future]} generated an exception:" + f" {error}" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) self.last_responses = responses self.task_history.append(task) diff --git a/swarms/swarms/groupchat.py b/swarms/swarms/groupchat.py index f3677023..12a33a41 100644 --- a/swarms/swarms/groupchat.py +++ b/swarms/swarms/groupchat.py @@ -50,8 +50,12 @@ class GroupChat: def next_agent(self, agent: Agent) -> Agent: """Return the next agent in the list.""" return self.agents[ +<<<<<<< HEAD (self.agent_names.index(agent.name) + 1) % len(self.agents) +======= + (self.agent_names.index(agent.name) + 1) % len(self.agents) +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ] def select_speaker_msg(self): @@ -83,10 +87,16 @@ class GroupChat: { "role": "system", "content": ( +<<<<<<< HEAD "Read the above conversation. Then" " select the next most suitable role" f" from {self.agent_names} to play. Only" " return the role." +======= + "Read the above conversation. Then select the next" + f" most suitable role from {self.agent_names} to" + " play. Only return the role." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ), } ] @@ -150,6 +160,7 @@ class GroupChatManager: self.selector = selector def __call__(self, task: str): +<<<<<<< HEAD """Call 'GroupChatManager' instance as a function. Args: @@ -158,6 +169,8 @@ class GroupChatManager: Returns: _type_: _description_ """ +======= +>>>>>>> 49c7b97c (code quality fixes: line length = 80) self.groupchat.messages.append( {"role": self.selector.name, "content": task} ) diff --git a/swarms/swarms/multi_agent_collab.py b/swarms/swarms/multi_agent_collab.py index 6347d106..146db534 100644 --- a/swarms/swarms/multi_agent_collab.py +++ b/swarms/swarms/multi_agent_collab.py @@ -13,8 +13,13 @@ from swarms.utils.logger import logger class BidOutputParser(RegexParser): def get_format_instructions(self) -> str: return ( +<<<<<<< HEAD "Your response should be an integrater delimited by" " angled brackets like this: " +======= + "Your response should be an integrater delimited by angled brackets" + " like this: " +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) @@ -200,11 +205,15 @@ class MultiAgentCollaboration: print("\n") n += 1 - def select_next_speaker_roundtable(self, step: int, agents: List[Flow]) -> int: + def select_next_speaker_roundtable( + self, step: int, agents: List[Flow] + ) -> int: """Selects the next speaker.""" return step % len(agents) - def select_next_speaker_director(step: int, agents: List[Flow], director) -> int: + def select_next_speaker_director( + step: int, agents: List[Flow], director + ) -> int: # if the step if even => director # => director selects next speaker if step % 2 == 1: diff --git a/swarms/swarms/orchestrate.py b/swarms/swarms/orchestrate.py new file mode 100644 index 00000000..b7a7d0e0 --- /dev/null +++ b/swarms/swarms/orchestrate.py @@ -0,0 +1,287 @@ +import logging +import queue +import threading +from concurrent.futures import ThreadPoolExecutor +from enum import Enum +from typing import Any, Dict, List + +import chromadb +from chromadb.utils import embedding_functions + + +class TaskStatus(Enum): + QUEUED = 1 + RUNNING = 2 + COMPLETED = 3 + FAILED = 4 + + +class Orchestrator: + """ + The Orchestrator takes in an agent, worker, or boss as input + then handles all the logic for + - task creation, + - task assignment, + - and task compeletion. + + And, the communication for millions of agents to chat with eachother through + a vector database that each agent has access to chat with. + + Each LLM agent chats with the orchestrator through a dedicated + communication layer. The orchestrator assigns tasks to each LLM agent, + which the agents then complete and return. + + This setup allows for a high degree of flexibility, scalability, and robustness. + + In the context of swarm LLMs, one could consider an **Omni-Vector Embedding Database + for communication. This database could store and manage + the high-dimensional vectors produced by each LLM agent. + + Strengths: This approach would allow for similarity-based lookup and matching of + LLM-generated vectors, which can be particularly useful for tasks that involve finding similar outputs or recognizing patterns. + + Weaknesses: An Omni-Vector Embedding Database might add complexity to the system in terms of setup and maintenance. + It might also require significant computational resources, + depending on the volume of data being handled and the complexity of the vectors. + The handling and transmission of high-dimensional vectors could also pose challenges + in terms of network load. + + # Orchestrator + * Takes in an agent class with vector store, + then handles all the communication and scales + up a swarm with number of agents and handles task assignment and task completion + + from swarms import OpenAI, Orchestrator, Swarm + + orchestrated = Orchestrate(OpenAI, nodes=40) #handles all the task assignment and allocation and agent communication using a vectorstore as a universal communication layer and also handlles the task completion logic + + Objective = "Make a business website for a marketing consultancy" + + Swarms = Swarms(orchestrated, auto=True, Objective)) + ``` + + In terms of architecture, the swarm might look something like this: + + ``` + (Orchestrator) + / \ + Tools + Vector DB -- (LLM Agent)---(Communication Layer) (Communication Layer)---(LLM Agent)-- Tools + Vector DB + / | | \ + (Task Assignment) (Task Completion) (Task Assignment) (Task Completion) + + + ###Usage + ``` + from swarms import Orchestrator + + # Instantiate the Orchestrator with 10 agents + orchestrator = Orchestrator(llm, agent_list=[llm]*10, task_queue=[]) + + # Add tasks to the Orchestrator + tasks = [{"content": f"Write a short story about a {animal}."} for animal in ["cat", "dog", "bird", "fish", "lion", "tiger", "elephant", "giraffe", "monkey", "zebra"]] + orchestrator.assign_tasks(tasks) + + # Run the Orchestrator + orchestrator.run() + + # Retrieve the results + for task in tasks: + print(orchestrator.retrieve_result(id(task))) + ``` + """ + + def __init__( + self, + agent, + agent_list: List[Any], + task_queue: List[Any], + collection_name: str = "swarm", + api_key: str = None, + model_name: str = None, + embed_func=None, + worker=None, + ): + self.agent = agent + self.agents = queue.Queue() + + for _ in range(agent_list): + self.agents.put(agent()) + + self.task_queue = queue.Queue() + + self.chroma_client = chromadb.Client() + + self.collection = self.chroma_client.create_collection( + name=collection_name + ) + + self.current_tasks = {} + + self.lock = threading.Lock() + self.condition = threading.Condition(self.lock) + self.executor = ThreadPoolExecutor(max_workers=len(agent_list)) + + self.embed_func = embed_func if embed_func else self.embed + + # @abstractmethod + + def assign_task(self, agent_id: int, task: Dict[str, Any]) -> None: + """Assign a task to a specific agent""" + + while True: + with self.condition: + while not self.task_queue: + self.condition.wait() + agent = self.agents.get() + task = self.task_queue.get() + + try: + result = self.worker.run(task["content"]) + + # using the embed method to get the vector representation of the result + vector_representation = self.embed( + result, self.api_key, self.model_name + ) + + self.collection.add( + embeddings=[vector_representation], + documents=[str(id(task))], + ids=[str(id(task))], + ) + + logging.info( + f"Task {id(str)} has been processed by agent" + f" {id(agent)} with" + ) + + except Exception as error: + logging.error( + f"Failed to process task {id(task)} by agent {id(agent)}." + f" Error: {error}" + ) + finally: + with self.condition: + self.agents.put(agent) + self.condition.notify() + + def embed(self, input, api_key, model_name): + openai = embedding_functions.OpenAIEmbeddingFunction( + api_key=api_key, model_name=model_name + ) + embedding = openai(input) + return embedding + + # @abstractmethod + + def retrieve_results(self, agent_id: int) -> Any: + """Retrieve results from a specific agent""" + + try: + # Query the vector database for documents created by the agents + results = self.collection.query( + query_texts=[str(agent_id)], n_results=10 + ) + + return results + except Exception as e: + logging.error( + f"Failed to retrieve results from agent {agent_id}. Error {e}" + ) + raise + + # @abstractmethod + def update_vector_db(self, data) -> None: + """Update the vector database""" + + try: + self.collection.add( + embeddings=[data["vector"]], + documents=[str(data["task_id"])], + ids=[str(data["task_id"])], + ) + + except Exception as e: + logging.error(f"Failed to update the vector database. Error: {e}") + raise + + # @abstractmethod + + def get_vector_db(self): + """Retrieve the vector database""" + return self.collection + + def append_to_db(self, result: str): + """append the result of the swarm to a specifici collection in the database""" + + try: + self.collection.add(documents=[result], ids=[str(id(result))]) + + except Exception as e: + logging.error( + f"Failed to append the agent output to database. Error: {e}" + ) + raise + + def run(self, objective: str): + """Runs""" + if not objective or not isinstance(objective, str): + logging.error("Invalid objective") + raise ValueError("A valid objective is required") + + try: + self.task_queue.append(objective) + + results = [ + self.assign_task(agent_id, task) + for agent_id, task in zip( + range(len(self.agents)), self.task_queue + ) + ] + + for result in results: + self.append_to_db(result) + + logging.info(f"Successfully ran swarms with results: {results}") + return results + except Exception as e: + logging.error(f"An error occured in swarm: {e}") + return None + + def chat(self, sender_id: int, receiver_id: int, message: str): + """ + + Allows the agents to chat with eachother thrught the vectordatabase + + # Instantiate the Orchestrator with 10 agents + orchestrator = Orchestrator( + llm, + agent_list=[llm]*10, + task_queue=[] + ) + + # Agent 1 sends a message to Agent 2 + orchestrator.chat(sender_id=1, receiver_id=2, message="Hello, Agent 2!") + + """ + + message_vector = self.embed(message, self.api_key, self.model_name) + + # store the mesage in the vector database + self.collection.add( + embeddings=[message_vector], + documents=[message], + ids=[f"{sender_id}_to_{receiver_id}"], + ) + + self.run(objective=f"chat with agent {receiver_id} about {message}") + + def add_agents(self, num_agents: int): + for _ in range(num_agents): + self.agents.put(self.agent()) + self.executor = ThreadPoolExecutor(max_workers=self.agents.qsize()) + + def remove_agents(self, num_agents): + for _ in range(num_agents): + if not self.agents.empty(): + self.agents.get() + self.executor = ThreadPoolExecutor(max_workers=self.agents.qsize()) diff --git a/swarms/tools/autogpt.py b/swarms/tools/autogpt.py new file mode 100644 index 00000000..07062d11 --- /dev/null +++ b/swarms/tools/autogpt.py @@ -0,0 +1,200 @@ +import asyncio +import os +from contextlib import contextmanager +from typing import Optional + +import pandas as pd +import torch +from langchain.agents import tool +from langchain.agents.agent_toolkits.pandas.base import ( + create_pandas_dataframe_agent, +) +from langchain.chains.qa_with_sources.loading import ( + BaseCombineDocumentsChain, +) +from langchain.docstore.document import Document +from langchain.text_splitter import RecursiveCharacterTextSplitter +from langchain.tools import BaseTool +from PIL import Image +from pydantic import Field +from transformers import ( + BlipForQuestionAnswering, + BlipProcessor, +) + +from swarms.utils.logger import logger + +ROOT_DIR = "./data/" + + +@contextmanager +def pushd(new_dir): + """Context manager for changing the current working directory.""" + prev_dir = os.getcwd() + os.chdir(new_dir) + try: + yield + finally: + os.chdir(prev_dir) + + +@tool +def process_csv( + llm, + csv_file_path: str, + instructions: str, + output_path: Optional[str] = None, +) -> str: + """Process a CSV by with pandas in a limited REPL.\ + Only use this after writing data to disk as a csv file.\ + Any figures must be saved to disk to be viewed by the human.\ + Instructions should be written in natural language, not code. Assume the dataframe is already loaded.""" + with pushd(ROOT_DIR): + try: + df = pd.read_csv(csv_file_path) + except Exception as e: + return f"Error: {e}" + agent = create_pandas_dataframe_agent( + llm, df, max_iterations=30, verbose=False + ) + if output_path is not None: + instructions += f" Save output to disk at {output_path}" + try: + result = agent.run(instructions) + return result + except Exception as e: + return f"Error: {e}" + + +async def async_load_playwright(url: str) -> str: + """Load the specified URLs using Playwright and parse using BeautifulSoup.""" + from bs4 import BeautifulSoup + from playwright.async_api import async_playwright + + results = "" + async with async_playwright() as p: + browser = await p.chromium.launch(headless=True) + try: + page = await browser.new_page() + await page.goto(url) + + page_source = await page.content() + soup = BeautifulSoup(page_source, "html.parser") + + for script in soup(["script", "style"]): + script.extract() + + text = soup.get_text() + lines = (line.strip() for line in text.splitlines()) + chunks = ( + phrase.strip() for line in lines for phrase in line.split(" ") + ) + results = "\n".join(chunk for chunk in chunks if chunk) + except Exception as e: + results = f"Error: {e}" + await browser.close() + return results + + +def run_async(coro): + event_loop = asyncio.get_event_loop() + return event_loop.run_until_complete(coro) + + +@tool +def browse_web_page(url: str) -> str: + """Verbose way to scrape a whole webpage. Likely to cause issues parsing.""" + return run_async(async_load_playwright(url)) + + +def _get_text_splitter(): + return RecursiveCharacterTextSplitter( + # Set a really small chunk size, just to show. + chunk_size=500, + chunk_overlap=20, + length_function=len, + ) + + +class WebpageQATool(BaseTool): + name = "query_webpage" + description = ( + "Browse a webpage and retrieve the information relevant to the" + " question." + ) + text_splitter: RecursiveCharacterTextSplitter = Field( + default_factory=_get_text_splitter + ) + qa_chain: BaseCombineDocumentsChain + + def _run(self, url: str, question: str) -> str: + """Useful for browsing websites and scraping the text information.""" + result = browse_web_page.run(url) + docs = [Document(page_content=result, metadata={"source": url})] + web_docs = self.text_splitter.split_documents(docs) + results = [] + # TODO: Handle this with a MapReduceChain + for i in range(0, len(web_docs), 4): + input_docs = web_docs[i : i + 4] + window_result = self.qa_chain( + {"input_documents": input_docs, "question": question}, + return_only_outputs=True, + ) + results.append(f"Response from window {i} - {window_result}") + results_docs = [ + Document(page_content="\n".join(results), metadata={"source": url}) + ] + return self.qa_chain( + {"input_documents": results_docs, "question": question}, + return_only_outputs=True, + ) + + async def _arun(self, url: str, question: str) -> str: + raise NotImplementedError + + +class EdgeGPTTool: + # Initialize the custom tool + def __init__( + self, + model, + name="EdgeGPTTool", + description="Tool that uses EdgeGPTModel to generate responses", + ): + super().__init__(name=name, description=description) + self.model = model + + def _run(self, prompt): + return self.model.__call__(prompt) + + +@tool +def VQAinference(self, inputs): + """ + Answer Question About The Image, VQA Multi-Modal Worker agent + description="useful when you need an answer for a question based on an image. " + "like: what is the background color of the last image, how many cats in this figure, what is in this figure. " + "The input to this tool should be a comma separated string of two, representing the image_path and the question", + + """ + device = "cuda:0" + torch_dtype = torch.float16 if "cuda" in device else torch.float32 + processor = BlipProcessor.from_pretrained("Salesforce/blip-vqa-base") + model = BlipForQuestionAnswering.from_pretrained( + "Salesforce/blip-vqa-base", torch_dtype=torch_dtype + ).to(device) + + image_path, question = inputs.split(",") + raw_image = Image.open(image_path).convert("RGB") + inputs = processor(raw_image, question, return_tensors="pt").to( + device, torch_dtype + ) + out = model.generate(**inputs) + answer = processor.decode(out[0], skip_special_tokens=True) + + logger.debug( + f"\nProcessed VisualQuestionAnswering, Input Image: {image_path}, Input" + f" Question: {question}, Output Answer: {answer}" + ) + + return answer diff --git a/swarms/tools/mm_models.py b/swarms/tools/mm_models.py new file mode 100644 index 00000000..a218ff50 --- /dev/null +++ b/swarms/tools/mm_models.py @@ -0,0 +1,284 @@ +import os +import uuid + +import numpy as np +import torch +from diffusers import ( + EulerAncestralDiscreteScheduler, + StableDiffusionInpaintPipeline, + StableDiffusionInstructPix2PixPipeline, + StableDiffusionPipeline, +) +from PIL import Image +from transformers import ( + BlipForConditionalGeneration, + BlipForQuestionAnswering, + BlipProcessor, + CLIPSegForImageSegmentation, + CLIPSegProcessor, +) + +from swarms.prompts.prebuild.multi_modal_prompts import IMAGE_PROMPT +from swarms.tools.tool import tool +from swarms.utils.logger import logger +from swarms.utils.main import BaseHandler, get_new_image_name + + +class MaskFormer: + def __init__(self, device): + print("Initializing MaskFormer to %s" % device) + self.device = device + self.processor = CLIPSegProcessor.from_pretrained( + "CIDAS/clipseg-rd64-refined" + ) + self.model = CLIPSegForImageSegmentation.from_pretrained( + "CIDAS/clipseg-rd64-refined" + ).to(device) + + def inference(self, image_path, text): + threshold = 0.5 + min_area = 0.02 + padding = 20 + original_image = Image.open(image_path) + image = original_image.resize((512, 512)) + inputs = self.processor( + text=text, images=image, padding="max_length", return_tensors="pt" + ).to(self.device) + with torch.no_grad(): + outputs = self.model(**inputs) + mask = torch.sigmoid(outputs[0]).squeeze().cpu().numpy() > threshold + area_ratio = len(np.argwhere(mask)) / (mask.shape[0] * mask.shape[1]) + if area_ratio < min_area: + return None + true_indices = np.argwhere(mask) + mask_array = np.zeros_like(mask, dtype=bool) + for idx in true_indices: + padded_slice = tuple( + slice(max(0, i - padding), i + padding + 1) for i in idx + ) + mask_array[padded_slice] = True + visual_mask = (mask_array * 255).astype(np.uint8) + image_mask = Image.fromarray(visual_mask) + return image_mask.resize(original_image.size) + + +class ImageEditing: + def __init__(self, device): + print("Initializing ImageEditing to %s" % device) + self.device = device + self.mask_former = MaskFormer(device=self.device) + self.revision = "fp16" if "cuda" in device else None + self.torch_dtype = torch.float16 if "cuda" in device else torch.float32 + self.inpaint = StableDiffusionInpaintPipeline.from_pretrained( + "runwayml/stable-diffusion-inpainting", + revision=self.revision, + torch_dtype=self.torch_dtype, + ).to(device) + + @tool( + name="Remove Something From The Photo", + description=( + "useful when you want to remove and object or something from the" + " photo from its description or location. The input to this tool" + " should be a comma separated string of two, representing the" + " image_path and the object need to be removed. " + ), + ) + def inference_remove(self, inputs): + image_path, to_be_removed_txt = inputs.split(",") + return self.inference_replace( + f"{image_path},{to_be_removed_txt},background" + ) + + @tool( + name="Replace Something From The Photo", + description=( + "useful when you want to replace an object from the object" + " description or location with another object from its description." + " The input to this tool should be a comma separated string of" + " three, representing the image_path, the object to be replaced," + " the object to be replaced with " + ), + ) + def inference_replace(self, inputs): + image_path, to_be_replaced_txt, replace_with_txt = inputs.split(",") + original_image = Image.open(image_path) + original_size = original_image.size + mask_image = self.mask_former.inference(image_path, to_be_replaced_txt) + updated_image = self.inpaint( + prompt=replace_with_txt, + image=original_image.resize((512, 512)), + mask_image=mask_image.resize((512, 512)), + ).images[0] + updated_image_path = get_new_image_name( + image_path, func_name="replace-something" + ) + updated_image = updated_image.resize(original_size) + updated_image.save(updated_image_path) + + logger.debug( + f"\nProcessed ImageEditing, Input Image: {image_path}, Replace" + f" {to_be_replaced_txt} to {replace_with_txt}, Output Image:" + f" {updated_image_path}" + ) + + return updated_image_path + + +class InstructPix2Pix: + def __init__(self, device): + print("Initializing InstructPix2Pix to %s" % device) + self.device = device + self.torch_dtype = torch.float16 if "cuda" in device else torch.float32 + self.pipe = StableDiffusionInstructPix2PixPipeline.from_pretrained( + "timbrooks/instruct-pix2pix", + safety_checker=None, + torch_dtype=self.torch_dtype, + ).to(device) + self.pipe.scheduler = EulerAncestralDiscreteScheduler.from_config( + self.pipe.scheduler.config + ) + + @tool( + name="Instruct Image Using Text", + description=( + "useful when you want to the style of the image to be like the" + " text. like: make it look like a painting. or make it like a" + " robot. The input to this tool should be a comma separated string" + " of two, representing the image_path and the text. " + ), + ) + def inference(self, inputs): + """Change style of image.""" + logger.debug("===> Starting InstructPix2Pix Inference") + image_path, text = inputs.split(",")[0], ",".join(inputs.split(",")[1:]) + original_image = Image.open(image_path) + image = self.pipe( + text, + image=original_image, + num_inference_steps=40, + image_guidance_scale=1.2, + ).images[0] + updated_image_path = get_new_image_name(image_path, func_name="pix2pix") + image.save(updated_image_path) + + logger.debug( + f"\nProcessed InstructPix2Pix, Input Image: {image_path}, Instruct" + f" Text: {text}, Output Image: {updated_image_path}" + ) + + return updated_image_path + + +class Text2Image: + def __init__(self, device): + print("Initializing Text2Image to %s" % device) + self.device = device + self.torch_dtype = torch.float16 if "cuda" in device else torch.float32 + self.pipe = StableDiffusionPipeline.from_pretrained( + "runwayml/stable-diffusion-v1-5", torch_dtype=self.torch_dtype + ) + self.pipe.to(device) + self.a_prompt = "best quality, extremely detailed" + self.n_prompt = ( + "longbody, lowres, bad anatomy, bad hands, missing fingers, extra" + " digit, fewer digits, cropped, worst quality, low quality" + ) + + @tool( + name="Generate Image From User Input Text", + description=( + "useful when you want to generate an image from a user input text" + " and save it to a file. like: generate an image of an object or" + " something, or generate an image that includes some objects. The" + " input to this tool should be a string, representing the text used" + " to generate image. " + ), + ) + def inference(self, text): + image_filename = os.path.join("image", str(uuid.uuid4())[0:8] + ".png") + prompt = text + ", " + self.a_prompt + image = self.pipe(prompt, negative_prompt=self.n_prompt).images[0] + image.save(image_filename) + + logger.debug( + f"\nProcessed Text2Image, Input Text: {text}, Output Image:" + f" {image_filename}" + ) + + return image_filename + + +class VisualQuestionAnswering: + def __init__(self, device): + print("Initializing VisualQuestionAnswering to %s" % device) + self.torch_dtype = torch.float16 if "cuda" in device else torch.float32 + self.device = device + self.processor = BlipProcessor.from_pretrained( + "Salesforce/blip-vqa-base" + ) + self.model = BlipForQuestionAnswering.from_pretrained( + "Salesforce/blip-vqa-base", torch_dtype=self.torch_dtype + ).to(self.device) + + @tool( + name="Answer Question About The Image", + description=( + "useful when you need an answer for a question based on an image." + " like: what is the background color of the last image, how many" + " cats in this figure, what is in this figure. The input to this" + " tool should be a comma separated string of two, representing the" + " image_path and the question" + ), + ) + def inference(self, inputs): + image_path, question = inputs.split(",") + raw_image = Image.open(image_path).convert("RGB") + inputs = self.processor(raw_image, question, return_tensors="pt").to( + self.device, self.torch_dtype + ) + out = self.model.generate(**inputs) + answer = self.processor.decode(out[0], skip_special_tokens=True) + + logger.debug( + f"\nProcessed VisualQuestionAnswering, Input Image: {image_path}," + f" Input Question: {question}, Output Answer: {answer}" + ) + + return answer + + +class ImageCaptioning(BaseHandler): + def __init__(self, device): + print("Initializing ImageCaptioning to %s" % device) + self.device = device + self.torch_dtype = torch.float16 if "cuda" in device else torch.float32 + self.processor = BlipProcessor.from_pretrained( + "Salesforce/blip-image-captioning-base" + ) + self.model = BlipForConditionalGeneration.from_pretrained( + "Salesforce/blip-image-captioning-base", + torch_dtype=self.torch_dtype, + ).to(self.device) + + def handle(self, filename: str): + img = Image.open(filename) + width, height = img.size + ratio = min(512 / width, 512 / height) + width_new, height_new = (round(width * ratio), round(height * ratio)) + img = img.resize((width_new, height_new)) + img = img.convert("RGB") + img.save(filename, "PNG") + print(f"Resize image form {width}x{height} to {width_new}x{height_new}") + + inputs = self.processor(Image.open(filename), return_tensors="pt").to( + self.device, self.torch_dtype + ) + out = self.model.generate(**inputs) + description = self.processor.decode(out[0], skip_special_tokens=True) + print( + f"\nProcessed ImageCaptioning, Input Image: {filename}, Output" + f" Text: {description}" + ) + + return IMAGE_PROMPT.format(filename=filename, description=description) diff --git a/swarms/tools/tool.py b/swarms/tools/tool.py index 1029a183..96cce4cb 100644 --- a/swarms/tools/tool.py +++ b/swarms/tools/tool.py @@ -143,11 +143,18 @@ class ChildTool(BaseTool): ...""" name = cls.__name__ raise SchemaAnnotationError( +<<<<<<< HEAD f"Tool definition for {name} must include valid" " type annotations for argument 'args_schema' to" " behave as expected.\nExpected annotation of" " 'Type[BaseModel]' but got" f" '{args_schema_type}'.\nExpected class looks" +======= + f"Tool definition for {name} must include valid type" + " annotations for argument 'args_schema' to behave as" + " expected.\nExpected annotation of 'Type[BaseModel]' but" + f" got '{args_schema_type}'.\nExpected class looks" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) f" like:\n{typehint_mandate}" ) @@ -270,9 +277,13 @@ class ChildTool(BaseTool): if input_args is not None: result = input_args.parse_obj(tool_input) return { +<<<<<<< HEAD k: v for k, v in result.dict().items() if k in tool_input +======= + k: v for k, v in result.dict().items() if k in tool_input +>>>>>>> 49c7b97c (code quality fixes: line length = 80) } return tool_input @@ -397,8 +408,13 @@ class ChildTool(BaseTool): observation = self.handle_tool_error(e) else: raise ValueError( +<<<<<<< HEAD "Got unexpected type of `handle_tool_error`." " Expected bool, str or callable. Received:" +======= + "Got unexpected type of `handle_tool_error`. Expected" + " bool, str or callable. Received:" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) f" {self.handle_tool_error}" ) run_manager.on_tool_end( @@ -489,8 +505,13 @@ class ChildTool(BaseTool): observation = self.handle_tool_error(e) else: raise ValueError( +<<<<<<< HEAD "Got unexpected type of `handle_tool_error`." " Expected bool, str or callable. Received:" +======= + "Got unexpected type of `handle_tool_error`. Expected" + " bool, str or callable. Received:" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) f" {self.handle_tool_error}" ) await run_manager.on_tool_end( @@ -563,8 +584,13 @@ class Tool(BaseTool): all_args = list(args) + list(kwargs.values()) if len(all_args) != 1: raise ToolException( +<<<<<<< HEAD "Too many arguments to single-input tool" f" {self.name}. Args: {all_args}" +======= + f"Too many arguments to single-input tool {self.name}. Args:" + f" {all_args}" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) return tuple(all_args), {} @@ -576,9 +602,15 @@ class Tool(BaseTool): ) -> Any: """Use the tool.""" if self.func: +<<<<<<< HEAD new_argument_supported = signature( self.func ).parameters.get("callbacks") +======= + new_argument_supported = signature(self.func).parameters.get( + "callbacks" + ) +>>>>>>> 49c7b97c (code quality fixes: line length = 80) return ( self.func( *args, @@ -710,9 +742,15 @@ class StructuredTool(BaseTool): ) -> Any: """Use the tool.""" if self.func: +<<<<<<< HEAD new_argument_supported = signature( self.func ).parameters.get("callbacks") +======= + new_argument_supported = signature(self.func).parameters.get( + "callbacks" + ) +>>>>>>> 49c7b97c (code quality fixes: line length = 80) return ( self.func( *args, diff --git a/swarms/utils/code_interpreter.py b/swarms/utils/code_interpreter.py index 98fbab70..639e0996 100644 --- a/swarms/utils/code_interpreter.py +++ b/swarms/utils/code_interpreter.py @@ -117,10 +117,14 @@ class SubprocessCodeInterpreter(BaseCodeInterpreter): # applescript yield {"output": traceback.format_exc()} yield { +<<<<<<< HEAD "output": ( "Retrying..." f" ({retry_count}/{max_retries})" ) +======= + "output": f"Retrying... ({retry_count}/{max_retries})" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) } yield {"output": "Restarting process."} @@ -130,8 +134,12 @@ class SubprocessCodeInterpreter(BaseCodeInterpreter): if retry_count > max_retries: yield { "output": ( +<<<<<<< HEAD "Maximum retries reached. Could not" " execute code." +======= + "Maximum retries reached. Could not execute code." +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) } return diff --git a/swarms/utils/decorators.py b/swarms/utils/decorators.py index e4c11574..0ca7800e 100644 --- a/swarms/utils/decorators.py +++ b/swarms/utils/decorators.py @@ -32,8 +32,12 @@ def timing_decorator(func): result = func(*args, **kwargs) end_time = time.time() logging.info( +<<<<<<< HEAD f"{func.__name__} executed in" f" {end_time - start_time} seconds" +======= + f"{func.__name__} executed in {end_time - start_time} seconds" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) return result @@ -84,8 +88,12 @@ def deprecated_decorator(func): @functools.wraps(func) def wrapper(*args, **kwargs): warnings.warn( +<<<<<<< HEAD f"{func.__name__} is deprecated", category=DeprecationWarning, +======= + f"{func.__name__} is deprecated", category=DeprecationWarning +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) return func(*args, **kwargs) diff --git a/swarms/utils/futures.py b/swarms/utils/futures.py new file mode 100644 index 00000000..a5ffdf51 --- /dev/null +++ b/swarms/utils/futures.py @@ -0,0 +1,12 @@ +from concurrent import futures +from typing import TypeVar + +T = TypeVar("T") + + +def execute_futures_dict(fs_dict: dict[str, futures.Future[T]]) -> dict[str, T]: + futures.wait( + fs_dict.values(), timeout=None, return_when=futures.ALL_COMPLETED + ) + + return {key: future.result() for key, future in fs_dict.items()} diff --git a/swarms/utils/loggers.py b/swarms/utils/loggers.py index a0dec94d..5ff65ca4 100644 --- a/swarms/utils/loggers.py +++ b/swarms/utils/loggers.py @@ -117,9 +117,14 @@ class Logger: ) error_handler.setLevel(logging.ERROR) error_formatter = AutoGptFormatter( +<<<<<<< HEAD "%(asctime)s %(levelname)s" " %(module)s:%(funcName)s:%(lineno)d %(title)s" " %(message_no_color)s" +======= + "%(asctime)s %(levelname)s %(module)s:%(funcName)s:%(lineno)d" + " %(title)s %(message_no_color)s" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) error_handler.setFormatter(error_formatter) @@ -299,9 +304,15 @@ class Logger: additionalText = ( "Please ensure you've setup and configured everything" " correctly. Read" +<<<<<<< HEAD " https://github.com/Torantulino/Auto-GPT#readme to" " double check. You can also create a github issue or" " join the discord and ask there!" +======= + " https://github.com/Torantulino/Auto-GPT#readme to double" + " check. You can also create a github issue or join the discord" + " and ask there!" +>>>>>>> 49c7b97c (code quality fixes: line length = 80) ) self.typewriter_log( diff --git a/swarms/utils/parse_code.py b/swarms/utils/parse_code.py index 2e0fa438..36dcbf34 100644 --- a/swarms/utils/parse_code.py +++ b/swarms/utils/parse_code.py @@ -18,6 +18,7 @@ import re def extract_code_in_backticks_in_string(s: str) -> str: """ +<<<<<<< HEAD Extracts code blocks from a markdown string. Args: @@ -29,3 +30,10 @@ def extract_code_in_backticks_in_string(s: str) -> str: pattern = r"```([\w\+\#\-\.\s]*)(.*?)```" matches = re.findall(pattern, s, re.DOTALL) return "\n".join(match[1].strip() for match in matches) +======= + pattern = r"`` ``(.*?)`` " # Non-greedy match between six backticks + match = re.search( + pattern, message, re.DOTALL + ) # re.DOTALL to match newline chars + return match.group(1).strip() if match else None +>>>>>>> 49c7b97c (code quality fixes: line length = 80) diff --git a/tests/agents/omni_modal.py b/tests/agents/omni_modal.py new file mode 100644 index 00000000..41aa050b --- /dev/null +++ b/tests/agents/omni_modal.py @@ -0,0 +1,40 @@ +import pytest +from langchain.base_language import BaseLanguageModel + +from swarms.agents.omni_modal_agent import ( + OmniModalAgent, # Replace `your_module_name` with the appropriate module name +) + + +# Mock objects or set up fixtures for dependent classes or external methods +@pytest.fixture +def mock_llm(): + # For this mock, we are assuming the BaseLanguageModel has a method named "process" + class MockLLM(BaseLanguageModel): + def process(self, input): + return "mock response" + + return MockLLM() + + +@pytest.fixture +def omni_agent(mock_llm): + return OmniModalAgent(mock_llm) + + +def test_omnimodalagent_initialization(omni_agent): + assert omni_agent.llm is not None, "LLM initialization failed" + assert len(omni_agent.tools) > 0, "Tools initialization failed" + + +def test_omnimodalagent_run(omni_agent): + input_string = "Hello, how are you?" + response = omni_agent.run(input_string) + assert response is not None, "Response generation failed" + assert isinstance(response, str), "Response should be a string" + + +def test_task_executor_initialization(omni_agent): + assert ( + omni_agent.task_executor is not None + ), "TaskExecutor initialization failed" diff --git a/tests/memory/oceandb.py b/tests/memory/oceandb.py new file mode 100644 index 00000000..c74b7c15 --- /dev/null +++ b/tests/memory/oceandb.py @@ -0,0 +1,97 @@ +import pytest +from unittest.mock import Mock, patch +from swarms.memory.ocean import OceanDB + + +def test_init(): + with patch("oceandb.Client") as MockClient: + MockClient.return_value.heartbeat.return_value = "OK" + db = OceanDB(MockClient) + MockClient.assert_called_once() + assert db.client == MockClient + + +def test_init_exception(): + with patch("oceandb.Client") as MockClient: + MockClient.side_effect = Exception("Client error") + with pytest.raises(Exception) as e: + OceanDB(MockClient) + assert str(e.value) == "Client error" + + +def test_create_collection(): + with patch("oceandb.Client") as MockClient: + db = OceanDB(MockClient) + db.create_collection("test", "modality") + MockClient.create_collection.assert_called_once_with( + "test", embedding_function=Mock.ANY + ) + + +def test_create_collection_exception(): + with patch("oceandb.Client") as MockClient: + MockClient.create_collection.side_effect = Exception( + "Create collection error" + ) + db = OceanDB(MockClient) + with pytest.raises(Exception) as e: + db.create_collection("test", "modality") + assert str(e.value) == "Create collection error" + + +def test_append_document(): + with patch("oceandb.Client") as MockClient: + db = OceanDB(MockClient) + collection = Mock() + db.append_document(collection, "doc", "id") + collection.add.assert_called_once_with(documents=["doc"], ids=["id"]) + + +def test_append_document_exception(): + with patch("oceandb.Client") as MockClient: + db = OceanDB(MockClient) + collection = Mock() + collection.add.side_effect = Exception("Append document error") + with pytest.raises(Exception) as e: + db.append_document(collection, "doc", "id") + assert str(e.value) == "Append document error" + + +def test_add_documents(): + with patch("oceandb.Client") as MockClient: + db = OceanDB(MockClient) + collection = Mock() + db.add_documents(collection, ["doc1", "doc2"], ["id1", "id2"]) + collection.add.assert_called_once_with( + documents=["doc1", "doc2"], ids=["id1", "id2"] + ) + + +def test_add_documents_exception(): + with patch("oceandb.Client") as MockClient: + db = OceanDB(MockClient) + collection = Mock() + collection.add.side_effect = Exception("Add documents error") + with pytest.raises(Exception) as e: + db.add_documents(collection, ["doc1", "doc2"], ["id1", "id2"]) + assert str(e.value) == "Add documents error" + + +def test_query(): + with patch("oceandb.Client") as MockClient: + db = OceanDB(MockClient) + collection = Mock() + db.query(collection, ["query1", "query2"], 2) + collection.query.assert_called_once_with( + query_texts=["query1", "query2"], n_results=2 + ) + + +def test_query_exception(): + with patch("oceandb.Client") as MockClient: + db = OceanDB(MockClient) + collection = Mock() + collection.query.side_effect = Exception("Query error") + with pytest.raises(Exception) as e: + db.query(collection, ["query1", "query2"], 2) + assert str(e.value) == "Query error" diff --git a/tests/models/bingchat.py b/tests/models/bingchat.py new file mode 100644 index 00000000..8f29f905 --- /dev/null +++ b/tests/models/bingchat.py @@ -0,0 +1,61 @@ +import unittest +import json +import os + +# Assuming the BingChat class is in a file named "bing_chat.py" +from bing_chat import BingChat + + +class TestBingChat(unittest.TestCase): + def setUp(self): + # Path to a mock cookies file for testing + self.mock_cookies_path = "./mock_cookies.json" + with open(self.mock_cookies_path, "w") as file: + json.dump({"mock_cookie": "mock_value"}, file) + + self.chat = BingChat(cookies_path=self.mock_cookies_path) + + def tearDown(self): + os.remove(self.mock_cookies_path) + + def test_init(self): + self.assertIsInstance(self.chat, BingChat) + self.assertIsNotNone(self.chat.bot) + + def test_call(self): + # Mocking the asynchronous behavior for the purpose of the test + self.chat.bot.ask = lambda *args, **kwargs: {"text": "Hello, Test!"} + response = self.chat("Test prompt") + self.assertEqual(response, "Hello, Test!") + + def test_create_img(self): + # Mocking the ImageGen behavior for the purpose of the test + class MockImageGen: + def __init__(self, *args, **kwargs): + pass + + def get_images(self, *args, **kwargs): + return [{"path": "mock_image.png"}] + + @staticmethod + def save_images(*args, **kwargs): + pass + + original_image_gen = BingChat.ImageGen + BingChat.ImageGen = MockImageGen + + img_path = self.chat.create_img( + "Test prompt", auth_cookie="mock_auth_cookie" + ) + self.assertEqual(img_path, "./output/mock_image.png") + + BingChat.ImageGen = original_image_gen + + def test_set_cookie_dir_path(self): + test_path = "./test_path" + BingChat.set_cookie_dir_path(test_path) + self.assertEqual(BingChat.Cookie.dir_path, test_path) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/models/gpt4v.py b/tests/models/gpt4v.py new file mode 100644 index 00000000..8532d313 --- /dev/null +++ b/tests/models/gpt4v.py @@ -0,0 +1,414 @@ +import logging +import os +from unittest.mock import Mock + +import pytest +from dotenv import load_dotenv +from requests.exceptions import ( + ConnectionError, + HTTPError, + RequestException, + Timeout, +) + +from swarms.models.gpt4v import GPT4Vision, GPT4VisionResponse + +load_dotenv + +api_key = os.getenv("OPENAI_API_KEY") + + +# Mock the OpenAI client +@pytest.fixture +def mock_openai_client(): + return Mock() + + +@pytest.fixture +def gpt4vision(mock_openai_client): + return GPT4Vision(client=mock_openai_client) + + +def test_gpt4vision_default_values(): + # Arrange and Act + gpt4vision = GPT4Vision() + + # Assert + assert gpt4vision.max_retries == 3 + assert gpt4vision.model == "gpt-4-vision-preview" + assert gpt4vision.backoff_factor == 2.0 + assert gpt4vision.timeout_seconds == 10 + assert gpt4vision.api_key is None + assert gpt4vision.quality == "low" + assert gpt4vision.max_tokens == 200 + + +def test_gpt4vision_api_key_from_env_variable(): + # Arrange + api_key = os.environ["OPENAI_API_KEY"] + + # Act + gpt4vision = GPT4Vision() + + # Assert + assert gpt4vision.api_key == api_key + + +def test_gpt4vision_set_api_key(): + # Arrange + gpt4vision = GPT4Vision(api_key=api_key) + + # Assert + assert gpt4vision.api_key == api_key + + +def test_gpt4vision_invalid_max_retries(): + # Arrange and Act + with pytest.raises(ValueError): + GPT4Vision(max_retries=-1) + + +def test_gpt4vision_invalid_backoff_factor(): + # Arrange and Act + with pytest.raises(ValueError): + GPT4Vision(backoff_factor=-1) + + +def test_gpt4vision_invalid_timeout_seconds(): + # Arrange and Act + with pytest.raises(ValueError): + GPT4Vision(timeout_seconds=-1) + + +def test_gpt4vision_invalid_max_tokens(): + # Arrange and Act + with pytest.raises(ValueError): + GPT4Vision(max_tokens=-1) + + +def test_gpt4vision_logger_initialized(): + # Arrange + gpt4vision = GPT4Vision() + + # Assert + assert isinstance(gpt4vision.logger, logging.Logger) + + +def test_gpt4vision_process_img_nonexistent_file(): + # Arrange + gpt4vision = GPT4Vision() + img_path = "nonexistent_image.jpg" + + # Act and Assert + with pytest.raises(FileNotFoundError): + gpt4vision.process_img(img_path) + + +def test_gpt4vision_call_single_task_single_image_no_openai_client(gpt4vision): + # Arrange + img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" + task = "Describe this image." + + # Act and Assert + with pytest.raises(AttributeError): + gpt4vision(img_url, [task]) + + +def test_gpt4vision_call_single_task_single_image_empty_response( + gpt4vision, mock_openai_client +): + # Arrange + img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" + task = "Describe this image." + + mock_openai_client.chat.completions.create.return_value.choices = [] + + # Act + response = gpt4vision(img_url, [task]) + + # Assert + assert response.answer == "" + mock_openai_client.chat.completions.create.assert_called_once() + + +def test_gpt4vision_call_multiple_tasks_single_image_empty_responses( + gpt4vision, mock_openai_client +): + # Arrange + img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" + tasks = ["Describe this image.", "What's in this picture?"] + + mock_openai_client.chat.completions.create.return_value.choices = [] + + # Act + responses = gpt4vision(img_url, tasks) + + # Assert + assert all(response.answer == "" for response in responses) + assert ( + mock_openai_client.chat.completions.create.call_count == 1 + ) # Should be called only once + + +def test_gpt4vision_call_single_task_single_image_timeout( + gpt4vision, mock_openai_client +): + # Arrange + img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" + task = "Describe this image." + + mock_openai_client.chat.completions.create.side_effect = Timeout( + "Request timed out" + ) + + # Act and Assert + with pytest.raises(Timeout): + gpt4vision(img_url, [task]) + + +def test_gpt4vision_call_retry_with_success_after_timeout( + gpt4vision, mock_openai_client +): + # Arrange + img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" + task = "Describe this image." + + # Simulate success after a timeout and retry + mock_openai_client.chat.completions.create.side_effect = [ + Timeout("Request timed out"), + { + "choices": [ + { + "message": { + "content": {"text": "A description of the image."} + } + } + ], + }, + ] + + # Act + response = gpt4vision(img_url, [task]) + + # Assert + assert response.answer == "A description of the image." + assert ( + mock_openai_client.chat.completions.create.call_count == 2 + ) # Should be called twice + + +def test_gpt4vision_process_img(): + # Arrange + img_path = "test_image.jpg" + gpt4vision = GPT4Vision() + + # Act + img_data = gpt4vision.process_img(img_path) + + # Assert + assert img_data.startswith("/9j/") # Base64-encoded image data + + +def test_gpt4vision_call_single_task_single_image( + gpt4vision, mock_openai_client +): + # Arrange + img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" + task = "Describe this image." + + expected_response = GPT4VisionResponse(answer="A description of the image.") + + mock_openai_client.chat.completions.create.return_value.choices[0].text = ( + expected_response.answer + ) + + # Act + response = gpt4vision(img_url, [task]) + + # Assert + assert response == expected_response + mock_openai_client.chat.completions.create.assert_called_once() + + +def test_gpt4vision_call_single_task_multiple_images( + gpt4vision, mock_openai_client +): + # Arrange + img_urls = [ + "https://example.com/image1.jpg", + "https://example.com/image2.jpg", + ] + task = "Describe these images." + + expected_response = GPT4VisionResponse(answer="Descriptions of the images.") + + mock_openai_client.chat.completions.create.return_value.choices[0].text = ( + expected_response.answer + ) + + # Act + response = gpt4vision(img_urls, [task]) + + # Assert + assert response == expected_response + mock_openai_client.chat.completions.create.assert_called_once() + + +def test_gpt4vision_call_multiple_tasks_single_image( + gpt4vision, mock_openai_client +): + # Arrange + img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" + tasks = ["Describe this image.", "What's in this picture?"] + + expected_responses = [ + GPT4VisionResponse(answer="A description of the image."), + GPT4VisionResponse(answer="It contains various objects."), + ] + + def create_mock_response(response): + return { + "choices": [{"message": {"content": {"text": response.answer}}}] + } + + mock_openai_client.chat.completions.create.side_effect = [ + create_mock_response(response) for response in expected_responses + ] + + # Act + responses = gpt4vision(img_url, tasks) + + # Assert + assert responses == expected_responses + assert ( + mock_openai_client.chat.completions.create.call_count == 1 + ) # Should be called only once + + def test_gpt4vision_call_multiple_tasks_single_image( + gpt4vision, mock_openai_client + ): + # Arrange + img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" + tasks = ["Describe this image.", "What's in this picture?"] + + expected_responses = [ + GPT4VisionResponse(answer="A description of the image."), + GPT4VisionResponse(answer="It contains various objects."), + ] + + mock_openai_client.chat.completions.create.side_effect = [ + { + "choices": [ + { + "message": { + "content": {"text": expected_responses[i].answer} + } + } + ] + } + for i in range(len(expected_responses)) + ] + + # Act + responses = gpt4vision(img_url, tasks) + + # Assert + assert responses == expected_responses + assert ( + mock_openai_client.chat.completions.create.call_count == 1 + ) # Should be called only once + + +def test_gpt4vision_call_multiple_tasks_multiple_images( + gpt4vision, mock_openai_client +): + # Arrange + img_urls = [ + "https://images.unsplash.com/photo-1694734479857-626882b6db37?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + "https://images.unsplash.com/photo-1694734479898-6ac4633158ac?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D", + ] + tasks = ["Describe these images.", "What's in these pictures?"] + + expected_responses = [ + GPT4VisionResponse(answer="Descriptions of the images."), + GPT4VisionResponse(answer="They contain various objects."), + ] + + mock_openai_client.chat.completions.create.side_effect = [ + {"choices": [{"message": {"content": {"text": response.answer}}}]} + for response in expected_responses + ] + + # Act + responses = gpt4vision(img_urls, tasks) + + # Assert + assert responses == expected_responses + assert ( + mock_openai_client.chat.completions.create.call_count == 1 + ) # Should be called only once + + +def test_gpt4vision_call_http_error(gpt4vision, mock_openai_client): + # Arrange + img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" + task = "Describe this image." + + mock_openai_client.chat.completions.create.side_effect = HTTPError( + "HTTP Error" + ) + + # Act and Assert + with pytest.raises(HTTPError): + gpt4vision(img_url, [task]) + + +def test_gpt4vision_call_request_error(gpt4vision, mock_openai_client): + # Arrange + img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" + task = "Describe this image." + + mock_openai_client.chat.completions.create.side_effect = RequestException( + "Request Error" + ) + + # Act and Assert + with pytest.raises(RequestException): + gpt4vision(img_url, [task]) + + +def test_gpt4vision_call_connection_error(gpt4vision, mock_openai_client): + # Arrange + img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" + task = "Describe this image." + + mock_openai_client.chat.completions.create.side_effect = ConnectionError( + "Connection Error" + ) + + # Act and Assert + with pytest.raises(ConnectionError): + gpt4vision(img_url, [task]) + + +def test_gpt4vision_call_retry_with_success(gpt4vision, mock_openai_client): + # Arrange + img_url = "https://images.unsplash.com/photo-1694734479942-8cc7f4660578?q=80&w=1287&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" + task = "Describe this image." + + # Simulate success after a retry + mock_openai_client.chat.completions.create.side_effect = [ + RequestException("Temporary error"), + { + "choices": [{"text": "A description of the image."}] + }, # fixed dictionary syntax + ] + + # Act + response = gpt4vision(img_url, [task]) + + # Assert + assert response.answer == "A description of the image." + assert ( + mock_openai_client.chat.completions.create.call_count == 2 + ) # Should be called twice diff --git a/tests/models/revgptv1.py b/tests/models/revgptv1.py new file mode 100644 index 00000000..5908b64e --- /dev/null +++ b/tests/models/revgptv1.py @@ -0,0 +1,90 @@ +import unittest +from unittest.mock import patch +from Sswarms.models.revgptv1 import RevChatGPTModelv1 + + +class TestRevChatGPT(unittest.TestCase): + def setUp(self): + self.access_token = "" + self.model = RevChatGPTModelv1(access_token=self.access_token) + + def test_run(self): + prompt = "What is the capital of France?" + response = self.model.run(prompt) + self.assertEqual(response, "The capital of France is Paris.") + + def test_run_time(self): + prompt = "Generate a 300 word essay about technology." + self.model.run(prompt) + self.assertLess(self.model.end_time - self.model.start_time, 60) + + def test_generate_summary(self): + text = ( + "This is a sample text to summarize. It has multiple sentences and" + " details. The summary should be concise." + ) + summary = self.model.generate_summary(text) + self.assertLess(len(summary), len(text) / 2) + + def test_enable_plugin(self): + plugin_id = "some_plugin_id" + self.model.enable_plugin(plugin_id) + self.assertIn(plugin_id, self.model.config["plugin_ids"]) + + def test_list_plugins(self): + plugins = self.model.list_plugins() + self.assertGreater(len(plugins), 0) + self.assertIsInstance(plugins[0], dict) + self.assertIn("id", plugins[0]) + self.assertIn("name", plugins[0]) + + def test_get_conversations(self): + conversations = self.model.chatbot.get_conversations() + self.assertIsInstance(conversations, list) + + @patch("RevChatGPTModelv1.Chatbot.get_msg_history") + def test_get_msg_history(self, mock_get_msg_history): + conversation_id = "convo_id" + self.model.chatbot.get_msg_history(conversation_id) + mock_get_msg_history.assert_called_with(conversation_id) + + @patch("RevChatGPTModelv1.Chatbot.share_conversation") + def test_share_conversation(self, mock_share_conversation): + self.model.chatbot.share_conversation() + mock_share_conversation.assert_called() + + def test_gen_title(self): + convo_id = "123" + message_id = "456" + title = self.model.chatbot.gen_title(convo_id, message_id) + self.assertIsInstance(title, str) + + def test_change_title(self): + convo_id = "123" + title = "New Title" + self.model.chatbot.change_title(convo_id, title) + self.assertEqual( + self.model.chatbot.get_msg_history(convo_id)["title"], title + ) + + def test_delete_conversation(self): + convo_id = "123" + self.model.chatbot.delete_conversation(convo_id) + with self.assertRaises(Exception): + self.model.chatbot.get_msg_history(convo_id) + + def test_clear_conversations(self): + self.model.chatbot.clear_conversations() + conversations = self.model.chatbot.get_conversations() + self.assertEqual(len(conversations), 0) + + def test_rollback_conversation(self): + original_convo_id = self.model.chatbot.conversation_id + self.model.chatbot.rollback_conversation(1) + self.assertNotEqual( + original_convo_id, self.model.chatbot.conversation_id + ) + + +if __name__ == "__main__": + unittest.main() diff --git a/tests/models/test_biogpt.py b/tests/models/test_biogpt.py index 38be125d..78e461d6 100644 --- a/tests/models/test_biogpt.py +++ b/tests/models/test_biogpt.py @@ -47,8 +47,13 @@ def test_cell_biology_response(biogpt_instance): # 40. Test for a question about protein structure def test_protein_structure_response(biogpt_instance): question = ( +<<<<<<< HEAD:tests/models/test_biogpt.py "What's the difference between alpha helix and beta sheet" " structures in proteins?" +======= + "What's the difference between alpha helix and beta sheet structures in" + " proteins?" +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/biogpt.py ) response = biogpt_instance(question) assert response and isinstance(response, str) diff --git a/tests/models/test_cohere.py b/tests/models/test_cohere.py index 5e6fc948..baa4a61e 100644 --- a/tests/models/test_cohere.py +++ b/tests/models/test_cohere.py @@ -564,8 +564,13 @@ def test_cohere_representation_model_multilingual_max_tokens_limit_exceeded( cohere_instance.model = "embed-multilingual-v3.0" cohere_instance.max_tokens = 10 prompt = ( +<<<<<<< HEAD:tests/models/test_cohere.py "This is a test prompt that will exceed the max tokens limit" " for multilingual model." +======= + "This is a test prompt that will exceed the max tokens limit for" + " multilingual model." +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/cohere.py ) with pytest.raises(ValueError): cohere_instance.embed(prompt) @@ -614,8 +619,13 @@ def test_cohere_representation_model_multilingual_light_max_tokens_limit_exceede cohere_instance.model = "embed-multilingual-light-v3.0" cohere_instance.max_tokens = 10 prompt = ( +<<<<<<< HEAD:tests/models/test_cohere.py "This is a test prompt that will exceed the max tokens limit" " for multilingual light model." +======= + "This is a test prompt that will exceed the max tokens limit for" + " multilingual light model." +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/cohere.py ) with pytest.raises(ValueError): cohere_instance.embed(prompt) @@ -687,8 +697,13 @@ def test_cohere_representation_model_english_max_tokens_limit_exceeded( cohere_instance.model = "embed-english-v3.0" cohere_instance.max_tokens = 10 prompt = ( +<<<<<<< HEAD:tests/models/test_cohere.py "This is a test prompt that will exceed the max tokens limit" " for English model." +======= + "This is a test prompt that will exceed the max tokens limit for" + " English model." +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/cohere.py ) with pytest.raises(ValueError): cohere_instance.embed(prompt) @@ -737,8 +752,13 @@ def test_cohere_representation_model_english_light_max_tokens_limit_exceeded( cohere_instance.model = "embed-english-light-v3.0" cohere_instance.max_tokens = 10 prompt = ( +<<<<<<< HEAD:tests/models/test_cohere.py "This is a test prompt that will exceed the max tokens limit" " for English light model." +======= + "This is a test prompt that will exceed the max tokens limit for" + " English light model." +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/cohere.py ) with pytest.raises(ValueError): cohere_instance.embed(prompt) diff --git a/tests/models/test_dalle3.py b/tests/models/test_dalle3.py index 00ba7bc9..06a17259 100644 --- a/tests/models/test_dalle3.py +++ b/tests/models/test_dalle3.py @@ -66,8 +66,17 @@ def test_dalle3_call_failure(dalle3, mock_openai_client, capsys): def test_dalle3_create_variations_success(dalle3, mock_openai_client): # Arrange +<<<<<<< HEAD:tests/models/test_dalle3.py img_url = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png" expected_variation_url = "https://cdn.openai.com/dall-e/encoded/feats/feats_02ABCDE.png" +======= + img_url = ( + "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png" + ) + expected_variation_url = ( + "https://cdn.openai.com/dall-e/encoded/feats/feats_02ABCDE.png" + ) +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/dalle3.py mock_openai_client.images.create_variation.return_value = Mock( data=[Mock(url=expected_variation_url)] ) @@ -88,7 +97,9 @@ def test_dalle3_create_variations_failure( dalle3, mock_openai_client, capsys ): # Arrange - img_url = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png" + img_url = ( + "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png" + ) expected_error_message = "Error running Dalle3: API Error" # Mocking OpenAIError @@ -237,9 +248,13 @@ def test_dalle3_call_with_retry(dalle3, mock_openai_client): # Simulate a retry scenario mock_openai_client.images.generate.side_effect = [ OpenAIError( +<<<<<<< HEAD:tests/models/test_dalle3.py "Temporary error", http_status=500, error="Internal Server Error", +======= + "Temporary error", http_status=500, error="Internal Server Error" +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/dalle3.py ), Mock(data=[Mock(url=expected_img_url)]), ] @@ -256,15 +271,28 @@ def test_dalle3_create_variations_with_retry( dalle3, mock_openai_client ): # Arrange +<<<<<<< HEAD:tests/models/test_dalle3.py img_url = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png" expected_variation_url = "https://cdn.openai.com/dall-e/encoded/feats/feats_02ABCDE.png" +======= + img_url = ( + "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png" + ) + expected_variation_url = ( + "https://cdn.openai.com/dall-e/encoded/feats/feats_02ABCDE.png" + ) +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/dalle3.py # Simulate a retry scenario mock_openai_client.images.create_variation.side_effect = [ OpenAIError( +<<<<<<< HEAD:tests/models/test_dalle3.py "Temporary error", http_status=500, error="Internal Server Error", +======= + "Temporary error", http_status=500, error="Internal Server Error" +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/dalle3.py ), Mock(data=[Mock(url=expected_variation_url)]), ] @@ -304,7 +332,9 @@ def test_dalle3_create_variations_exception_logging( dalle3, mock_openai_client, capsys ): # Arrange - img_url = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png" + img_url = ( + "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png" + ) expected_error_message = "Error running Dalle3: API Error" # Mocking OpenAIError @@ -351,7 +381,9 @@ def test_dalle3_call_no_api_key(): def test_dalle3_create_variations_no_api_key(): # Arrange - img_url = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png" + img_url = ( + "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png" + ) dalle3 = Dalle3(api_key=None) expected_error_message = ( "Error running Dalle3: API Key is missing" @@ -388,7 +420,9 @@ def test_dalle3_create_variations_with_retry_max_retries_exceeded( dalle3, mock_openai_client ): # Arrange - img_url = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png" + img_url = ( + "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png" + ) # Simulate max retries exceeded mock_openai_client.images.create_variation.side_effect = ( @@ -414,9 +448,13 @@ def test_dalle3_call_retry_with_success(dalle3, mock_openai_client): # Simulate success after a retry mock_openai_client.images.generate.side_effect = [ OpenAIError( +<<<<<<< HEAD:tests/models/test_dalle3.py "Temporary error", http_status=500, error="Internal Server Error", +======= + "Temporary error", http_status=500, error="Internal Server Error" +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/dalle3.py ), Mock(data=[Mock(url=expected_img_url)]), ] @@ -433,15 +471,28 @@ def test_dalle3_create_variations_retry_with_success( dalle3, mock_openai_client ): # Arrange +<<<<<<< HEAD:tests/models/test_dalle3.py img_url = "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png" expected_variation_url = "https://cdn.openai.com/dall-e/encoded/feats/feats_02ABCDE.png" +======= + img_url = ( + "https://cdn.openai.com/dall-e/encoded/feats/feats_01J9J5ZKJZJY9.png" + ) + expected_variation_url = ( + "https://cdn.openai.com/dall-e/encoded/feats/feats_02ABCDE.png" + ) +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/dalle3.py # Simulate success after a retry mock_openai_client.images.create_variation.side_effect = [ OpenAIError( +<<<<<<< HEAD:tests/models/test_dalle3.py "Temporary error", http_status=500, error="Internal Server Error", +======= + "Temporary error", http_status=500, error="Internal Server Error" +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/dalle3.py ), Mock(data=[Mock(url=expected_variation_url)]), ] diff --git a/tests/models/test_distill_whisper.py b/tests/models/test_distill_whisper.py index 775bb896..07893c35 100644 --- a/tests/models/test_distill_whisper.py +++ b/tests/models/test_distill_whisper.py @@ -54,6 +54,7 @@ def test_transcribe_audio_file(distil_whisper_model): @pytest.mark.asyncio async def test_async_transcribe_audio_file(distil_whisper_model): +<<<<<<< HEAD:tests/models/test_distill_whisper.py test_data = np.random.rand( 16000 ) # Simulated audio data (1 second) @@ -63,6 +64,11 @@ async def test_async_transcribe_audio_file(distil_whisper_model): audio_file_path = create_audio_file( test_data, 16000, audio_file.name ) +======= + test_data = np.random.rand(16000) # Simulated audio data (1 second) + with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as audio_file: + audio_file_path = create_audio_file(test_data, 16000, audio_file.name) +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/distill_whisper.py transcription = await distil_whisper_model.async_transcribe( audio_file_path ) @@ -86,9 +92,13 @@ def test_transcribe_audio_data(distil_whisper_model): @pytest.mark.asyncio async def test_async_transcribe_audio_data(distil_whisper_model): +<<<<<<< HEAD:tests/models/test_distill_whisper.py test_data = np.random.rand( 16000 ) # Simulated audio data (1 second) +======= + test_data = np.random.rand(16000) # Simulated audio data (1 second) +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/distill_whisper.py transcription = await distil_whisper_model.async_transcribe( test_data.tobytes() ) @@ -193,9 +203,13 @@ def test_create_audio_file(): 16000 ) # Simulated audio data (1 second) sample_rate = 16000 +<<<<<<< HEAD:tests/models/test_distill_whisper.py with tempfile.NamedTemporaryFile( suffix=".wav", delete=False ) as audio_file: +======= + with tempfile.NamedTemporaryFile(suffix=".wav", delete=False) as audio_file: +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/distill_whisper.py audio_file_path = create_audio_file( test_data, sample_rate, audio_file.name ) @@ -323,9 +337,13 @@ async def test_async_transcribe_with_mocked_model( ): model_mock, processor_mock = mocked_model # Set up what the mock should return when it's called +<<<<<<< HEAD:tests/models/test_distill_whisper.py model_mock.return_value.generate.return_value = torch.tensor( [[0]] ) +======= + model_mock.return_value.generate.return_value = torch.tensor([[0]]) +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/distill_whisper.py processor_mock.return_value.batch_decode.return_value = [ "mocked transcription" ] diff --git a/tests/models/test_elevenlab.py b/tests/models/test_elevenlab.py index b28ecb31..5bbc80f5 100644 --- a/tests/models/test_elevenlab.py +++ b/tests/models/test_elevenlab.py @@ -84,8 +84,12 @@ def test_run_text_to_speech_error_handling(eleven_labs_tool): with pytest.raises( RuntimeError, match=( +<<<<<<< HEAD:tests/models/test_elevenlab.py "Error while running ElevenLabsText2SpeechTool: Test" " Exception" +======= + "Error while running ElevenLabsText2SpeechTool: Test Exception" +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/elevenlab.py ), ): eleven_labs_tool.run(SAMPLE_TEXT) diff --git a/tests/models/test_fuyu.py b/tests/models/test_fuyu.py index e76e11bb..a7775392 100644 --- a/tests/models/test_fuyu.py +++ b/tests/models/test_fuyu.py @@ -80,12 +80,18 @@ def test_tokenizer_type(fuyu_instance): def test_processor_has_image_processor_and_tokenizer(fuyu_instance): assert ( +<<<<<<< HEAD:tests/models/test_fuyu.py fuyu_instance.processor.image_processor == fuyu_instance.image_processor ) assert ( fuyu_instance.processor.tokenizer == fuyu_instance.tokenizer ) +======= + fuyu_instance.processor.image_processor == fuyu_instance.image_processor + ) + assert fuyu_instance.processor.tokenizer == fuyu_instance.tokenizer +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/fuyu.py def test_model_device_map(fuyu_instance): diff --git a/tests/models/test_idefics.py b/tests/models/test_idefics.py index 25a8dd5b..67b0921f 100644 --- a/tests/models/test_idefics.py +++ b/tests/models/test_idefics.py @@ -46,12 +46,17 @@ def test_run(idefics_instance): prompts = [["User: Test"]] with patch.object( idefics_instance, "processor" +<<<<<<< HEAD:tests/models/test_idefics.py ) as mock_processor, patch.object( idefics_instance, "model" ) as mock_model: mock_processor.return_value = { "input_ids": torch.tensor([1, 2, 3]) } +======= + ) as mock_processor, patch.object(idefics_instance, "model") as mock_model: + mock_processor.return_value = {"input_ids": torch.tensor([1, 2, 3])} +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/idefics.py mock_model.generate.return_value = torch.tensor([1, 2, 3]) mock_processor.batch_decode.return_value = ["Test"] @@ -65,12 +70,17 @@ def test_call(idefics_instance): prompts = [["User: Test"]] with patch.object( idefics_instance, "processor" +<<<<<<< HEAD:tests/models/test_idefics.py ) as mock_processor, patch.object( idefics_instance, "model" ) as mock_model: mock_processor.return_value = { "input_ids": torch.tensor([1, 2, 3]) } +======= + ) as mock_processor, patch.object(idefics_instance, "model") as mock_model: + mock_processor.return_value = {"input_ids": torch.tensor([1, 2, 3])} +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/idefics.py mock_model.generate.return_value = torch.tensor([1, 2, 3]) mock_processor.batch_decode.return_value = ["Test"] diff --git a/tests/models/test_kosmos2.py b/tests/models/test_kosmos2.py index 7e4f0e5f..fbd4fc78 100644 --- a/tests/models/test_kosmos2.py +++ b/tests/models/test_kosmos2.py @@ -56,9 +56,12 @@ def test_kosmos2_with_mocked_extraction_and_detection( kosmos2, sample_image, monkeypatch ): monkeypatch.setattr( +<<<<<<< HEAD:tests/models/test_kosmos2.py kosmos2, "extract_entities", mock_extract_entities ) monkeypatch.setattr( +======= +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/kosmos2.py kosmos2, "process_entities_to_detections", mock_process_entities_to_detections, diff --git a/tests/models/test_nougat.py b/tests/models/test_nougat.py index 858845a6..c200ff8b 100644 --- a/tests/models/test_nougat.py +++ b/tests/models/test_nougat.py @@ -22,9 +22,13 @@ def test_nougat_default_initialization(setup_nougat): def test_nougat_custom_initialization(): nougat = Nougat( +<<<<<<< HEAD:tests/models/test_nougat.py model_name_or_path="custom_path", min_length=10, max_new_tokens=50, +======= + model_name_or_path="custom_path", min_length=10, max_new_tokens=50 +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/nougat.py ) assert nougat.model_name_or_path == "custom_path" assert nougat.min_length == 10 diff --git a/tests/models/test_speech_t5.py b/tests/models/test_speech_t5.py index a33272fc..72369af5 100644 --- a/tests/models/test_speech_t5.py +++ b/tests/models/test_speech_t5.py @@ -18,9 +18,13 @@ def test_speecht5_init(speecht5_model): speecht5_model.processor, SpeechT5.processor.__class__ ) assert isinstance(speecht5_model.model, SpeechT5.model.__class__) +<<<<<<< HEAD:tests/models/test_speech_t5.py assert isinstance( speecht5_model.vocoder, SpeechT5.vocoder.__class__ ) +======= + assert isinstance(speecht5_model.vocoder, SpeechT5.vocoder.__class__) +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/speech_t5.py assert isinstance( speecht5_model.embeddings_dataset, torch.utils.data.Dataset ) diff --git a/tests/models/test_vilt.py b/tests/models/test_vilt.py index 99e6848e..e486a373 100644 --- a/tests/models/test_vilt.py +++ b/tests/models/test_vilt.py @@ -38,9 +38,13 @@ def test_vilt_prediction( # 3. Test Exception Handling for network @patch.object( +<<<<<<< HEAD:tests/models/test_vilt.py requests, "get", side_effect=requests.RequestException("Network error"), +======= + requests, "get", side_effect=requests.RequestException("Network error") +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/vilt.py ) def test_vilt_network_exception(vilt_instance): with pytest.raises(requests.RequestException): diff --git a/tests/models/test_yi_200k.py b/tests/models/test_yi_200k.py index 9f3c236f..9847f4e6 100644 --- a/tests/models/test_yi_200k.py +++ b/tests/models/test_yi_200k.py @@ -114,8 +114,12 @@ def test_yi34b_generate_text_with_invalid_repitition_penalty( prompt = "There's a place where time stands still." repitition_penalty = 0.0 # Invalid repitition_penalty with pytest.raises( +<<<<<<< HEAD:tests/models/test_yi_200k.py ValueError, match="repitition_penalty must be a positive float", +======= + ValueError, match="repitition_penalty must be a positive float" +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/models/yi_200k.py ): yi34b_model(prompt, repitition_penalty=repitition_penalty) diff --git a/tests/structs/test_agent.py b/tests/structs/test_agent.py index a8e1cf92..96c293b7 100644 --- a/tests/structs/test_agent.py +++ b/tests/structs/test_agent.py @@ -30,10 +30,15 @@ def basic_flow(mocked_llm): @pytest.fixture def flow_with_condition(mocked_llm): +<<<<<<< HEAD:tests/structs/test_agent.py return Agent( llm=mocked_llm, max_loops=5, stopping_condition=stop_when_repeats, +======= + return Flow( + llm=mocked_llm, max_loops=5, stopping_condition=stop_when_repeats +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/structs/flow.py ) @@ -116,6 +121,7 @@ def test_flow_with_custom_stopping_condition(mocked_llm): def stopping_condition(x): return "terminate" in x.lower() +<<<<<<< HEAD:tests/structs/test_agent.py agent = Agent( llm=mocked_llm, max_loops=5, @@ -123,6 +129,13 @@ def test_flow_with_custom_stopping_condition(mocked_llm): ) assert agent.stopping_condition("Please terminate now") assert not agent.stopping_condition("Continue the process") +======= + flow = Flow( + llm=mocked_llm, max_loops=5, stopping_condition=stopping_condition + ) + assert flow.stopping_condition("Please terminate now") + assert not flow.stopping_condition("Continue the process") +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/structs/flow.py # Test calling the agent directly @@ -191,9 +204,13 @@ def test_save_different_memory(basic_flow, tmp_path): # Test the stopping condition check def test_check_stopping_condition(flow_with_condition): +<<<<<<< HEAD:tests/structs/test_agent.py assert flow_with_condition._check_stopping_condition( "Stop this process" ) +======= + assert flow_with_condition._check_stopping_condition("Stop this process") +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/structs/flow.py assert not flow_with_condition._check_stopping_condition( "Continue the task" ) @@ -399,11 +416,16 @@ def test_flow_autosave_path(flow_instance): def test_flow_response_length(flow_instance): # Test checking the length of the response response = flow_instance.run( +<<<<<<< HEAD:tests/structs/test_agent.py "Generate a 10,000 word long blog on mental clarity and the" " benefits of meditation." ) assert ( len(response) > flow_instance.get_response_length_threshold() +======= + "Generate a 10,000 word long blog on mental clarity and the benefits of" + " meditation." +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/structs/flow.py ) @@ -583,6 +605,7 @@ def test_flow_rollback(flow_instance): flow_instance.get_current_prompt() == state1["current_prompt"] ) assert flow_instance.get_instructions() == state1["instructions"] +<<<<<<< HEAD:tests/structs/test_agent.py assert ( flow_instance.get_user_messages() == state1["user_messages"] ) @@ -594,6 +617,11 @@ def test_flow_rollback(flow_instance): flow_instance.get_conversation_log() == state1["conversation_log"] ) +======= + assert flow_instance.get_user_messages() == state1["user_messages"] + assert flow_instance.get_response_history() == state1["response_history"] + assert flow_instance.get_conversation_log() == state1["conversation_log"] +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/structs/flow.py assert ( flow_instance.is_dynamic_pacing_enabled() == state1["dynamic_pacing_enabled"] @@ -772,12 +800,17 @@ def test_flow_disable_message_history(flow_instance): response = flow_instance.run( "This message should not be recorded in history." ) +<<<<<<< HEAD:tests/structs/test_agent.py assert ( "This message should not be recorded in history." in response ) assert ( len(flow_instance.get_message_history()) == 0 ) # History is empty +======= + assert "This message should not be recorded in history." in response + assert len(flow_instance.get_message_history()) == 0 # History is empty +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/structs/flow.py def test_flow_enable_message_history(flow_instance): diff --git a/tests/swarms/multi_agent_debate.py b/tests/swarms/multi_agent_debate.py new file mode 100644 index 00000000..25e15ae5 --- /dev/null +++ b/tests/swarms/multi_agent_debate.py @@ -0,0 +1,66 @@ +from unittest.mock import patch +from swarms.swarms.multi_agent_debate import ( + MultiAgentDebate, + Worker, + select_speaker, +) + + +def test_multiagentdebate_initialization(): + multiagentdebate = MultiAgentDebate( + agents=[Worker] * 5, selection_func=select_speaker + ) + assert isinstance(multiagentdebate, MultiAgentDebate) + assert len(multiagentdebate.agents) == 5 + assert multiagentdebate.selection_func == select_speaker + + +@patch("swarms.workers.Worker.reset") +def test_multiagentdebate_reset_agents(mock_reset): + multiagentdebate = MultiAgentDebate( + agents=[Worker] * 5, selection_func=select_speaker + ) + multiagentdebate.reset_agents() + assert mock_reset.call_count == 5 + + +def test_multiagentdebate_inject_agent(): + multiagentdebate = MultiAgentDebate( + agents=[Worker] * 5, selection_func=select_speaker + ) + multiagentdebate.inject_agent(Worker) + assert len(multiagentdebate.agents) == 6 + + +@patch("swarms.workers.Worker.run") +def test_multiagentdebate_run(mock_run): + multiagentdebate = MultiAgentDebate( + agents=[Worker] * 5, selection_func=select_speaker + ) + results = multiagentdebate.run("Write a short story.") + assert len(results) == 5 + assert mock_run.call_count == 5 + + +def test_multiagentdebate_update_task(): + multiagentdebate = MultiAgentDebate( + agents=[Worker] * 5, selection_func=select_speaker + ) + multiagentdebate.update_task("Write a short story.") + assert multiagentdebate.task == "Write a short story." + + +def test_multiagentdebate_format_results(): + multiagentdebate = MultiAgentDebate( + agents=[Worker] * 5, selection_func=select_speaker + ) + results = [ + {"agent": "Agent 1", "response": "Hello, world!"}, + {"agent": "Agent 2", "response": "Goodbye, world!"}, + ] + formatted_results = multiagentdebate.format_results(results) + assert ( + formatted_results + == "Agent Agent 1 responded: Hello, world!\nAgent Agent 2 responded:" + " Goodbye, world!" + ) diff --git a/tests/tools/test_base.py b/tests/tools/test_base.py index 9f9c700f..69c62489 100644 --- a/tests/tools/test_base.py +++ b/tests/tools/test_base.py @@ -86,9 +86,13 @@ def test_tool_ainvoke_with_coroutine(): return input_data tool = Tool( +<<<<<<< HEAD:tests/tools/test_base.py name="test_tool", coroutine=async_function, description="Test tool", +======= + name="test_tool", coroutine=async_function, description="Test tool" +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/tools/base.py ) result = tool.ainvoke("input_data") assert result == "input_data" diff --git a/tests/utils/test_subprocess_code_interpreter.py b/tests/utils/test_subprocess_code_interpreter.py index 2c7f7e47..2ad3af96 100644 --- a/tests/utils/test_subprocess_code_interpreter.py +++ b/tests/utils/test_subprocess_code_interpreter.py @@ -88,8 +88,12 @@ def test_subprocess_code_interpreter_run_with_keyboard_interrupt( ) result = list(subprocess_code_interpreter.run(code)) assert any( +<<<<<<< HEAD:tests/utils/test_subprocess_code_interpreter.py "KeyboardInterrupt" in output.get("output", "") for output in result +======= + "KeyboardInterrupt" in output.get("output", "") for output in result +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/utils/subprocess_code_interpreter.py ) @@ -302,6 +306,10 @@ def test_subprocess_code_interpreter_run_with_unicode_characters( code = 'print("こんにちは、世界")' # Contains unicode characters result = list(subprocess_code_interpreter.run(code)) assert any( +<<<<<<< HEAD:tests/utils/test_subprocess_code_interpreter.py "こんにちは、世界" in output.get("output", "") for output in result +======= + "こんにちは、世界" in output.get("output", "") for output in result +>>>>>>> 49c7b97c (code quality fixes: line length = 80):tests/utils/subprocess_code_interpreter.py )