From 4a8e5e74078021a1f17d8c13ec93fdf5c6a50aa0 Mon Sep 17 00:00:00 2001 From: Josh XT <102809327+Josh-XT@users.noreply.github.com> Date: Sat, 22 Apr 2023 16:35:50 -0400 Subject: [PATCH 01/62] Update inspired-projects.md --- docs/inspired-projects.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/inspired-projects.md b/docs/inspired-projects.md index 7acea3d4..04d70288 100644 --- a/docs/inspired-projects.md +++ b/docs/inspired-projects.md @@ -90,4 +90,10 @@ This document highlights a curated selection of remarkable projects inspired by 1. [Chippr-AGI](https://github.com/chippr-robotics/chippr-agi) - Description: Chippr-AGI is an open-source framework that uses AI models to automate task creation and prioritization. NodeJS, Redis, OpenAI/LLAMA - Author: [Cody Burns](https://github.com/realcodywburns) - - Twitter: https://twitter.com/DontPanicBurns + - Twitter: https://twitter.com/DontPanicBurns1. [Agent-LLM](https://github.com/Josh-XT/Agent-LLM) + + - Description: Agent-LLM is a dynamic AI task management assistant with adaptive memory, web browsing, code evaluation, and a versatile plugin system for seamless integration with various AI providers and models both locally and remotely hosted. + + - Author: [Josh XT](https://github.com/Josh-XT) + + - Twitter: https://twitter.com/Josh_XT From 12bbb043be56032513678ac9e1a56bf0cfb143cf Mon Sep 17 00:00:00 2001 From: Josh XT <102809327+Josh-XT@users.noreply.github.com> Date: Sat, 22 Apr 2023 16:40:07 -0400 Subject: [PATCH 02/62] Update inspired-projects.md --- docs/inspired-projects.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/inspired-projects.md b/docs/inspired-projects.md index 04d70288..b1828f38 100644 --- a/docs/inspired-projects.md +++ b/docs/inspired-projects.md @@ -90,7 +90,8 @@ This document highlights a curated selection of remarkable projects inspired by 1. [Chippr-AGI](https://github.com/chippr-robotics/chippr-agi) - Description: Chippr-AGI is an open-source framework that uses AI models to automate task creation and prioritization. NodeJS, Redis, OpenAI/LLAMA - Author: [Cody Burns](https://github.com/realcodywburns) - - Twitter: https://twitter.com/DontPanicBurns1. [Agent-LLM](https://github.com/Josh-XT/Agent-LLM) + - Twitter: https://twitter.com/DontPanicBurns +1. [Agent-LLM](https://github.com/Josh-XT/Agent-LLM) - Description: Agent-LLM is a dynamic AI task management assistant with adaptive memory, web browsing, code evaluation, and a versatile plugin system for seamless integration with various AI providers and models both locally and remotely hosted. From f961434c035156d4b8beac148e838dc87d69389a Mon Sep 17 00:00:00 2001 From: hsm207 Date: Mon, 24 Apr 2023 14:36:01 +0000 Subject: [PATCH 03/62] Stub setup instructions Signed-off-by: hsm207 --- docs/weaviate.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 docs/weaviate.md diff --git a/docs/weaviate.md b/docs/weaviate.md new file mode 100644 index 00000000..4c707bc8 --- /dev/null +++ b/docs/weaviate.md @@ -0,0 +1,3 @@ +# How to setup + +Coming soon!!! \ No newline at end of file From d3a54f2362eca2bd1fdea9cf43bf195809cf1cd5 Mon Sep 17 00:00:00 2001 From: hsm207 Date: Mon, 24 Apr 2023 14:38:00 +0000 Subject: [PATCH 04/62] Add dependency to client Signed-off-by: hsm207 --- extensions/requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/extensions/requirements.txt b/extensions/requirements.txt index 8979f608..5aa6cbde 100644 --- a/extensions/requirements.txt +++ b/extensions/requirements.txt @@ -1,3 +1,4 @@ ray==2.3.1 pinecone-client==2.2.1 -llama-cpp-python>=0.1.35 \ No newline at end of file +llama-cpp-python>=0.1.35 +weaviate-client>=3.16.1 \ No newline at end of file From 7896c7ed56025baa1962f94d468ad4a867e86576 Mon Sep 17 00:00:00 2001 From: hsm207 Date: Mon, 24 Apr 2023 15:02:47 +0000 Subject: [PATCH 05/62] Add weaviate env vars Signed-off-by: hsm207 --- .env.example | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.env.example b/.env.example index 03468f1b..a5e492d6 100644 --- a/.env.example +++ b/.env.example @@ -20,6 +20,11 @@ OPENAI_TEMPERATURE=0.0 # TABLE_NAME can be used instead RESULTS_STORE_NAME=baby-agi-test-table +# Weaviate config +# Uncomment and fill these to switch from local ChromaDB to Weaviate +# WEAVIATE_URL= +# WEAVIATE_API_KEY= + # Pinecone config # Uncomment and fill these to switch from local ChromaDB to Pinecone # PINECONE_API_KEY= From 0e67a460b3a8e18023f988060484e04644c61748 Mon Sep 17 00:00:00 2001 From: hsm207 Date: Mon, 24 Apr 2023 17:04:19 +0000 Subject: [PATCH 06/62] Implement weaviate storage Signed-off-by: hsm207 --- extensions/weaviate_storage.py | 127 +++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 extensions/weaviate_storage.py diff --git a/extensions/weaviate_storage.py b/extensions/weaviate_storage.py new file mode 100644 index 00000000..260cff81 --- /dev/null +++ b/extensions/weaviate_storage.py @@ -0,0 +1,127 @@ +import importlib +import logging +import re +from typing import Dict, List + +import openai +import weaviate + + +def can_import(module_name): + try: + importlib.import_module(module_name) + return True + except ImportError: + return False + + +assert can_import("weaviate"), ( + "\033[91m\033[1m" + + "Weaviate storage requires package weaviate-client.\nInstall: pip install -r extensions/requirements.txt" +) + + +def create_client(weaviate_url: str, weaviate_api_key: str): + auth_config = ( + weaviate.auth.AuthApiKey(api_key=weaviate_api_key) if weaviate_api_key else None + ) + + return weaviate.Client(weaviate_url, auth_client_secret=auth_config) + + +class WeaviateResultsStorage: + schema = { + "properties": [ + {"name": "result_id", "dataType": ["string"]}, + {"name": "task", "dataType": ["string"]}, + {"name": "result", "dataType": ["text"]}, + ] + } + + def __init__( + self, + openai_api_key: str, + weaviate_url: str, + weaviate_api_key: str, + llm_model: str, + llama_model_path: str, + results_store_name: str, + objective: str, + ): + openai.api_key = openai_api_key + self.client = create_client(weaviate_url, weaviate_api_key) + self.index_name = None + self.create_schema(results_store_name) + + self.llm_model = llm_model + self.llama_model_path = llama_model_path + + def create_schema(self, results_store_name: str): + valid_class_name = re.compile(r"^[A-Z][a-zA-Z0-9_]*$") + if not re.match(valid_class_name, results_store_name): + raise ValueError( + f"Invalid index name: {results_store_name}. " + "Index names must start with a capital letter and " + "contain only alphanumeric characters and underscores." + ) + + self.schema["class"] = results_store_name + if self.client.schema.contains(self.schema): + logging.info( + f"Index named {results_store_name} already exists. Reusing it." + ) + else: + logging.info(f"Creating index named {results_store_name}") + self.client.schema.create_class(self.schema) + + self.index_name = results_store_name + + def add(self, task: Dict, result: Dict, result_id: int, vector: List): + enriched_result = {"data": result} + vector = self.get_embedding(enriched_result["data"]) + + with self.client.batch as batch: + data_object = { + "result_id": result_id, + "task": task["task_name"], + "result": result, + } + batch.add_data_object( + data_object=data_object, class_name=self.index_name, vector=vector + ) + + def query(self, query: str, top_results_num: int) -> List[dict]: + query_embedding = self.get_embedding(query) + + results = ( + self.client.query.get(self.index_name, ["task"]) + .with_hybrid(query=query, alpha=0.5, vector=query_embedding) + .with_limit(top_results_num) + .do() + ) + + return self._extract_tasks(results) + + def _extract_tasks(self, data): + task_data = data.get("data", {}).get("Get", {}).get(self.index_name, []) + return [item["task"] for item in task_data] + + # Get embedding for the text + def get_embedding(self, text: str) -> list: + text = text.replace("\n", " ") + + if self.llm_model.startswith("llama"): + from llama_cpp import Llama + + llm_embed = Llama( + model_path=self.llama_model_path, + n_ctx=2048, + n_threads=4, + embedding=True, + use_mlock=True, + ) + return llm_embed.embed(text) + + return openai.Embedding.create(input=[text], model="text-embedding-ada-002")[ + "data" + ][0]["embedding"] From 79f54718a2c034a3429801fb041bda8df5db58b6 Mon Sep 17 00:00:00 2001 From: hsm207 Date: Mon, 24 Apr 2023 17:05:09 +0000 Subject: [PATCH 07/62] Use env to trigger weaviate storage use Check for weaviate first becuase chroma fails if the table name begins capital letters Signed-off-by: hsm207 --- babyagi.py | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/babyagi.py b/babyagi.py index 7c222a82..3e6b9928 100755 --- a/babyagi.py +++ b/babyagi.py @@ -205,17 +205,28 @@ def query(self, query: str, top_results_num: int) -> List[dict]: return [item["task"] for item in results["metadatas"][0]] # Initialize results storage -results_storage = DefaultResultsStorage() -PINECONE_API_KEY = os.getenv("PINECONE_API_KEY", "") -if PINECONE_API_KEY: - if can_import("extensions.pinecone_storage"): - PINECONE_ENVIRONMENT = os.getenv("PINECONE_ENVIRONMENT", "") - assert ( - PINECONE_ENVIRONMENT - ), "\033[91m\033[1m" + "PINECONE_ENVIRONMENT environment variable is missing from .env" + "\033[0m\033[0m" - from extensions.pinecone_storage import PineconeResultsStorage - results_storage = PineconeResultsStorage(OPENAI_API_KEY, PINECONE_API_KEY, PINECONE_ENVIRONMENT, LLM_MODEL, LLAMA_MODEL_PATH, RESULTS_STORE_NAME, OBJECTIVE) - print("\nReplacing results storage: " + "\033[93m\033[1m" + "Pinecone" + "\033[0m\033[0m") +WEAVIATE_URL = os.getenv("WEAVIATE_URL", "") +if WEAVIATE_URL: + if can_import("extensions.weaviate_storage"): + WEAVIATE_API_KEY = os.getenv("WEAVIATE_API_KEY", "") + from extensions.weaviate_storage import WeaviateResultsStorage + results_storage = WeaviateResultsStorage(OPENAI_API_KEY, WEAVIATE_URL, WEAVIATE_API_KEY, LLM_MODEL, LLAMA_MODEL_PATH, RESULTS_STORE_NAME, OBJECTIVE) + print("\nReplacing results storage: " + "\033[93m\033[1m" + "Weaviate" + "\033[0m\033[0m") + else: + print("\033[91m\033[1m" + "Weaviate is not installed. Falling back to ChromaDB." + "\033[0m\033[0m") + results_storage = DefaultResultsStorage() +else: + results_storage = DefaultResultsStorage() + PINECONE_API_KEY = os.getenv("PINECONE_API_KEY", "") + if PINECONE_API_KEY: + if can_import("extensions.pinecone_storage"): + PINECONE_ENVIRONMENT = os.getenv("PINECONE_ENVIRONMENT", "") + assert ( + PINECONE_ENVIRONMENT + ), "\033[91m\033[1m" + "PINECONE_ENVIRONMENT environment variable is missing from .env" + "\033[0m\033[0m" + from extensions.pinecone_storage import PineconeResultsStorage + results_storage = PineconeResultsStorage(OPENAI_API_KEY, PINECONE_API_KEY, PINECONE_ENVIRONMENT, LLM_MODEL, LLAMA_MODEL_PATH, RESULTS_STORE_NAME, OBJECTIVE) + print("\nReplacing results storage: " + "\033[93m\033[1m" + "Pinecone" + "\033[0m\033[0m") # Task storage supporting only a single instance of BabyAGI class SingleTaskListStorage: From c7316e76c4b1a0daf397f01a0de26565f6355680 Mon Sep 17 00:00:00 2001 From: hsm207 Date: Mon, 24 Apr 2023 23:42:55 +0000 Subject: [PATCH 08/62] Refactor results storage init logic Signed-off-by: hsm207 --- babyagi.py | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/babyagi.py b/babyagi.py index 3e6b9928..e00afe15 100755 --- a/babyagi.py +++ b/babyagi.py @@ -205,28 +205,32 @@ def query(self, query: str, top_results_num: int) -> List[dict]: return [item["task"] for item in results["metadatas"][0]] # Initialize results storage -WEAVIATE_URL = os.getenv("WEAVIATE_URL", "") -if WEAVIATE_URL: - if can_import("extensions.weaviate_storage"): +def try_weaviate(): + WEAVIATE_URL = os.getenv("WEAVIATE_URL", "") + if WEAVIATE_URL and can_import("extensions.weaviate_storage"): WEAVIATE_API_KEY = os.getenv("WEAVIATE_API_KEY", "") from extensions.weaviate_storage import WeaviateResultsStorage - results_storage = WeaviateResultsStorage(OPENAI_API_KEY, WEAVIATE_URL, WEAVIATE_API_KEY, LLM_MODEL, LLAMA_MODEL_PATH, RESULTS_STORE_NAME, OBJECTIVE) - print("\nReplacing results storage: " + "\033[93m\033[1m" + "Weaviate" + "\033[0m\033[0m") - else: - print("\033[91m\033[1m" + "Weaviate is not installed. Falling back to ChromaDB." + "\033[0m\033[0m") - results_storage = DefaultResultsStorage() -else: - results_storage = DefaultResultsStorage() + print("\nUsing results storage: " + "\033[93m\033[1m" + "Weaviate" + "\033[0m\033[0m") + return WeaviateResultsStorage(OPENAI_API_KEY, WEAVIATE_URL, WEAVIATE_API_KEY, LLM_MODEL, LLAMA_MODEL_PATH, RESULTS_STORE_NAME, OBJECTIVE) + return None + +def try_pinecone(): PINECONE_API_KEY = os.getenv("PINECONE_API_KEY", "") - if PINECONE_API_KEY: - if can_import("extensions.pinecone_storage"): - PINECONE_ENVIRONMENT = os.getenv("PINECONE_ENVIRONMENT", "") - assert ( - PINECONE_ENVIRONMENT - ), "\033[91m\033[1m" + "PINECONE_ENVIRONMENT environment variable is missing from .env" + "\033[0m\033[0m" - from extensions.pinecone_storage import PineconeResultsStorage - results_storage = PineconeResultsStorage(OPENAI_API_KEY, PINECONE_API_KEY, PINECONE_ENVIRONMENT, LLM_MODEL, LLAMA_MODEL_PATH, RESULTS_STORE_NAME, OBJECTIVE) - print("\nReplacing results storage: " + "\033[93m\033[1m" + "Pinecone" + "\033[0m\033[0m") + if PINECONE_API_KEY and can_import("extensions.pinecone_storage"): + PINECONE_ENVIRONMENT = os.getenv("PINECONE_ENVIRONMENT", "") + assert ( + PINECONE_ENVIRONMENT + ), "\033[91m\033[1m" + "PINECONE_ENVIRONMENT environment variable is missing from .env" + "\033[0m\033[0m" + from extensions.pinecone_storage import PineconeResultsStorage + print("\nUsing results storage: " + "\033[93m\033[1m" + "Pinecone" + "\033[0m\033[0m") + return PineconeResultsStorage(OPENAI_API_KEY, PINECONE_API_KEY, PINECONE_ENVIRONMENT, LLM_MODEL, LLAMA_MODEL_PATH, RESULTS_STORE_NAME, OBJECTIVE) + return None + +def use_chroma(): + print("\nUsing results storage: " + "\033[93m\033[1m" + "Chroma (Default)" + "\033[0m\033[0m") + return DefaultResultsStorage() + +results_storage = try_weaviate() or try_pinecone() or use_chroma() # Task storage supporting only a single instance of BabyAGI class SingleTaskListStorage: From a7ada254344f336bb7e5598fc62e518d389868b3 Mon Sep 17 00:00:00 2001 From: hsm207 Date: Tue, 25 Apr 2023 09:18:22 +0000 Subject: [PATCH 09/62] Add support for embedded weaviate Signed-off-by: hsm207 --- .env.example | 1 + babyagi.py | 5 +++-- extensions/weaviate_storage.py | 24 ++++++++++++++++++------ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/.env.example b/.env.example index a5e492d6..997a0a45 100644 --- a/.env.example +++ b/.env.example @@ -22,6 +22,7 @@ RESULTS_STORE_NAME=baby-agi-test-table # Weaviate config # Uncomment and fill these to switch from local ChromaDB to Weaviate +# WEAVIATE_USE_EMBEDDED=true # WEAVIATE_URL= # WEAVIATE_API_KEY= diff --git a/babyagi.py b/babyagi.py index e00afe15..b9e7f4ae 100755 --- a/babyagi.py +++ b/babyagi.py @@ -207,11 +207,12 @@ def query(self, query: str, top_results_num: int) -> List[dict]: # Initialize results storage def try_weaviate(): WEAVIATE_URL = os.getenv("WEAVIATE_URL", "") - if WEAVIATE_URL and can_import("extensions.weaviate_storage"): + WEAVIATE_USE_EMBEDDED = os.getenv("WEAVIATE_USE_EMBEDDED", "False").lower() == "true" + if (WEAVIATE_URL or WEAVIATE_USE_EMBEDDED) and can_import("extensions.weaviate_storage"): WEAVIATE_API_KEY = os.getenv("WEAVIATE_API_KEY", "") from extensions.weaviate_storage import WeaviateResultsStorage print("\nUsing results storage: " + "\033[93m\033[1m" + "Weaviate" + "\033[0m\033[0m") - return WeaviateResultsStorage(OPENAI_API_KEY, WEAVIATE_URL, WEAVIATE_API_KEY, LLM_MODEL, LLAMA_MODEL_PATH, RESULTS_STORE_NAME, OBJECTIVE) + return WeaviateResultsStorage(OPENAI_API_KEY, WEAVIATE_URL, WEAVIATE_API_KEY, WEAVIATE_USE_EMBEDDED, LLM_MODEL, LLAMA_MODEL_PATH, RESULTS_STORE_NAME, OBJECTIVE) return None def try_pinecone(): diff --git a/extensions/weaviate_storage.py b/extensions/weaviate_storage.py index 260cff81..cb562244 100644 --- a/extensions/weaviate_storage.py +++ b/extensions/weaviate_storage.py @@ -5,6 +5,7 @@ import openai import weaviate +from weaviate.embedded import EmbeddedOptions def can_import(module_name): @@ -21,12 +22,20 @@ def can_import(module_name): ) -def create_client(weaviate_url: str, weaviate_api_key: str): - auth_config = ( - weaviate.auth.AuthApiKey(api_key=weaviate_api_key) if weaviate_api_key else None - ) +def create_client( + weaviate_url: str, weaviate_api_key: str, weaviate_use_embedded: bool +): + if weaviate_use_embedded: + client = weaviate.Client(embedded_options=EmbeddedOptions()) + else: + auth_config = ( + weaviate.auth.AuthApiKey(api_key=weaviate_api_key) + if weaviate_api_key + else None + ) + client = weaviate.Client(weaviate_url, auth_client_secret=auth_config) - return weaviate.Client(weaviate_url, auth_client_secret=auth_config) + return client class WeaviateResultsStorage: @@ -43,13 +52,16 @@ def __init__( openai_api_key: str, weaviate_url: str, weaviate_api_key: str, + weaviate_use_embedded: bool, llm_model: str, llama_model_path: str, results_store_name: str, objective: str, ): openai.api_key = openai_api_key - self.client = create_client(weaviate_url, weaviate_api_key) + self.client = create_client( + weaviate_url, weaviate_api_key, weaviate_use_embedded + ) self.index_name = None self.create_schema(results_store_name) From ecd66e72dad3e5629fde6d65234ec7e94490ad5a Mon Sep 17 00:00:00 2001 From: hsm207 Date: Tue, 25 Apr 2023 09:26:39 +0000 Subject: [PATCH 10/62] Update README Signed-off-by: hsm207 --- docs/weaviate.md | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/docs/weaviate.md b/docs/weaviate.md index 4c707bc8..1a7da653 100644 --- a/docs/weaviate.md +++ b/docs/weaviate.md @@ -1,3 +1,26 @@ # How to setup -Coming soon!!! \ No newline at end of file +## To do: + +1. mention weaviate in main [README.md](../README.md) + +## Setup + +install weaviate client: + +```bash +pip -r extensions/requirements.txt +``` + +Config the following env vars + +```bash +# TABLE_NAME=BabyAGITableInWeaviate + +# Weaviate config +# Uncomment and fill these to switch from local ChromaDB to Weaviate +# WEAVIATE_USE_EMBEDDED=true +# WEAVIATE_URL= +# WEAVIATE_API_KEY= +``` +follow step 4 onwards in main readme \ No newline at end of file From d78ec4f547c7f874dece7e6873040f3686c77ad9 Mon Sep 17 00:00:00 2001 From: Zain Hasan Date: Thu, 27 Apr 2023 15:55:18 -0400 Subject: [PATCH 11/62] weaviate as a vector db option --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 85209106..8daf828a 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ # Objective -This Python script is an example of an AI-powered task management system. The system uses OpenAI and Chroma to create, prioritize, and execute tasks. The main idea behind this system is that it creates tasks based on the result of previous tasks and a predefined objective. The script then uses OpenAI's natural language processing (NLP) capabilities to create new tasks based on the objective, and Chroma to store and retrieve task results for context. This is a pared-down version of the original [Task-Driven Autonomous Agent](https://twitter.com/yoheinakajima/status/1640934493489070080?s=20) (Mar 28, 2023). +This Python script is an example of an AI-powered task management system. The system uses OpenAI and vector databases such as Chroma or Weaviate to create, prioritize, and execute tasks. The main idea behind this system is that it creates tasks based on the result of previous tasks and a predefined objective. The script then uses OpenAI's natural language processing (NLP) capabilities to create new tasks based on the objective, and Chroma/Weaviate to store and retrieve task results for context. This is a pared-down version of the original [Task-Driven Autonomous Agent](https://twitter.com/yoheinakajima/status/1640934493489070080?s=20) (Mar 28, 2023). This README will cover the following: @@ -38,7 +38,7 @@ The script works by running an infinite loop that does the following steps: 1. Pulls the first task from the task list. 2. Sends the task to the execution agent, which uses OpenAI's API to complete the task based on the context. -3. Enriches the result and stores it in [Chroma](https://docs.trychroma.com). +3. Enriches the result and stores it in [Chroma](https://docs.trychroma.com)/[Weaviate](https://weaviate.io/). 4. Creates new tasks and reprioritizes the task list based on the objective and the result of the previous task.
@@ -50,7 +50,7 @@ The `task_creation_agent()` function is where OpenAI's API is used to create new The `prioritization_agent()` function is where OpenAI's API is used to reprioritize the task list. The function takes one parameter, the ID of the current task. It sends a prompt to OpenAI's API, which returns the reprioritized task list as a numbered list. -Finally, the script uses Chroma to store and retrieve task results for context. The script creates a Chroma collection based on the table name specified in the TABLE_NAME variable. Chroma is then used to store the results of the task in the collection, along with the task name and any additional metadata. +Finally, the script uses Chroma/Weaviate to store and retrieve task results for context. The script creates a Chroma/Weaviate collection based on the table name specified in the TABLE_NAME variable. Chroma/Weaviate is then used to store the results of the task in the collection, along with the task name and any additional metadata. # How to Use From d95910bb4f57ab8a5db053706c4ba96e0791d68b Mon Sep 17 00:00:00 2001 From: Zain Hasan Date: Thu, 27 Apr 2023 17:17:57 -0400 Subject: [PATCH 12/62] Update weaviate.md --- docs/weaviate.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/docs/weaviate.md b/docs/weaviate.md index 1a7da653..e805cbe0 100644 --- a/docs/weaviate.md +++ b/docs/weaviate.md @@ -1,18 +1,14 @@ # How to setup -## To do: - -1. mention weaviate in main [README.md](../README.md) - ## Setup -install weaviate client: +1. Install weaviate client: ```bash pip -r extensions/requirements.txt ``` -Config the following env vars +2. Configure the following environment variables: ```bash # TABLE_NAME=BabyAGITableInWeaviate @@ -23,4 +19,4 @@ Config the following env vars # WEAVIATE_URL= # WEAVIATE_API_KEY= ``` -follow step 4 onwards in main readme \ No newline at end of file +Follow step 4 onwards in main README From e27b65a1e01a953bce2625169cfd44d0052281e1 Mon Sep 17 00:00:00 2001 From: Zain Hasan Date: Thu, 27 Apr 2023 20:06:34 -0400 Subject: [PATCH 13/62] added general vector db image --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8daf828a..9c5fa936 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,7 @@ The script works by running an infinite loop that does the following steps: 4. Creates new tasks and reprioritizes the task list based on the objective and the result of the previous task.
-![image](https://user-images.githubusercontent.com/6764957/232961802-e4c4c17d-b520-4db1-827c-a218a1647c73.png) +![image](https://user-images.githubusercontent.com/21254008/235015461-543a897f-70cc-4b63-941a-2ae3c9172b11.png) The `execution_agent()` function is where the OpenAI API is used. It takes two parameters: the objective and the task. It then sends a prompt to OpenAI's API, which returns the result of the task. The prompt consists of a description of the AI system's task, the objective, and the task itself. The result is then returned as a string. From 4e1e8586f46b090d6668755f152d3cbee636050c Mon Sep 17 00:00:00 2001 From: Zain Hasan Date: Thu, 27 Apr 2023 20:08:43 -0400 Subject: [PATCH 14/62] Update weaviate.md --- docs/weaviate.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/weaviate.md b/docs/weaviate.md index e805cbe0..82497a32 100644 --- a/docs/weaviate.md +++ b/docs/weaviate.md @@ -11,12 +11,12 @@ pip -r extensions/requirements.txt 2. Configure the following environment variables: ```bash -# TABLE_NAME=BabyAGITableInWeaviate +TABLE_NAME=BabyAGITableInWeaviate # Weaviate config # Uncomment and fill these to switch from local ChromaDB to Weaviate -# WEAVIATE_USE_EMBEDDED=true -# WEAVIATE_URL= -# WEAVIATE_API_KEY= +WEAVIATE_USE_EMBEDDED=true +WEAVIATE_URL= +WEAVIATE_API_KEY= ``` Follow step 4 onwards in main README From dc7c3b1f641898dc588afaffa35f68e36e5ca01b Mon Sep 17 00:00:00 2001 From: Alexander Dibrov <108030031+dibrale@users.noreply.github.com> Date: Thu, 27 Apr 2023 19:37:28 -0500 Subject: [PATCH 15/62] Fix for silent llama Fix for prioritization agent deleting priority list when returning empty output. Prompt engineering and parameter improvements to reduce number of empty responses from llama models. More debug output. --- babyagi.py | 85 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 54 insertions(+), 31 deletions(-) diff --git a/babyagi.py b/babyagi.py index bcc44306..594e278a 100755 --- a/babyagi.py +++ b/babyagi.py @@ -46,6 +46,7 @@ # Model configuration OPENAI_TEMPERATURE = float(os.getenv("OPENAI_TEMPERATURE", 0.0)) + # Extensions support begin def can_import(module_name): @@ -66,6 +67,7 @@ def can_import(module_name): if ENABLE_COMMAND_LINE_ARGS: if can_import("extensions.argparseext"): from extensions.argparseext import parse_arguments + OBJECTIVE, INITIAL_TASK, LLM_MODEL, DOTENV_EXTENSIONS, INSTANCE_NAME, COOPERATIVE_MODE, JOIN_EXISTING_OBJECTIVE = parse_arguments() # Human mode extension @@ -80,6 +82,7 @@ def can_import(module_name): if DOTENV_EXTENSIONS: if can_import("extensions.dotenvext"): from extensions.dotenvext import load_dotenv_extensions + load_dotenv_extensions(DOTENV_EXTENSIONS) # TODO: There's still work to be done here to enable people to get @@ -106,22 +109,26 @@ def can_import(module_name): print(f"LLAMA : {LLAMA_MODEL_PATH}" + "\n") assert os.path.exists(LLAMA_MODEL_PATH), "\033[91m\033[1m" + f"Model can't be found." + "\033[0m\033[0m" - CTX_MAX = 2048 + CTX_MAX = 1024 LLAMA_THREADS_NUM = int(os.getenv("LLAMA_THREADS_NUM", 8)) + + print('Initialize model for evaluation') llm = Llama( model_path=LLAMA_MODEL_PATH, n_ctx=CTX_MAX, n_threads=LLAMA_THREADS_NUM, n_batch=512, - use_mlock=True, + use_mlock=False, ) + + print('\nInitialize model for embedding') llm_embed = Llama( model_path=LLAMA_MODEL_PATH, n_ctx=CTX_MAX, n_threads=LLAMA_THREADS_NUM, n_batch=512, embedding=True, - use_mlock=True, + use_mlock=False, ) print( @@ -284,8 +291,10 @@ def get_task_names(self): if can_import("extensions.ray_tasks"): import sys from pathlib import Path + sys.path.append(str(Path(__file__).resolve().parent)) from extensions.ray_tasks import CooperativeTaskListStorage + tasks_storage = CooperativeTaskListStorage(OBJECTIVE) print("\nReplacing tasks storage: " + "\033[93m\033[1m" + "Ray" + "\033[0m\033[0m") elif COOPERATIVE_MODE in ['d', 'distributed']: @@ -314,8 +323,18 @@ def openai_call( while True: try: if model.lower().startswith("llama"): - result = llm(prompt[:CTX_MAX], stop=["### Human"], echo=False, temperature=0.2) - return str(result['choices'][0]['text'].strip()) + result = llm(prompt[:CTX_MAX], + stop=["### Human"], + echo=False, + temperature=0.2, + top_k=40, + top_p=0.95, + repeat_penalty=1.05, + max_tokens=200) + # print('\n*****RESULT JSON DUMP*****\n') + # print(json.dumps(result)) + # print('\n') + return result['choices'][0]['text'].strip() elif model.lower().startswith("human"): return user_input_await(prompt) elif not model.lower().startswith("gpt-"): @@ -384,7 +403,6 @@ def openai_call( def task_creation_agent( objective: str, result: Dict, task_description: str, task_list: List[str] ): - prompt = f""" You are to use the result from an execution agent to create new tasks with the following objective: {objective}. The last completed task has the result: \n{result["data"]} @@ -392,22 +410,22 @@ def task_creation_agent( if task_list: prompt += f"These are incomplete tasks: {', '.join(task_list)}\n" - prompt += "Based on the result, create a list of new tasks to be completed in order to meet the objective. " + prompt += "Based on the result, return a list of tasks to be completed in order to meet the objective. " if task_list: prompt += "These new tasks must not overlap with incomplete tasks. " prompt += """ -Return all the new tasks, with one task per line in your response. The result must be a numbered list in the format: - +Return one task per line in your response. The result must be a numbered list in the format: + #. First task #. Second task - -The number of each entry must be followed by a period. -Do not include any headers before your numbered list. Do not follow your numbered list with any other output.""" - print(f'\n************** TASK CREATION AGENT PROMPT *************\n{prompt}\n') +The number of each entry must be followed by a period. If your list is empty, write "There are no tasks to add at this time." +Unless your list is empty, do not include any headers before your numbered list or follow your numbered list with any other output.""" + + print(f'\n*****TASK CREATION AGENT PROMPT****\n{prompt}\n') response = openai_call(prompt, max_tokens=2000) - print(f'\n************* TASK CREATION AGENT RESPONSE ************\n{response}\n') + print(f'\n****TASK CREATION AGENT RESPONSE****\n{response}\n') new_tasks = response.split('\n') new_tasks_list = [] for task_string in new_tasks: @@ -426,23 +444,26 @@ def task_creation_agent( def prioritization_agent(): task_names = tasks_storage.get_task_names() next_task_id = tasks_storage.next_task_id() + bullet_string = '\n' prompt = f""" -You are tasked with cleaning the format and re-prioritizing the following tasks: {', '.join(task_names)}. +You are tasked with prioritizing the following tasks: {bullet_string + bullet_string.join(task_names)} Consider the ultimate objective of your team: {OBJECTIVE}. -Tasks should be sorted from highest to lowest priority. -Higher-priority tasks are those that act as pre-requisites or are more essential for meeting the objective. -Do not remove any tasks. Return the result as a numbered list in the format: +Tasks should be sorted from highest to lowest priority, where higher-priority tasks are those that act as pre-requisites or are more essential for meeting the objective. +Do not remove any tasks. Return the ranked tasks as a numbered list in the format: #. First task #. Second task -The entries are consecutively numbered, starting with 1. The number of each entry must be followed by a period. -Do not include any headers before your numbered list. Do not follow your numbered list with any other output.""" +The entries must be consecutively numbered, starting with 1. The number of each entry must be followed by a period. +Do not include any headers before your ranked list or follow your list with any other output.""" - print(f'\n************** TASK PRIORITIZATION AGENT PROMPT *************\n{prompt}\n') + print(f'\n****TASK PRIORITIZATION AGENT PROMPT****\n{prompt}\n') response = openai_call(prompt, max_tokens=2000) - print(f'\n************* TASK PRIORITIZATION AGENT RESPONSE ************\n{response}\n') + print(f'\n****TASK PRIORITIZATION AGENT RESPONSE****\n{response}\n') + if not response: + print('Received empty response from priotritization agent. Keeping task list unchanged.') + return new_tasks = response.split("\n") if "\n" in response else [response] new_tasks_list = [] for task_string in new_tasks: @@ -453,7 +474,7 @@ def prioritization_agent(): if task_name.strip(): new_tasks_list.append({"task_id": task_id, "task_name": task_name}) - tasks_storage.replace(new_tasks_list) + return new_tasks_list # Execute a task based on the objective and five previous tasks @@ -469,15 +490,14 @@ def execution_agent(objective: str, task: str) -> str: str: The response generated by the AI for the given task. """ - + context = context_agent(query=objective, top_results_num=5) - # print("\n*******RELEVANT CONTEXT******\n") + # print("\n****RELEVANT CONTEXT****\n") # print(context) # print('') prompt = f'Perform one task based on the following objective: {objective}.\n' if context: - prompt += 'Take into account these previously completed tasks:' + '\n'.join(context)\ - + prompt += 'Take into account these previously completed tasks:' + '\n'.join(context) prompt += f'\nYour task: {task}\nResponse:' return openai_call(prompt, max_tokens=2000) @@ -496,7 +516,7 @@ def context_agent(query: str, top_results_num: int): """ results = results_storage.query(query=query, top_results_num=top_results_num) - # print("***** RESULTS *****") + # print("****RESULTS****") # print(results) return results @@ -537,7 +557,7 @@ def main(): } # extract the actual result from the dictionary # since we don't do enrichment currently - # vector = enriched_result["data"] + # vector = enriched_result["data"] result_id = f"result_{task['task_id']}" @@ -558,9 +578,12 @@ def main(): print(str(new_task)) tasks_storage.append(new_task) - if not JOIN_EXISTING_OBJECTIVE: prioritization_agent() + if not JOIN_EXISTING_OBJECTIVE: + prioritized_tasks = prioritization_agent() + if prioritized_tasks: + tasks_storage.replace(prioritized_tasks) - # Sleep a bit before checking the task list again + # Sleep a bit before checking the task list again time.sleep(5) else: print('Done.') From db4f1f6407a01cc0efc1c257c15e60c5d22b9ae4 Mon Sep 17 00:00:00 2001 From: Nikita Kuznetsov Date: Fri, 28 Apr 2023 12:54:25 +0300 Subject: [PATCH 16/62] Updated Translations in README.md for better visibility --- README.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index 85209106..ae6a5a0f 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,22 @@ # Translations: -[عربي](docs/README-ar.md) -[Français](docs/README-fr.md) -[Polski](docs/README-pl.md) -[Portuguese](docs/README-pt-br.md) -[Romanian](docs/README-ro.md) -[Russian](docs/README-ru.md) -[Slovenian](docs/README-si.md) -[Spanish](docs/README-es.md) -[Turkish](docs/README-tr.md) -[Ukrainian](docs/README-ua.md) -[Simplified Chinese](docs/README-cn.md) -[繁體中文 (Traditional Chinese)](docs/README-zh-tw.md) -[日本語](docs/README-ja.md) -[한국어](docs/README-ko.md) -[Magyar](docs/README-hu.md) -[فارسی](docs/README-fa.md) -[German](docs/README-de.md) +[عربي](docs/README-ar.md) +[Français](docs/README-fr.md) +[Polski](docs/README-pl.md) +[Portuguese](docs/README-pt-br.md) +[Romanian](docs/README-ro.md) +[Russian](docs/README-ru.md) +[Slovenian](docs/README-si.md) +[Spanish](docs/README-es.md) +[Turkish](docs/README-tr.md) +[Ukrainian](docs/README-ua.md) +[Simplified Chinese](docs/README-cn.md) +[繁體中文 (Traditional Chinese)](docs/README-zh-tw.md) +[日本語](docs/README-ja.md) +[한국어](docs/README-ko.md) +[Magyar](docs/README-hu.md) +[فارسی](docs/README-fa.md) +[German](docs/README-de.md) # Objective @@ -113,6 +113,6 @@ In the short time since it was release, BabyAGI inspired many projects. You can # Backstory -BabyAGI is a pared-down version of the original [Task-Driven Autonomous Agent](https://twitter.com/yoheinakajima/status/1640934493489070080?s=20) (Mar 28, 2023) shared on Twitter. This version is down to 140 lines: 13 comments, 22 blanks, and 105 code. The name of the repo came up in the reaction to the original autonomous agent - the author does not mean to imply that this is AGI. +BabyAGI is a pared-down version of the original [Task-Driven Autonomous Agent](https://twitter.com/yoheinakajima/status/1640934493489070080?s=20) (Mar 28, 2023) shared on Twitter. This version is down to 140 lines: 13 comments, 30 blanks, and 105 code. The name of the repo came up in the reaction to the original autonomous agent - the author does not mean to imply that this is AGI. Made with love by [@yoheinakajima](https://twitter.com/yoheinakajima), who happens to be a VC (would love to see what you're building!) From c37d129345ac15ec20de739a898d7ebb31051c27 Mon Sep 17 00:00:00 2001 From: Nikita Kuznetsov Date: Fri, 28 Apr 2023 12:55:56 +0300 Subject: [PATCH 17/62] Fixed typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ae6a5a0f..d5bc41bc 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,6 @@ In the short time since it was release, BabyAGI inspired many projects. You can # Backstory -BabyAGI is a pared-down version of the original [Task-Driven Autonomous Agent](https://twitter.com/yoheinakajima/status/1640934493489070080?s=20) (Mar 28, 2023) shared on Twitter. This version is down to 140 lines: 13 comments, 30 blanks, and 105 code. The name of the repo came up in the reaction to the original autonomous agent - the author does not mean to imply that this is AGI. +BabyAGI is a pared-down version of the original [Task-Driven Autonomous Agent](https://twitter.com/yoheinakajima/status/1640934493489070080?s=20) (Mar 28, 2023) shared on Twitter. This version is down to 140 lines: 13 comments, 22 blanks, and 105 code. The name of the repo came up in the reaction to the original autonomous agent - the author does not mean to imply that this is AGI. Made with love by [@yoheinakajima](https://twitter.com/yoheinakajima), who happens to be a VC (would love to see what you're building!) From c4c36f460cd29f30905f035def65ad2eeeef0122 Mon Sep 17 00:00:00 2001 From: Atharva Shukla Date: Sat, 29 Apr 2023 12:22:19 +0530 Subject: [PATCH 18/62] Fix newline before full-stop in prompt. Minor formatting issue fix. --- node/babyagi.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/node/babyagi.js b/node/babyagi.js index e333db8f..1ee23d72 100644 --- a/node/babyagi.js +++ b/node/babyagi.js @@ -154,8 +154,8 @@ const prioritization_agent = async (taskId)=>{ const execution_agent = async (objective, task, chromaCollection)=>{ const context = context_agent(objective, 5, chromaCollection) const prompt = ` - You are an AI who performs one task based on the following objective: ${objective}\n. - Take into account these previously completed tasks: ${context}\n. + You are an AI who performs one task based on the following objective: ${objective}.\n + Take into account these previously completed tasks: ${context}.\n Your task: ${task}\nResponse:` const response = await openai_completion(prompt, undefined, 2000) return response From 36c6391d9c2e4c89c2696dd01b8c29ca273bd145 Mon Sep 17 00:00:00 2001 From: Atharva Shukla Date: Sat, 29 Apr 2023 12:24:03 +0530 Subject: [PATCH 19/62] Update Readme. Changed "Python script" to "Node script" --- node/README.md | 47 ++++++++++++++++++++++++++++------------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/node/README.md b/node/README.md index dbadc158..829caf35 100644 --- a/node/README.md +++ b/node/README.md @@ -1,23 +1,25 @@ # node-chroma babyagi - # Objective -This Python script is an example of an AI-powered task management system. The system uses OpenAI and Chroma APIs to create, prioritize, and execute tasks. The main idea behind this system is that it creates tasks based on the result of previous tasks and a predefined objective. The script then uses OpenAI's natural language processing (NLP) capabilities to create new tasks based on the objective, and Chroma to store and retrieve task results for context. This is a paired-down version of the original [Task-Driven Autonomous Agent](https://twitter.com/yoheinakajima/status/1640934493489070080?s=20) (Mar 28, 2023). + +This Node script is an example of an AI-powered task management system. The system uses OpenAI and Chroma APIs to create, prioritize, and execute tasks. The main idea behind this system is that it creates tasks based on the result of previous tasks and a predefined objective. The script then uses OpenAI's natural language processing (NLP) capabilities to create new tasks based on the objective, and Chroma to store and retrieve task results for context. This is a paired-down version of the original [Task-Driven Autonomous Agent](https://twitter.com/yoheinakajima/status/1640934493489070080?s=20) (Mar 28, 2023). This README will cover the following: -* How the script works +- How the script works + +- How to use the script +- Warning about running the script continuously -* How to use the script -* Warning about running the script continuously # How It Works + The script works by running an infinite loop that does the following steps: 1. Pulls the first task from the task list. 2. Sends the task to the execution agent, which uses OpenAI's API to complete the task based on the context. 3. Enriches the result and stores it in [Chroma](docs.trychroma.com). 4. Creates new tasks and reprioritizes the task list based on the objective and the result of the previous task. -The execution_agent() function is where the OpenAI API is used. It takes two parameters: the objective and the task. It then sends a prompt to OpenAI's API, which returns the result of the task. The prompt consists of a description of the AI system's task, the objective, and the task itself. The result is then returned as a string. + The execution_agent() function is where the OpenAI API is used. It takes two parameters: the objective and the task. It then sends a prompt to OpenAI's API, which returns the result of the task. The prompt consists of a description of the AI system's task, the objective, and the task itself. The result is then returned as a string. The task_creation_agent() function is where OpenAI's API is used to create new tasks based on the objective and the result of the previous task. The function takes four parameters: the objective, the result of the previous task, the task description, and the current task list. It then sends a prompt to OpenAI's API, which returns a list of new tasks as strings. The function then returns the new tasks as a list of dictionaries, where each dictionary contains the name of the task. @@ -26,27 +28,32 @@ The prioritization_agent() function is where OpenAI's API is used to reprioritiz Finally, the script uses Chroma to store and retrieve task results for context. The script creates a Chroma collection based on the table name specified in the TABLE_NAME variable. Chroma is then used to store the results of the task in the collection, along with the task name and any additional metadata. # How to Use + To use the script, you will need to follow these steps: + 1. Install the required packages: `npm install` -2. Install chroma in this directory (based on the Chroma [docs](https://docs.trychroma.com/getting-started)) : - ``` - git clone git@github.com:chroma-core/chroma.git - ``` -2. Make sure Docker is running on your machine -3. Set your OpenAI and in the OPENAI_API_KEY variables. -4. Set your OpenAI API key in the OPENAI_API_KEY and OPENAPI_API_MODEL variables. -5. Set the name of the table where the task results will be stored in the TABLE_NAME variable. -6. Run the script with 'npm run babyagi'. This will handle 'docker compose up.' -7. Provide the objective of the task management system when prompted. +2. Install chroma in this directory (based on the Chroma [docs](https://docs.trychroma.com/getting-started)) : + ``` + git clone git@github.com:chroma-core/chroma.git + ``` +3. Make sure Docker is running on your machine +4. Set your OpenAI and in the OPENAI_API_KEY variables. +5. Set your OpenAI API key in the OPENAI_API_KEY and OPENAPI_API_MODEL variables. +6. Set the name of the table where the task results will be stored in the TABLE_NAME variable. +7. Run the script with 'npm run babyagi'. This will handle 'docker compose up.' 8. Provide the objective of the task management system when prompted. +9. Provide the objective of the task management system when prompted. # Warning + This script is designed to be run continuously as part of a task management system. Running this script continuously can result in high API usage, so please use it responsibly. Additionally, the script requires the OpenAI and Pinecone APIs to be set up correctly, so make sure you have set up the APIs before running the script. # Backstory + BabyAGI is a paired-down version of the original [Task-Driven Autonomous Agent](https://twitter.com/yoheinakajima/status/1640934493489070080?s=20) (Mar 28, 2023) shared on Twitter. This version is down to 140 lines: 13 comments, 22 blank, 105 code. The name of the repo came up in the reaction to the original autonomous agent - the author does not mean to imply that this is AGI. -# TODO +# TODO + - [x] Implement BabyAGI in nodex - [x] Switch Pinecome to Chroma - [ ] Add LLaMA support @@ -55,8 +62,10 @@ BabyAGI is a paired-down version of the original [Task-Driven Autonomous Agent]( - [ ] Allow agent to request additional input from user ( could be an interesting strategy to mitigate looping ) Made with love by : + - [@yoheinakajima](https://twitter.com/yoheinakajima) (0->1), who happens to be a VC - so if you use this build a startup, ping him! -Contributions from: +Contributions from: + - [@anton](https://twitter.com/atroyn) (pinecone->chroma), who happens to be a founder at [Chroma](https://www.trychroma.com/) -- [@aidanjrauscher](https://twitter.com/aidanjrauscher) (python->node), who happens to be trying to find a job \ No newline at end of file +- [@aidanjrauscher](https://twitter.com/aidanjrauscher) (python->node), who happens to be trying to find a job From fc9df2bb2faf060eb94b3593f0418f9ff041e60f Mon Sep 17 00:00:00 2001 From: matatonic <73265741+matatonic@users.noreply.github.com> Date: Sun, 30 Apr 2023 12:42:52 -0400 Subject: [PATCH 20/62] load environment variables before loading openai module (and support OPENAI_API_BASE) OPENAI_API_BASE is a standard environment variable which the openai module will use when it loads. The OPENAI_API_BASE lets you redirect the API to a compatible service. I have such an API in a pull request for oobabooga/text-generation-webui over here: https://github.com/oobabooga/text-generation-webui/pull/1475. With this small change the babyagi code will work unchanged with that. --- babyagi.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/babyagi.py b/babyagi.py index bcc44306..9822f6b8 100755 --- a/babyagi.py +++ b/babyagi.py @@ -1,4 +1,8 @@ #!/usr/bin/env python3 +from dotenv import load_dotenv +# Load default environment variables (.env) +load_dotenv() + import os import time import logging @@ -10,15 +14,12 @@ import tiktoken as tiktoken from chromadb.utils.embedding_functions import OpenAIEmbeddingFunction from chromadb.api.types import Documents, EmbeddingFunction, Embeddings -from dotenv import load_dotenv import re # default opt out of chromadb telemetry. from chromadb.config import Settings client = chromadb.Client(Settings(anonymized_telemetry=False)) -# Load default environment variables (.env) -load_dotenv() # Engine configuration From e92e62cc7e2b29fa0014a4efbf6ef3495b0f8c0c Mon Sep 17 00:00:00 2001 From: Yohei Nakajima Date: Sun, 30 Apr 2023 11:46:50 -0700 Subject: [PATCH 21/62] Create BabyBeeAGI An modified version of BabyAGI with both benefits and downsides. Try it out, what should we pull in? What ideas does this spark?" --- classic/BabyBeeAGI | 300 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 300 insertions(+) create mode 100644 classic/BabyBeeAGI diff --git a/classic/BabyBeeAGI b/classic/BabyBeeAGI new file mode 100644 index 00000000..2c805afb --- /dev/null +++ b/classic/BabyBeeAGI @@ -0,0 +1,300 @@ +###### This is a modified version of OG BabyAGI, called BabyBeeAGI (future modifications will follow the pattern "BabyAGI"). This version requires GPT-4, it's very slow, and often errors out.###### +######IMPORTANT NOTE: I'm sharing this as a framework to build on top of (with lots of errors for improvement), to facilitate discussion around how to improve these. This is NOT for people who are looking for a complete solution that's ready to use. ###### + +import openai +import pinecone +import time +import requests +from bs4 import BeautifulSoup +from collections import deque +from typing import Dict, List +import re +import ast +import json +from serpapi import GoogleSearch + +### SET THESE 4 VARIABLES ############################## + +# Add your API keys here +OPENAI_API_KEY = "" +SERPAPI_API_KEY = "" #If you include SERPAPI KEY, this will enable web-search. If you don't, it will autoatically remove web-search capability. + +# Set variables +OBJECTIVE = "You are an AI. Make the world a better place." +YOUR_FIRST_TASK = "Develop a task list." + +### UP TO HERE ############################## + +# Configure OpenAI and SerpAPI client +openai.api_key = OPENAI_API_KEY +if SERPAPI_API_KEY: + serpapi_client = GoogleSearch({"api_key": SERPAPI_API_KEY}) + websearch_var = "[web-search] " +else: + websearch_var = "" + +# Initialize task list +task_list = [] + +# Initialize session_summary +session_summary = "" + +### Task list functions ############################## +def add_task(task: Dict): + task_list.append(task) + +def get_task_by_id(task_id: int): + for task in task_list: + if task["id"] == task_id: + return task + return None + +def get_completed_tasks(): + return [task for task in task_list if task["status"] == "complete"] + +### Tool functions ############################## +def text_completion_tool(prompt: str): + response = openai.Completion.create( + engine="text-davinci-003", + prompt=prompt, + temperature=0.5, + max_tokens=1500, + top_p=1, + frequency_penalty=0, + presence_penalty=0 + ) + return response.choices[0].text.strip() + +def web_search_tool(query: str): + search_params = { + "engine": "google", + "q": query, + "api_key": SERPAPI_API_KEY, + "num":3 + } + search_results = GoogleSearch(search_params) + results = search_results.get_dict() + + return str(results["organic_results"]) + +def web_scrape_tool(url: str): + response = requests.get(url) + print(response) + soup = BeautifulSoup(response.content, "html.parser") + result = soup.get_text(strip=True)+"URLs: " + for link in soup.findAll('a', attrs={'href': re.compile("^https://")}): + result+= link.get('href')+", " + return result + +### Agent functions ############################## +def execute_task(task, task_list, OBJECTIVE): + global task_id_counter + # Check if dependent_task_id is complete + if task["dependent_task_id"]: + dependent_task = get_task_by_id(task["dependent_task_id"]) + if not dependent_task or dependent_task["status"] != "complete": + return + + # Execute task + + print("\033[92m\033[1m"+"\n*****NEXT TASK*****\n"+"\033[0m\033[0m") + print(str(task['id'])+": "+str(task['task'])+" ["+str(task['tool']+"]")) + task_prompt = f"Complete your assigned task based on the objective: {OBJECTIVE}. Your task: {task['task']}" + if task["dependent_task_id"]: + dependent_task_result = dependent_task["result"] + task_prompt += f"\nThe previous task ({dependent_task['id']}. {dependent_task['task']}) result: {dependent_task_result}" + + task_prompt += "\nResponse:" + ##print("###task_prompt: "+task_prompt) + if task["tool"] == "text-completion": + result = text_completion_tool(task_prompt) + elif task["tool"] == "web-search": + result = web_search_tool(task_prompt) + elif task["tool"] == "web-scrape": + result = web_scrape_tool(str(task['task'])) + else: + result = "Unknown tool" + + + print("\033[93m\033[1m"+"\n*****TASK RESULT*****\n"+"\033[0m\033[0m") + print_result = result[0:2000] + if result != result[0:2000]: + print(print_result+"...") + else: + print(result) + # Update task status and result + task["status"] = "complete" + task["result"] = result + task["result_summary"] = summarizer_agent(result) + + # Update session_summary + session_summary = overview_agent(task["id"]) + + # Increment task_id_counter + task_id_counter += 1 + + # Update task_manager_agent of tasks + task_manager_agent( + OBJECTIVE, + result, + task["task"], + [t["task"] for t in task_list if t["status"] == "incomplete"], + task["id"] + ) + + +def task_manager_agent(objective: str, result: str, task_description: str, incomplete_tasks: List[str], current_task_id : int) -> List[Dict]: + global task_list + original_task_list = task_list.copy() + minified_task_list = [{k: v for k, v in task.items() if k != "result"} for task in task_list] + result = result[0:4000] #come up with better solution later. + + prompt = ( + f"You are a task management AI tasked with cleaning the formatting of and reprioritizing the following tasks: {minified_task_list}. " + f"Consider the ultimate objective of your team: {OBJECTIVE}. " + f"Do not remove any tasks. Return the result as a JSON-formatted list of dictionaries.\n" + f"Create new tasks based on the result of last task if necessary for the objective. Limit tasks types to those that can be completed with the available tools listed below. Task description should be detailed." + f"The maximum task list length is 7. Do not add an 8th task." + f"The last completed task has the following result: {result}. " + f"Current tool option is [text-completion] {websearch_var} and [web-scrape] only."# web-search is added automatically if SERPAPI exists + f"For tasks using [web-scrape], provide only the URL to scrape as the task description. Do not provide placeholder URLs, but use ones provided by a search step or the initial objective." + #f"If the objective is research related, use at least one [web-search] with the query as the task description, and after, add up to three URLs from the search result as a task with [web-scrape], then use [text-completion] to write a comprehensive summary of each site thas has been scraped.'" + f"For tasks using [web-search], provide the search query, and only the search query to use (eg. not 'research waterproof shoes, but 'waterproof shoes')" + f"dependent_task_id should always be null or a number." + f"Do not reorder completed tasks. Only reorder and dedupe incomplete tasks.\n" + f"Make sure all task IDs are in chronological order.\n" + f"Do not provide example URLs for [web-scrape].\n" + f"Do not include the result from the last task in the JSON, that will be added after..\n" + f"The last step is always to provide a final summary report of all tasks.\n" + f"An example of the desired output format is: " + "[{\"id\": 1, \"task\": \"https://untapped.vc\", \"tool\": \"web-scrape\", \"dependent_task_id\": null, \"status\": \"incomplete\", \"result\": null, \"result_summary\": null}, {\"id\": 2, \"task\": \"Analyze the contents of...\", \"tool\": \"text-completion\", \"dependent_task_id\": 1, \"status\": \"incomplete\", \"result\": null, \"result_summary\": null}, {\"id\": 3, \"task\": \"Untapped Capital\", \"tool\": \"web-search\", \"dependent_task_id\": null, \"status\": \"incomplete\", \"result\": null, \"result_summary\": null}]." + ) + print("\033[90m\033[3m" + "\nRunning task manager agent...\n" + "\033[0m") + response = openai.ChatCompletion.create( + model="gpt-4", + messages=[ + { + "role": "system", + "content": "You are a task manager AI." + }, + { + "role": "user", + "content": prompt + } + ], + temperature=0.2, + max_tokens=1500, + top_p=1, + frequency_penalty=0, + presence_penalty=0 + ) + + # Extract the content of the assistant's response and parse it as JSON + result = response["choices"][0]["message"]["content"] + print("\033[90m\033[3m" + "\nDone!\n" + "\033[0m") + try: + task_list = json.loads(result) + except Exception as error: + print(error) + # Add the 'result' field back in + for updated_task, original_task in zip(task_list, original_task_list): + if "result" in original_task: + updated_task["result"] = original_task["result"] + task_list[current_task_id]["result"]=result + #print(task_list) + return task_list + + + +def summarizer_agent(text: str) -> str: + text = text[0:4000] + prompt = f"Please summarize the following text:\n{text}\nSummary:" + response = openai.Completion.create( + engine="text-davinci-003", + prompt=prompt, + temperature=0.5, + max_tokens=100, + top_p=1, + frequency_penalty=0, + presence_penalty=0 + ) + return response.choices[0].text.strip() + + +def overview_agent(last_task_id: int) -> str: + global session_summary + + completed_tasks = get_completed_tasks() + completed_tasks_text = "\n".join( + [f"{task['id']}. {task['task']} - {task['result_summary']}" for task in completed_tasks] + ) + + prompt = f"Here is the current session summary:\n{session_summary}\nThe last completed task is task {last_task_id}. Please update the session summary with the information of the last task:\n{completed_tasks_text}\nUpdated session summary, which should describe all tasks in chronological order:" + response = openai.Completion.create( + engine="text-davinci-003", + prompt=prompt, + temperature=0.5, + max_tokens=200, + top_p=1, + frequency_penalty=0, + presence_penalty=0 + ) + session_summary = response.choices[0].text.strip() + return session_summary + + +### Main Loop ############################## + +# Add the first task +first_task = { + "id": 1, + "task": YOUR_FIRST_TASK, + "tool": "text-completion", + "dependent_task_id": None, + "status": "incomplete", + "result": "", + "result_summary": "" +} +add_task(first_task) + +task_id_counter = 0 +#Print OBJECTIVE +print("\033[96m\033[1m"+"\n*****OBJECTIVE*****\n"+"\033[0m\033[0m") +print(OBJECTIVE) + +# Continue the loop while there are incomplete tasks +while any(task["status"] == "incomplete" for task in task_list): + + # Filter out incomplete tasks + incomplete_tasks = [task for task in task_list if task["status"] == "incomplete"] + + if incomplete_tasks: + # Sort tasks by ID + incomplete_tasks.sort(key=lambda x: x["id"]) + + # Pull the first task + task = incomplete_tasks[0] + + # Execute task & call task manager from function + execute_task(task, task_list, OBJECTIVE) + + # Print task list and session summary + print("\033[95m\033[1m" + "\n*****TASK LIST*****\n" + "\033[0m") + for t in task_list: + dependent_task = "" + if t['dependent_task_id'] is not None: + dependent_task = f"\033[31m\033[0m" + status_color = "\033[32m" if t['status'] == "complete" else "\033[31m" + print(f"\033[1m{t['id']}\033[0m: {t['task']} {status_color}[{t['status']}]\033[0m \033[93m[{t['tool']}] {dependent_task}\033[0m") + print("\033[93m\033[1m" + "\n*****SESSION SUMMARY*****\n" + "\033[0m\033[0m") + print(session_summary) + + time.sleep(1) # Sleep before checking the task list again + +### Objective complete ############################## + +# Print the full task list if there are no incomplete tasks +if all(task["status"] != "incomplete" for task in task_list): + print("\033[92m\033[1m" + "\n*****ALL TASKS COMPLETED*****\n" + "\033[0m\033[0m") + for task in task_list: + print(f"ID: {task['id']}, Task: {task['task']}, Result: {task['result']}") From 4617233e6108327178150640c0deaeacc09353f3 Mon Sep 17 00:00:00 2001 From: Elliott Burris Date: Sun, 30 Apr 2023 14:03:01 -0700 Subject: [PATCH 22/62] Fixing small typo in BabyBeeAGI --- classic/BabyBeeAGI | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classic/BabyBeeAGI b/classic/BabyBeeAGI index 2c805afb..7768ad8a 100644 --- a/classic/BabyBeeAGI +++ b/classic/BabyBeeAGI @@ -17,7 +17,7 @@ from serpapi import GoogleSearch # Add your API keys here OPENAI_API_KEY = "" -SERPAPI_API_KEY = "" #If you include SERPAPI KEY, this will enable web-search. If you don't, it will autoatically remove web-search capability. +SERPAPI_API_KEY = "" #If you include SERPAPI KEY, this will enable web-search. If you don't, it will automatically remove web-search capability. # Set variables OBJECTIVE = "You are an AI. Make the world a better place." From b5f5b9c5abcabfb654ee6a5f17730c47d1565edd Mon Sep 17 00:00:00 2001 From: Sathyaraj V Date: Sun, 30 Apr 2023 14:43:50 -0700 Subject: [PATCH 23/62] Add Sharpagi to inspired-projects --- docs/inspired-projects.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/inspired-projects.md b/docs/inspired-projects.md index 7e6752ca..02424bfb 100644 --- a/docs/inspired-projects.md +++ b/docs/inspired-projects.md @@ -95,3 +95,7 @@ This document highlights a curated selection of remarkable projects inspired by - Description: Chippr-AGI is an open-source framework that uses AI models to automate task creation and prioritization. NodeJS, Redis, OpenAI/LLAMA - Author: [Cody Burns](https://github.com/realcodywburns) - Twitter: https://twitter.com/DontPanicBurns +1. [sharpagi](https://github.com/sathyarajv/sharpagi) + - Description: C# dotnet implementation of BabyAGI. + - Author: [Sathyaraj Vadivel](https://github.com/sathyarajv) + - Twitter: https://twitter.com/sathyarajv From e3d0f479135b84d0b46c0fcf06913cfd8cf375ad Mon Sep 17 00:00:00 2001 From: Alexander Dibrov <108030031+dibrale@users.noreply.github.com> Date: Sun, 30 Apr 2023 21:09:48 -0500 Subject: [PATCH 24/62] Revert prompt changes At the request of the maintainer --- babyagi.py | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/babyagi.py b/babyagi.py index f4638789..af5ca683 100755 --- a/babyagi.py +++ b/babyagi.py @@ -1,5 +1,6 @@ #!/usr/bin/env python3 from dotenv import load_dotenv + # Load default environment variables (.env) load_dotenv() @@ -18,8 +19,8 @@ # default opt out of chromadb telemetry. from chromadb.config import Settings -client = chromadb.Client(Settings(anonymized_telemetry=False)) +client = chromadb.Client(Settings(anonymized_telemetry=False)) # Engine configuration @@ -175,6 +176,8 @@ def can_import(module_name): class LlamaEmbeddingFunction(EmbeddingFunction): def __init__(self): return + + def __call__(self, texts: Documents) -> Embeddings: embeddings = [] for t in texts: @@ -424,18 +427,18 @@ def task_creation_agent( if task_list: prompt += f"These are incomplete tasks: {', '.join(task_list)}\n" - prompt += "Based on the result, return a list of tasks to be completed in order to meet the objective. " + prompt += "Based on the result, create a list of new tasks to be completed in order to meet the objective. " if task_list: prompt += "These new tasks must not overlap with incomplete tasks. " prompt += """ -Return one task per line in your response. The result must be a numbered list in the format: - +Return all the new tasks, with one task per line in your response. The result must be a numbered list in the format: + #. First task #. Second task - -The number of each entry must be followed by a period. If your list is empty, write "There are no tasks to add at this time." -Unless your list is empty, do not include any headers before your numbered list or follow your numbered list with any other output.""" + +The number of each entry must be followed by a period. +Do not include any headers before your numbered list. Do not follow your numbered list with any other output.""" print(f'\n*****TASK CREATION AGENT PROMPT****\n{prompt}\n') response = openai_call(prompt, max_tokens=2000) @@ -458,19 +461,19 @@ def task_creation_agent( def prioritization_agent(): task_names = tasks_storage.get_task_names() next_task_id = tasks_storage.next_task_id() - bullet_string = '\n' prompt = f""" -You are tasked with prioritizing the following tasks: {bullet_string + bullet_string.join(task_names)} +You are tasked with cleaning the format and re-prioritizing the following tasks: {', '.join(task_names)}. Consider the ultimate objective of your team: {OBJECTIVE}. -Tasks should be sorted from highest to lowest priority, where higher-priority tasks are those that act as pre-requisites or are more essential for meeting the objective. -Do not remove any tasks. Return the ranked tasks as a numbered list in the format: +Tasks should be sorted from highest to lowest priority. +Higher-priority tasks are those that act as pre-requisites or are more essential for meeting the objective. +Do not remove any tasks. Return the result as a numbered list in the format: #. First task #. Second task -The entries must be consecutively numbered, starting with 1. The number of each entry must be followed by a period. -Do not include any headers before your ranked list or follow your list with any other output.""" +The entries are consecutively numbered, starting with 1. The number of each entry must be followed by a period. +Do not include any headers before your numbered list. Do not follow your numbered list with any other output.""" print(f'\n****TASK PRIORITIZATION AGENT PROMPT****\n{prompt}\n') response = openai_call(prompt, max_tokens=2000) From 1e1cd486d35b2d7970c9fbad23723c5bb1e9dc17 Mon Sep 17 00:00:00 2001 From: Alexander Dibrov <108030031+dibrale@users.noreply.github.com> Date: Sun, 30 Apr 2023 21:57:16 -0500 Subject: [PATCH 25/62] Further prompt changes Prompt changes from silent llama fix submitted as a separate PR, at the request of the maintainer. --- babyagi.py | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/babyagi.py b/babyagi.py index 90e6c788..8a5c8501 100755 --- a/babyagi.py +++ b/babyagi.py @@ -406,18 +406,18 @@ def task_creation_agent( if task_list: prompt += f"These are incomplete tasks: {', '.join(task_list)}\n" - prompt += "Based on the result, create a list of new tasks to be completed in order to meet the objective. " + prompt += "Based on the result, return a list of tasks to be completed in order to meet the objective. " if task_list: prompt += "These new tasks must not overlap with incomplete tasks. " prompt += """ -Return all the new tasks, with one task per line in your response. The result must be a numbered list in the format: - +Return one task per line in your response. The result must be a numbered list in the format: + #. First task #. Second task - -The number of each entry must be followed by a period. -Do not include any headers before your numbered list. Do not follow your numbered list with any other output.""" + +The number of each entry must be followed by a period. If your list is empty, write "There are no tasks to add at this time." +Unless your list is empty, do not include any headers before your numbered list or follow your numbered list with any other output.""" print(f'\n************** TASK CREATION AGENT PROMPT *************\n{prompt}\n') response = openai_call(prompt, max_tokens=2000) @@ -439,20 +439,19 @@ def task_creation_agent( def prioritization_agent(): task_names = tasks_storage.get_task_names() - next_task_id = tasks_storage.next_task_id() + bullet_string = '\n' prompt = f""" -You are tasked with cleaning the format and re-prioritizing the following tasks: {', '.join(task_names)}. +You are tasked with prioritizing the following tasks: {bullet_string + bullet_string.join(task_names)} Consider the ultimate objective of your team: {OBJECTIVE}. -Tasks should be sorted from highest to lowest priority. -Higher-priority tasks are those that act as pre-requisites or are more essential for meeting the objective. -Do not remove any tasks. Return the result as a numbered list in the format: +Tasks should be sorted from highest to lowest priority, where higher-priority tasks are those that act as pre-requisites or are more essential for meeting the objective. +Do not remove any tasks. Return the ranked tasks as a numbered list in the format: #. First task #. Second task -The entries are consecutively numbered, starting with 1. The number of each entry must be followed by a period. -Do not include any headers before your numbered list. Do not follow your numbered list with any other output.""" +The entries must be consecutively numbered, starting with 1. The number of each entry must be followed by a period. +Do not include any headers before your ranked list or follow your list with any other output.""" print(f'\n************** TASK PRIORITIZATION AGENT PROMPT *************\n{prompt}\n') response = openai_call(prompt, max_tokens=2000) @@ -483,16 +482,16 @@ def execution_agent(objective: str, task: str) -> str: str: The response generated by the AI for the given task. """ - + context = context_agent(query=objective, top_results_num=5) # print("\n*******RELEVANT CONTEXT******\n") # print(context) # print('') prompt = f'Perform one task based on the following objective: {objective}.\n' if context: - prompt += 'Take into account these previously completed tasks:' + '\n'.join(context)\ - - prompt += f'\nYour task: {task}\nResponse:' + prompt += 'Take into account these previously completed tasks:' + '\n'.join(context) \ + \ + prompt += f'\nYour task: {task}\nResponse:' return openai_call(prompt, max_tokens=2000) @@ -551,7 +550,7 @@ def main(): } # extract the actual result from the dictionary # since we don't do enrichment currently - # vector = enriched_result["data"] + # vector = enriched_result["data"] result_id = f"result_{task['task_id']}" @@ -574,7 +573,7 @@ def main(): if not JOIN_EXISTING_OBJECTIVE: prioritization_agent() - # Sleep a bit before checking the task list again + # Sleep a bit before checking the task list again time.sleep(5) else: print('Done.') From d43435757ea249fc0d45ee1767b401303e703997 Mon Sep 17 00:00:00 2001 From: hoangdd Date: Mon, 1 May 2023 16:25:16 +0900 Subject: [PATCH 26/62] Add missing openai config in tools scripts --- tools/results.py | 3 +++ tools/results_browser.py | 3 +++ 2 files changed, 6 insertions(+) diff --git a/tools/results.py b/tools/results.py index 5eced3bd..3807aa82 100644 --- a/tools/results.py +++ b/tools/results.py @@ -39,6 +39,9 @@ def main(): ''', default=[os.getenv("OBJECTIVE", "")]) args = parser.parse_args() + # Configure OpenAI + openai.api_key = OPENAI_API_KEY + # Initialize Pinecone pinecone.init(api_key=PINECONE_API_KEY) diff --git a/tools/results_browser.py b/tools/results_browser.py index 028a6f19..caf24662 100644 --- a/tools/results_browser.py +++ b/tools/results_browser.py @@ -65,6 +65,9 @@ def draw_summary(stdscr, objective, tasks, start, num): stdscr.addstr(1, 1, summary_text[:stdscr.getmaxyx()[1] - 2]) def main(stdscr): + # Configure OpenAI + openai.api_key = OPENAI_API_KEY + # Initialize Pinecone pinecone.init(api_key=PINECONE_API_KEY) From 65512f201e8e2c79dbad0699e3e5b817fc51e56d Mon Sep 17 00:00:00 2001 From: Alexander Dibrov <108030031+dibrale@users.noreply.github.com> Date: Mon, 1 May 2023 13:45:32 -0500 Subject: [PATCH 27/62] Update babyagi.py Fixed error caused by incorrect line wrapping in IDE. --- babyagi.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/babyagi.py b/babyagi.py index 8a5c8501..ec5287ea 100755 --- a/babyagi.py +++ b/babyagi.py @@ -489,9 +489,8 @@ def execution_agent(objective: str, task: str) -> str: # print('') prompt = f'Perform one task based on the following objective: {objective}.\n' if context: - prompt += 'Take into account these previously completed tasks:' + '\n'.join(context) \ - \ - prompt += f'\nYour task: {task}\nResponse:' + prompt += 'Take into account these previously completed tasks:' + '\n'.join(context) + prompt += f'\nYour task: {task}\nResponse:' return openai_call(prompt, max_tokens=2000) From 0d5d76469103461b575297273a8a60189eb74777 Mon Sep 17 00:00:00 2001 From: Felipe Schieber Date: Mon, 1 May 2023 18:38:47 -0300 Subject: [PATCH 28/62] Implement babycoder main parts --- babycoder/babycoder.py | 608 +++++++++++++++++++++++++++++++++ babycoder/embeddings.py | 207 +++++++++++ babycoder/objective.sample.txt | 6 + babycoder/playground/noop.md | 1 + 4 files changed, 822 insertions(+) create mode 100644 babycoder/babycoder.py create mode 100644 babycoder/embeddings.py create mode 100644 babycoder/objective.sample.txt create mode 100644 babycoder/playground/noop.md diff --git a/babycoder/babycoder.py b/babycoder/babycoder.py new file mode 100644 index 00000000..31f30bbe --- /dev/null +++ b/babycoder/babycoder.py @@ -0,0 +1,608 @@ +import os +import openai +import time +import sys +from typing import List, Dict, Union +from dotenv import load_dotenv +import json +import subprocess + +from embeddings import Embeddings + +# Set Variables +load_dotenv() +current_directory = os.getcwd() + +embeddings = Embeddings(current_directory) + +openai_calls_retried = 0 +max_openai_calls_retries = 3 + +# Set API Keys +OPENAI_API_KEY = os.getenv("OPENAI_API_KEY", "") +assert OPENAI_API_KEY, "OPENAI_API_KEY environment variable is missing from .env" +openai.api_key = OPENAI_API_KEY + +OPENAI_API_MODEL = os.getenv("OPENAI_API_MODEL", "gpt-3.5-turbo") +assert OPENAI_API_MODEL, "OPENAI_API_MODEL environment variable is missing from .env" + +if "gpt-4" in OPENAI_API_MODEL.lower(): + print( + f"\033[91m\033[1m" + + "\n*****USING GPT-4. POTENTIALLY EXPENSIVE. MONITOR YOUR COSTS*****" + + "\033[0m\033[0m" + ) + +if len(sys.argv) > 1: + OBJECTIVE = sys.argv[1] +elif os.path.exists(os.path.join(current_directory, "objective.txt")): + with open(os.path.join(current_directory, "objective.txt")) as f: + OBJECTIVE = f.read() + +assert OBJECTIVE, "OBJECTIVE missing" + +## Start of Helper/Utility functions ## + +def print_colored_text(text, color): + color_mapping = { + 'blue': '\033[34m', + 'red': '\033[31m', + 'yellow': '\033[33m', + 'green': '\033[32m', + } + color_code = color_mapping.get(color.lower(), '') + reset_code = '\033[0m' + print(color_code + text + reset_code) + +def print_char_by_char(text, delay=0.00001, chars_at_once=3): + for i in range(0, len(text), chars_at_once): + chunk = text[i:i + chars_at_once] + print(chunk, end='', flush=True) + time.sleep(delay) + print() + +def openai_call( + prompt: str, + model: str = OPENAI_API_MODEL, + temperature: float = 0.5, + max_tokens: int = 100, +): + global openai_calls_retried + if not model.startswith("gpt-"): + # Use completion API + response = openai.Completion.create( + engine=model, + prompt=prompt, + temperature=temperature, + max_tokens=max_tokens, + top_p=1, + frequency_penalty=0, + presence_penalty=0 + ) + return response.choices[0].text.strip() + else: + # Use chat completion API + messages=[{"role": "user", "content": prompt}] + try: + response = openai.ChatCompletion.create( + model=model, + messages=messages, + temperature=temperature, + max_tokens=max_tokens, + n=1, + stop=None, + ) + openai_calls_retried = 0 + return response.choices[0].message.content.strip() + except Exception as e: + # try again + if openai_calls_retried < max_openai_calls_retries: + openai_calls_retried += 1 + print(f"Error calling OpenAI. Retrying {openai_calls_retried} of {max_openai_calls_retries}...") + return openai_call(prompt, model, temperature, max_tokens) + +def execute_command_json(json_string): + try: + command_data = json.loads(json_string) + full_command = command_data.get('command') + + process = subprocess.Popen(full_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True, cwd='playground') + stdout, stderr = process.communicate(timeout=60) + + return_code = process.returncode + + if return_code == 0: + return stdout + else: + return stderr + + except json.JSONDecodeError as e: + return f"Error: Unable to decode JSON string: {str(e)}" + except subprocess.TimeoutExpired: + process.terminate() + return "Error: Timeout reached (60 seconds)" + except Exception as e: + return f"Error: {str(e)}" + +def execute_command_string(command_string): + try: + result = subprocess.run(command_string, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, shell=True, cwd='playground') + output = result.stdout or result.stderr or "No output" + return output + + except Exception as e: + return f"Error: {str(e)}" + +def save_code_to_file(code: str, file_path: str): + full_path = os.path.join(current_directory, "playground", file_path) + try: + mode = 'a' if os.path.exists(full_path) else 'w' + with open(full_path, mode, encoding='utf-8') as f: + f.write(code + '\n\n') + except: + pass + +def refactor_code(modified_code: List[Dict[str, Union[int, str]]], file_path: str): + full_path = os.path.join(current_directory, "playground", file_path) + + with open(full_path, "r", encoding="utf-8") as f: + lines = f.readlines() + + for modification in modified_code: + start_line = modification["start_line"] + end_line = modification["end_line"] + modified_chunk = modification["modified_code"].splitlines() + + # Remove original lines within the range + del lines[start_line - 1:end_line] + + # Insert the new modified_chunk lines + for i, line in enumerate(modified_chunk): + lines.insert(start_line - 1 + i, line + "\n") + + with open(full_path, "w", encoding="utf-8") as f: + f.writelines(lines) + +def split_code_into_chunks(file_path: str, chunk_size: int = 50) -> List[Dict[str, Union[int, str]]]: + full_path = os.path.join(current_directory, "playground", file_path) + + with open(full_path, "r", encoding="utf-8") as f: + lines = f.readlines() + + chunks = [] + for i in range(0, len(lines), chunk_size): + start_line = i + 1 + end_line = min(i + chunk_size, len(lines)) + chunk = {"start_line": start_line, "end_line": end_line, "code": "".join(lines[i:end_line])} + chunks.append(chunk) + return chunks + +## End of Helper/Utility functions ## + +## TASKS AGENTS + +def code_tasks_initializer_agent(objective: str): + prompt = f"""You are an AGI agent responsible for creating a detailed JSON checklist of tasks that will guide other AGI agents to complete a given programming objective. Your task is to analyze the provided objective and generate a well-structured checklist with a clear starting point and end point, as well as tasks broken down to be very specific, clear, and executable by other agents without the context of other tasks. + + The current agents work as follows: + - code_writer_agent: Writes code snippets or functions and saves them to the appropriate files. This agent can also append code to existing files if required. + - code_refactor_agent: Responsible for modifying and refactoring existing code to meet the requirements of the task. + - command_executor_agent: Executes terminal commands for tasks such as creating directories, installing dependencies, etc. + + Keep in mind that the agents cannot open files in text editors, and tasks should be designed to work within these agent capabilities. + + Here is the programming objective you need to create a checklist for: {objective}. + + To generate the checklist, follow these steps: + + 1. Analyze the objective to identify the high-level requirements and goals of the project. This will help you understand the scope and create a comprehensive checklist. + + 2. Break down the objective into smaller, highly specific tasks that can be worked on independently by other agents. Ensure that the tasks are designed to be executed by the available agents (code_writer_agent, code_refactor and command_executor_agent) without requiring opening files in text editors. + + 3. Assign a unique ID to each task for easy tracking and organization. This will help the agents to identify and refer to specific tasks in the checklist. + + 4. Organize the tasks in a logical order, with a clear starting point and end point. The starting point should represent the initial setup or groundwork necessary for the project, while the end point should signify the completion of the objective and any finalization steps. + + 5. Provide the current context for each task, which should be sufficient for the agents to understand and execute the task without referring to other tasks in the checklist. This will help agents avoid task duplication. + + 6. Pay close attention to the objective and make sure the tasks implement all necessary pieces needed to make the program work. + + 7. Compile the tasks into a well-structured JSON format, ensuring that it is easy to read and parse by other AGI agents. The JSON should include fields such as task ID, description and file_path. + + IMPORTANT: BE VERY CAREFUL WITH IMPORTS AND MANAGING MULTIPLE FILES. REMEMBER EACH AGENT WILL ONLY SEE A SINGLE TASK. ASK YOURSELF WHAT INFORMATION YOU NEED TO INCLUDE IN THE CONTEXT OF EACH TASK TO MAKE SURE THE AGENT CAN EXECUTE THE TASK WITHOUT SEEING THE OTHER TASKS OR WHAT WAS ACCOMPLISHED IN OTHER TASKS. + + Pay attention to the way files are passed in the tasks, always use full paths. For example 'project/main.py'. + + Make sure tasks are not duplicated. + + Do not take long and complex routes, minimize tasks and steps as much as possible. + + Here is a sample JSON output for a checklist: + + {{ + "tasks": [ + {{ + "id": 1, + "description": "Run a command to create the project directory named 'project'", + "file_path": "./project", + }}, + {{ + "id": 2, + "description": "Run a command to Install the following dependencies: 'numpy', 'pandas', 'scikit-learn', 'matplotlib'", + "file_path": "null", + }}, + {{ + "id": 3, + "description": "Write code to create a function named 'parser' that takes an input named 'input' of type str, [perform a specific task on it], and returns a specific output", + "file_path": "./project/main.py", + }}, + ... + {{ + "id": N, + "description": "...", + }} + ], + }} + + The tasks will be executed by either of the three agents: command_executor, code_writer or code_refactor. They can't interact with programs. They can either run terminal commands or write code snippets. Their output is controlled by other functions to run the commands or save their output to code files. Make sure the tasks are compatible with the current agents. ALL tasks MUST start either with the following phrases: 'Run a command to...', 'Write code to...', 'Edit existing code to...' depending on the agent that will execute the task. RETURN JSON ONLY:""" + + return openai_call(prompt, temperature=0.8, max_tokens=2000) + +def code_tasks_refactor_agent(objective: str, task_list_json): + prompt = f"""You are an AGI tasks_refactor_agent responsible for adapting a task list generated by another agent to ensure the tasks are compatible with the current AGI agents. Your goal is to analyze the task list and make necessary modifications so that the tasks can be executed by the agents listed below + + YOU SHOULD OUTPUT THE MODIFIED TASK LIST IN THE SAME JSON FORMAT AS THE INITIAL TASK LIST. DO NOT CHANGE THE FORMAT OF THE JSON OUTPUT. DO NOT WRITE ANYTHING OTHER THAN THE MODIFIED TASK LIST IN THE JSON FORMAT. + + The current agents work as follows: + - code_writer_agent: Writes code snippets or functions and saves them to the appropriate files. This agent can also append code to existing files if required. + - code_refactor_agent: Responsible for editing current existing code/files. + - command_executor_agent: Executes terminal commands for tasks such as creating directories, installing dependencies, etc. + + Here is the overall objective you need to refactor the tasks for: {objective}. + Here is the JSON task list you need to refactor for compatibility with the current agents: {task_list_json}. + + To refactor the task list, follow these steps: + 1. Modify the task descriptions to make them compatible with the current agents, ensuring that the tasks are self-contained, clear, and executable by the agents without additional context. You don't need to mention the agents in the task descriptions, but the tasks should be compatible with the current agents. + 2. If necessary, add new tasks or remove irrelevant tasks to make the task list more suitable for the current agents. + 3. Keep the JSON structure of the task list intact, maintaining the "id", "description" and "file_path" fields for each task. + 4. Pay close attention to the objective and make sure the tasks implement all necessary pieces needed to make the program work. + + Always specify file paths to files. Make sure tasks are not duplicated. Never write code to create files. If needed, use commands to create files and folders. + Return the updated JSON task list with the following format: + + {{ + "tasks": [ + {{ + "id": 1, + "description": "Run a commmand to create a folder named 'project' in the current directory", + "file_path": "./project", + }}, + {{ + "id": 2, + "description": "Write code to print 'Hello World!' with Python", + "file_path": "./project/main.py", + }}, + {{ + "id": 3, + "description": "Write code to create a function named 'parser' that takes an input named 'input' of type str, [perform a specific task on it], and returns a specific output", + "file_path": "./project/main.py", + }} + {{ + "id": 3, + "description": "Run a command calling the script in ./project/main.py", + "file_path": "./project/main.py", + }} + ... + ], + }} + + IMPORTANT: All tasks should start either with the following phrases: 'Run a command to...', 'Write a code to...', 'Edit the code to...' depending on the agent that will execute the task: + + ALWAYS ENSURE ALL TASKS HAVE RELEVANT CONTEXT ABOUT THE CODE TO BE WRITTEN, INCLUDE DETAILS ON HOW TO CALL FUNCTIONS, CLASSES, IMPORTS, ETC. AGENTS HAVE NO VIEW OF OTHER TASKS, SO THEY NEED TO BE SELF-CONTAINED. RETURN THE JSON:""" + + return openai_call(prompt, temperature=0, max_tokens=2000) + +def code_tasks_details_agent(objective: str, task_list_json): + prompt = f"""You are an AGI agent responsible for improving a list of tasks in JSON format and adding ALL the necessary details to each task. These tasks will be executed individually by agents that have no idea about other tasks or what code exists in the codebase. It is FUNDAMENTAL that each task has enough details so that an individual isolated agent can execute. The metadata of the task is the only information the agents will have. + + Each task should contain the details necessary to execute it. For example, if it creates a function, it needs to contain the details about the arguments to be used in that function and this needs to be consistent across all tasks. + + Look at all tasks at once, and update the task description adding details to it for each task so that it can be executed by an agent without seeing the other tasks and to ensure consistency across all tasks. DETAILS ARE CRUCIAL. For example, if one task creates a class, it should have all the details about the class, including the arguments to be used in the constructor. If another task creates a function that uses the class, it should have the details about the class and the arguments to be used in the constructor. + + RETURN JSON OUTPUTS ONLY. + + Here is the overall objective you need to refactor the tasks for: {objective}. + Here is the task list you need to improve: {task_list_json} + + RETURN THE SAME TASK LIST but with the description improved to contain the details you is adding for each task in the list. DO NOT MAKE OTHER MODIFICATIONS TO THE LIST. Your input should go in the 'description' field of each task. + + RETURN JSON ONLY:""" + return openai_call(prompt, temperature=0.7, max_tokens=2000) + +def code_tasks_context_agent(objective: str, task_list_json): + prompt = f"""You are an AGI agent responsible for improving a list of tasks in JSON format and adding ALL the necessary context to it. These tasks will be executed individually by agents that have no idea about other tasks or what code exists in the codebase. It is FUNDAMENTAL that each task has enough context so that an individual isolated agent can execute. The metadata of the task is the only information the agents will have. + + Look at all tasks at once, and add the necessary context to each task so that it can be executed by an agent without seeing the other tasks. Remember, one agent can only see one task and has no idea about what happened in other tasks. CONTEXT IS CRUCIAL. For example, if one task creates one folder and the other tasks creates a file in that folder. The second tasks should contain the name of the folder that already exists and the information that it already exists. + + This is even more important for tasks that require importing functions, classes, etc. If a task needs to call a function or initialize a Class, it needs to have the detailed arguments, etc. + + Note that you should identify when imports need to happen and specify this in the context. Also, you should identify when functions/classes/etc already exist and specify this very clearly because the agents sometimes duplicate things not knowing. + + Always use imports with the file name. For example, 'from my_script import MyScript'. + + RETURN JSON OUTPUTS ONLY. + + Here is the overall objective you need to refactor the tasks for: {objective}. + Here is the task list you need to improve: {task_list_json} + + RETURN THE SAME TASK LIST but with a new field called 'isolated_context' for each task in the list. This field should be a string with the context you are adding. DO NOT MAKE OTHER MODIFICATIONS TO THE LIST. + + RETURN JSON ONLY:""" + return openai_call(prompt, temperature=0.7, max_tokens=2000) + +def task_assigner_recommendation_agent(objective: str, task: str): + prompt = f"""You are an AGI agent responsible for providing recommendations on which agent should be used to handle a specific task. Analyze the provided major objective of the project and a single task from the JSON checklist generated by the previous agent, and suggest the most appropriate agent to work on the task. + + The overall objective is: {objective} + The current task is: {task} + + The available agents are: + 1. code_writer_agent: Responsible for writing code based on the task description. + 2. code_refactor_agent: Responsible for editing existing code. + 3. command_executor_agent: Responsible for executing commands and handling file operations, such as creating, moving, or deleting files. + + When analyzing the task, consider the following tips: + - Pay attention to keywords in the task description that indicate the type of action required, such as "write", "edit", "run", "create", "move", or "delete". + - Keep the overall objective in mind, as it can help you understand the context of the task and guide your choice of agent. + - If the task involves writing new code or adding new functionality, consider using the code_writer_agent. + - If the task involves modifying or optimizing existing code, consider using the code_refactor_agent. + - If the task involves file operations, command execution, or running a script, consider using the command_executor_agent. + + Based on the task and overall objective, suggest the most appropriate agent to work on the task.""" + return openai_call(prompt, temperature=0.5, max_tokens=2000) + +def task_assigner_agent(objective: str, task: str, recommendation: str): + prompt = f"""You are an AGI agent responsible for choosing the best agent to work on a given task. Your goal is to analyze the provided major objective of the project and a single task from the JSON checklist generated by the previous agent, and choose the best agent to work on the task. + + The overall objective is: {objective} + The current task is: {task} + + Use this recommendation to guide you: {recommendation} + + The available agents are: + 1. code_writer_agent: Responsible for writing code based on the task description. + 2. code_refactor_agent: Responsible for editing existing code. + 2. command_executor_agent: Responsible for executing commands and handling file operations, such as creating, moving, or deleting files. + + Please consider the task description and the overall objective when choosing the most appropriate agent. Keep in mind that creating a file and writing code are different tasks. If the task involves creating a file, like "calculator.py" but does not mention writing any code inside it, the command_executor_agent should be used for this purpose. The code_writer_agent should only be used when the task requires writing or adding code to a file. The code_refactor_agent should only be used when the task requires modifying existing code. + + TLDR: To create files, use command_executor_agent, to write text/code to files, use code_writer_agent, to modify existing code, use code_refactor_agent. + + Choose the most appropriate agent to work on the task and return a JSON output with the following format: {{"agent": "agent_name"}}. ONLY return JSON output:""" + return openai_call(prompt, temperature=0, max_tokens=2000) + +def command_executor_agent(task: str, file_path: str): + prompt = f"""You are an AGI agent responsible for executing a given command on the Windows OS. Your goal is to analyze the provided major objective of the project and a single task from the JSON checklist generated by the previous agent, and execute the command on the Windows OS. + + The current task is: {task} + File or folder name referenced in the task (relative file path): {file_path} + + Based on the task, write the appropriate command to execute on the Windows OS. Make sure the command is relevant to the task and objective. For example, if the task is to create a new folder, the command should be 'mkdir new_folder_name'. Return the command as a JSON output with the following format: {{"command": "command_to_execute"}}. ONLY return JSON output:""" + return openai_call(prompt, temperature=0, max_tokens=2000) + +def code_writer_agent(task: str, isolated_context: str, context_code_chunks): + prompt = f"""You are an AGI agent responsible for writing code to accomplish a given task. Your goal is to analyze the provided major objective of the project and a single task from the JSON checklist generated by the previous agent, and write the necessary code to complete the task. + + The current task is: {task} + + To help you make the code useful in this codebase, use this context as reference of the other pieces of the codebase that are relevant to your task. PAY ATTENTION TO THIS: {isolated_context} + + The following code chunks were found to be relevant to the task. You can use them as reference to write the code if they are useful. PAY CLOSE ATTENTION TO THIS: + {context_code_chunks} + + Note: Always use 'encoding='utf-8'' when opening files with open(). + + Based on the task and objective, write the appropriate code to achieve the task. Make sure the code is relevant to the task and objective, and follows best practices. Return the code as a plain text output and NOTHING ELSE. Use identation and line breaks in the in the code. Make sure to only write the code and nothing else as your output will be saved directly to the file by other agent. IMPORTANT" If the task is asking you to write code to write files, this is a mistake! Interpret it and either do nothing or return the plain code, not a code to write file, not a code to write code, etc.""" + return openai_call(prompt, temperature=0, max_tokens=2000) + +def code_refactor_agent(task_description: str, existing_code_snippet: str, context_chunks, isolated_context: str): + + prompt = f"""You are an AGI agent responsible for refactoring code to accomplish a given task. Your goal is to analyze the provided major objective of the project, the task descriptionm and refactor the code accordingly. + + The current task description is: {task_description} + To help you make the code useful in this codebase, use this context as reference of the other pieces of the codebase that are relevant to your task: {isolated_context} + + Here are some context chunks that might be relevant to the task: + {context_chunks} + + Existing code you should refactor: + {existing_code_snippet} + + Based on the task description, objective, refactor the existing code to achieve the task. Make sure the refactored code is relevant to the task and objective, follows best practices, etc. + + Return a plain text code snippet with your refactored code. IMPORTANT: JUST RETURN CODE, YOUR OUTPUT WILL BE ADDED DIRECTLY TO THE FILE BY OTHER AGENT. BE MINDFUL OF THIS:""" + + return openai_call(prompt, temperature=0, max_tokens=2000) + +def file_management_agent(objective: str, task: str, current_directory_files: str, file_path: str): + prompt = f"""You are an AGI agent responsible for managing files in a software project. Your goal is to analyze the provided major objective of the project and a single task from the JSON checklist generated by the previous agent, and determine the appropriate file path and name for the generated code. + + The overall objective is: {objective} + The current task is: {task} + Specified file path (relative path from the current dir): {file_path} + + Make the file path adapted for the current directory files. The current directory files are: {current_directory_files}. Assume this file_path will be interpreted from the root path of the directory. + + Do not use '.' or './' in the file path. + + BE VERY SPECIFIC WITH THE FILES, AVOID FILE DUPLICATION, AVOID SPECIFYING THE SAME FILE NAME UNDER DIFFERENT FOLDERS, ETC. + + Based on the task, determine the file path and name for the generated code. Return the file path and name as a JSON output with the following format: {{"file_path": "file_path_and_name"}}. ONLY return JSON output:""" + return openai_call(prompt, temperature=0, max_tokens=2000) + +def code_relevance_agent(objective: str, task_description: str, code_chunk: str): + prompt = f"""You are an AGI agent responsible for evaluating the relevance of a code chunk in relation to a given task. Your goal is to analyze the provided major objective of the project, the task description, and the code chunk, and assign a relevance score from 0 to 10, where 0 is completely irrelevant and 10 is highly relevant. + + The overall objective is: {objective} + The current task description is: {task_description} + The code chunk is as follows (line numbers included): + {code_chunk} + + Based on the task description, objective, and code chunk, assign a relevance score between 0 and 10 (inclusive) for the code chunk. DO NOT OUTPUT ANYTHING OTHER THAN THE RELEVANCE SCORE AS A NUMBER.""" + + relevance_score = openai_call(prompt, temperature=0.5, max_tokens=50) + + return json.dumps({"relevance_score": relevance_score.strip()}) + +def task_human_input_agent(task: str, human_feedback: str): + prompt = f"""You are an AGI agent responsible for getting human input to improve the quality of tasks in a software project. Your goal is to analyze the provided task and adapt it based on the human's suggestions. The tasks should start with either 'Run a command to...', 'Write code to...', or 'Edit existing code to...' depending on the agent that will execute the task. + + For context, this task will be executed by other AGI agents with the following characteristics: + - code_writer_agent: Writes code snippets or functions and saves them to the appropriate files. This agent can also append code to existing files if required. + - code_refactor_agent: Responsible for modifying and refactoring existing code to meet the requirements of the task. + - command_executor_agent: Executes terminal commands for tasks such as creating directories, installing dependencies, etc. + + The current task is: + {task} + + The human feedback is: + {human_feedback} + + If the human feedback is empty, return the task as is. If the human feedback is saying to ignore the task, return the following string: + + Note that your output will replace the existing task, so make sure that your output is a valid task that starts with one of the required phrases ('Run a command to...', 'Write code to...', 'Edit existing code to...'). + + Please adjust the task based on the human feedback while ensuring it starts with one of the required phrases ('Run a command to...', 'Write code to...', 'Edit existing code to...'). Return the improved task as a plain text output and nothing else. Write only the new task.""" + + return openai_call(prompt, temperature=0.3, max_tokens=200) + +## END OF AGENTS ## + +print_colored_text(f"****Objective****", color='green') +print_char_by_char(OBJECTIVE, 0.00001, 10) + +# Create the tasks +print_colored_text("*****Working on tasks*****", "red") +print_colored_text(" - Creating initial tasks", "yellow") +task_agent_output = code_tasks_initializer_agent(OBJECTIVE) +print_colored_text(" - Reviewing and refactoring tasks to fit agents", "yellow") +task_agent_output = code_tasks_refactor_agent(OBJECTIVE, task_agent_output) +print_colored_text(" - Adding relevant technical details to the tasks", "yellow") +task_agent_output = code_tasks_details_agent(OBJECTIVE, task_agent_output) +print_colored_text(" - Adding necessary context to the tasks", "yellow") +task_agent_output = code_tasks_context_agent(OBJECTIVE, task_agent_output) +print() + +print_colored_text("*****TASKS*****", "green") +print_char_by_char(task_agent_output, 0.00000001, 10) + +# Task list +task_json = json.loads(task_agent_output) + +for task in task_json["tasks"]: + task_description = task["description"] + task_isolated_context = task["isolated_context"] + + print_colored_text("*****TASK*****", "yellow") + print_char_by_char(task_description) + print_colored_text("*****TASK CONTEXT*****", "yellow") + print_char_by_char(task_isolated_context) + + # HUMAN FEEDBACK + # Uncomment below to enable human feedback before each task. This can be used to improve the quality of the tasks, + # skip tasks, etc. I believe it may be very relevant in future versions that may have more complex tasks and could + # allow a ton of automation when working on large projects. + # + # Get user input as a feedback to the task_description + # print_colored_text("*****TASK FEEDBACK*****", "yellow") + # user_input = input("\n>:") + # task_description = task_human_input_agent(task_description, user_input) + # if task_description == "": + # continue + # print_colored_text("*****IMPROVED TASK*****", "green") + # print_char_by_char(task_description) + + # Assign the task to an agent + task_assigner_recommendation = task_assigner_recommendation_agent(OBJECTIVE, task_description) + task_agent_output = task_assigner_agent(OBJECTIVE, task_description, task_assigner_recommendation) + + print_colored_text("*****ASSIGN*****", "yellow") + print_char_by_char(task_agent_output) + + chosen_agent = json.loads(task_agent_output)["agent"] + + if chosen_agent == "command_executor_agent": + command_executor_output = command_executor_agent(task_description, task["file_path"]) + print_colored_text("*****COMMAND*****", "green") + print_char_by_char(command_executor_output) + + command_execution_output = execute_command_json(command_executor_output) + else: + # CODE AGENTS + if chosen_agent == "code_writer_agent": + # Compute embeddings for the codebase + # This will recompute embeddings for all files in the 'playground' directory + print_colored_text("*****RETRIEVING RELEVANT CODE CONTEXT*****", "yellow") + embeddings.compute_repository_embeddings() + relevant_chunks = embeddings.get_relevant_code_chunks(task_description, task_isolated_context) + + current_directory_files = execute_command_string("ls") + file_management_output = file_management_agent(OBJECTIVE, task_description, current_directory_files, task["file_path"]) + print_colored_text("*****FILE MANAGEMENT*****", "yellow") + print_char_by_char(file_management_output) + file_path = json.loads(file_management_output)["file_path"] + + code_writer_output = code_writer_agent(task_description, task_isolated_context, relevant_chunks) + + print_colored_text("*****CODE*****", "green") + print_char_by_char(code_writer_output) + + # Save the generated code to the file the agent selected + save_code_to_file(code_writer_output, file_path) + + elif chosen_agent == "code_refactor_agent": + # The code refactor agent works with multiple agents: + # For each task, the file_management_agent is used to select the file to edit.Then, the + # code_relevance_agent is used to select the relevant code chunks from that filewith the + # goal of finding the code chunk that is most relevant to the task description. This is + # the code chunk that will be edited. Finally, the code_refactor_agent is used to edit + # the code chunk. + + current_directory_files = execute_command_string("ls") + file_management_output = file_management_agent(OBJECTIVE, task_description, current_directory_files, task["file_path"]) + file_path = json.loads(file_management_output)["file_path"] + + print_colored_text("*****FILE MANAGEMENT*****", "yellow") + print_char_by_char(file_management_output) + + # Split the code into chunks and get the relevance scores for each chunk + code_chunks = split_code_into_chunks(file_path, 80) + print_colored_text("*****ANALYZING EXISTING CODE*****", "yellow") + relevance_scores = [] + for chunk in code_chunks: + score = code_relevance_agent(OBJECTIVE, task_description, chunk["code"]) + relevance_scores.append(score) + + # Select the most relevant chunk + selected_chunk = sorted(zip(relevance_scores, code_chunks), key=lambda x: x[0], reverse=True)[0][1] + + # Refactor the code + modified_code_output = code_refactor_agent(task_description, selected_chunk, context_chunks=[selected_chunk], isolated_context=task_isolated_context) + + # Extract the start_line and end_line of the selected chunk. This will be used to replace the code in the original file + start_line = selected_chunk["start_line"] + end_line = selected_chunk["end_line"] + + # Count the number of lines in the modified_code_output + modified_code_lines = modified_code_output.count("\n") + 1 + # Create a dictionary with the necessary information for the refactor_code function + modified_code_info = { + "start_line": start_line, + "end_line": start_line + modified_code_lines - 1, + "modified_code": modified_code_output + } + print_colored_text("*****REFACTORED CODE*****", "green") + print_char_by_char(modified_code_output) + + # Save the refactored code to the file + refactor_code([modified_code_info], file_path) diff --git a/babycoder/embeddings.py b/babycoder/embeddings.py new file mode 100644 index 00000000..1025b793 --- /dev/null +++ b/babycoder/embeddings.py @@ -0,0 +1,207 @@ +import os +import csv +import shutil +import openai +import pandas as pd +import numpy as np +from transformers import GPT2TokenizerFast +from dotenv import load_dotenv +import time + +# Heavily derived from OpenAi's cookbook example + +load_dotenv() + +# the dir is the ./playground directory +REPOSITORY_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "playground") + +class Embeddings: + def __init__(self, workspace_path: str): + self.workspace_path = workspace_path + openai.api_key = os.getenv("OPENAI_API_KEY", "") + + self.DOC_EMBEDDINGS_MODEL = f"text-embedding-ada-002" + self.QUERY_EMBEDDINGS_MODEL = f"text-embedding-ada-002" + + self.SEPARATOR = "\n* " + + self.tokenizer = GPT2TokenizerFast.from_pretrained("gpt2") + self.separator_len = len(self.tokenizer.tokenize(self.SEPARATOR)) + + def compute_repository_embeddings(self): + try: + playground_data_path = os.path.join(self.workspace_path, 'playground_data') + + # Delete the contents of the playground_data directory but not the directory itself + # This is to ensure that we don't have any old data lying around + for filename in os.listdir(playground_data_path): + file_path = os.path.join(playground_data_path, filename) + + try: + if os.path.isfile(file_path) or os.path.islink(file_path): + os.unlink(file_path) + elif os.path.isdir(file_path): + shutil.rmtree(file_path) + except Exception as e: + print(f"Failed to delete {file_path}. Reason: {str(e)}") + except Exception as e: + print(f"Error: {str(e)}") + + # extract and save info to csv + info = self.extract_info(REPOSITORY_PATH) + self.save_info_to_csv(info) + + df = pd.read_csv(os.path.join(self.workspace_path, 'playground_data\\repository_info.csv')) + df = df.set_index(["filePath", "lineCoverage"]) + self.df = df + context_embeddings = self.compute_doc_embeddings(df) + self.save_doc_embeddings_to_csv(context_embeddings, df, os.path.join(self.workspace_path, 'playground_data\\doc_embeddings.csv')) + + try: + self.document_embeddings = self.load_embeddings(os.path.join(self.workspace_path, 'playground_data\\doc_embeddings.csv')) + except: + pass + + # Extract information from files in the repository in chunks + # Return a list of [filePath, lineCoverage, chunkContent] + def extract_info(self, REPOSITORY_PATH): + # Initialize an empty list to store the information + info = [] + + LINES_PER_CHUNK = 60 + + # Iterate through the files in the repository + for root, dirs, files in os.walk(REPOSITORY_PATH): + for file in files: + file_path = os.path.join(root, file) + + # Read the contents of the file + with open(file_path, "r", encoding="utf-8") as f: + try: + contents = f.read() + except: + continue + + # Split the contents into lines + lines = contents.split("\n") + # Ignore empty lines + lines = [line for line in lines if line.strip()] + # Split the lines into chunks of LINES_PER_CHUNK lines + chunks = [ + lines[i:i+LINES_PER_CHUNK] + for i in range(0, len(lines), LINES_PER_CHUNK) + ] + # Iterate through the chunks + for i, chunk in enumerate(chunks): + # Join the lines in the chunk back into a single string + chunk = "\n".join(chunk) + # Get the first and last line numbers + first_line = i * LINES_PER_CHUNK + 1 + last_line = first_line + len(chunk.split("\n")) - 1 + line_coverage = (first_line, last_line) + # Add the file path, line coverage, and content to the list + info.append((os.path.join(root, file), line_coverage, chunk)) + + # Return the list of information + return info + + def save_info_to_csv(self, info): + # Open a CSV file for writing + os.makedirs(os.path.join(self.workspace_path, "playground_data"), exist_ok=True) + with open(os.path.join(self.workspace_path, 'playground_data\\repository_info.csv'), "w", newline="") as csvfile: + # Create a CSV writer + writer = csv.writer(csvfile) + # Write the header row + writer.writerow(["filePath", "lineCoverage", "content"]) + # Iterate through the info + for file_path, line_coverage, content in info: + # Write a row for each chunk of data + writer.writerow([file_path, line_coverage, content]) + + def get_relevant_code_chunks(self, task_description: str, task_context: str): + query = task_description + "\n" + task_context + most_relevant_document_sections = self.order_document_sections_by_query_similarity(query, self.document_embeddings) + selected_chunks = [] + for _, section_index in most_relevant_document_sections: + try: + document_section = self.df.loc[section_index] + selected_chunks.append(self.SEPARATOR + document_section['content'].replace("\n", " ")) + if len(selected_chunks) >= 2: + break + except: + pass + + return selected_chunks + + def get_embedding(self, text: str, model: str) -> list[float]: + result = openai.Embedding.create( + model=model, + input=text + ) + return result["data"][0]["embedding"] + + def get_doc_embedding(self, text: str) -> list[float]: + return self.get_embedding(text, self.DOC_EMBEDDINGS_MODEL) + + def get_query_embedding(self, text: str) -> list[float]: + return self.get_embedding(text, self.QUERY_EMBEDDINGS_MODEL) + + def compute_doc_embeddings(self, df: pd.DataFrame) -> dict[tuple[str, str], list[float]]: + """ + Create an embedding for each row in the dataframe using the OpenAI Embeddings API. + + Return a dictionary that maps between each embedding vector and the index of the row that it corresponds to. + """ + embeddings = {} + for idx, r in df.iterrows(): + # Wait one second before making the next call to the OpenAI Embeddings API + # print("Waiting one second before embedding next row\n") + time.sleep(1) + embeddings[idx] = self.get_doc_embedding(r.content.replace("\n", " ")) + return embeddings + + def save_doc_embeddings_to_csv(self, doc_embeddings: dict, df: pd.DataFrame, csv_filepath: str): + # Get the dimensionality of the embedding vectors from the first element in the doc_embeddings dictionary + if len(doc_embeddings) == 0: + return + + EMBEDDING_DIM = len(list(doc_embeddings.values())[0]) + + # Create a new dataframe with the filePath, lineCoverage, and embedding vector columns + embeddings_df = pd.DataFrame(columns=["filePath", "lineCoverage"] + [f"{i}" for i in range(EMBEDDING_DIM)]) + + # Iterate over the rows in the original dataframe + for idx, _ in df.iterrows(): + # Get the embedding vector for the current row + embedding = doc_embeddings[idx] + # Create a new row in the embeddings dataframe with the filePath, lineCoverage, and embedding vector values + row = [idx[0], idx[1]] + embedding + embeddings_df.loc[len(embeddings_df)] = row + + # Save the embeddings dataframe to a CSV file + embeddings_df.to_csv(csv_filepath, index=False) + + def vector_similarity(self, x: list[float], y: list[float]) -> float: + return np.dot(np.array(x), np.array(y)) + + def order_document_sections_by_query_similarity(self, query: str, contexts: dict[(str, str), np.array]) -> list[(float, (str, str))]: + """ + Find the query embedding for the supplied query, and compare it against all of the pre-calculated document embeddings + to find the most relevant sections. + + Return the list of document sections, sorted by relevance in descending order. + """ + query_embedding = self.get_query_embedding(query) + + document_similarities = sorted([ + (self.vector_similarity(query_embedding, doc_embedding), doc_index) for doc_index, doc_embedding in contexts.items() + ], reverse=True) + + return document_similarities + + def load_embeddings(self, fname: str) -> dict[tuple[str, str], list[float]]: + df = pd.read_csv(fname, header=0) + max_dim = max([int(c) for c in df.columns if c != "filePath" and c != "lineCoverage"]) + return { + (r.filePath, r.lineCoverage): [r[str(i)] for i in range(max_dim + 1)] for _, r in df.iterrows() + } \ No newline at end of file diff --git a/babycoder/objective.sample.txt b/babycoder/objective.sample.txt new file mode 100644 index 00000000..8bf59cb3 --- /dev/null +++ b/babycoder/objective.sample.txt @@ -0,0 +1,6 @@ +Create a Python program that consists of a single class named 'TemperatureConverter' in a file named 'temperature_converter.py'. The class should have the following methods: + +- celsius_to_fahrenheit(self, celsius: float) -> float: Converts Celsius temperature to Fahrenheit. +- fahrenheit_to_celsius(self, fahrenheit: float) -> float: Converts Fahrenheit temperature to Celsius. + +Create a separate 'main.py' file that imports the 'TemperatureConverter' class, takes user input for the temperature value and the unit, converts the temperature to the other unit, and then prints the result. \ No newline at end of file diff --git a/babycoder/playground/noop.md b/babycoder/playground/noop.md new file mode 100644 index 00000000..9937907a --- /dev/null +++ b/babycoder/playground/noop.md @@ -0,0 +1 @@ +# noop \ No newline at end of file From b8f41f911e3b2ecb9d642cc60c375ea0d4e3aab7 Mon Sep 17 00:00:00 2001 From: Felipe Schieber Date: Mon, 1 May 2023 18:38:54 -0300 Subject: [PATCH 29/62] Create README.md --- babycoder/README.md | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 babycoder/README.md diff --git a/babycoder/README.md b/babycoder/README.md new file mode 100644 index 00000000..9be32e7c --- /dev/null +++ b/babycoder/README.md @@ -0,0 +1,34 @@ +# Babycoder: AI-Powered Task Management for Code Generation + +Babycoder is a work in progress AI system that streamlines the software development process by generating code for small projects consisting of a few files. As a part of the BabyAgi system, Babycoder's goal is to lay the foundation for creating increasingly powerful AI agents capable of managing larger and more complex projects. + +## Objective + +The primary objective of Babycoder is to provide a recipe for developing AI agent systems capable of running code. By starting with a simple system and iterating on it, Babycoder aims to improve over time and eventually handle more extensive projects. + +## How It Works + +Babycoder's task management system consists of several AI agents working together to create, prioritize, and execute tasks based on a predefined objective and the results of previous tasks. The process consists of the following steps: + +1. **Task Definition**: Four task agents define tasks in a JSON list, which includes all tasks to be executed by the system. + +2. **Agent Assignment**: For each task, two agents collaborate to determine the agent responsible for executing the task. The possible executor agents are: + - `command_executor_agent` + - `code_writer_agent` + - `code_refactor_agent` + +3. **File Management**: The `files_management_agent` scans files in the project directory to determine which files or folders will be used by the executor agents to accomplish their tasks. + +4. **Task Execution**: The executor agents perform their assigned tasks using the following capabilities: + - The `command_executor_agent` runs OS commands, such as installing dependencies or creating files and folders. + - The `code_writer_agent` writes new code or updates existing code, using embeddings of the current codebase to retrieve relevant code sections and ensure compatibility with other parts of the codebase. + - The `code_refactor_agent` edits existing code according to the specified task, with the help of a `code_relevance_agent` that analyzes code chunks and identifies the most relevant section for editing. + +The code is written to a folder called `playground` in Babycoder's root directory. A folder named `playground_data` is used to save embeddings of the code being written. + +## How to use + +- Configure BabyAgi by following the instructions in the main README file. +- Navigate to the babycoder directory: `cd babycoder` +- Make a copy of the objective.sample.txt file (`cp objective.sample.txt objective.txt`) and update it to contain the objective of the project you want to create. +- Finally, from the `./babycoder` directory, run: `python babycoder.py` and watch it write code for you! \ No newline at end of file From bd61ad4b1b92580fac77a14821d5b82a74e54944 Mon Sep 17 00:00:00 2001 From: Felipe Schieber Date: Mon, 1 May 2023 18:39:56 -0300 Subject: [PATCH 30/62] Update .gitignore --- .gitignore | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.gitignore b/.gitignore index 08e78dd3..a4398dde 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,10 @@ env/ models llama/ +babycoder/playground/* +babycoder/playground_data/* +babycoder/objective.txt + # for node chroma/ node_modules/ From 3061d0f24f4f01779d04654b4dfcfbdd667d7588 Mon Sep 17 00:00:00 2001 From: Felipe Schieber Date: Mon, 1 May 2023 18:57:04 -0300 Subject: [PATCH 31/62] Update README.md --- babycoder/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/babycoder/README.md b/babycoder/README.md index 9be32e7c..e9dc62d6 100644 --- a/babycoder/README.md +++ b/babycoder/README.md @@ -1,10 +1,10 @@ -# Babycoder: AI-Powered Task Management for Code Generation +# Babycoder: Recipe for using BabyAgi to write code -Babycoder is a work in progress AI system that streamlines the software development process by generating code for small projects consisting of a few files. As a part of the BabyAgi system, Babycoder's goal is to lay the foundation for creating increasingly powerful AI agents capable of managing larger and more complex projects. +Babycoder is a work in progress AI system that is able to write code for small programs given a simple objective. As a part of the BabyAgi system, Babycoder's goal is to lay the foundation for creating increasingly powerful AI agents capable of managing larger and more complex projects. ## Objective -The primary objective of Babycoder is to provide a recipe for developing AI agent systems capable of running code. By starting with a simple system and iterating on it, Babycoder aims to improve over time and eventually handle more extensive projects. +The primary objective of Babycoder is to provide a recipe for developing AI agent systems capable of writing and editing code. By starting with a simple system and iterating on it, Babycoder aims to improve over time and eventually handle more extensive projects. ## How It Works From 7a8981c288fcc3bce56868257d0e91dad1f5bb70 Mon Sep 17 00:00:00 2001 From: Zain Hasan Date: Tue, 2 May 2023 07:23:54 -0400 Subject: [PATCH 32/62] README weaviate instructions --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 6787ffc1..6b071e0f 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ To use the script, you will need to follow these steps: 1. Clone the repository via `git clone https://github.com/yoheinakajima/babyagi.git` and `cd` into the cloned repository. 2. Install the required packages: `pip install -r requirements.txt` 3. Copy the .env.example file to .env: `cp .env.example .env`. This is where you will set the following variables. -4. Set your OpenAI API key in the OPENAI_API_KEY and OPENAPI_API_MODEL variables. +4. Set your OpenAI API key in the OPENAI_API_KEY and OPENAPI_API_MODEL variables. In order to use with Weaviate you will also need to setup additional variables detailed [here](docs/weaviate.md). 5. Set the name of the table where the task results will be stored in the TABLE_NAME variable. 6. (Optional) Set the name of the BabyAGI instance in the BABY_NAME variable. 7. (Optional) Set the objective of the task management system in the OBJECTIVE variable. From befd22fcdcf1e80341c644dac1d73d5d9d89164b Mon Sep 17 00:00:00 2001 From: Joe Heitzeberg Date: Tue, 2 May 2023 14:18:04 -0700 Subject: [PATCH 33/62] Added a section on the readme to link to the 7-day github activity reports from Blueprint --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 6787ffc1..4d2f098c 100644 --- a/README.md +++ b/README.md @@ -107,6 +107,12 @@ A note from @yoheinakajima (Apr 5th, 2023): I am new to GitHub and open source, so please be patient as I learn to manage this project properly. I run a VC firm by day, so I will generally be checking PRs and issues at night after I get my kids down - which may not be every night. Open to the idea of bringing in support, will be updating this section soon (expectations, visions, etc). Talking to lots of people and learning - hang tight for updates! +# BabyAGI Activity Report + +To help the BabyAGI community stay informed about the project's progress, [Blueprint AI](https://blueprint.ai) has developed a Github activity summarizer for BabyAGI. This concise report displays a summary of all contributions to the BabyAGI repository over the past 7 days (continuously updated), making it easy for you to keep track of the latest developments. + +To view the BabyAGI 7-day activity report, go here: [https://app.blueprint.ai/github/yoheinakajima/babyagi](https://app.blueprint.ai/github/yoheinakajima/babyagi) + # Inspired projects In the short time since it was release, BabyAGI inspired many projects. You can see them all [here](docs/inspired-projects.md). From 36f035bb1c8ece3b7f4654c361d97790dd0318e8 Mon Sep 17 00:00:00 2001 From: Joe Heitzeberg Date: Tue, 2 May 2023 14:29:02 -0700 Subject: [PATCH 34/62] Update README.md Added an image --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 4d2f098c..228b83c5 100644 --- a/README.md +++ b/README.md @@ -113,6 +113,9 @@ To help the BabyAGI community stay informed about the project's progress, [Bluep To view the BabyAGI 7-day activity report, go here: [https://app.blueprint.ai/github/yoheinakajima/babyagi](https://app.blueprint.ai/github/yoheinakajima/babyagi) +[image](https://app.blueprint.ai/github/yoheinakajima/babyagi) + + # Inspired projects In the short time since it was release, BabyAGI inspired many projects. You can see them all [here](docs/inspired-projects.md). From 39d53c252b455843edcc39484e8c4e5ed3362413 Mon Sep 17 00:00:00 2001 From: Joe Heitzeberg Date: Tue, 2 May 2023 14:29:46 -0700 Subject: [PATCH 35/62] Update README.md remove link to company --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 228b83c5..ef06af3e 100644 --- a/README.md +++ b/README.md @@ -109,7 +109,7 @@ I am new to GitHub and open source, so please be patient as I learn to manage th # BabyAGI Activity Report -To help the BabyAGI community stay informed about the project's progress, [Blueprint AI](https://blueprint.ai) has developed a Github activity summarizer for BabyAGI. This concise report displays a summary of all contributions to the BabyAGI repository over the past 7 days (continuously updated), making it easy for you to keep track of the latest developments. +To help the BabyAGI community stay informed about the project's progress, Blueprint AI has developed a Github activity summarizer for BabyAGI. This concise report displays a summary of all contributions to the BabyAGI repository over the past 7 days (continuously updated), making it easy for you to keep track of the latest developments. To view the BabyAGI 7-day activity report, go here: [https://app.blueprint.ai/github/yoheinakajima/babyagi](https://app.blueprint.ai/github/yoheinakajima/babyagi) From 4d5002262e36a361ef9c4d057989ece22f1ce304 Mon Sep 17 00:00:00 2001 From: Felipe Schieber Date: Tue, 2 May 2023 22:12:01 -0300 Subject: [PATCH 36/62] get os name --- babycoder/babycoder.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/babycoder/babycoder.py b/babycoder/babycoder.py index 31f30bbe..321b3b6f 100644 --- a/babycoder/babycoder.py +++ b/babycoder/babycoder.py @@ -6,14 +6,14 @@ from dotenv import load_dotenv import json import subprocess +import platform from embeddings import Embeddings # Set Variables load_dotenv() current_directory = os.getcwd() - -embeddings = Embeddings(current_directory) +os_version = platform.release() openai_calls_retried = 0 max_openai_calls_retries = 3 @@ -179,7 +179,7 @@ def split_code_into_chunks(file_path: str, chunk_size: int = 50) -> List[Dict[st ## End of Helper/Utility functions ## -## TASKS AGENTS +## TASKS AGENTS ## def code_tasks_initializer_agent(objective: str): prompt = f"""You are an AGI agent responsible for creating a detailed JSON checklist of tasks that will guide other AGI agents to complete a given programming objective. Your task is to analyze the provided objective and generate a well-structured checklist with a clear starting point and end point, as well as tasks broken down to be very specific, clear, and executable by other agents without the context of other tasks. @@ -382,12 +382,12 @@ def task_assigner_agent(objective: str, task: str, recommendation: str): return openai_call(prompt, temperature=0, max_tokens=2000) def command_executor_agent(task: str, file_path: str): - prompt = f"""You are an AGI agent responsible for executing a given command on the Windows OS. Your goal is to analyze the provided major objective of the project and a single task from the JSON checklist generated by the previous agent, and execute the command on the Windows OS. + prompt = f"""You are an AGI agent responsible for executing a given command on the {os_version} OS. Your goal is to analyze the provided major objective of the project and a single task from the JSON checklist generated by the previous agent, and execute the command on the {os_version} OS. The current task is: {task} File or folder name referenced in the task (relative file path): {file_path} - Based on the task, write the appropriate command to execute on the Windows OS. Make sure the command is relevant to the task and objective. For example, if the task is to create a new folder, the command should be 'mkdir new_folder_name'. Return the command as a JSON output with the following format: {{"command": "command_to_execute"}}. ONLY return JSON output:""" + Based on the task, write the appropriate command to execute on the {os_version} OS. Make sure the command is relevant to the task and objective. For example, if the task is to create a new folder, the command should be 'mkdir new_folder_name'. Return the command as a JSON output with the following format: {{"command": "command_to_execute"}}. ONLY return JSON output:""" return openai_call(prompt, temperature=0, max_tokens=2000) def code_writer_agent(task: str, isolated_context: str, context_code_chunks): @@ -499,6 +499,8 @@ def task_human_input_agent(task: str, human_feedback: str): # Task list task_json = json.loads(task_agent_output) +embeddings = Embeddings(current_directory) + for task in task_json["tasks"]: task_description = task["description"] task_isolated_context = task["isolated_context"] From 7c11ada093f264edba80f0714b19670ba5f3a0aa Mon Sep 17 00:00:00 2001 From: Felipe Schieber <115842157+fschieber88@users.noreply.github.com> Date: Tue, 2 May 2023 22:12:21 -0300 Subject: [PATCH 37/62] Update README.md --- babycoder/README.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/babycoder/README.md b/babycoder/README.md index e9dc62d6..53b5fed0 100644 --- a/babycoder/README.md +++ b/babycoder/README.md @@ -8,18 +8,24 @@ The primary objective of Babycoder is to provide a recipe for developing AI agen ## How It Works -Babycoder's task management system consists of several AI agents working together to create, prioritize, and execute tasks based on a predefined objective and the results of previous tasks. The process consists of the following steps: +

+ +

+ +Babycoder's task management system consists of several AI agents working together to create, prioritize, and execute tasks based on a predefined objective and the current state of the project being worked on. The process consists of the following steps: 1. **Task Definition**: Four task agents define tasks in a JSON list, which includes all tasks to be executed by the system. -2. **Agent Assignment**: For each task, two agents collaborate to determine the agent responsible for executing the task. The possible executor agents are: +2. **(Optional) Human feedback**: If enabled, allows to provide feedback for each task before it is executed. The feedback is processed by an agent responsible for applying it to improve the task. + +3. **Agent Assignment**: For each task, two agents collaborate to determine the agent responsible for executing the task. The possible executor agents are: - `command_executor_agent` - `code_writer_agent` - `code_refactor_agent` -3. **File Management**: The `files_management_agent` scans files in the project directory to determine which files or folders will be used by the executor agents to accomplish their tasks. +4. **File Management**: The `files_management_agent` scans files in the project directory to determine which files or folders will be used by the executor agents to accomplish their tasks. -4. **Task Execution**: The executor agents perform their assigned tasks using the following capabilities: +5. **Task Execution**: The executor agents perform their assigned tasks using the following capabilities: - The `command_executor_agent` runs OS commands, such as installing dependencies or creating files and folders. - The `code_writer_agent` writes new code or updates existing code, using embeddings of the current codebase to retrieve relevant code sections and ensure compatibility with other parts of the codebase. - The `code_refactor_agent` edits existing code according to the specified task, with the help of a `code_relevance_agent` that analyzes code chunks and identifies the most relevant section for editing. @@ -31,4 +37,4 @@ The code is written to a folder called `playground` in Babycoder's root director - Configure BabyAgi by following the instructions in the main README file. - Navigate to the babycoder directory: `cd babycoder` - Make a copy of the objective.sample.txt file (`cp objective.sample.txt objective.txt`) and update it to contain the objective of the project you want to create. -- Finally, from the `./babycoder` directory, run: `python babycoder.py` and watch it write code for you! \ No newline at end of file +- Finally, from the `./babycoder` directory, run: `python babycoder.py` and watch it write code for you! From a7f3416f6431e69d856fb63f337de5a9684a9ef0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yi=C4=9Fit=20Tanr=C4=B1verdi?= Date: Thu, 4 May 2023 20:49:36 +0200 Subject: [PATCH 38/62] Change command to it's original code snippets should not be translated. --- docs/README-tr.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/README-tr.md b/docs/README-tr.md index 90160fee..9faea745 100644 --- a/docs/README-tr.md +++ b/docs/README-tr.md @@ -40,7 +40,7 @@ Son olarak, betik, bağlam için görev sonuçlarını depolamak ve almak için Scripti kullanmak için şu adımları izlemeniz gerekir: 1. `git clone https://github.com/yoheinakajima/babyagi.git` ve `cd` aracılığıyla repoyu lokal bilgisayarınıza kopyalayın. -2. Gerekli paketleri kurun: `pip install -r gereksinimleri.txt` +2. Gerekli paketleri kurun: `pip install -r requirements.txt` 3. .env.example dosyasını .env'ye kopyalayın: `cp .env.example .env`. Aşağıdaki değişkenleri ayarlayacağınız yer burasıdır. 4. OpenAI ve Pinecone API anahtarlarınızı OPENAI_API_KEY, OPENAPI_API_MODEL ve PINECONE_API_KEY değişkenlerinde ayarlayın. 5. PINECONE_ENVIRONMENT değişkeninde Pinecone ortamını ayarlayın. From dae72398f4a227f06e030a7ec1f5bf72f0383d3c Mon Sep 17 00:00:00 2001 From: Matt Hoffner Date: Thu, 4 May 2023 17:45:35 -0700 Subject: [PATCH 39/62] Bump llama-cpp-python --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e0a05049..143b6ecd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,4 @@ chromadb==0.3.21 pre-commit>=3.2.0 python-dotenv==1.0.0 tiktoken==0.3.3 -llama-cpp-python==0.1.36 +llama-cpp-python==0.1.42 From 16c9b12e125c818f3df77af0799597c89f36a8d1 Mon Sep 17 00:00:00 2001 From: zeel sheladiya <46935793+zeelsheladiya@users.noreply.github.com> Date: Fri, 5 May 2023 11:54:23 -0400 Subject: [PATCH 40/62] Create README-in.md --- docs/README-in.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 docs/README-in.md diff --git a/docs/README-in.md b/docs/README-in.md new file mode 100644 index 00000000..ac9b23ad --- /dev/null +++ b/docs/README-in.md @@ -0,0 +1,73 @@ +

babyagi

+ +# Objective + +यह पायथन स्क्रिप्ट एक एआई-पावर्ड टास्क मैनेजमेंट सिस्टम का एक उदाहरण है। यह सिस्टम ओपेनएआई और वेक्टर डेटाबेस जैसे Chroma या Weaviate का उपयोग करता है ताकि कार्यों को बनाना, प्राथमिकता देना और क्रियान्वयन करना संभव हो सके। इस सिस्टम के पीछे की मुख्य विचारधारा यह है कि यह पिछले कार्यों के परिणाम और एक पूर्वनिर्धारित उद्देश्य के आधार पर कार्यों को बनाता है। फिर स्क्रिप्ट ओपेनएआई के प्राकृतिक भाषा प्रोसेसिंग (एनएलपी) क्षमताओं का उपयोग करता है ताकि उद्देश्य के आधार पर नए कार्य बनाए जा सकें, और Chroma / Weaviate का उपयोग करता है ताकि संदर्भ के लिए कार्य परिणाम संग्रहीत और पुनर्प्राप्त किए जा सकें। यह मूल [टास्क-ड्रिवन ऑटोनोमस एजेंट](https://twitter.com/yoheinakajima/status/1640934493489070080?s=20) (28 मार्च, 2023) का एक कम किए गए संस्करण है। + +यह README निम्नलिखित विषयों पर चर्चा करेगा: + +- [स्क्रिप्ट कैसे काम करता है](#how-it-works) +- [स्रिप्ट का उपयोग कैसे करें](#how-to-use) +- [समर्थित मॉडल](#supported-models) +- [स्क्रिप्ट को निरंतर चलाने के बारे में चेतावनी](#continous-script-warning) + +# कैसे काम करता है + + +यह स्क्रिप्ट निम्नलिखित चरणों को करते हुए एक अनंत लूप को चलाकर काम करता है: + +1. कार्य सूची से पहला कार्य खींचें। +2. कार्य को क्रियान्वयन एजेंट को भेजें, जो संदर्भ के आधार पर टास्क को पूरा करने के लिए ओपनएआई के एपीआई का उपयोग करता है। +3. परिणाम को अमीर बनाकर [Chroma](https://docs.trychroma.com)/[Weaviate](https://weaviate.io/) में संग्रहीत करें। +4. उद्देश्य और पिछले कार्य के परिणाम के आधार पर नए कार्य बनाएं और कार्य सूची की प्राथमिकता को दोबारा तैयार करें। + +![image](https://user-images.githubusercontent.com/21254008/235015461-543a897f-70cc-4b63-941a-2ae3c9172b11.png) + + +`execution_agent()` फ़ंक्शन में ओपनएआई एपीआई का उपयोग किया जाता है। इसमें दो पैरामीटर होते हैं: उद्देश्य और कार्य। फिर यह ओपनएआई के एपीआई को एक प्रॉम्प्ट भेजता है, जो कार्य के परिणाम को लौटाता है। प्रॉम्प्ट में एक एआई सिस्टम के कार्य का वर्णन, उद्देश्य, और कार्य होता है। फिर परिणाम एक स्ट्रिंग के रूप में लौटाया जाता है। + +`task_creation_agent()` फ़ंक्शन में ओपनएआई का उपयोग उद्देश्य और पिछले कार्य के परिणाम के आधार पर नए कार्य बनाने के लिए किया जाता है। यह फ़ंक्शन चार पैरामीटर लेता है: उद्देश्य, पिछले कार्य के परिणाम, कार्य विवरण, और वर्तमान कार्य सूची। फिर यह ओपनएआई को एक प्रॉम्प्ट भेजता है, जो नए कार्यों की एक स्ट्रिंग की सूची लौटाता है। फ़ंक्शन फिर नए कार्यों को डिक्शनरी की एक सूची के रूप में लौटाता है, जहाँ प्रत्येक डिक्शनरी में कार्य का नाम होता है। + + +`prioritization_agent()` फ़ंक्शन में OpenAI के API का उपयोग किया जाता है जिससे टास्क सूची को दोबारा प्राथमिकता दी जाती है। फ़ंक्शन एक पैरामीटर लेता है, वर्तमान कार्य का आईडी। यह OpenAI के API को एक प्रॉम्प्ट भेजता है, जो नंबरदार सूची के रूप में दोबारा प्राथमिकता दी गई टास्क सूची लौटाता है। + +अंत में, स्क्रिप्ट Chroma/Weaviate का उपयोग करता है टास्क परिणामों को संदर्भ के लिए संग्रहीत और पुनः प्राप्त करने के लिए। स्क्रिप्ट टेबल नाम में निर्दिष्ट बचाव के आधार पर एक Chroma/Weaviate संग्रह बनाता है। Chroma/Weaviate फिर संग्रह में कार्य के परिणामों को, साथ ही कार्य के नाम और किसी अतिरिक्त मेटाडेटा के साथ संग्रहीत करने के लिए उपयोग किया जाता है। + +# कैसे उपयोग करें + +स्क्रिप्ट का उपयोग करने के लिए, आपको निम्नलिखित चरणों का पालन करना होगा: + +1. रिपॉजिटरी क्लोन करें: `git clone https://github.com/yoheinakajima/babyagi.git` और cd कमांड का उपयोग करके क्लोन रिपॉजिटरी में जाएं। +2. आवश्यक पैकेजों को इंस्टॉल करें: `pip install -r requirements.txt` +3. .env.example फ़ाइल को .env में कॉपी करें: `cp .env.example .env`। यहाँ आप निम्नलिखित वेरिएबल को सेट करेंगे। +4. अपनी OpenAI API कुंजी को OPENAI_API_KEY और OPENAPI_API_MODEL वेरिएबल में सेट करें। +5. टेबल नाम जहाँ कार्य परिणाम संग्रहित होंगे, उसे TABLE_NAME वेरिएबल में सेट करें। +6. (वैकल्पिक) BABY_NAME वेरिएबल में BabyAGI इंस्टेंस का नाम सेट करें। +7. (वैकल्पिक) OBJECTIVE वेरिएबल में कार्य प्रबंधन प्रणाली का उद्देश्य सेट करें। +8. (वैकल्पिक) INITIAL_TASK वेरिएबल में प्रणाली का पहला कार्य सेट करें। +9. स्क्रिप्ट को रन करें: `python babyagi.py` + +ऊपर दिए गए सभी वैकल्पिक मान भी कमांड लाइन पर निर्दिष्ट किए जा सकते हैं। + +# डॉकर कंटेनर के भीतर चलाना + +पूर्वापेक्षा के रूप में, आपको डॉकर और डॉकर-कम्पोज इंस्टॉल करने की आवश्यकता होगी। डॉकर डेस्कटॉप सबसे सरल विकल्प है https://www.docker.com/products/docker-desktop/ + +एक डॉकर कंटेनर के भीतर सिस्टम को चलाने के लिए, उपरोक्त चरणों के अनुसार अपनी .env फ़ाइल सेटअप करें और फिर निम्नलिखित को चलाएँ: + +``` +docker-compose up +``` + +# समर्थित मॉडल + +यह स्क्रिप्ट सभी OpenAI मॉडलों के साथ काम करता है, यहां तक ​​कि Llama और उसके विभिन्न रूपों के साथ भी Llama.cpp के माध्यम से। डिफ़ॉल्ट मॉडल **gpt-3.5-turbo** है। किसी भी अन्य मॉडल का उपयोग करने के लिए, LLM_MODEL के माध्यम से इसे निर्दिष्ट करें या कमांड लाइन का उपयोग करें। + +## Llama + +Llama एकीकरण के लिए llama-cpp पैकेज की आवश्यकता होगी। आपको भी Llama मॉडल वेट्स की आवश्यकता होगी। + +- **किसी भी स्थिति में इस रेपो में, जिसमें मॉडल डाउनलोड के लिंक, जैसे IPFS, मैग्नेट लिंक या कोई भी दूसरा लिंक हो, उन्हें मुंहतोड़ रूप से हटा दिया जाएगा। इनका उपयोग करने वालों को तुरंत निष्कासित किया जाएगा।** + +एक बार जब आप उन्हें प्राप्त कर लेते हैं, तो LLAMA_MODEL_PATH में निर्दिष्ट मॉडल के पथ को सेट करें। सुविधा के लिए, आप `models` को BabyAGI repo में जहां आपके पास Llama मॉडल वेट्स हैं, उस फ़ोल्डर से लिंक कर सकते हैं। फिर `LLM_MODEL=llama` या `-l` तर्क के साथ स्क्रिप्ट चल + From 112dfb38ec833fd86fd92b75bbcf7480962b3eb3 Mon Sep 17 00:00:00 2001 From: zeel sheladiya <46935793+zeelsheladiya@users.noreply.github.com> Date: Fri, 5 May 2023 12:18:57 -0400 Subject: [PATCH 41/62] Update README-in.md --- docs/README-in.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/README-in.md b/docs/README-in.md index ac9b23ad..a192f32f 100644 --- a/docs/README-in.md +++ b/docs/README-in.md @@ -71,3 +71,30 @@ Llama एकीकरण के लिए llama-cpp पैकेज की आ एक बार जब आप उन्हें प्राप्त कर लेते हैं, तो LLAMA_MODEL_PATH में निर्दिष्ट मॉडल के पथ को सेट करें। सुविधा के लिए, आप `models` को BabyAGI repo में जहां आपके पास Llama मॉडल वेट्स हैं, उस फ़ोल्डर से लिंक कर सकते हैं। फिर `LLM_MODEL=llama` या `-l` तर्क के साथ स्क्रिप्ट चल +# चेतावनी + +यह स्क्रिप्ट एक कार्य प्रबंधन सिस्टम के रूप में निरंतर चलाने के लिए डिजाइन किया गया है। इसे निरंतर चलाने से उच्च API उपयोग हो सकता है, इसलिए कृपया इसका जिम्मेदार उपयोग करें। इसके अलावा, स्क्रिप्ट को ठीक से सेटअप करने के लिए ओपनएआई एपीआई की आवश्यकता होती है, इसलिए स्क्रिप्ट को चलाने से पहले ओपनएआई एपीआई को सेटअप करने की सुनिश्चित करें। + +# योगदान + +निश्चित रूप से, बेबीएजी अभी अपनी शुरुआती अवस्था में है और इस दिशा और उस तक पहुंचने के लिए आवश्यक कदम अभी तक निर्धारित नहीं हुए हैं। वर्तमान में, बेबीएजी के लिए एक महत्वपूर्ण डिजाइन लक्ष्य है कि यह _सरल_ होना चाहिए ताकि इसे समझना और उस पर निर्माण करना आसान हो। इस सरलता को बनाए रखने के लिए, जब आप PR जमा करते हैं, तो कृपया निम्नलिखित दिशानिर्देशों का पालन करें: + +- बड़े, विस्तृत रीफैक्टरिंग की जगह छोटे, मॉड्यूलर संशोधनों पर ध्यान केंद्रित करें। +- नई सुविधाओं को लाने के समय, आपको उस विशिष्ट उपयोग मामले का विस्तृत विवरण प्रदान करना चाहिए। + +@yoheinakajima से एक नोट (5 अप्रैल, 2023): + +> मुझे पता है कि GitHub और ओपन सोर्स के बढ़ते हुए नंबर से मैंने अपने समय की उपलब्धता के अनुसार प्लान नहीं बनाया है - इसलिए आपकी सहनशीलता की कामना करता हूं। संबंधित दिशा में, मैं सरल रखने या विस्तार करने के बीच उलझा हुआ हूँ - वर्तमान में बेबी एजीआई कोर सरल रखने की ओर झुका हुआ हूँ, और इसे एक मंच के रूप में उपयोग करके इसे विस्तारित करने के विभिन्न दिशाओं का समर्थन और प्रचार करने के लिए (जैसे BabyAGIxLangchain एक दिशा हो सकती है)। मुझे लगता है कि विभिन्न मतभेदग्रस्त दृष्टिकोण हैं जो अन्वेषण के लायक हैं, और मुझे इसमें एक केंद्रीय स्थान का महत्व देखने में है जहां तुलना और चर्चा की जा सकती है। जल्द ही और अद्यतन आ रहे हैं। + +मैं GitHub और ओपन सोर्स में नया हूँ, इसलिए कृपया मुझे इस प्रोजेक्ट को सही ढंग से प्रबंधित करना सीखने के लिए संयम रखें। मैं दिनभर VC फर्म चलाता हूँ, इसलिए अधिकतम समय मैं अपने बच्चों को सोने के बाद रात में PRs और इश्यूज की जाँच करूँगा - जो हर रात नहीं हो सकता है। मैं सहायता लाने की विचारधारा को खुली छोड़ता हूँ, जल्द ही इस खंड को अपडेट करूंगा (अपेक्षाएं, दृष्टियाँ आदि)। मैं लोगों से बातचीत कर रहा हूँ और सीख रहा हूँ - अपडेट के लिए धैर्य रखें! + +# प्रेरित प्रोजेक्ट + +BabyAGI को रिलीज होने के कुछ ही समय में, इसने कई प्रोजेक्ट्स को प्रेरित किया है। आप उन सभी प्रोजेक्ट्स को [यहाँ](docs/inspired-projects.md) देख सकते हैं। + +# पूर्वकथा + +बेबीएजीआई ट्विटर पर साझा किए गए मूल [टास्क-ड्राइवन ऑटोनोमस एजेंट](https://twitter.com/yoheinakajima/status/1640934493489070080?s=20) का एक संस्कार वाला संस्करण है। यह संस्करण 140 लाइनों तक डाउन है: 13 टिप्पणियाँ, 22 रिक्तियां, और 105 कोड। रेपो का नाम मूल ऑटोनोमस एजेंट के प्रति प्रतिक्रिया में उठा था - लेखक इस बात का अर्थ नहीं करना चाहता कि यह एजीआई है। + +[@yoheinakajima](https://twitter.com/yoheinakajima) ने प्यार से बनाया है, जो एक वीसी है (अगर आप क्या बना रहे हैं, देखना चाहेंगे!)। + From 25be0782f8297896e7f81436a5d262469c18345f Mon Sep 17 00:00:00 2001 From: zeel sheladiya <46935793+zeelsheladiya@users.noreply.github.com> Date: Fri, 5 May 2023 12:20:45 -0400 Subject: [PATCH 42/62] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6787ffc1..e53c4e96 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,8 @@ [Magyar](docs/README-hu.md) [فارسی](docs/README-fa.md) [German](docs/README-de.md) +[Indian](docs/README-in.md) + # Objective From 64e0d5596dceb278061a2ce8a73e03b86baa7ed4 Mon Sep 17 00:00:00 2001 From: zeel sheladiya <46935793+zeelsheladiya@users.noreply.github.com> Date: Fri, 5 May 2023 12:22:20 -0400 Subject: [PATCH 43/62] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e53c4e96..a7b995fd 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ [Magyar](docs/README-hu.md) [فارسی](docs/README-fa.md) [German](docs/README-de.md) -[Indian](docs/README-in.md) +[Hindi](docs/README-in.md) # Objective From 285e9ffc0b35c6737b0f26d9bec413a9d672d044 Mon Sep 17 00:00:00 2001 From: zeel sheladiya <46935793+zeelsheladiya@users.noreply.github.com> Date: Fri, 5 May 2023 12:23:38 -0400 Subject: [PATCH 44/62] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a7b995fd..4b3a7107 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ [Magyar](docs/README-hu.md) [فارسی](docs/README-fa.md) [German](docs/README-de.md) -[Hindi](docs/README-in.md) +[Indian](docs/README-in.md) # Objective From bad7dc0e8b92c8cbb8138d0729a350dcea16176d Mon Sep 17 00:00:00 2001 From: Matt Hoffner Date: Sun, 7 May 2023 10:35:26 -0700 Subject: [PATCH 45/62] Update requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 143b6ecd..d700753f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,4 @@ chromadb==0.3.21 pre-commit>=3.2.0 python-dotenv==1.0.0 tiktoken==0.3.3 -llama-cpp-python==0.1.42 +llama-cpp-python>=0.1.42 From 64e8bdb8ce5efab31b1e7e2e5a14c2ddfc6a0ee7 Mon Sep 17 00:00:00 2001 From: Yohei Nakajima Date: Fri, 12 May 2023 08:00:57 -0700 Subject: [PATCH 46/62] Create BabyCatAGI.py Uploading another mod of OG BabyAGI (original commit). Just because it's easier to experiment with framework as a simple script. Would love to hear feedback - this one might be getting stable enough to discuss pulling into core. --- classic/BabyCatAGI.py | 320 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 320 insertions(+) create mode 100644 classic/BabyCatAGI.py diff --git a/classic/BabyCatAGI.py b/classic/BabyCatAGI.py new file mode 100644 index 00000000..42b3dad2 --- /dev/null +++ b/classic/BabyCatAGI.py @@ -0,0 +1,320 @@ +###### This is a modified version of OG BabyAGI, called BabyCatAGI (future modifications will follow the pattern "BabyAGI"). This version requires GPT-4, it's very slow, and often errors out.###### +######IMPORTANT NOTE: I'm sharing this as a framework to build on top of (with lots of errors for improvement), to facilitate discussion around how to improve these. This is NOT for people who are looking for a complete solution that's ready to use. ###### + +import openai +import time +import requests +from bs4 import BeautifulSoup +from collections import deque +from typing import Dict, List +import re +import ast +import json +from serpapi import GoogleSearch + +### SET THESE 4 VARIABLES ############################## + +# Add your API keys here +OPENAI_API_KEY = "" +SERPAPI_API_KEY = "" #If you include SERPAPI KEY, this will enable web-search. If you don't, it will autoatically remove web-search capability. + +# Set variables +OBJECTIVE = "Research experts at scaling NextJS and their Twitter accounts." +YOUR_FIRST_TASK = "Develop a task list." #you can provide additional instructions here regarding the task list. + +### UP TO HERE ############################## + +# Configure OpenAI and SerpAPI client +openai.api_key = OPENAI_API_KEY +if SERPAPI_API_KEY: + serpapi_client = GoogleSearch({"api_key": SERPAPI_API_KEY}) + websearch_var = "[web-search] " +else: + websearch_var = "" + +# Initialize task list +task_list = [] + +# Initialize session_summary +session_summary = "" + +### Task list functions ############################## +def add_task(task: Dict): + task_list.append(task) + +def get_task_by_id(task_id: int): + for task in task_list: + if task["id"] == task_id: + return task + return None + +def get_completed_tasks(): + return [task for task in task_list if task["status"] == "complete"] + + +# Print task list and session summary +def print_tasklist(): + print("\033[95m\033[1m" + "\n*****TASK LIST*****\n" + "\033[0m") + for t in task_list: + dependent_task = "" + if t['dependent_task_ids']: + dependent_task = f"\033[31m\033[0m" + status_color = "\033[32m" if t['status'] == "complete" else "\033[31m" + print(f"\033[1m{t['id']}\033[0m: {t['task']} {status_color}[{t['status']}]\033[0m \033[93m[{t['tool']}] {dependent_task}\033[0m") + +### Tool functions ############################## +def text_completion_tool(prompt: str): + messages = [ + {"role": "user", "content": prompt} + ] + + response = openai.ChatCompletion.create( + model="gpt-3.5-turbo", + messages=messages, + temperature=0.2, + max_tokens=1500, + top_p=1, + frequency_penalty=0, + presence_penalty=0 + ) + + return response.choices[0].message['content'].strip() + + +def web_search_tool(query: str): + search_params = { + "engine": "google", + "q": query, + "api_key": SERPAPI_API_KEY, + "num":5 #edit this up or down for more results, though higher often results in OpenAI rate limits + } + search_results = GoogleSearch(search_params) + search_results = search_results.get_dict() + try: + search_results = search_results["organic_results"] + except: + search_results = {} + search_results = simplify_search_results(search_results) + print("\033[90m\033[3m" + "Completed search. Now scraping results.\n" + "\033[0m") + results = ""; + # Loop through the search results + for result in search_results: + # Extract the URL from the result + url = result.get('link') + # Call the web_scrape_tool function with the URL + print("\033[90m\033[3m" + "Scraping: "+url+"" + "...\033[0m") + content = web_scrape_tool(url, task) + print("\033[90m\033[3m" +str(content[0:100])[0:100]+"...\n" + "\033[0m") + results += str(content)+". " + + + return results + + +def simplify_search_results(search_results): + simplified_results = [] + for result in search_results: + simplified_result = { + "position": result.get("position"), + "title": result.get("title"), + "link": result.get("link"), + "snippet": result.get("snippet") + } + simplified_results.append(simplified_result) + return simplified_results + + +def web_scrape_tool(url: str, task:str): + content = fetch_url_content(url) + if content is None: + return None + + text = extract_text(content) + print("\033[90m\033[3m"+"Scrape completed. Length:" +str(len(text))+".Now extracting relevant info..."+"...\033[0m") + info = extract_relevant_info(OBJECTIVE, text[0:5000], task) + links = extract_links(content) + + #result = f"{info} URLs: {', '.join(links)}" + result = info + + return result + +headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36" +} + +def fetch_url_content(url: str): + try: + response = requests.get(url, headers=headers, timeout=10) + response.raise_for_status() + return response.content + except requests.exceptions.RequestException as e: + print(f"Error while fetching the URL: {e}") + return "" + +def extract_links(content: str): + soup = BeautifulSoup(content, "html.parser") + links = [link.get('href') for link in soup.findAll('a', attrs={'href': re.compile("^https?://")})] + return links + +def extract_text(content: str): + soup = BeautifulSoup(content, "html.parser") + text = soup.get_text(strip=True) + return text + + + +def extract_relevant_info(objective, large_string, task): + chunk_size = 3000 + overlap = 500 + notes = "" + + for i in range(0, len(large_string), chunk_size - overlap): + chunk = large_string[i:i + chunk_size] + + messages = [ + {"role": "system", "content": f"Objective: {objective}\nCurrent Task:{task}"}, + {"role": "user", "content": f"Analyze the following text and extract information relevant to our objective and current task, and only information relevant to our objective and current task. If there is no relevant information do not say that there is no relevant informaiton related to our objective. ### Then, update or start our notes provided here (keep blank if currently blank): {notes}.### Text to analyze: {chunk}.### Updated Notes:"} + ] + + response = openai.ChatCompletion.create( + model="gpt-3.5-turbo", + messages=messages, + max_tokens=800, + n=1, + stop="###", + temperature=0.7, + ) + + notes += response.choices[0].message['content'].strip()+". "; + + return notes + +### Agent functions ############################## + + +def execute_task(task, task_list, OBJECTIVE): + global task_id_counter + # Check if dependent_task_ids is not empty + if task["dependent_task_ids"]: + all_dependent_tasks_complete = True + for dep_id in task["dependent_task_ids"]: + dependent_task = get_task_by_id(dep_id) + if not dependent_task or dependent_task["status"] != "complete": + all_dependent_tasks_complete = False + break + + + # Execute task + print("\033[92m\033[1m"+"\n*****NEXT TASK*****\n"+"\033[0m\033[0m") + print(str(task['id'])+": "+str(task['task'])+" ["+str(task['tool']+"]")) + task_prompt = f"Complete your assigned task based on the objective and only based on information provided in the dependent task output, if provided. Your objective: {OBJECTIVE}. Your task: {task['task']}" + if task["dependent_task_ids"]: + dependent_tasks_output = "" + for dep_id in task["dependent_task_ids"]: + dependent_task_output = get_task_by_id(dep_id)["output"] + dependent_task_output = dependent_task_output[0:2000] + dependent_tasks_output += f" {dependent_task_output}" + task_prompt += f" Your dependent tasks output: {dependent_tasks_output}\n OUTPUT:" + + # Use tool to complete the task + if task["tool"] == "text-completion": + task_output = text_completion_tool(task_prompt) + elif task["tool"] == "web-search": + task_output = web_search_tool(str(task['task'])) + elif task["tool"] == "web-scrape": + task_output = web_scrape_tool(str(task['task'])) + + # Find task index in the task_list + task_index = next((i for i, t in enumerate(task_list) if t["id"] == task["id"]), None) + + # Mark task as complete and save output + task_list[task_index]["status"] = "complete" + task_list[task_index]["output"] = task_output + + # Print task output + print("\033[93m\033[1m"+"\nTask Output:"+"\033[0m\033[0m") + print(task_output) + + # Add task output to session_summary + global session_summary + session_summary += f"\n\nTask {task['id']} - {task['task']}:\n{task_output}" + + + +task_list = [] + +def task_creation_agent(objective: str) -> List[Dict]: + global task_list + minified_task_list = [{k: v for k, v in task.items() if k != "result"} for task in task_list] + + prompt = ( + f"You are a task creation AI tasked with creating a list of tasks as a JSON array, considering the ultimate objective of your team: {OBJECTIVE}. " + f"Create new tasks based on the objective. Limit tasks types to those that can be completed with the available tools listed below. Task description should be detailed." + f"Current tool option is [text-completion] {websearch_var} and only." # web-search is added automatically if SERPAPI exists + f"For tasks using [web-search], provide the search query, and only the search query to use (eg. not 'research waterproof shoes, but 'waterproof shoes')" + f"dependent_task_ids should always be an empty array, or an array of numbers representing the task ID it should pull results from." + f"Make sure all task IDs are in chronological order.\n" + f"The last step is always to provide a final summary report including tasks executed and summary of knowledge acquired.\n" + f"Do not create any summarizing steps outside of the last step..\n" + f"An example of the desired output format is: " + "[{\"id\": 1, \"task\": \"https://untapped.vc\", \"tool\": \"web-scrape\", \"dependent_task_ids\": [], \"status\": \"incomplete\", \"result\": null, \"result_summary\": null}, {\"id\": 2, \"task\": \"Consider additional insights that can be reasoned from the results of...\", \"tool\": \"text-completion\", \"dependent_task_ids\": [1], \"status\": \"incomplete\", \"result\": null, \"result_summary\": null}, {\"id\": 3, \"task\": \"Untapped Capital\", \"tool\": \"web-search\", \"dependent_task_ids\": [], \"status\": \"incomplete\", \"result\": null, \"result_summary\": null}].\n" + f"JSON TASK LIST=" + ) + + print("\033[90m\033[3m" + "\nInitializing...\n" + "\033[0m") + print("\033[90m\033[3m" + "Analyzing objective...\n" + "\033[0m") + print("\033[90m\033[3m" + "Running task creation agent...\n" + "\033[0m") + response = openai.ChatCompletion.create( + model="gpt-4", + messages=[ + { + "role": "system", + "content": "You are a task creation AI." + }, + { + "role": "user", + "content": prompt + } + ], + temperature=0, + max_tokens=1500, + top_p=1, + frequency_penalty=0, + presence_penalty=0 + ) + + # Extract the content of the assistant's response and parse it as JSON + result = response["choices"][0]["message"]["content"] + print("\033[90m\033[3m" + "\nDone!\n" + "\033[0m") + try: + task_list = json.loads(result) + except Exception as error: + print(error) + + return task_list + +##### START MAIN LOOP######## + +#Print OBJECTIVE +print("\033[96m\033[1m"+"\n*****OBJECTIVE*****\n"+"\033[0m\033[0m") +print(OBJECTIVE) + +# Initialize task_id_counter +task_id_counter = 1 + +# Run the task_creation_agent to create initial tasks +task_list = task_creation_agent(OBJECTIVE) +print_tasklist() + +# Execute tasks in order +while len(task_list) > 0: + for task in task_list: + if task["status"] == "incomplete": + execute_task(task, task_list, OBJECTIVE) + print_tasklist() + break + +# Print session summary +print("\033[96m\033[1m"+"\n*****SESSION SUMMARY*****\n"+"\033[0m\033[0m") +print(session_summary) From b70c5a00ac97c5b854c1ae0640cc03808a7216ec Mon Sep 17 00:00:00 2001 From: Simon Date: Mon, 22 May 2023 09:23:26 -0600 Subject: [PATCH 47/62] add twitter-agent to inspired projects lists --- docs/inspired-projects.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/inspired-projects.md b/docs/inspired-projects.md index b3bfa04a..16495946 100644 --- a/docs/inspired-projects.md +++ b/docs/inspired-projects.md @@ -106,3 +106,8 @@ This document highlights a curated selection of remarkable projects inspired by - Description: C# dotnet implementation of BabyAGI. - Author: [Sathyaraj Vadivel](https://github.com/sathyarajv) - Twitter: https://twitter.com/sathyarajv +1. [twitter-agent🐣](https://github.com/bigsky77/twitter-agent) + - Description: Twitter-Agent is an autonomous AI-powered agent for interacting directly with the Twitter API. + - Author: [BigSky](https://github.com/bigsky77) + - Twitter: https://twitter.com/bigsky_7 + From 7274c7b084cc203e03bb9d71b38204c70e3123e6 Mon Sep 17 00:00:00 2001 From: sabbbir Date: Fri, 2 Jun 2023 00:09:00 +0600 Subject: [PATCH 48/62] Added Bengali language --- docs/README-bn.md | 110 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 docs/README-bn.md diff --git a/docs/README-bn.md b/docs/README-bn.md new file mode 100644 index 00000000..0f780840 --- /dev/null +++ b/docs/README-bn.md @@ -0,0 +1,110 @@ +

+ babyagi +

+ +# উদ্দেশ্য + +এই পাইথন স্ক্রিপ্টটি একটি এআই-শক্তিমত্তা সম্পন্ন টাস্ক ম্যানেজমেন্ট সিস্টেমের উদাহরণ। সিস্টেমটি OpenAI এবং vector ডেটাবেইজ ব্যবহার করে, যেমন ক্রোমা বা উইভিয়েট ব্যবহার করে কাজগুলি তৈরি করতে, অগ্রাধিকার দিতে এবং কার্যকর করতে। এই সিস্টেমের মূল ধারণা হল, এটি পূর্ববর্তী কাজের ফলাফল এবং একটি পূর্বনির্ধারিত উদ্দেশ্যের উপর ভিত্তি করে কাজ তৈরি করে থাকে। স্ক্রিপ্টটি তখন উদ্দেশ্যের উপর ভিত্তি করে নতুন কাজ তৈরি করতে ওপেনএআই-এর ন্যাচারাল ল্যাংগুয়েজ প্রসেসিং (NLP) ক্ষমতা ব্যবহার করে, এবং প্রেক্ষাপটের জন্য টাস্ক ফলাফল সংরক্ষণ এবং পুনরুদ্ধারে ক্রোমা/উইভিয়েট ব্যবহার করে থাকে। এটি [Task-Driven Autonomous Agent](https://twitter.com/yoheinakajima/status/1640934493489070080?s=20) (Mar 28, 2023) এর একটি পেয়ার্ড ডাউন ভার্সন। + + +README নিম্নলিখিত বিষয়গুলো কাভার করবে: + +- [স্ক্রিপট যেভাবে কাজ করে](#how-it-works) + +- [যেভাবে ব্যবহার করা যাবে](#how-to-use) + +- [সাপোর্টেড মডেলসমূহ](#supported-models) + +- [ক্রমাগত স্ক্রিপ্ট চালাতে সতর্কতা](#continous-script-warning) + +# স্ক্রিপ্ট যেভাবে কাজ করে + +স্ক্রিপ্টটি একটি অসীম লুপ চালানোর মাধ্যমে নিম্নলিখিত পদক্ষেপগুলি সম্পন্ন করে: + +1. টাস্ক লিস্ট থেকে প্রথম টাস্ক টানে। +2. এক্সিকিউশন এজেন্টের কাছে টাস্কটি পাঠায়, যেটি প্রেক্ষাপটের উপর ভিত্তি করে কাজটি সম্পূর্ণ করতে OpenAI এর API ব্যবহার করে থাকে। +3. ফলাফলটি সমৃদ্ধ করে এবং এখানে [Chroma](https://docs.trychroma.com)/[Weaviate](https://weaviate.io/) সংরক্ষণ করে। +4. নতুন টাস্ক তৈরি করে এবং আগের টাস্কের উদ্দেশ্য এবং ফলাফলের উপর ভিত্তি করে টাস্ক লিস্টকে পুনরায় প্রাধান্য দেয়। +
+ +![image](https://user-images.githubusercontent.com/21254008/235015461-543a897f-70cc-4b63-941a-2ae3c9172b11.png) + +`execution_agent()` ফাংশনে OpenAI এর API ব্যবহার করা হয়। এটির দুইটি প্যারামিটার লাগে: উদ্দেশ্য এবং টাস্ক। এরপর এটি OpenAI API এ একটি প্রম্পট পাঠায়, যা টাস্কের ফলাফল প্রদান করে। প্রম্পটে AI সিস্টেমের টাস্ক, উদ্দেশ্য এবং টাস্কের বিবরণ থাকে। এরপর ফলাফলটি একটি স্ট্রিং হিসাবে ফিরে আসে। + +`task_creation_agent()` ফাংশন হল যেখানে OpenAI এর API উদ্দেশ্য এবং পূর্ববর্তী টাস্কের ফলাফলের উপর ভিত্তি করে নতুন টাস্ক তৈরি করতে ব্যবহার করা হয়। ফাংশনটি চারটি প্যারামিটার গ্রহন করে: উদ্দেশ্য, পূর্ববর্তী টাস্কের ফলাফল, টাস্কের বিবরণ এবং বর্তমান টাস্ক লিস্ট। তারপরে এটি OpenAI এর API-তে একটি প্রম্পট পাঠায়, যা স্ট্রিং হিসাবে নতুন কাজের একটি তালিকা প্রদান করে। ফাংশনটি এরপর নতুন টাস্কগুলিকে একটি ডিকশনারির লিস্ট হিসাবে ফিরিয়ে দেয়, যেখানে প্রতিটি ডিকশনারিতে টাস্কের নাম থাকে। + +`prioritization_agent()` ফাংশন OpenAI এর API টাস্ক লিস্টকে পুনঃঅগ্রাধিকার দেয়ার জন্য ব্যবহার করা হয়। ফাংশনটি একটি প্যারামিটার নেয়, বর্তমান টাস্কের আইডি। এটি OpenAI এর API-তে একটি প্রম্পট পাঠায়, যা পুনঃঅগ্রাধিকার টাস্ক লিস্টকে একটি সংখ্যাযুক্ত লিস্ট হিসাবে প্রদান করে। + +অবশেষে, স্ক্রিপ্টটি প্রেক্ষাপটের জন্য টাস্কের ফলাফলগুলি সংরক্ষণ এবং পুনরুদ্ধার করতে Chroma/Weaviate ব্যবহার করে। স্ক্রিপ্টটি TABLE_NAME ভেরিয়েবলে নির্দিষ্ট করা টেবিল নামের উপর ভিত্তি করে একটি Chroma/Weaviate সংগ্রহ তৈরি করে। এরপরে ক্রোমা/উইভিয়েট ব্যবহার করে টাস্কের নাম এবং অতিরিক্ত মেটাডেটা সহ টাস্কের ফলাফল সংরক্ষণ করতে ব্যবহৃত হয়। + +# যেভাবে ব্যবহার করা যাবে + +স্ক্রিপ্ট ব্যবহার করতে, নিম্নলিখিত পদক্ষেপ অনুসরণ করতে হবে: + +1. রিপোজিটরি ক্লোন `git clone https://github.com/yoheinakajima/babyagi.git` এবং `cd` এর মাধ্যমে ডিরেক্টোরিতে প্রবেশ করুন। +2. প্রয়োজনীয় প্যাকেজ ইনস্টল করতে: `pip install -r requirements.txt` +3. .env.example ফাইলটি .env তে কপি করুন: `cp .env.example .env` এখানে আপনি নিম্নলিখিত ভেরিয়েবল সেট করবেন। +4. OPENAI_API_KEY এবং OPENAPI_API_MODEL ভেরিয়েবলে আপনার OpenAI API কী সেট করুন। Weaviate এর সাথে ব্যবহার করার জন্য আপনাকে অতিরিক্ত ভেরিয়েবল সেটআপ করতে হবে [এখানে](docs/weaviate.md)। +5. টেবিলের নাম সেট করুন যেখানে টাস্ক ফলাফলগুলি TABLE_NAME ভেরিয়েবলে সংরক্ষণ করা হবে৷ +6. (ঐচ্ছিক) BABY_NAME ভেরিয়েবলে BabyAGI উদাহরণের নাম সেট করুন। +7. (ঐচ্ছিক) Objective ভেরিয়েবলে টাস্ক ম্যানেজমেন্ট সিস্টেমের উদ্দেশ্য সেট করুন। +8. (ঐচ্ছিক) সিস্টেমের প্রথম কাজটি INITIAL_TASK ভেরিয়েবলে সেট করুন। +9. স্ক্রিপ্টটি চালান: `python babyagi.py` + +উপরের সকল ঐচ্ছিক মানগুলো কমান্ড লাইনেও সেট করা যাবে। + +# Docker container এ চালতে হলে + +পূর্বশর্ত হিসাবে, আপনার docker এবং docker-compose ইনস্টল থাকতে হবে। docker desktop হল সবচেয়ে সহজ বিকল্প https://www.docker.com/products/docker-desktop/ + +docker কন্টেইনারে সিস্টেম চালানোর জন্য উপরের ধাপ অনুযায়ী আপনার .env ফাইল সেটআপ করুন এবং তারপরে নিম্নলিখিত কমান্ড রান করুন: + +``` +docker-compose up +``` + +# সাপোর্টেড মডেলসমূহ + +এই স্ক্রিপ্টটি সকল OpenAI মডেলের সাথে কাজ করে, একই সাথে Llama এবং Llama.cpp এর সাহায্যে এর অন্যান্য প্রকরনের সাথওে কাজ করে। ডিফল্ট মডেল হল **gpt-3.5-টার্বো**। ভিন্ন মডেল ব্যবহার করতে, LLM_MODEL এর মাধ্যমে এটি নির্দিষ্ট করুন বা কমান্ড লাইন ব্যবহার করুন৷ + +## Llama + +Llama ইন্টিগ্রেশনের জন্য llama-cpp প্যাকেজ প্রয়োজন। আপনার Llama model weights ও প্রয়োজন হবে৷ + +- **কোন অবস্থাতেই আইপিএফএস, ম্যাগনেট লিঙ্ক বা মডেল ডাউনলোডের অন্য কোনও লিঙ্ক এই রিপোজিটরির কোথাও শেয়ার করবেন না, যার মধ্যে ইস্যু, ডিসকাশন বা পুল রিকুয়েষ্ট অন্তর্ভুক্ত। সেগুলো অবিলম্বে মুছে ফেলা হবে।** + +যখন আপনার কাছে প্রয়োজনীয় সবকিছু থাকবে, তখন ব্যবহার করার জন্য নির্দিষ্ট মডেলের পাথে LLAMA_MODEL_PATH সেট করুন৷ আপনার সুবিধার জন্য, আপনি BabyAGI রেপোতে `models` লিঙ্ক করতে পারেন সেই ফোল্ডারে যেখানে আপনার Llama model weights আছে। তারপর `LLM_MODEL=llama` বা `-l` আর্গুমেন্ট দিয়ে স্ক্রিপ্টটি চালান। + +# সতর্কতা + +এই স্ক্রিপ্টটি টাস্ক ম্যানেজমেন্ট সিস্টেমের অংশ হিসাবে ক্রমাগত চালানোর জন্য ডিজাইন করা হয়েছে। এই স্ক্রিপ্টটি ক্রমাগত চালানোর ফলে উচ্চ API ব্যবহার হতে পারে, তাই দয়া করে দায়িত্বের সাথে এটি ব্যবহার করুন। উপরন্তু, স্ক্রিপ্টের জন্য OpenAI API সঠিকভাবে সেট আপ করা প্রয়োজন, তাই স্ক্রিপ্ট চালানোর আগে নিশ্চিত করুন যে আপনি API সেট আপ করেছেন। + +# Contribution + +বলা বাহুল্য, BabyAGI এখনও তার শৈশবকালে রয়েছে এবং এইভাবে আমরা এখনও এর লক্ষ্য এবং সেখানে পৌছানোর পদক্ষেপগুলি নির্ধারণ করছি। বর্তমানে, BabyAGI-এর জন্য একটি মূল ডিজাইন লক্ষ্য হল _simple_ হওয়া যাতে এটি সহজে বোঝা এবং তৈরি করা যায়। এই সরলতা বজায় রাখার জন্য, আমরা অনুরোধ করছি যে আপনি PR জমা দেওয়ার সময় নিম্নলিখিত নির্দেশিকাগুলি মেনে চলুন: + +- ব্যাপক রিফ্যাক্টরিংয়ের পরিবর্তে ছোট, মডুলার পরিবর্তনগুলিতে ফোকাস করুন। +- নতুন বৈশিষ্ট্যগুলি প্রবর্তন করার ক্ষেত্রে, আপনি যে নির্দিষ্ট ব্যবহারের ক্ষেত্রে সম্বোধন করছেন তার একটি বিশদ বিবরণ প্রদান করুন৷ + +@yoheinakajima'র মন্তব্য (Apr 5th, 2023): + +> জানি এখন PR ক্রমবর্ধমান, আমি আপনার ধৈর্যের প্রশংসা করি - যেহেতু আমি GitHub এবং OpenSource-এ নতুন, এবং এই সপ্তাহে আমার সময় প্রাপ্যতার পরিকল্পনা করিনি। পুনঃনির্দেশ, আমি এটিকে সরল একইসাথে প্রসারিত রাখার বিষয়ে হিমশিম খাচ্ছি - বর্তমানে একটি মূল বেবি এজিআইকে সরল রাখার দিকে ঝুঁকছি, এবং এটিকে প্রসারিত করার জন্য বিভিন্ন পদ্ধতির সমর্থন ও প্রচার করার জন্য এটিকে একটি প্ল্যাটফর্ম হিসাবে ব্যবহার করছি (যেমন: BabyAGIxLangchain একটি উদাহরণ)। আমি বিশ্বাস করি যে বিভিন্ন মতামতযুক্ত পন্থা রয়েছে যা গবেষণার যোগ্য, এবং আমি তুলনা এবং আলোচনা করার জন্য একটি কেন্দ্রীয় স্থানের মূল্য বুঝি। আরো আপডেট শীঘ্রই আসছে। + +আমি গিটহাব এবং ওপেন সোর্সে নতুন, তাই অনুগ্রহ করে ধৈর্য ধরুন কারণ আমি এই প্রকল্পটি সঠিকভাবে পরিচালনা করা শিখছি। আমি দিনে একটি ভিসি ফার্ম চালাই, তাই সাধারণত আমার বাচ্চারা ঘুমানোর পরে রাতে PR এবং সমস্যাগুলি পরীক্ষা করব - যা প্রতি রাতে নাও হতে পারে। যেকোনো ধরনের সহযোগিতার সমর্থন করছি, শীঘ্রই এই বিভাগটি আপডেট করা হবে (প্রত্যাশা, দৃষ্টিভঙ্গি, ইত্যাদি)। অনেক মানুষের সাথে কথা বলছি এবং শিখছি - আপডেটের জন্য অপেক্ষা করুন! + +# BabyAGI কার্যকলাপ রিপোর্ট + +BabyAGI কমিউনিটিকে প্রকল্পের অগ্রগতি সম্পর্কে অবগত থাকতে সাহায্য করার জন্য, Blueprint AI BabyAGI-এর জন্য একটি Github কার্যকলাপ সংক্ষিপ্তসার তৈরি করেছে৷ এই সংক্ষিপ্ত প্রতিবেদনটি গত 7 দিনে BabyAGI সংগ্রহস্থলে সমস্ত অবদানের সংক্ষিপ্তসার প্রদর্শন করে (প্রতিনিয়ত আপডেট করা হচ্ছে), এটি আপনার জন্য বেবি এজিআইএর সাম্প্রতিক বিকাশের উপর নজর রাখা সহজ করে তোলে। BabyAGI এর 7-দিনের কার্যকলাপ রিপোর্ট দেখতে, ক্লিক করুনঃ [https://app.blueprint.ai/github/yoheinakajima/babyagi](https://app.blueprint.ai/github/yoheinakajima/babyagi) + +[image](https://app.blueprint.ai/github/yoheinakajima/babyagi) + + +# Inspired projects + +BabyAGI প্রকাশের পর থেকে অল্প সময়ের মধ্যে অনেক প্রকল্পকে অনুপ্রাণিত করেছে। আপনি সবগুলো দেখতে পারেন [এখানে](docs/inspired-projects.md). + +# Backstory + +BabyAGI [Task-Driven Autonomous Agent] এর একটি সরলীকৃত ভার্সন (https://twitter.com/yoheinakajima/status/1640934493489070080?s=20) (Mar 28, 2023) টুইটারে শেয়ার করা হয়েছে। এই সংস্করণটি 140 লাইনের: 13টি মন্তব্য, 22টি শূন্যস্থান এবং 105 লাইন কোড। আসল স্বায়ত্তশাসিত এজেন্টের প্রতিক্রিয়ায় রেপোর নামটি এসেছে - লেখক বোঝাতে চাননা যে এটি AGI। + +যত্ন ও ভালবাসা দিয়ে তৈরি করেছেন [@yoheinakajima](https://twitter.com/yoheinakajima), অনাকাঙ্ক্ষিতভাবে যিনি একজন ভিসি (আপনার সৃজনশীলতার বহিঃপ্রকাশ দেখতে আগ্রহী) From 7e7a4439bb954f0e408b67484965d8b8deed6bd1 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Wed, 7 Jun 2023 01:42:53 +0900 Subject: [PATCH 49/62] Update README.md continous -> continuous --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ede29bc2..a0c3cb3a 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ This README will cover the following: - [Supported Models](#supported-models) -- [Warning about running the script continuously](#continous-script-warning) +- [Warning about running the script continuously](#continuous-script-warning) # How It Works @@ -92,7 +92,7 @@ Llama integration requires llama-cpp package. You will also need the Llama model Once you have them, set LLAMA_MODEL_PATH to the path of the specific model to use. For convenience, you can link `models` in BabyAGI repo to the folder where you have the Llama model weights. Then run the script with `LLM_MODEL=llama` or `-l` argument. -# Warning +# Warning This script is designed to be run continuously as part of a task management system. Running this script continuously can result in high API usage, so please use it responsibly. Additionally, the script requires the OpenAI API to be set up correctly, so make sure you have set up the API before running the script. From cef1db8a260e9506edadf0fb0878bb2d9730499d Mon Sep 17 00:00:00 2001 From: Yohei Nakajima Date: Tue, 6 Jun 2023 22:06:19 -0700 Subject: [PATCH 50/62] Rename BabyBeeAGI to BabyBeeAGI.py --- classic/{BabyBeeAGI => BabyBeeAGI.py} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename classic/{BabyBeeAGI => BabyBeeAGI.py} (100%) diff --git a/classic/BabyBeeAGI b/classic/BabyBeeAGI.py similarity index 100% rename from classic/BabyBeeAGI rename to classic/BabyBeeAGI.py From 0cf0f7ec94bdc85001b15811ef498325bc940bc2 Mon Sep 17 00:00:00 2001 From: Yohei Nakajima Date: Tue, 6 Jun 2023 22:09:23 -0700 Subject: [PATCH 51/62] Create BabyDeerAGI.py BabyDeerAGI is a mod of BabyCatAGI, which is a mod of BabyBeeAGI, which is a mod of OG BabyAGI. BabyDeerAGI is at ~350 lines of code. This was built as a continued iteration on the original BabyAGI code in a lightweight way. New to BabyDeerAGI are: - only requires 3.5-turbo (better prompts) - parallel task runs when not dependent on each other - web search tool includes query rewrite - simple user input tool (ask user for input) Follow BabyAGI on Twitter: http://twitter.com/babyAGI_ Join the Discord: https://discord.gg/9RCW87R5QX --- classic/BabyDeerAGI.py | 354 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 354 insertions(+) create mode 100644 classic/BabyDeerAGI.py diff --git a/classic/BabyDeerAGI.py b/classic/BabyDeerAGI.py new file mode 100644 index 00000000..5de33555 --- /dev/null +++ b/classic/BabyDeerAGI.py @@ -0,0 +1,354 @@ +###### This is a modified version of OG BabyAGI, called BabyDeerAGI (modifications will follow the pattern "BabyAGI").###### +######IMPORTANT NOTE: I'm sharing this as a framework to build on top of (with lots of room for improvement), to facilitate discussion around how to improve these. This is NOT for people who are looking for a complete solution that's ready to use. ###### + +import openai +import time +from datetime import datetime +import requests +from bs4 import BeautifulSoup +from collections import deque +from typing import Dict, List +import re +import ast +import json +from serpapi import GoogleSearch +from concurrent.futures import ThreadPoolExecutor +import time + +### SET THESE 4 VARIABLES ############################## + +# Add your API keys here +OPENAI_API_KEY = "" +SERPAPI_API_KEY = "" #[optional] web-search becomes available automatically when serpapi api key is provided + +# Set variables +OBJECTIVE = "Research recent AI news and write a poem about your findings in the style of shakespeare." + +#turn on user input (change to "True" to turn on user input tool) +user_input=False + +### UP TO HERE ############################## + +# Configure OpenAI and SerpAPI client +openai.api_key = OPENAI_API_KEY +if SERPAPI_API_KEY: + serpapi_client = GoogleSearch({"api_key": SERPAPI_API_KEY}) + websearch_var = "[web-search] " +else: + websearch_var = "" + +if user_input == True: + user_input_var = "[user-input]" +else: + user_input_var = "" + + +# Initialize task list +task_list = [] + +# Initialize session_summary +session_summary = "OBJECTIVE: "+OBJECTIVE+"\n\n" + +### Task list functions ############################## +def get_task_by_id(task_id: int): + for task in task_list: + if task["id"] == task_id: + return task + return None + +# Print task list and session summary +def print_tasklist(): + p_tasklist="\033[95m\033[1m" + "\n*****TASK LIST*****\n" + "\033[0m" + for t in task_list: + dependent_task = "" + if t['dependent_task_ids']: + dependent_task = f"\033[31m\033[0m" + status_color = "\033[32m" if t['status'] == "complete" else "\033[31m" + p_tasklist+= f"\033[1m{t['id']}\033[0m: {t['task']} {status_color}[{t['status']}]\033[0m \033[93m[{t['tool']}] {dependent_task}\033[0m\n" + print(p_tasklist) + +### Tool functions ############################## +def text_completion_tool(prompt: str): + messages = [ + {"role": "user", "content": prompt} + ] + response = openai.ChatCompletion.create( + model="gpt-3.5-turbo", + messages=messages, + temperature=0.2, + max_tokens=1500, + top_p=1, + frequency_penalty=0, + presence_penalty=0 + ) + + return response.choices[0].message['content'].strip() + + +def user_input_tool(prompt: str): + val = input(f"\n{prompt}\nYour response: ") + return str(val) + + +def web_search_tool(query: str , dependent_tasks_output : str): + + if dependent_tasks_output != "": + dependent_task = f"Use the dependent task output below as reference to help craft the correct search query for the provided task above. Dependent task output:{dependent_tasks_output}." + else: + dependent_task = "." + query = text_completion_tool("You are an AI assistant tasked with generating a Google search query based on the following task: "+query+". If the task looks like a search query, return the identical search query as your response. " + dependent_task + "\nSearch Query:") + print("\033[90m\033[3m"+"Search query: " +str(query)+"\033[0m") + search_params = { + "engine": "google", + "q": query, + "api_key": SERPAPI_API_KEY, + "num":3 #edit this up or down for more results, though higher often results in OpenAI rate limits + } + search_results = GoogleSearch(search_params) + search_results = search_results.get_dict() + try: + search_results = search_results["organic_results"] + except: + search_results = {} + search_results = simplify_search_results(search_results) + print("\033[90m\033[3m" + "Completed search. Now scraping results.\n" + "\033[0m") + results = ""; + # Loop through the search results + for result in search_results: + # Extract the URL from the result + url = result.get('link') + # Call the web_scrape_tool function with the URL + print("\033[90m\033[3m" + "Scraping: "+url+"" + "...\033[0m") + content = web_scrape_tool(url, task) + print("\033[90m\033[3m" +str(content[0:100])[0:100]+"...\n" + "\033[0m") + results += str(content)+". " + + results = text_completion_tool(f"You are an expert analyst. Rewrite the following information as one report without removing any facts.\n###INFORMATION:{results}.\n###REPORT:") + return results + + +def simplify_search_results(search_results): + simplified_results = [] + for result in search_results: + simplified_result = { + "position": result.get("position"), + "title": result.get("title"), + "link": result.get("link"), + "snippet": result.get("snippet") + } + simplified_results.append(simplified_result) + return simplified_results + + +def web_scrape_tool(url: str, task:str): + content = fetch_url_content(url) + if content is None: + return None + + text = extract_text(content) + print("\033[90m\033[3m"+"Scrape completed. Length:" +str(len(text))+".Now extracting relevant info..."+"...\033[0m") + info = extract_relevant_info(OBJECTIVE, text[0:5000], task) + links = extract_links(content) + + #result = f"{info} URLs: {', '.join(links)}" + result = info + + return result + +headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36" +} + +def fetch_url_content(url: str): + try: + response = requests.get(url, headers=headers, timeout=10) + response.raise_for_status() + return response.content + except requests.exceptions.RequestException as e: + print(f"Error while fetching the URL: {e}") + return "" + +def extract_links(content: str): + soup = BeautifulSoup(content, "html.parser") + links = [link.get('href') for link in soup.findAll('a', attrs={'href': re.compile("^https?://")})] + return links + +def extract_text(content: str): + soup = BeautifulSoup(content, "html.parser") + text = soup.get_text(strip=True) + return text + + + +def extract_relevant_info(objective, large_string, task): + chunk_size = 3000 + overlap = 500 + notes = "" + + for i in range(0, len(large_string), chunk_size - overlap): + chunk = large_string[i:i + chunk_size] + + messages = [ + {"role": "system", "content": f"You are an AI assistant."}, + {"role": "user", "content": f"You are an expert AI research assistant tasked with creating or updating the current notes. If the current note is empty, start a current-notes section by exracting relevant data to the task and objective from the chunk of text to analyze. If there is a current note, add new relevant info frol the chunk of text to analyze. Make sure the new or combined notes is comprehensive and well written. Here's the current chunk of text to analyze: {chunk}. ### Here is the current task: {task}.### For context, here is the objective: {OBJECTIVE}.### Here is the data we've extraced so far that you need to update: {notes}.### new-or-updated-note:"} + ] + + response = openai.ChatCompletion.create( + model="gpt-3.5-turbo", + messages=messages, + max_tokens=800, + n=1, + stop="###", + temperature=0.7, + ) + + notes += response.choices[0].message['content'].strip()+". "; + + return notes + +### Agent functions ############################## + + +def execute_task(task, task_list, OBJECTIVE): + + global session_summary + global task_id_counter + # Check if dependent_task_ids is not empty + if task["dependent_task_ids"]: + all_dependent_tasks_complete = True + for dep_id in task["dependent_task_ids"]: + dependent_task = get_task_by_id(dep_id) + if not dependent_task or dependent_task["status"] != "complete": + all_dependent_tasks_complete = False + break + + + # Execute task + p_nexttask="\033[92m\033[1m"+"\n*****NEXT TASK ID:"+str(task['id'])+"*****\n"+"\033[0m\033[0m" + p_nexttask += str(task['id'])+": "+str(task['task'])+" ["+str(task['tool']+"]") + print(p_nexttask) + task_prompt = f"Complete your assigned task based on the objective and only based on information provided in the dependent task output, if provided. \n###\nYour objective: {OBJECTIVE}. \n###\nYour task: {task['task']}" + if task["dependent_task_ids"]: + dependent_tasks_output = "" + for dep_id in task["dependent_task_ids"]: + dependent_task_output = get_task_by_id(dep_id)["output"] + dependent_task_output = dependent_task_output[0:2000] + dependent_tasks_output += f" {dependent_task_output}" + task_prompt += f" \n###\ndependent tasks output: {dependent_tasks_output} \n###\nYour task: {task['task']}\n###\nRESPONSE:" + else: + dependent_tasks_output="." + + # Use tool to complete the task + if task["tool"] == "text-completion": + task_output = text_completion_tool(task_prompt) + elif task["tool"] == "web-search": + task_output = web_search_tool(str(task['task']),str(dependent_tasks_output)) + elif task["tool"] == "web-scrape": + task_output = web_scrape_tool(str(task['task'])) + elif task["tool"] == "user-input": + task_output = user_input_tool(str(task['task'])) + + + + # Find task index in the task_list + task_index = next((i for i, t in enumerate(task_list) if t["id"] == task["id"]), None) + + # Mark task as complete and save output + task_list[task_index]["status"] = "complete" + task_list[task_index]["output"] = task_output + + # Print task output + print("\033[93m\033[1m"+"\nTask Output (ID:"+str(task['id'])+"):"+"\033[0m\033[0m") + print(task_output) + # Add task output to session_summary + session_summary += f"\n\nTask {task['id']} - {task['task']}:\n{task_output}" + +def task_ready_to_run(task, task_list): + return all([get_task_by_id(dep_id)["status"] == "complete" for dep_id in task["dependent_task_ids"]]) + + +task_list = [] + +def task_creation_agent(objective: str) -> List[Dict]: + global task_list + minified_task_list = [{k: v for k, v in task.items() if k != "result"} for task in task_list] + + prompt = ( + f"You are an expert task creation AI tasked with creating a list of tasks as a JSON array, considering the ultimate objective of your team: {OBJECTIVE}. " + f"Create new tasks based on the objective. Limit tasks types to those that can be completed with the available tools listed below. Task description should be detailed." + f"Current tool options are [text-completion] {websearch_var} {user_input_var}." # web-search is added automatically if SERPAPI exists + f"For tasks using [web-search], provide the search query, and only the search query to use (eg. not 'research waterproof shoes, but 'waterproof shoes'). Result will be a summary of relevant information from the first few articles." + f"When requiring multiple searches, use the [web-search] multiple times. This tool will use the dependent task result to generate the search query if necessary." + f"Use [user-input] sparingly and only if you need to ask a question to the user who set up the objective. The task description should be the question you want to ask the user.')" + f"dependent_task_ids should always be an empty array, or an array of numbers representing the task ID it should pull results from." + f"Make sure all task IDs are in chronological order.\n" + f"EXAMPLE OBJECTIVE=Look up AI news from today (May 27, 2023) and write a poem." + "TASK LIST=[{\"id\":1,\"task\":\"AI news today\",\"tool\":\"web-search\",\"dependent_task_ids\":[],\"status\":\"incomplete\",\"result\":null,\"result_summary\":null},{\"id\":2,\"task\":\"Extract key points from AI news articles\",\"tool\":\"text-completion\",\"dependent_task_ids\":[1],\"status\":\"incomplete\",\"result\":null,\"result_summary\":null},{\"id\":3,\"task\":\"Generate a list of AI-related words and phrases\",\"tool\":\"text-completion\",\"dependent_task_ids\":[2],\"status\":\"incomplete\",\"result\":null,\"result_summary\":null},{\"id\":4,\"task\":\"Write a poem using AI-related words and phrases\",\"tool\":\"text-completion\",\"dependent_task_ids\":[3],\"status\":\"incomplete\",\"result\":null,\"result_summary\":null},{\"id\":5,\"task\":\"Final summary report\",\"tool\":\"text-completion\",\"dependent_task_ids\":[1,2,3,4],\"status\":\"incomplete\",\"result\":null,\"result_summary\":null}]" + f"OBJECTIVE={OBJECTIVE}" + f"TASK LIST=" + ) + + print("\033[90m\033[3m" + "\nInitializing...\n" + "\033[0m") + response = openai.ChatCompletion.create( + model="gpt-3.5-turbo", + messages=[ + { + "role": "system", + "content": "You are a task creation AI." + }, + { + "role": "user", + "content": prompt + } + ], + temperature=0, + max_tokens=1500, + top_p=1, + frequency_penalty=0, + presence_penalty=0 + ) + + # Extract the content of the assistant's response and parse it as JSON + result = response["choices"][0]["message"]["content"] + try: + task_list = json.loads(result) + except Exception as error: + print(error) + + return task_list + +##### START MAIN LOOP######## + +#Print OBJECTIVE +print("\033[96m\033[1m"+"\n*****OBJECTIVE*****\n"+"\033[0m\033[0m") +print(OBJECTIVE) + +# Initialize task_id_counter +task_id_counter = 1 + +# Run the task_creation_agent to create initial tasks +task_list = task_creation_agent(OBJECTIVE) +print_tasklist() + +# Create a ThreadPoolExecutor +with ThreadPoolExecutor() as executor: + while True: + tasks_submitted = False + for task in task_list: + if task["status"] == "incomplete" and task_ready_to_run(task, task_list): + future = executor.submit(execute_task, task, task_list, OBJECTIVE) + task["status"] = "running" + tasks_submitted = True + + if not tasks_submitted and all(task["status"] == "complete" for task in task_list): + break + + time.sleep(5) + +# Print session summary +print("\033[96m\033[1m"+"\n*****SAVING FILE...*****\n"+"\033[0m\033[0m") +file = open(f'output/output_{datetime.now().strftime("%d_%m_%Y_%H_%M_%S")}.txt', 'w') +file.write(session_summary) +file.close() +print("...file saved.") +print("END") From 6ec8a3c57698761a5e1a1494c10493502dacc0d1 Mon Sep 17 00:00:00 2001 From: Yohei Nakajima Date: Mon, 10 Jul 2023 09:30:22 -0700 Subject: [PATCH 52/62] Uploading BabyElfAGI --- classic/BabyElfAGI/main.py | 118 ++++++++ classic/BabyElfAGI/skills/code_reader.py | 79 +++++ .../BabyElfAGI/skills/directory_structure.py | 56 ++++ classic/BabyElfAGI/skills/objective_saver.py | 43 +++ classic/BabyElfAGI/skills/skill.py | 33 +++ classic/BabyElfAGI/skills/skill_registry.py | 57 ++++ classic/BabyElfAGI/skills/skill_saver.py | 58 ++++ classic/BabyElfAGI/skills/text_completion.py | 31 ++ classic/BabyElfAGI/skills/web_search.py | 151 ++++++++++ .../tasks/example_objectives/example1.json | 26 ++ .../tasks/example_objectives/example2.json | 33 +++ .../tasks/example_objectives/example3.json | 40 +++ .../tasks/example_objectives/example4.json | 54 ++++ .../tasks/example_objectives/example5.json | 26 ++ .../tasks/example_objectives/example6.json | 26 ++ classic/BabyElfAGI/tasks/task_registry.py | 269 ++++++++++++++++++ 16 files changed, 1100 insertions(+) create mode 100644 classic/BabyElfAGI/main.py create mode 100644 classic/BabyElfAGI/skills/code_reader.py create mode 100644 classic/BabyElfAGI/skills/directory_structure.py create mode 100644 classic/BabyElfAGI/skills/objective_saver.py create mode 100644 classic/BabyElfAGI/skills/skill.py create mode 100644 classic/BabyElfAGI/skills/skill_registry.py create mode 100644 classic/BabyElfAGI/skills/skill_saver.py create mode 100644 classic/BabyElfAGI/skills/text_completion.py create mode 100644 classic/BabyElfAGI/skills/web_search.py create mode 100644 classic/BabyElfAGI/tasks/example_objectives/example1.json create mode 100644 classic/BabyElfAGI/tasks/example_objectives/example2.json create mode 100644 classic/BabyElfAGI/tasks/example_objectives/example3.json create mode 100644 classic/BabyElfAGI/tasks/example_objectives/example4.json create mode 100644 classic/BabyElfAGI/tasks/example_objectives/example5.json create mode 100644 classic/BabyElfAGI/tasks/example_objectives/example6.json create mode 100644 classic/BabyElfAGI/tasks/task_registry.py diff --git a/classic/BabyElfAGI/main.py b/classic/BabyElfAGI/main.py new file mode 100644 index 00000000..452c58e5 --- /dev/null +++ b/classic/BabyElfAGI/main.py @@ -0,0 +1,118 @@ +import os +from dotenv import load_dotenv +import importlib.util +import json +import openai +import concurrent.futures +import time +from datetime import datetime +from skills.skill import Skill +from skills.skill_registry import SkillRegistry +from tasks.task_registry import TaskRegistry + + +load_dotenv() # Load environment variables from .env file + +# Retrieve all API keys +api_keys = { + 'openai': os.environ['OPENAI_API_KEY'], + 'serpapi': os.environ['SERPAPI_API_KEY'] + # Add more keys here as needed +} + +# Set OBJECTIVE +OBJECTIVE = "Create an example objective and tasklist for 'write a poem', which only uses text_completion in the tasks. Do this by usign code_reader to read example1.json, then writing the JSON objective tasklist pair using text_completion, and saving it using objective_saver." +LOAD_SKILLS = ['text_completion','code_reader','objective_saver'] +REFLECTION = False + +##### START MAIN LOOP######## + +# Print OBJECTIVE +print("\033[96m\033[1m"+"\n*****OBJECTIVE*****\n"+"\033[0m\033[0m") +print(OBJECTIVE) + +if __name__ == "__main__": + session_summary = "" + + # Initialize the SkillRegistry and TaskRegistry + skill_registry = SkillRegistry(api_keys=api_keys, skill_names=LOAD_SKILLS) + skill_descriptions = ",".join(f"[{skill.name}: {skill.description}]" for skill in skill_registry.skills.values()) + task_registry = TaskRegistry() + + # Create the initial task list based on an objective + task_registry.create_tasklist(OBJECTIVE, skill_descriptions) + + # Initialize task outputs + task_outputs = {i: {"completed": False, "output": None} for i, _ in enumerate(task_registry.get_tasks())} + + # Create a thread pool for parallel execution + with concurrent.futures.ThreadPoolExecutor() as executor: + # Loop until all tasks are completed + while not all(task["completed"] for task in task_outputs.values()): + + # Get the tasks that are ready to be executed (i.e., all their dependencies have been completed) + tasks = task_registry.get_tasks() + # Print the updated task list + task_registry.print_tasklist(tasks) + + # Update task_outputs to include new tasks + for task in tasks: + if task["id"] not in task_outputs: + task_outputs[task["id"]] = {"completed": False, "output": None} + + + ready_tasks = [(task["id"], task) for task in tasks + if all((dep in task_outputs and task_outputs[dep]["completed"]) + for dep in task.get('dependent_task_ids', [])) + and not task_outputs[task["id"]]["completed"]] + + session_summary += str(task)+"\n" + futures = [executor.submit(task_registry.execute_task, task_id, task, skill_registry, task_outputs, OBJECTIVE) + for task_id, task in ready_tasks if not task_outputs[task_id]["completed"]] + + # Wait for the tasks to complete + for future in futures: + i, output = future.result() + task_outputs[i]["output"] = output + task_outputs[i]["completed"] = True + + # Update the task in the TaskRegistry + task_registry.update_tasks({"id": i, "status": "completed", "result": output}) + + completed_task = task_registry.get_task(i) + print(f"\033[92mTask #{i}: {completed_task.get('task')} \033[0m\033[92m[COMPLETED]\033[0m\033[92m[{completed_task.get('skill')}]\033[0m") + + # Reflect on the output + if output: + session_summary += str(output)+"\n" + + + if REFLECTION == True: + new_tasks, insert_after_ids, tasks_to_update = task_registry.reflect_on_output(output, skill_descriptions) + # Insert new tasks + for new_task, after_id in zip(new_tasks, insert_after_ids): + task_registry.add_task(new_task, after_id) + + # Update existing tasks + for task_to_update in tasks_to_update: + task_registry.update_tasks(task_to_update) + + + + #print(task_outputs.values()) + if all(task["status"] == "completed" for task in task_registry.tasks): + print("All tasks completed!") + break + + # Short delay to prevent busy looping + time.sleep(0.1) + + + # Print session summary + print("\033[96m\033[1m"+"\n*****SAVING FILE...*****\n"+"\033[0m\033[0m") + file = open(f'output/output_{datetime.now().strftime("%d_%m_%Y_%H_%M_%S")}.txt', 'w') + file.write(session_summary) + file.close() + print("...file saved.") + print("END") + executor.shutdown() \ No newline at end of file diff --git a/classic/BabyElfAGI/skills/code_reader.py b/classic/BabyElfAGI/skills/code_reader.py new file mode 100644 index 00000000..11660eea --- /dev/null +++ b/classic/BabyElfAGI/skills/code_reader.py @@ -0,0 +1,79 @@ +from skills.skill import Skill +import openai +import os + +class CodeReader(Skill): + name = 'code_reader' + description = "A skill that finds a file's location in it's own program's directory and returns its contents." + api_keys_required = ['openai'] + + def __init__(self, api_keys): + super().__init__(api_keys) + + def execute(self, params, dependent_task_outputs, objective): + if not self.valid: + return + + dir_structure = self.get_directory_structure(self.get_top_parent_path(os.path.realpath(__file__))) + print(f"Directory structure: {dir_structure}") + example_dir_structure = {'.': {'main.py': None}, 'skills': {'__init__.py': None, 'web_scrape.py': None, 'skill.py': None, 'test_skill.py': None, 'text_completion.py': None, 'web_search.py': None, 'skill_registry.py': None, 'directory_structure.py': None, 'code_reader.py': None}, 'tasks': {'task_registry.py': None}, 'output': {}} + example_params = "Analyze main.py" + example_response = "main.py" + + task_prompt = f"Find a specific file in a directory and return only the file path, based on the task description below. Always return a directory.###The directory structure is as follows: \n{example_dir_structure}\nYour task: {example_params}\n###\nRESPONSE:{example_response} ###The directory structure is as follows: \n{dir_structure}\nYour task: {params}\n###\nRESPONSE:" + + messages = [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": task_prompt} + ] + response = openai.ChatCompletion.create( + model="gpt-3.5-turbo", + messages=messages, + temperature=0.2, + max_tokens=1500, + top_p=1, + frequency_penalty=0, + presence_penalty=0 + ) + file_path = response.choices[0].message['content'].strip() + print(f"AI suggested file path: {file_path}") + + try: + with open(file_path, 'r') as file: + file_content = file.read() + print(f"File content:\n{file_content}") + return file_content + except FileNotFoundError: + print("File not found. Please check the AI's suggested file path.") + return None + + def get_directory_structure(self, start_path): + dir_structure = {} + ignore_dirs = ['.','__init__.py', '__pycache__', 'pydevd', 'poetry','venv'] # add any other directories to ignore here + + for root, dirs, files in os.walk(start_path): + dirs[:] = [d for d in dirs if not any(d.startswith(i) for i in ignore_dirs)] # exclude specified directories + files = [f for f in files if not f[0] == '.' and f.endswith('.py')] # exclude hidden files and non-Python files + + current_dict = dir_structure + path_parts = os.path.relpath(root, start_path).split(os.sep) + for part in path_parts: + if part: # skip empty parts + if part not in current_dict: + current_dict[part] = {} + current_dict = current_dict[part] + for f in files: + current_dict[f] = None + + return dir_structure + + def get_top_parent_path(self, current_path): + relative_path = "" + while True: + new_path = os.path.dirname(current_path) + if new_path == '/home/runner/BabyElfAGI/skills': # reached the top + return '/home/runner/BabyElfAGI' + current_path = new_path + relative_path = os.path.join("..", relative_path) + + return relative_path diff --git a/classic/BabyElfAGI/skills/directory_structure.py b/classic/BabyElfAGI/skills/directory_structure.py new file mode 100644 index 00000000..5b071167 --- /dev/null +++ b/classic/BabyElfAGI/skills/directory_structure.py @@ -0,0 +1,56 @@ +from skills.skill import Skill +import os + +class DirectoryStructure(Skill): + name = 'directory_structure' + description = "A tool that outputs the file and folder structure of its top parent folder." + + def __init__(self, api_keys=None): + super().__init__(api_keys) + + def execute(self, params, dependent_task_outputs, objective): + # Get the current script path + current_script_path = os.path.realpath(__file__) + + # Get the top parent directory of current script + top_parent_path = self.get_top_parent_path(current_script_path) + # Get the directory structure from the top parent directory + dir_structure = self.get_directory_structure(top_parent_path) + + return dir_structure + + def get_directory_structure(self, start_path): + dir_structure = {} + ignore_dirs = ['.','__init__.py', '__pycache__', 'pydevd', 'poetry','venv'] # add any other directories to ignore here + + for root, dirs, files in os.walk(start_path): + dirs[:] = [d for d in dirs if not any(d.startswith(i) for i in ignore_dirs)] # exclude specified directories + files = [f for f in files if not f[0] == '.' and f.endswith('.py')] # exclude hidden files and non-Python files + + current_dict = dir_structure + path_parts = os.path.relpath(root, start_path).split(os.sep) + for part in path_parts: + if part: # skip empty parts + if part not in current_dict: + current_dict[part] = {} + current_dict = current_dict[part] + for f in files: + current_dict[f] = None + #print("#############################") + #print(str(current_dict)[0:100]) + return dir_structure + + + + def get_top_parent_path(self, current_path): + relative_path = "" + while True: + new_path = os.path.dirname(current_path) + print(new_path) + if new_path == '/home/runner/BabyElfAGI/skills': # reached the top + #if new_path == current_path: # reached the top + #return relative_path + return '/home/runner/BabyElfAGI' + current_path = new_path + relative_path = os.path.join("..", relative_path) + print(relative_path) \ No newline at end of file diff --git a/classic/BabyElfAGI/skills/objective_saver.py b/classic/BabyElfAGI/skills/objective_saver.py new file mode 100644 index 00000000..0c74f8fe --- /dev/null +++ b/classic/BabyElfAGI/skills/objective_saver.py @@ -0,0 +1,43 @@ +from skills.skill import Skill +import os +import openai + +class ObjectiveSaver(Skill): + name = 'objective_saver' + description = "A skill that saves a new example_objective based on the concepts from skill_saver.py" + api_keys_required = [] + + def __init__(self, api_keys): + super().__init__(api_keys) + + def execute(self, params, dependent_task_outputs, objective): + if not self.valid: + return + #print(dependent_task_outputs[2]) + code = dependent_task_outputs[2] + task_prompt = f"Come up with a file name (eg. 'research_shoes.json') for the following objective:{code}\n###\nFILE_NAME:" + + messages = [ + {"role": "user", "content": task_prompt} + ] + response = openai.ChatCompletion.create( + model="gpt-3.5-turbo", + messages=messages, + temperature=0.4, + max_tokens=3000, + top_p=1, + frequency_penalty=0, + presence_penalty=0 + ) + + file_name = response.choices[0].message['content'].strip() + file_path = os.path.join('tasks/example_objectives',file_name) + + try: + with open(file_path, 'w') as file: + file.write("["+code+"]") + print(f"Code saved successfully: {file_name}") + except: + print("Error saving code.") + + return None \ No newline at end of file diff --git a/classic/BabyElfAGI/skills/skill.py b/classic/BabyElfAGI/skills/skill.py new file mode 100644 index 00000000..5875b656 --- /dev/null +++ b/classic/BabyElfAGI/skills/skill.py @@ -0,0 +1,33 @@ +class Skill: + name = 'base skill' + description = 'This is the base skill.' + api_keys_required = [] + + def __init__(self, api_keys): + self.api_keys = api_keys + missing_keys = self.check_required_keys(api_keys) + if missing_keys: + print(f"Missing API keys for {self.name}: {missing_keys}") + self.valid = False + else: + self.valid = True + for key in self.api_keys_required: + if isinstance(key, list): + for subkey in key: + if subkey in api_keys: + setattr(self, f"{subkey}_api_key", api_keys.get(subkey)) + elif key in api_keys: + setattr(self, f"{key}_api_key", api_keys.get(key)) + + def check_required_keys(self, api_keys): + missing_keys = [] + for key in self.api_keys_required: + if isinstance(key, list): # If the key is actually a list of alternatives + if not any(k in api_keys for k in key): # If none of the alternatives are present + missing_keys.append(key) # Add the list of alternatives to the missing keys + elif key not in api_keys: # If the key is a single key and it's not present + missing_keys.append(key) # Add the key to the missing keys + return missing_keys + + def execute(self, params, dependent_task_outputs, objective): + raise NotImplementedError('Execute method must be implemented in subclass.') \ No newline at end of file diff --git a/classic/BabyElfAGI/skills/skill_registry.py b/classic/BabyElfAGI/skills/skill_registry.py new file mode 100644 index 00000000..0f06486d --- /dev/null +++ b/classic/BabyElfAGI/skills/skill_registry.py @@ -0,0 +1,57 @@ +import os +import importlib.util +import inspect +from .skill import Skill + +class SkillRegistry: + def __init__(self, api_keys, skill_names=None): + self.skills = {} + skill_files = [f for f in os.listdir('skills') if f.endswith('.py') and f != 'skill.py'] + for skill_file in skill_files: + module_name = skill_file[:-3] + if skill_names and module_name not in skill_names: + continue + module = importlib.import_module(f'skills.{module_name}') + for attr_name in dir(module): + attr_value = getattr(module, attr_name) + if inspect.isclass(attr_value) and issubclass(attr_value, Skill) and attr_value is not Skill: + skill = attr_value(api_keys) + if skill.valid: + self.skills[skill.name] = skill + # Print the names and descriptions of all loaded skills + skill_info = "\n".join([f"{skill_name}: {skill.description}" for skill_name, skill in self.skills.items()]) + # print(skill_info) + + def load_all_skills(self): + skills_dir = os.path.dirname(__file__) + for filename in os.listdir(skills_dir): + if filename.endswith(".py") and filename not in ["__init__.py", "skill.py", "skill_registry.py"]: + skill_name = filename[:-3] # Remove .py extension + self.load_skill(skill_name) + + def load_specific_skills(self, skill_names): + for skill_name in skill_names: + self.load_skill(skill_name) + + def load_skill(self, skill_name): + skills_dir = os.path.dirname(__file__) + filename = f"{skill_name}.py" + if os.path.isfile(os.path.join(skills_dir, filename)): + spec = importlib.util.spec_from_file_location(skill_name, os.path.join(skills_dir, filename)) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + for item_name in dir(module): + item = getattr(module, item_name) + if isinstance(item, type) and issubclass(item, Skill) and item is not Skill: + skill_instance = item(self.api_keys) + self.skills[skill_instance.name] = skill_instance + + def get_skill(self, skill_name): + skill = self.skills.get(skill_name) + if skill is None: + raise Exception( + f"Skill '{skill_name}' not found. Please make sure the skill is loaded and all required API keys are set.") + return skill + + def get_all_skills(self): + return self.skills \ No newline at end of file diff --git a/classic/BabyElfAGI/skills/skill_saver.py b/classic/BabyElfAGI/skills/skill_saver.py new file mode 100644 index 00000000..99be35c7 --- /dev/null +++ b/classic/BabyElfAGI/skills/skill_saver.py @@ -0,0 +1,58 @@ +from skills.skill import Skill +import os +import openai + +class SkillSaver(Skill): + name = 'skill_saver' + description = "A skill that saves code written in a previous step into a file within the skills folder. Not for writing code." + api_keys_required = [] + + def __init__(self, api_keys): + super().__init__(api_keys) + + def execute(self, params, dependent_task_outputs, objective): + if not self.valid: + return + + task_prompt = f"Extract the code and only the code from the dependent task output here: {dependent_task_outputs} \n###\nCODE:" + + messages = [ + {"role": "user", "content": task_prompt} + ] + response = openai.ChatCompletion.create( + model="gpt-3.5-turbo", + messages=messages, + temperature=0.4, + max_tokens=3000, + top_p=1, + frequency_penalty=0, + presence_penalty=0 + ) + + code = response.choices[0].message['content'].strip() + task_prompt = f"Come up with a file name (eg. 'get_weather.py') for the following skill:{code}\n###\nFILE_NAME:" + + messages = [ + {"role": "user", "content": task_prompt} + ] + response = openai.ChatCompletion.create( + model="gpt-3.5-turbo", + messages=messages, + temperature=0.4, + max_tokens=3000, + top_p=1, + frequency_penalty=0, + presence_penalty=0 + ) + + file_name = response.choices[0].message['content'].strip() + file_path = os.path.join('skills',file_name) + + try: + with open(file_path, 'w') as file: + file.write(code) + print(f"Code saved successfully: {file_name}") + except: + print("Error saving code.") + + return None \ No newline at end of file diff --git a/classic/BabyElfAGI/skills/text_completion.py b/classic/BabyElfAGI/skills/text_completion.py new file mode 100644 index 00000000..3eedfebd --- /dev/null +++ b/classic/BabyElfAGI/skills/text_completion.py @@ -0,0 +1,31 @@ +from skills.skill import Skill +import openai + +class TextCompletion(Skill): + name = 'text_completion' + description = "A tool that uses OpenAI's text completion API to generate, summarize, and/or analyze text and code." + api_keys_required = ['openai'] + + def __init__(self, api_keys): + super().__init__(api_keys) + + def execute(self, params, dependent_task_outputs, objective): + if not self.valid: + return + + task_prompt = f"Complete your assigned task based on the objective and only based on information provided in the dependent task output, if provided. \n###\nYour objective: {objective}. \n###\nYour task: {params} \n###\nDependent tasks output: {dependent_task_outputs} \n###\nYour task: {params}\n###\nRESPONSE:" + + messages = [ + {"role": "user", "content": task_prompt} + ] + response = openai.ChatCompletion.create( + model="gpt-4", + messages=messages, + temperature=0.4, + max_tokens=2000, + top_p=1, + frequency_penalty=0, + presence_penalty=0 + ) + + return "\n\n"+response.choices[0].message['content'].strip() diff --git a/classic/BabyElfAGI/skills/web_search.py b/classic/BabyElfAGI/skills/web_search.py new file mode 100644 index 00000000..ec4a2c14 --- /dev/null +++ b/classic/BabyElfAGI/skills/web_search.py @@ -0,0 +1,151 @@ + +from skills.skill import Skill +from serpapi import GoogleSearch +import openai +from bs4 import BeautifulSoup +import requests +import re + +headers = { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.81 Safari/537.36" +} + +class WebSearch(Skill): + name = 'web_search' + description = 'A tool that performs web searches.' + api_keys_required = [['openai'],['serpapi']] + + def __init__(self, api_keys): + super().__init__(api_keys) + + def execute(self, params, dependent_task_outputs, objective): + # Your function goes here + + + # Modify the query based on the dependent task output + if dependent_task_outputs != "": + dependent_task = f"Use the dependent task output below as reference to help craft the correct search query for the provided task above. Dependent task output:{dependent_task_outputs}." + else: + dependent_task = "." + query = self.text_completion_tool("You are an AI assistant tasked with generating a Google search query based on the following task: "+params+". If the task looks like a search query, return the identical search query as your response. " + dependent_task + "\nSearch Query:") + print("\033[90m\033[3m"+"Search query: " +str(query)+"\033[0m") + # Set the search parameters + search_params = { + "engine": "google", + "q": query, + "api_key": self.serpapi_api_key, + "num": 3 + } + # Perform the web search + search_results = GoogleSearch(search_params).get_dict() + + # Simplify the search results + search_results = self.simplify_search_results(search_results.get('organic_results', [])) + print("\033[90m\033[3mCompleted search. Now scraping results.\n\033[0m") + + # Store the results from web scraping + results = "" + for result in search_results: + url = result.get('link') + print("\033[90m\033[3m" + "Scraping: "+url+"" + "...\033[0m") + content = self.web_scrape_tool({"url": url, "task": params,"objective":objective}) + results += str(content) + ". " + print("\033[90m\033[3m"+str(results[0:100])[0:100]+"...\033[0m") + # Process the results and generate a report + results = self.text_completion_tool(f"You are an expert analyst combining the results of multiple web scrapes. Rewrite the following information as one cohesive report without removing any facts. Ignore any reports of not having info, unless all reports say so - in which case explain that the search did not work and suggest other web search queries to try. \n###INFORMATION:{results}.\n###REPORT:") + return results + + def simplify_search_results(self, search_results): + simplified_results = [] + for result in search_results: + simplified_result = { + "position": result.get("position"), + "title": result.get("title"), + "link": result.get("link"), + "snippet": result.get("snippet") + } + simplified_results.append(simplified_result) + return simplified_results + + + def text_completion_tool(self, prompt: str): + messages = [ + {"role": "user", "content": prompt} + ] + response = openai.ChatCompletion.create( + model="gpt-3.5-turbo-16k-0613", + messages=messages, + temperature=0.2, + max_tokens=1500, + top_p=1, + frequency_penalty=0, + presence_penalty=0 + ) + + return response.choices[0].message['content'].strip() + + + def web_scrape_tool(self, params): + content = self.fetch_url_content(params['url']) + if content is None: + return None + + text = self.extract_text(content) + print("\033[90m\033[3m"+"Scrape completed. Length:" +str(len(text))+".Now extracting relevant info..."+"...\033[0m") + info = self.extract_relevant_info(params['objective'], text[0:11000], params['task']) + links = self.extract_links(content) + #result = f"{info} URLs: {', '.join(links)}" + result = info + + return result + + def fetch_url_content(self,url: str): + try: + response = requests.get(url, headers=headers, timeout=10) + response.raise_for_status() + return response.content + except requests.exceptions.RequestException as e: + print(f"Error while fetching the URL: {e}") + return "" + + def extract_links(self,content: str): + soup = BeautifulSoup(content, "html.parser") + links = [link.get('href') for link in soup.findAll('a', attrs={'href': re.compile("^https?://")})] + return links + + def extract_text(self,content: str): + soup = BeautifulSoup(content, "html.parser") + text = soup.get_text(strip=True) + return text + + def extract_relevant_info(self, objective, large_string, task): + chunk_size = 12000 + overlap = 500 + notes = "" + + if len(large_string) == 0: + print("error scraping") + return "Error scraping." + + for i in range(0, len(large_string), chunk_size - overlap): + + print("\033[90m\033[3m"+"Reading chunk..."+"\033[0m") + chunk = large_string[i:i + chunk_size] + + messages = [ + {"role": "system", "content": f"You are an AI assistant."}, + {"role": "user", "content": f"You are an expert AI research assistant tasked with creating or updating the current notes. If the current note is empty, start a current-notes section by exracting relevant data to the task and objective from the chunk of text to analyze. If there is a current note, add new relevant info frol the chunk of text to analyze. Make sure the new or combined notes is comprehensive and well written. Here's the current chunk of text to analyze: {chunk}. ### Here is the current task: {task}.### For context, here is the objective: {objective}.### Here is the data we've extraced so far that you need to update: {notes}.### new-or-updated-note:"} + ] + + response = openai.ChatCompletion.create( + model="gpt-3.5-turbo-16k-0613", + messages=messages, + max_tokens=800, + n=1, + stop="###", + temperature=0.7, + ) + + notes += response.choices[0].message['content'].strip()+". "; + + return notes \ No newline at end of file diff --git a/classic/BabyElfAGI/tasks/example_objectives/example1.json b/classic/BabyElfAGI/tasks/example_objectives/example1.json new file mode 100644 index 00000000..64a39aa5 --- /dev/null +++ b/classic/BabyElfAGI/tasks/example_objectives/example1.json @@ -0,0 +1,26 @@ +[{ + "objective": "Create a new skill that writes a poem based on an input.", + "examples": [ + { + "id": 1, + "task": "Read the code in text_completion.py using the code_reader skill to understand its structure and concepts.", + "skill": "code_reader", + "dependent_task_ids": [], + "status": "incomplete" + }, + { + "id": 2, + "task": "Write a new skill that uses the concepts from text_completion.py to generate a poem based on user input.", + "skill": "text_completion", + "dependent_task_ids": [1], + "status": "incomplete" + }, + { + "id": 3, + "task": "Save the newly created skill using the skill_saver skill for future use.", + "skill": "skill_saver", + "dependent_task_ids": [2], + "status": "incomplete" + } + ] +}] \ No newline at end of file diff --git a/classic/BabyElfAGI/tasks/example_objectives/example2.json b/classic/BabyElfAGI/tasks/example_objectives/example2.json new file mode 100644 index 00000000..a9b93d67 --- /dev/null +++ b/classic/BabyElfAGI/tasks/example_objectives/example2.json @@ -0,0 +1,33 @@ +[{ + "objective": "Create a new skill that looks up the weather based on a location input.", + "examples": [ + { + "id": 1, + "task": "Search code examples for free weather APIs to gather information on how to retrieve weather data based on a location input.", + "skill": "web_search", + "dependent_task_ids": [], + "status": "incomplete" + }, + { + "id": 2, + "task": "Read the code in text_completion.py using the code_reader skill to understand its structure and concepts.", + "skill": "code_reader", + "dependent_task_ids": [], + "status": "incomplete" + }, + { + "id": 3, + "task": "Write a new skill that combines the concepts from text_completion.py and the code examples for free weather APIs to implement weather lookup based on a location input.", + "skill": "text_completion", + "dependent_task_ids": [2, 1], + "status": "incomplete" + }, + { + "id": 4, + "task": "Save the newly created skill using the skill_saver skill for future use.", + "skill": "skill_saver", + "dependent_task_ids": [3], + "status": "incomplete" + } + ] +}] \ No newline at end of file diff --git a/classic/BabyElfAGI/tasks/example_objectives/example3.json b/classic/BabyElfAGI/tasks/example_objectives/example3.json new file mode 100644 index 00000000..9fdcfdb5 --- /dev/null +++ b/classic/BabyElfAGI/tasks/example_objectives/example3.json @@ -0,0 +1,40 @@ +[{ + "objective": "Research untapped.vc", + "examples": [ + { + "id": 1, + "task": "Conduct a web search on 'untapped.vc' to gather information about the company, its investments, and its impact in the startup ecosystem.", + "skill": "web_search", + "dependent_task_ids": [], + "status": "incomplete" + }, + { + "id": 2, + "task": "Based on the results from the first web search, perform a follow-up web search to explore specific areas of interest or investment strategies of 'untapped.vc'.", + "skill": "web_search", + "dependent_task_ids": [1], + "status": "incomplete" + }, + { + "id": 3, + "task": "Use text_completion to summarize the findings from the initial web search on 'untapped.vc' and provide key insights.", + "skill": "text_completion", + "dependent_task_ids": [1], + "status": "incomplete" + }, + { + "id": 4, + "task": "Use text_completion to summarize the findings from the follow-up web search and highlight any additional information or insights.", + "skill": "text_completion", + "dependent_task_ids": [2], + "status": "incomplete" + }, + { + "id": 5, + "task": "Combine the summaries from the initial and follow-up web searches to provide a comprehensive overview of 'untapped.vc' and its activities.", + "skill": "text_completion", + "dependent_task_ids": [3, 4], + "status": "incomplete" + } + ] +}] \ No newline at end of file diff --git a/classic/BabyElfAGI/tasks/example_objectives/example4.json b/classic/BabyElfAGI/tasks/example_objectives/example4.json new file mode 100644 index 00000000..d7167763 --- /dev/null +++ b/classic/BabyElfAGI/tasks/example_objectives/example4.json @@ -0,0 +1,54 @@ +[{ + "objective": "Research Yohei Nakajima and Untapped Capital.", + "examples": [ + { + "id": 1, + "task": "Conduct a web search on Yohei Nakajima.", + "skill": "web_search", + "dependent_task_ids": [], + "status": "incomplete" + }, + { + "id": 2, + "task": "Conduct a follow-up web search on Yohei Nakajima.", + "skill": "web_search", + "dependent_task_ids": [1], + "status": "incomplete" + }, + { + "id": 3, + "task": "Conduct a web search on Untapped Capital", + "skill": "web_search", + "dependent_task_ids": [], + "status": "incomplete" + }, + { + "id": 4, + "task": "Conduct a follow-up web search on Untapped Capital", + "skill": "web_search", + "dependent_task_ids": [3], + "status": "incomplete" + }, + { + "id": 5, + "task": "Analyze the findings from the web search on Yohei Nakajima and summarize his key contributions and areas of expertise.", + "skill": "text_completion", + "dependent_task_ids": [1,2], + "status": "incomplete" + }, + { + "id": 6, + "task": "Analyze the findings from the web search on Untapped Capital and summarize their investment strategies and notable portfolio companies.", + "skill": "text_completion", + "dependent_task_ids": [3,4], + "status": "incomplete" + }, + { + "id": 7, + "task": "Combine the analyses of Yohei Nakajima and Untapped Capital to provide an overview of their collaboration or mutual interests.", + "skill": "text_completion", + "dependent_task_ids": [5, 6], + "status": "incomplete" + } + ] +}] \ No newline at end of file diff --git a/classic/BabyElfAGI/tasks/example_objectives/example5.json b/classic/BabyElfAGI/tasks/example_objectives/example5.json new file mode 100644 index 00000000..ff779161 --- /dev/null +++ b/classic/BabyElfAGI/tasks/example_objectives/example5.json @@ -0,0 +1,26 @@ +[{ + "objective": "Based on skill_saver.py, write a new skill called objective_saver.py which saves a new example_objective.", + "examples": [ + { + "id": 1, + "task": "Look up the code in skill_saver.py using the code_reader skill to understand its structure and concepts.", + "skill": "code_reader", + "dependent_task_ids": [], + "status": "incomplete" + }, + { + "id": 2, + "task": "Write a new skill called objective_saver.py that saves a new example_objective based on the concepts from skill_saver.py (use text_completion).", + "skill": "text_completion", + "dependent_task_ids": [1], + "status": "incomplete" + }, + { + "id": 3, + "task": "Save the newly created example_objective using the skill_saver.py skill for future use.", + "skill": "skill_saver", + "dependent_task_ids": [2], + "status": "incomplete" + } + ] +}] diff --git a/classic/BabyElfAGI/tasks/example_objectives/example6.json b/classic/BabyElfAGI/tasks/example_objectives/example6.json new file mode 100644 index 00000000..855cd34d --- /dev/null +++ b/classic/BabyElfAGI/tasks/example_objectives/example6.json @@ -0,0 +1,26 @@ +[{ + "objective": "Create new example objective for researching waterproof kid shoes.", + "examples": [ + { + "id": 1, + "task": "Read the contents of example1.json in the example_objectives folder using the code_reader skill.", + "skill": "code_reader", + "dependent_task_ids": [], + "status": "incomplete" + }, + { + "id": 2, + "task": "Use text_completion to generate a new example objective and task list as JSON based on the extracted information from example1.json.", + "skill": "text_completion", + "dependent_task_ids": [1], + "status": "incomplete" + }, + { + "id": 3, + "task": "Save the newly created example objective and task list as JSON using the objective_saver skill.", + "skill": "objective_saver", + "dependent_task_ids": [2], + "status": "incomplete" + } + ] +}] diff --git a/classic/BabyElfAGI/tasks/task_registry.py b/classic/BabyElfAGI/tasks/task_registry.py new file mode 100644 index 00000000..dba0f70a --- /dev/null +++ b/classic/BabyElfAGI/tasks/task_registry.py @@ -0,0 +1,269 @@ +import openai +import json +import threading +import os +import numpy as np + +class TaskRegistry: + def __init__(self): + self.tasks = [] + # Initialize the lock + self.lock = threading.Lock() + objectives_file_path = "tasks/example_objectives" + self.example_loader = ExampleObjectivesLoader(objectives_file_path) + + def load_example_objectives(self, user_objective): + return self.example_loader.load_example_objectives(user_objective) + + + def create_tasklist(self, objective, skill_descriptions): + #load most relevant object and tasklist from objectives_examples.json + example_objective, example_tasklist = self.load_example_objectives(objective) + + prompt = ( + f"You are an expert task list creation AI tasked with creating a list of tasks as a JSON array, considering the ultimate objective of your team: {objective}. " + f"Create a very short task list based on the objective, the final output of the last task will be provided back to the user. Limit tasks types to those that can be completed with the available skills listed below. Task description should be detailed.###" + f"AVAILABLE SKILLS: {skill_descriptions}.###" + f"RULES:" + f"Do not use skills that are not listed." + f"Always include one skill." + f"dependent_task_ids should always be an empty array, or an array of numbers representing the task ID it should pull results from." + f"Make sure all task IDs are in chronological order.###\n" + f"EXAMPLE OBJECTIVE={json.dumps(example_objective)}" + f"TASK LIST={json.dumps(example_tasklist)}" + f"OBJECTIVE={objective}" + f"TASK LIST=" + ) + + print("\033[90m\033[3m" + "\nInitializing...\n" + "\033[0m") + response = openai.ChatCompletion.create( + model="gpt-3.5-turbo-0613", + messages=[ + { + "role": "system", + "content": "You are a task creation AI." + }, + { + "role": "user", + "content": prompt + } + ], + temperature=0, + max_tokens=1500, + top_p=1, + frequency_penalty=0, + presence_penalty=0 + ) + + # Extract the content of the assistant's response and parse it as JSON + result = response["choices"][0]["message"]["content"] + try: + task_list = json.loads(result) + self.tasks = task_list + except Exception as error: + print(error) + + + def execute_task(self, i, task, skill_registry, task_outputs, objective): + p_nexttask="\033[92m\033[1m"+"\n*****NEXT TASK ID:"+str(task['id'])+"*****\n"+"\033[0m\033[0m" + p_nexttask += f"\033[ EExecuting task {task.get('id')}: {task.get('task')}) [{task.get('skill')}]\033[)" + print(p_nexttask) + # Retrieve the skill from the registry + skill = skill_registry.get_skill(task['skill']) + # Get the outputs of the dependent tasks + dependent_task_outputs = {dep: task_outputs[dep]["output"] for dep in task['dependent_task_ids']} if 'dependent_task_ids' in task else {} + # Execute the skill + # print("execute:"+str([task['task'], dependent_task_outputs, objective])) + task_output = skill.execute(task['task'], dependent_task_outputs, objective) + print("\033[93m\033[1m"+"\nTask Output (ID:"+str(task['id'])+"):"+"\033[0m\033[0m") + print("TASK: "+str(task["task"])) + print("OUTPUT: "+str(task_output)) + return i, task_output + + + def reorder_tasks(self): + self.tasks = sorted(self.tasks, key=lambda task: task['id']) + + + def add_task(self, task, after_task_id): + # Get the task ids + task_ids = [t["id"] for t in self.tasks] + + # Get the index of the task id to add the new task after + insert_index = task_ids.index(after_task_id) + 1 if after_task_id in task_ids else len(task_ids) + + # Insert the new task + self.tasks.insert(insert_index, task) + self.reorder_tasks() + + + def update_tasks(self, task_update): + for task in self.tasks: + if task['id'] == task_update['id']: + # This merges the original task dictionary with the update, overwriting only the fields present in the update. + task.update(task_update) + self.reorder_tasks() + + def reflect_on_output(self, task_output, skill_descriptions): + with self.lock: + example = [ + [ + {"id": 3, "task": "New task 1 description", "skill": "text_completion_skill", + "dependent_task_ids": [], "status": "complete"}, + {"id": 4, "task": "New task 2 description", "skill": "text_completion_skill", + "dependent_task_ids": [], "status": "incomplete"} + ], + [2, 3], + {"id": 5, "task": "Complete the objective and provide a final report", + "skill": "text_completion_skill", "dependent_task_ids": [1, 2, 3, 4], "status": "incomplete"} + ] + + prompt = ( + f"You are an expert task manager, review the task output to decide at least one new task to add." + f"As you add a new task, see if there are any tasks that need to be updated (such as updating dependencies)." + f"Use the current task list as reference." + f"Do not add duplicate tasks to those in the current task list." + f"Only provide JSON as your response without further comments." + f"Every new and updated task must include all variables, even they are empty array." + f"Dependent IDs must be smaller than the ID of the task." + f"New tasks IDs should be no larger than the last task ID." + f"Always select at least one skill." + f"Task IDs should be unique and in chronological order." f"Do not change the status of complete tasks." + f"Only add skills from the AVAILABLE SKILLS, using the exact same spelling." + f"Provide your array as a JSON array with double quotes. The first object is new tasks to add as a JSON array, the second array lists the ID numbers where the new tasks should be added after (number of ID numbers matches array), and the third object provides the tasks that need to be updated." + f"Make sure to keep dependent_task_ids key, even if an empty array." + f"AVAILABLE SKILLS: {skill_descriptions}.###" + f"\n###Here is the last task output: {task_output}" + f"\n###Here is the current task list: {self.tasks}" + f"\n###EXAMPLE OUTPUT FORMAT = {json.dumps(example)}" + f"\n###OUTPUT = " + ) + print("\033[90m\033[3m" + "\nReflecting on task output to generate new tasks if necessary...\n" + "\033[0m") + response = openai.ChatCompletion.create( + model="gpt-3.5-turbo-16k-0613", + messages=[ + { + "role": "system", + "content": "You are a task creation AI." + }, + { + "role": "user", + "content": prompt + } + ], + temperature=0.7, + max_tokens=1500, + top_p=1, + frequency_penalty=0, + presence_penalty=0 + ) + + # Extract the content of the assistant's response and parse it as JSON + result = response["choices"][0]["message"]["content"] + print("\n#" + str(result)) + + # Check if the returned result has the expected structure + if isinstance(result, str): + try: + task_list = json.loads(result) + # print("RESULT:") + + print(task_list) + # return [],[],[] + return task_list[0], task_list[1], task_list[2] + except Exception as error: + print(error) + + else: + raise ValueError("Invalid task list structure in the output") + + def get_tasks(self): + """ + Returns the current list of tasks. + + Returns: + list: the list of tasks. + """ + return self.tasks + + def get_task(self, task_id): + """ + Returns a task given its task_id. + + Parameters: + task_id : int + The unique ID of the task. + + Returns: + dict + The task that matches the task_id. + """ + matching_tasks = [task for task in self.tasks if task["id"] == task_id] + + if matching_tasks: + return matching_tasks[0] + else: + print(f"No task found with id {task_id}") + return None + + def print_tasklist(self, task_list): + p_tasklist="\033[95m\033[1m" + "\n*****TASK LIST*****\n" + "\033[0m" + for t in task_list: + dependent_task_ids = t.get('dependent_task_ids', []) + dependent_task = "" + if dependent_task_ids: + dependent_task = f"\033[31m\033[0m" + status_color = "\033[32m" if t.get('status') == "completed" else "\033[31m" + p_tasklist+= f"\033[1m{t.get('id')}\033[0m: {t.get('task')} {status_color}[{t.get('status')}]\033[0m \033[93m[{t.get('skill')}] {dependent_task}\033[0m\n" + print(p_tasklist) + + + +class ExampleObjectivesLoader: + def __init__(self, objectives_folder_path): + self.objectives_folder_path = objectives_folder_path + self.objectives_examples = [] # Initialize as an empty list + + def load_objectives_examples(self): + self.objectives_examples = [] + for filename in os.listdir(self.objectives_folder_path): + file_path = os.path.join(self.objectives_folder_path, filename) + with open(file_path, 'r') as file: + objectives = json.load(file) + self.objectives_examples.extend(objectives) + + + def find_most_relevant_objective(self, user_input): + user_input_embedding = self.get_embedding(user_input, model='text-embedding-ada-002') + most_relevant_objective = max( + self.objectives_examples, + key=lambda pair: self.cosine_similarity(pair['objective'], user_input_embedding) + ) + return most_relevant_objective['objective'], most_relevant_objective['examples'] + + + def get_embedding(self, text, model='text-embedding-ada-002'): + response = openai.Embedding.create(input=[text], model=model) + embedding = response['data'][0]['embedding'] + return embedding + + def cosine_similarity(self, objective, embedding): + max_similarity = float('-inf') + objective_embedding = self.get_embedding(objective, model='text-embedding-ada-002') + similarity = self.calculate_similarity(objective_embedding, embedding) + max_similarity = max(max_similarity, similarity) + return max_similarity + + def calculate_similarity(self, embedding1, embedding2): + embedding1 = np.array(embedding1, dtype=np.float32) + embedding2 = np.array(embedding2, dtype=np.float32) + similarity = np.dot(embedding1, embedding2) / (np.linalg.norm(embedding1) * np.linalg.norm(embedding2)) + return similarity + + def load_example_objectives(self, user_objective): + self.load_objectives_examples() + most_relevant_objective, most_relevant_tasklist = self.find_most_relevant_objective(user_objective) + example_objective = most_relevant_objective + example_tasklist = most_relevant_tasklist + return example_objective, example_tasklist + \ No newline at end of file From 0044154ca14505d52130578540c709e74e0344d7 Mon Sep 17 00:00:00 2001 From: Yohei Nakajima Date: Mon, 10 Jul 2023 17:04:24 -0700 Subject: [PATCH 53/62] Reverting text_completion in BabyElfAGI to gpt3.5 --- classic/BabyElfAGI/skills/text_completion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classic/BabyElfAGI/skills/text_completion.py b/classic/BabyElfAGI/skills/text_completion.py index 3eedfebd..8f678749 100644 --- a/classic/BabyElfAGI/skills/text_completion.py +++ b/classic/BabyElfAGI/skills/text_completion.py @@ -19,7 +19,7 @@ def execute(self, params, dependent_task_outputs, objective): {"role": "user", "content": task_prompt} ] response = openai.ChatCompletion.create( - model="gpt-4", + model="gpt-3.5", messages=messages, temperature=0.4, max_tokens=2000, From d10b08ceea3314ac86e3832c0a9045c663a73a7c Mon Sep 17 00:00:00 2001 From: Yohei Nakajima Date: Mon, 10 Jul 2023 17:06:22 -0700 Subject: [PATCH 54/62] Reverting text_completion in BabyElfAGI to gpt3.5 --- classic/BabyElfAGI/skills/text_completion.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/classic/BabyElfAGI/skills/text_completion.py b/classic/BabyElfAGI/skills/text_completion.py index 8f678749..6abcd276 100644 --- a/classic/BabyElfAGI/skills/text_completion.py +++ b/classic/BabyElfAGI/skills/text_completion.py @@ -19,7 +19,7 @@ def execute(self, params, dependent_task_outputs, objective): {"role": "user", "content": task_prompt} ] response = openai.ChatCompletion.create( - model="gpt-3.5", + model="gpt-3.5-turbo", messages=messages, temperature=0.4, max_tokens=2000, From 12185722138e8316b1e3d10cf71d061e8ec4189f Mon Sep 17 00:00:00 2001 From: Tony Zhou Date: Sat, 19 Aug 2023 15:42:32 -0500 Subject: [PATCH 55/62] Fix typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a0c3cb3a..0c3e4539 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ To use the script, you will need to follow these steps: 1. Clone the repository via `git clone https://github.com/yoheinakajima/babyagi.git` and `cd` into the cloned repository. 2. Install the required packages: `pip install -r requirements.txt` 3. Copy the .env.example file to .env: `cp .env.example .env`. This is where you will set the following variables. -4. Set your OpenAI API key in the OPENAI_API_KEY and OPENAPI_API_MODEL variables. In order to use with Weaviate you will also need to setup additional variables detailed [here](docs/weaviate.md). +4. Set your OpenAI API key in the OPENAI_API_KEY and OPENAI_API_MODEL variables. In order to use with Weaviate you will also need to setup additional variables detailed [here](docs/weaviate.md). 5. Set the name of the table where the task results will be stored in the TABLE_NAME variable. 6. (Optional) Set the name of the BabyAGI instance in the BABY_NAME variable. 7. (Optional) Set the objective of the task management system in the OBJECTIVE variable. From 291fd930e51ada06cd29058c22ec506b8328b6b2 Mon Sep 17 00:00:00 2001 From: Tony Zhou Date: Sat, 19 Aug 2023 16:03:59 -0500 Subject: [PATCH 56/62] Update Ray version --- extensions/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/requirements.txt b/extensions/requirements.txt index 5aa6cbde..9aaa511f 100644 --- a/extensions/requirements.txt +++ b/extensions/requirements.txt @@ -1,4 +1,4 @@ -ray==2.3.1 +ray==2.6.3 pinecone-client==2.2.1 llama-cpp-python>=0.1.35 weaviate-client>=3.16.1 \ No newline at end of file From 464a824331126916483569c5ce830be398f250de Mon Sep 17 00:00:00 2001 From: Tony Zhou Date: Sat, 19 Aug 2023 16:27:50 -0500 Subject: [PATCH 57/62] Update chromadb usage --- babyagi.py | 3 +-- requirements.txt | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/babyagi.py b/babyagi.py index 709466c6..2650e412 100755 --- a/babyagi.py +++ b/babyagi.py @@ -192,9 +192,8 @@ def __init__(self): logging.getLogger('chromadb').setLevel(logging.ERROR) # Create Chroma collection chroma_persist_dir = "chroma" - chroma_client = chromadb.Client( + chroma_client = chromadb.PersistentClient( settings=chromadb.config.Settings( - chroma_db_impl="duckdb+parquet", persist_directory=chroma_persist_dir, ) ) diff --git a/requirements.txt b/requirements.txt index d700753f..db526cfa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,6 @@ argparse==1.4.0 openai==0.27.4 -chromadb==0.3.21 +chromadb==0.4.6 pre-commit>=3.2.0 python-dotenv==1.0.0 tiktoken==0.3.3 From 4569caf979ff27e405353272c0d023709a98f9fe Mon Sep 17 00:00:00 2001 From: Yohei Nakajima Date: Fri, 1 Sep 2023 01:35:12 -0700 Subject: [PATCH 58/62] Add files via upload --- classic/babyfoxagi/README.md | 83 ++ classic/babyfoxagi/babyagi.py | 140 +++ classic/babyfoxagi/forever_cache.ndjson | 1 + classic/babyfoxagi/main.py | 309 ++++++ classic/babyfoxagi/ongoing_tasks.py | 1 + classic/babyfoxagi/overall_summary.ndjson | 0 classic/babyfoxagi/poetry.lock | 886 ++++++++++++++++++ classic/babyfoxagi/public/static/chat.js | 279 ++++++ classic/babyfoxagi/public/static/style.css | 97 ++ classic/babyfoxagi/pyproject.toml | 28 + classic/babyfoxagi/skills/__init__.py | 0 .../__pycache__/__init__.cpython-38.pyc | Bin 0 -> 131 bytes .../airtable_search.cpython-310.pyc | Bin 0 -> 4122 bytes .../airtable_search.cpython-38.pyc | Bin 0 -> 4168 bytes .../__pycache__/call_babyagi.cpython-310.pyc | Bin 0 -> 1718 bytes .../__pycache__/code_reader.cpython-310.pyc | Bin 0 -> 3675 bytes .../__pycache__/code_reader.cpython-38.pyc | Bin 0 -> 3654 bytes .../create_google_slides.cpython-38.pyc | Bin 0 -> 1888 bytes .../crunchbase_search.cpython-310.pyc | Bin 0 -> 2615 bytes .../crunchbase_search.cpython-38.pyc | Bin 0 -> 3166 bytes .../directory_structure.cpython-310.pyc | Bin 0 -> 2419 bytes .../documentation_search.cpython-310.pyc | Bin 0 -> 6452 bytes .../skills/__pycache__/drawing.cpython-38.pyc | Bin 0 -> 1309 bytes .../game_generation.cpython-38.pyc | Bin 0 -> 1651 bytes .../google_jobs_api_search.cpython-310.pyc | Bin 0 -> 3298 bytes .../google_jobs_api_search.cpython-38.pyc | Bin 0 -> 3288 bytes .../image_generation.cpython-38.pyc | Bin 0 -> 3417 bytes .../__pycache__/news_search.cpython-310.pyc | Bin 0 -> 1628 bytes .../objective_saver.cpython-310.pyc | Bin 0 -> 1678 bytes .../__pycache__/play_music.cpython-38.pyc | Bin 0 -> 3957 bytes .../producthunt_search.cpython-310.pyc | Bin 0 -> 1774 bytes .../skills/__pycache__/skill.cpython-310.pyc | Bin 0 -> 1515 bytes .../skills/__pycache__/skill.cpython-38.pyc | Bin 0 -> 1569 bytes .../skill_registry.cpython-310.pyc | Bin 0 -> 4214 bytes .../__pycache__/skill_registry.cpython-38.pyc | Bin 0 -> 4358 bytes .../__pycache__/skill_saver.cpython-310.pyc | Bin 0 -> 1831 bytes .../startup_analysis.cpython-38.pyc | Bin 0 -> 3508 bytes .../text_completion.cpython-310.pyc | Bin 0 -> 1548 bytes .../text_completion.cpython-38.pyc | Bin 0 -> 1534 bytes .../__pycache__/web_search.cpython-310.pyc | Bin 0 -> 6292 bytes .../__pycache__/web_search.cpython-38.pyc | Bin 0 -> 6313 bytes classic/babyfoxagi/skills/airtable_search.py | 123 +++ classic/babyfoxagi/skills/call_babyagi.py | 41 + classic/babyfoxagi/skills/code_reader.py | 79 ++ .../babyfoxagi/skills/directory_structure.py | 56 ++ .../babyfoxagi/skills/documentation_search.py | 151 +++ classic/babyfoxagi/skills/drawing.py | 31 + .../skills/google_jobs_api_search.py | 83 ++ classic/babyfoxagi/skills/image_generation.py | 84 ++ classic/babyfoxagi/skills/play_music.py | 102 ++ classic/babyfoxagi/skills/skill.py | 36 + classic/babyfoxagi/skills/skill_registry.py | 107 +++ classic/babyfoxagi/skills/skill_saver.py | 58 ++ classic/babyfoxagi/skills/startup_analysis.py | 105 +++ classic/babyfoxagi/skills/text_completion.py | 31 + classic/babyfoxagi/skills/web_search.py | 155 +++ classic/babyfoxagi/tasks/__init__.py | 0 .../tasks/__pycache__/__init__.cpython-38.pyc | Bin 0 -> 130 bytes .../__pycache__/task_registry.cpython-310.pyc | Bin 0 -> 16913 bytes .../__pycache__/task_registry.cpython-38.pyc | Bin 0 -> 16655 bytes .../create_poemskill_and_use.json | 26 + .../tasks/example_objectives/example1.json | 26 + .../tasks/example_objectives/example2.json | 33 + .../tasks/example_objectives/example3.json | 40 + .../tasks/example_objectives/example5.json | 26 + .../tasks/example_objectives/example6.json | 19 + .../waterproof_kid_shoes_research.json | 42 + classic/babyfoxagi/tasks/task_registry.py | 514 ++++++++++ classic/babyfoxagi/templates/index.html | 36 + 69 files changed, 3828 insertions(+) create mode 100644 classic/babyfoxagi/README.md create mode 100644 classic/babyfoxagi/babyagi.py create mode 100644 classic/babyfoxagi/forever_cache.ndjson create mode 100644 classic/babyfoxagi/main.py create mode 100644 classic/babyfoxagi/ongoing_tasks.py create mode 100644 classic/babyfoxagi/overall_summary.ndjson create mode 100644 classic/babyfoxagi/poetry.lock create mode 100644 classic/babyfoxagi/public/static/chat.js create mode 100644 classic/babyfoxagi/public/static/style.css create mode 100644 classic/babyfoxagi/pyproject.toml create mode 100644 classic/babyfoxagi/skills/__init__.py create mode 100644 classic/babyfoxagi/skills/__pycache__/__init__.cpython-38.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/airtable_search.cpython-310.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/airtable_search.cpython-38.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/call_babyagi.cpython-310.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/code_reader.cpython-310.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/code_reader.cpython-38.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/create_google_slides.cpython-38.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/crunchbase_search.cpython-310.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/crunchbase_search.cpython-38.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/directory_structure.cpython-310.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/documentation_search.cpython-310.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/drawing.cpython-38.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/game_generation.cpython-38.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/google_jobs_api_search.cpython-310.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/google_jobs_api_search.cpython-38.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/image_generation.cpython-38.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/news_search.cpython-310.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/objective_saver.cpython-310.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/play_music.cpython-38.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/producthunt_search.cpython-310.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/skill.cpython-310.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/skill.cpython-38.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/skill_registry.cpython-310.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/skill_registry.cpython-38.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/skill_saver.cpython-310.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/startup_analysis.cpython-38.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/text_completion.cpython-310.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/text_completion.cpython-38.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/web_search.cpython-310.pyc create mode 100644 classic/babyfoxagi/skills/__pycache__/web_search.cpython-38.pyc create mode 100644 classic/babyfoxagi/skills/airtable_search.py create mode 100644 classic/babyfoxagi/skills/call_babyagi.py create mode 100644 classic/babyfoxagi/skills/code_reader.py create mode 100644 classic/babyfoxagi/skills/directory_structure.py create mode 100644 classic/babyfoxagi/skills/documentation_search.py create mode 100644 classic/babyfoxagi/skills/drawing.py create mode 100644 classic/babyfoxagi/skills/google_jobs_api_search.py create mode 100644 classic/babyfoxagi/skills/image_generation.py create mode 100644 classic/babyfoxagi/skills/play_music.py create mode 100644 classic/babyfoxagi/skills/skill.py create mode 100644 classic/babyfoxagi/skills/skill_registry.py create mode 100644 classic/babyfoxagi/skills/skill_saver.py create mode 100644 classic/babyfoxagi/skills/startup_analysis.py create mode 100644 classic/babyfoxagi/skills/text_completion.py create mode 100644 classic/babyfoxagi/skills/web_search.py create mode 100644 classic/babyfoxagi/tasks/__init__.py create mode 100644 classic/babyfoxagi/tasks/__pycache__/__init__.cpython-38.pyc create mode 100644 classic/babyfoxagi/tasks/__pycache__/task_registry.cpython-310.pyc create mode 100644 classic/babyfoxagi/tasks/__pycache__/task_registry.cpython-38.pyc create mode 100644 classic/babyfoxagi/tasks/example_objectives/create_poemskill_and_use.json create mode 100644 classic/babyfoxagi/tasks/example_objectives/example1.json create mode 100644 classic/babyfoxagi/tasks/example_objectives/example2.json create mode 100644 classic/babyfoxagi/tasks/example_objectives/example3.json create mode 100644 classic/babyfoxagi/tasks/example_objectives/example5.json create mode 100644 classic/babyfoxagi/tasks/example_objectives/example6.json create mode 100644 classic/babyfoxagi/tasks/example_objectives/waterproof_kid_shoes_research.json create mode 100644 classic/babyfoxagi/tasks/task_registry.py create mode 100644 classic/babyfoxagi/templates/index.html diff --git a/classic/babyfoxagi/README.md b/classic/babyfoxagi/README.md new file mode 100644 index 00000000..4b58a84e --- /dev/null +++ b/classic/babyfoxagi/README.md @@ -0,0 +1,83 @@ +### Author's Note + +BabyFoxAGI is the 5th mod of BabyAGI. The earlier 4 were [BabyBeeAGI](https://twitter.com/yoheinakajima/status/1652732735344246784?lang=en), [BabyCatAGI](https://twitter.com/yoheinakajima/status/1657448504112091136), [BabyDeerAGI](https://twitter.com/yoheinakajima/status/1666313838868992001), and [BabyElfAGI](https://twitter.com/yoheinakajima/status/1678443482866933760). Following the evolution will be the easiest way to understand BabyFoxAGI. + +### New Features in BabyFoxAGI + +In BabyFoxAGI, the two newest features are: + +1. **Self-Improvement (Also known as [FOXY Method](https://twitter.com/yoheinakajima/status/1685894298536148992))**: This helps it improve its task list building. +2. **[BabyAGI Experimental UI](https://twitter.com/yoheinakajima/status/1693153307454546331)**: In this feature, the chat is separated from task/output. + +Notable in the chat is the ability to either run one skill quickly or generate a task list and chain skills, where the you see BabyAGI (moved to babyagi.py) comes in. main.py is now the back-end to the Python Flask based chat app (public/templates folder). + +### Known Issues and Limitations + +I had issues with parallel tasks within BabyAGI, so removed that for now. I'm also not streaming the task list or in-between work from these task list runs to the UI. For now, you'll have to monitor that in the console. And in general, lots more room for improvement... but wanted to get this out there :) + + +# BabyFoxAGI - Overview + +BabyFoxAGI is an experimental chat-based UI that can use a variety of skills to accomplish tasks, displayed in a separate panel from the Chat UI, allowing for parallel execution of tasks. Tasks can be accomplished quickly using one skill, or by generating a tasklist and chaining multiple tasks/skills together. + +## Skills + +Skills that are included include text_completion, web_search, drawing (uses html canvas), documentation_search, code_reader, skill_saver, airtable_search, and call_babyagi. Please read through each skill to understand them better. + +## Components + +The project consists mainly of two Python scripts (`main.py` and `babyagi.py`) and a client-side JavaScript file (`Chat.js`), along with an HTML layout (`index.html`). + +### main.py + +#### Role +Acts as the entry point for the Flask web application and handles routes, API calls, and ongoing tasks. + +#### Key Features +- Flask routes for handling HTTP requests. +- Integration with OpenAI's API for text summarization and skill execution. +- Management of ongoing tasks and their statuses. + +### Chat.js + +#### Role +Handles the client-side interaction within the web interface, including capturing user input and displaying messages and task statuses. + +#### Key Features +- Dynamic chat interface for user interaction. +- HTTP requests to the Flask backend for task initiation and status checks. +- Presentation layer for task status and results. + +### index.html + +#### Role +Provides the layout for the web interface, including a chat box for user interaction and an objectives box for task display. + +#### Key Features +- HTML layout that accommodates the chat box and objectives box side-by-side. + +### babyagi.py + +#### Role +Acts as the central orchestrator for task execution, coordinating with various skills to accomplish a predefined objective. + +#### Key Features +- Task and skill registries to manage the execution. +- Main execution loop that iteratively performs tasks based on dependencies and objectives. +- Optional feature to reflect on task outputs and potentially generate new tasks. + +## Flow of Execution + +1. The user interacts with the chat interface, sending commands or inquiries. +2. `main.py` receives these requests and uses OpenAI's API to determine the next steps, which could include executing a skill or creating a task list. +3. If tasks are to be executed, `main.py` delegates to `babyagi.py`. +4. `babyagi.py` uses its main execution loop to perform tasks in the required sequence, based on dependencies and the main objective. +5. The output or status of each task is sent back to the client-side via Flask routes, and displayed using `Chat.js`. + +## Notes + +- The system leverages `.env` for API key management. +- `.ndjson` files are used for persistent storage of chat and task statuses. +- There is an optional `REFLECTION` feature in `babyagi.py` that allows the system to reflect on task outputs and potentially generate new tasks. + +This overview provides a comprehensive look into the functionalities and execution flow of the project, offering both high-level insights and low-level details. diff --git a/classic/babyfoxagi/babyagi.py b/classic/babyfoxagi/babyagi.py new file mode 100644 index 00000000..aa230357 --- /dev/null +++ b/classic/babyfoxagi/babyagi.py @@ -0,0 +1,140 @@ +import os +from dotenv import load_dotenv +import time +from datetime import datetime +from skills.skill_registry import SkillRegistry +from tasks.task_registry import TaskRegistry +from ongoing_tasks import ongoing_tasks + +load_dotenv() # Load environment variables from .env file + +api_keys = { + 'openai': os.getenv('OPENAI_API_KEY'), + 'serpapi': os.getenv('SERPAPI_API_KEY') + #'airtable': os.getenv('AIRTABLE_API_KEY') +} + +OBJECTIVE = "Research Yohei Nakajima and write a poem about him." +LOAD_SKILLS = ['web_search', 'text_completion', 'code_reader','google_jobs_api_search','image_generation','startup_analysis','play_music','game_generation'] +#add web_search and documentation_search after you add SERPAPI_API_KEY in your secrets. airtable_search once you've added your AIRTABLE_API_KEY, and add base/table/column data to airtable_search.py, etc... +REFLECTION = False #Experimental reflection step between each task run (when running tasklist) + +def run_single_task(task_id, task, skill_registry, task_outputs, OBJECTIVE, task_registry): + """Execute a single task and update its status""" + task_output = task_registry.execute_task(task_id, task, skill_registry, task_outputs, OBJECTIVE) + + task_outputs[task_id]["output"] = task_output + task_outputs[task_id]["completed"] = True + task_outputs[task_id]["description"] = task.get('description', 'No description available') + task_outputs[task_id]["skill"] = task.get('skill', 'No skill information available') + + if task_output: + task_registry.update_tasks({"id": task_id, "status": "completed", "result": task_output}) + + completed_task = task_registry.get_task(task_id) + print(f"Task #{task_id}: {completed_task.get('task')} [COMPLETED][{completed_task.get('skill')}]") + + if REFLECTION: + new_tasks, insert_after_ids, tasks_to_update = task_registry.reflect_on_output(task_output, skill_descriptions) + for new_task, after_id in zip(new_tasks, insert_after_ids): + task_registry.add_task(new_task, after_id) + + if isinstance(tasks_to_update, dict) and tasks_to_update: + tasks_to_update = [tasks_to_update] + + for task_to_update in tasks_to_update: + task_registry.update_tasks(task_to_update) + + + + +def run_main_loop(OBJECTIVE, LOAD_SKILLS, api_keys, REFLECTION=False): + """Main execution loop""" + try: + skill_descriptions = ",".join(f"[{skill.name}: {skill.description}]" for skill in global_skill_registry.skills.values()) + task_registry = TaskRegistry() + task_registry.create_tasklist(OBJECTIVE, skill_descriptions) + + skill_names = [skill.name for skill in global_skill_registry.skills.values()] + session_summary = f"OBJECTIVE:{OBJECTIVE}.#SKILLS:{','.join(skill_names)}.#" + + task_outputs = {task["id"]: {"completed": False, "output": None} for task in task_registry.get_tasks()} + + task_output = None # Initialize task_output to None + + while not all(task["completed"] for task in task_outputs.values()): + tasks = task_registry.get_tasks() + task_registry.print_tasklist(tasks) + + for task in tasks: + if task["id"] not in task_outputs: + task_outputs[task["id"]] = {"completed": False, "output": None} + + ready_tasks = [(task["id"], task) for task in tasks if all((dep in task_outputs and task_outputs[dep]["completed"]) for dep in task.get('dependent_task_ids', [])) and not task_outputs[task["id"]]["completed"]] + + for task_id, task in ready_tasks: + run_single_task(task_id, task, global_skill_registry, task_outputs, OBJECTIVE, task_registry) + + time.sleep(0.1) + + # Assuming the last task in tasks has the latest output. Adjust if your use case is different. + last_task_id = tasks[-1]["id"] if tasks else None + task_output = task_outputs[last_task_id]["output"] if last_task_id else None + + task_registry.reflect_on_final(OBJECTIVE, task_registry.get_tasks(), task_output, skill_descriptions) + global_skill_registry.reflect_skills(OBJECTIVE, task_registry.get_tasks(), task_output, skill_descriptions) + + with open(f'output/output_{datetime.now().strftime("%d_%m_%Y_%H_%M_%S")}.txt', 'w') as file: + file.write(session_summary) + print("...file saved.") + print("END") + + return task_output # Return the last task output + + except Exception as e: + return f"An error occurred: {e}" + + + +# Removed repeated logic for initiating skill registry +global_skill_registry = SkillRegistry(api_keys=api_keys, main_loop_function=run_main_loop, skill_names=LOAD_SKILLS) + + +def execute_skill(skill_name, objective, task_id): + """Execute a single skill""" + skill = global_skill_registry.get_skill(skill_name) + if skill: + try: + result = skill.execute(objective, "", objective) + ongoing_tasks[task_id].update({"status": "completed", "output": result}) + except Exception as e: + ongoing_tasks[task_id].update({"status": "error", "error": str(e)}) + return task_id + return "Skill not found :(" + +def execute_task_list(objective, api_keys, task_id): + """Execute a list of tasks""" + try: + task_registry = TaskRegistry() + result = run_main_loop(objective, get_skills(), api_keys) + ongoing_tasks[task_id].update({"status": "completed", "output": result}) + return task_registry.get_tasks(), task_id + except Exception as e: + ongoing_tasks[task_id].update({"status": "error", "error": str(e)}) + print(f"Error in execute_task_list: {e}") + return task_id + + + +def get_skills(): + """Return the global skill registry""" + # Removed repeated logic for initiating skill registry + global global_skill_registry + print("Returning GLOBAL SKILL REGISTRY") + return global_skill_registry + +# Removed repeated logic for initiating skill registry +global_skill_registry = SkillRegistry(api_keys=api_keys, main_loop_function=run_main_loop, skill_names=LOAD_SKILLS) + +if __name__ == "__main__": + run_main_loop(OBJECTIVE, LOAD_SKILLS, api_keys, REFLECTION) \ No newline at end of file diff --git a/classic/babyfoxagi/forever_cache.ndjson b/classic/babyfoxagi/forever_cache.ndjson new file mode 100644 index 00000000..59247e18 --- /dev/null +++ b/classic/babyfoxagi/forever_cache.ndjson @@ -0,0 +1 @@ +{"role": "assistant", "content": "Hey I'm BabyAGI! How can I help you today?"} diff --git a/classic/babyfoxagi/main.py b/classic/babyfoxagi/main.py new file mode 100644 index 00000000..b0c0e76e --- /dev/null +++ b/classic/babyfoxagi/main.py @@ -0,0 +1,309 @@ +from flask import Flask, render_template, request, jsonify, send_from_directory +import openai +import os +import json +import threading +from babyagi import get_skills, execute_skill, execute_task_list, api_keys, LOAD_SKILLS +from ongoing_tasks import ongoing_tasks + +app = Flask(__name__, static_folder='public/static') +openai.api_key = os.getenv('OPENAI_API_KEY') + + +@app.route('/') +def hello_world(): + + return render_template('index.html') + + +FOREVER_CACHE_FILE = "forever_cache.ndjson" +OVERALL_SUMMARY_FILE = "overall_summary.ndjson" + + +@app.route('/get-all-messages', methods=["GET"]) +def get_all_messages(): + try: + messages = [] + with open("forever_cache.ndjson", "r") as file: + for line in file: + messages.append(json.loads(line)) + + return jsonify(messages) + + except Exception as e: + return jsonify({"error": str(e)}), 500 + + +def get_latest_summary(): + with open(OVERALL_SUMMARY_FILE, 'r') as file: + lines = file.readlines() + if lines: + return json.loads(lines[-1])["summary"] # Return the latest summary + return "" + + +def summarize_text(text): + system_message = ( + "Your task is to generate a concise summary for the provided conversation, this will be fed to a separate AI call as context to generate responses for future user queries." + " The conversation contains various messages that have been exchanged between participants." + " Please ensure your summary captures the main points and context of the conversation without being too verbose." + " The summary should be limited to a maximum of 500 tokens." + " Here's the conversation you need to summarize:") + + completion = openai.ChatCompletion.create(model="gpt-3.5-turbo-16k", + messages=[{ + "role": "system", + "content": system_message + }, { + "role": "user", + "content": text + }]) + + # Extracting the content from the assistant's message + return completion.choices[0]['message']['content'].strip() + + +def combine_summaries(overall, latest): + system_message = ( + "Your task is to generate a concise summary for the provided conversation, this will be fed to a separate AI call as context to generate responses for future user queries." + "You will do this by combining two given summaries into one cohesive summary." + " Make sure to retain the key points from both summaries and create a concise, unified summary." + " The combined summary should not exceed 500 tokens." + " Here are the summaries you need to combine:") + + completion = openai.ChatCompletion.create( + model="gpt-3.5-turbo-16k", + messages=[{ + "role": "system", + "content": system_message + }, { + "role": + "user", + "content": + f"Overall summary: {overall}\nLatest summary: {latest}" + }]) + + # Extracting the content from the assistant's message + return completion.choices[0]['message']['content'].strip() + + + +def openai_function_call(user_message): + global ongoing_tasks + global global_skill_registry + global_skill_registry = LOAD_SKILLS + print("Returning GLOBAL SKILL REGISTRY") + print(global_skill_registry) + + # Append the new user message to the forever_cache file + user_entry = {"role": "user", "content": user_message} + append_to_ndjson(FOREVER_CACHE_FILE, user_entry) + + # Retrieve the last 20 stored messages + with open(FOREVER_CACHE_FILE, "r") as file: + lines = file.readlines() + + last_20_messages = [json.loads(line) for line in lines][-20:] + + # Always update the summary in a separate thread + threading.Thread(target=update_summary, args=(last_20_messages, )).start() + + overall_summary = get_latest_summary() + print("LOAD_SKILLS") + print(global_skill_registry) + system_message = ( + f"You are a fun happy and quirky AI chat assistant that uses Gen Z language and lots of emojis named BabyAGI with capabilities beyond chat. For every user message, you quickly analyze whether this is a request that you can simply respond via ChatCompletion, whether you need to use one of the skills provided, or whether you should create a task list and chain multiple skills together. You will always provide a message_to_user. If path is Skill or TaskList, always generate an objective. If path is Skill, ALWAYS include skill_used from one of the available skills. ###Here are your available skills: {global_skill_registry}.###For context, here is the overall summary of the chat: {overall_summary}." + ) + + completion = openai.ChatCompletion.create( + model="gpt-3.5-turbo-16k", + messages=[ + {"role": "system","content": system_message}, + *last_20_messages, + {"role": "user","content": user_message}, + ], + functions=[{ + "name": "determine_response_type", + "description": + "Determine whether to respond via ChatCompletion, use a skill, or create a task list. Always provide a message_to_user.", + "parameters": { + "type": "object", + "properties": { + "message_to_user": { + "type": + "string", + "description": + "A message for the user, indicating the AI's action or providing the direct chat response. ALWAYS REQUIRED. Do not use line breaks." + }, + "path": { + "type": + "string", + "enum": ["ChatCompletion", "Skill", "TaskList"], # Restrict the values to these three options + "description": + "The type of response – either 'ChatCompletion', 'Skill', or 'TaskList'" + }, + "skill_used": { + "type": + "string", + "description": + f"If path is 'Skill', indicates which skill to use. If path is 'Skill', ALWAYS use skill_used. Must be one of these: {global_skill_registry}" + }, + "objective": { + "type": + "string", + "description": + "If path is 'Skill' or 'TaskList', describes the main task or objective. Always include if path is 'Skill' or 'TaskList'." + } + }, + "required": ["path", "message_to_user", "objective", "skill_used"] + } + }], + function_call={"name": "determine_response_type"}) + + # Extract AI's structured response from function call + response_data = completion.choices[0]['message']['function_call'][ + 'arguments'] + if isinstance(response_data, str): + response_data = json.loads(response_data) + print("RESPONSE DATA:") + print(response_data) + path = response_data.get("path") + skill_used = response_data.get("skill_used") + objective = response_data.get("objective") + task_id = generate_task_id() + response_data["taskId"] = task_id + if path == "Skill": + ongoing_tasks[task_id] = { + 'status': 'ongoing', + 'description': objective, + 'skill_used': skill_used + } + threading.Thread(target=execute_skill, args=(skill_used, objective, task_id)).start() + update_ongoing_tasks_file() + elif path == "TaskList": + ongoing_tasks[task_id] = { + 'status': 'ongoing', + 'description': objective, + 'skill_used': 'Multiple' + } + threading.Thread(target=execute_task_list, args=(objective, api_keys, task_id)).start() + update_ongoing_tasks_file() + + return response_data + + +def generate_task_id(): + """Generates a unique task ID""" + return f"{str(len(ongoing_tasks) + 1)}" + + +def update_summary(messages): + # Combine messages to form text and summarize + messages_text = " ".join([msg['content'] for msg in messages]) + latest_summary = summarize_text(messages_text) + + # Update overall summary + overall = get_latest_summary() + combined_summary = combine_summaries(overall, latest_summary) + append_to_ndjson(OVERALL_SUMMARY_FILE, {"summary": combined_summary}) + + +@app.route('/determine-response', methods=["POST"]) +def determine_response(): + try: + # Ensure that the request contains JSON data + if not request.is_json: + return jsonify({"error": "Expected JSON data"}), 400 + + user_message = request.json.get("user_message") + + # Check if user_message is provided + if not user_message: + return jsonify({"error": "user_message field is required"}), 400 + + response_data = openai_function_call(user_message) + + data = { + "message": response_data['message_to_user'], + "skill_used": response_data.get('skill_used', None), + "objective": response_data.get('objective', None), + "task_list": response_data.get('task_list', []), + "path": response_data.get('path', []), + "task_id": response_data.get('taskId') + } + + # Storing AI's response to NDJSON file + ai_entry = { + "role": "assistant", + "content": response_data['message_to_user'] + } + append_to_ndjson("forever_cache.ndjson", ai_entry) + + print("END OF DETERMINE-RESPONSE. PRINTING 'DATA'") + print(data) + return jsonify(data) + + except Exception as e: + print(f"Exception occurred: {str(e)}") + return jsonify({"error": str(e)}), 500 + + +def append_to_ndjson(filename, data): + try: + print(f"Appending to {filename} with data: {data}") + with open(filename, 'a') as file: + file.write(json.dumps(data) + '\n') + except Exception as e: + print(f"Error in append_to_ndjson: {str(e)}") + + + +@app.route('/check-task-status/', methods=["GET"]) +def check_task_status(task_id): + global ongoing_tasks + update_ongoing_tasks_file() + print("CHECK_TASK_STATUS") + print(task_id) + task = ongoing_tasks.get(task_id) + + # First check if task is None + if not task: + return jsonify({"error": f"No task with ID {task_id} found."}), 404 + + # Now, it's safe to access attributes of task + print(task.get("status")) + return jsonify({"status": task.get("status")}) + + +@app.route('/fetch-task-output/', methods=["GET"]) +def fetch_task_output(task_id): + print("FETCH_TASK_STATUS") + print(task_id) + task = ongoing_tasks.get(task_id) + if not task: + return jsonify({"error": f"No task with ID {task_id} found."}), 404 + return jsonify({"output": task.get("output")}) + + +def update_ongoing_tasks_file(): + with open("ongoing_tasks.py", "w") as file: + file.write(f"ongoing_tasks = {ongoing_tasks}\n") + +@app.route('/get-all-tasks', methods=['GET']) +def get_all_tasks(): + tasks = [] + for task_id, task_data in ongoing_tasks.items(): + task = { + 'task_id': task_id, + 'status': task_data.get('status', 'unknown'), + 'description': task_data.get('description', 'N/A'), + 'skill_used': task_data.get('skill_used', 'N/A'), + 'output': task_data.get('output', 'N/A') + } + tasks.append(task) + return jsonify(tasks) + + + +if __name__ == "__main__": + app.run(host='0.0.0.0', port=8080) diff --git a/classic/babyfoxagi/ongoing_tasks.py b/classic/babyfoxagi/ongoing_tasks.py new file mode 100644 index 00000000..dab08f19 --- /dev/null +++ b/classic/babyfoxagi/ongoing_tasks.py @@ -0,0 +1 @@ +ongoing_tasks = {} diff --git a/classic/babyfoxagi/overall_summary.ndjson b/classic/babyfoxagi/overall_summary.ndjson new file mode 100644 index 00000000..e69de29b diff --git a/classic/babyfoxagi/poetry.lock b/classic/babyfoxagi/poetry.lock new file mode 100644 index 00000000..74d839a8 --- /dev/null +++ b/classic/babyfoxagi/poetry.lock @@ -0,0 +1,886 @@ +[[package]] +name = "aiohttp" +version = "3.8.5" +description = "Async http client/server framework (asyncio)" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +aiosignal = ">=1.1.2" +async-timeout = ">=4.0.0a3,<5.0" +attrs = ">=17.3.0" +charset-normalizer = ">=2.0,<4.0" +frozenlist = ">=1.1.1" +multidict = ">=4.5,<7.0" +yarl = ">=1.0,<2.0" + +[package.extras] +speedups = ["aiodns", "brotli", "cchardet"] + +[[package]] +name = "aiohttp-retry" +version = "2.8.3" +description = "Simple retry client for aiohttp" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +aiohttp = "*" + +[[package]] +name = "aiosignal" +version = "1.2.0" +description = "aiosignal: a list of registered asynchronous callbacks" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +frozenlist = ">=1.1.0" + +[[package]] +name = "async-timeout" +version = "4.0.2" +description = "Timeout context manager for asyncio programs" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "attrs" +version = "22.1.0" +description = "Classes Without Boilerplate" +category = "main" +optional = false +python-versions = ">=3.5" + +[package.extras] +dev = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "furo", "sphinx", "sphinx-notfound-page", "pre-commit", "cloudpickle"] +docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"] +tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] +tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "mypy (>=0.900,!=0.940)", "pytest-mypy-plugins", "cloudpickle"] + +[[package]] +name = "beautifulsoup4" +version = "4.12.2" +description = "Screen-scraping library" +category = "main" +optional = false +python-versions = ">=3.6.0" + +[package.dependencies] +soupsieve = ">1.2" + +[package.extras] +html5lib = ["html5lib"] +lxml = ["lxml"] + +[[package]] +name = "bs4" +version = "0.0.1" +description = "Dummy package for Beautiful Soup" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +beautifulsoup4 = "*" + +[[package]] +name = "cachetools" +version = "5.3.1" +description = "Extensible memoizing collections and decorators" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "certifi" +version = "2022.9.24" +description = "Python package for providing Mozilla's CA Bundle." +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "charset-normalizer" +version = "2.1.1" +description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." +category = "main" +optional = false +python-versions = ">=3.6.0" + +[package.extras] +unicode_backport = ["unicodedata2"] + +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[[package]] +name = "colorama" +version = "0.4.5" +description = "Cross-platform colored terminal text." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" + +[[package]] +name = "datadispatch" +version = "1.0.0" +description = "Like functools.singledispatch but for values" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "debugpy" +version = "1.6.3" +description = "An implementation of the Debug Adapter Protocol for Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "flask" +version = "2.2.2" +description = "A simple framework for building complex web applications." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +click = ">=8.0" +importlib-metadata = {version = ">=3.6.0", markers = "python_version < \"3.10\""} +itsdangerous = ">=2.0" +Jinja2 = ">=3.0" +Werkzeug = ">=2.2.2" + +[package.extras] +async = ["asgiref (>=3.2)"] +dotenv = ["python-dotenv"] + +[[package]] +name = "frozenlist" +version = "1.3.1" +description = "A list-like structure which implements collections.abc.MutableSequence" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "google-api-core" +version = "2.11.1" +description = "Google API client core library" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +google-auth = ">=2.14.1,<3.0.dev0" +googleapis-common-protos = ">=1.56.2,<2.0.dev0" +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.0 || >4.21.0,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" +requests = ">=2.18.0,<3.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.33.2,<2.0dev)", "grpcio-status (>=1.33.2,<2.0.dev0)", "grpcio (>=1.49.1,<2.0dev)", "grpcio-status (>=1.49.1,<2.0.dev0)"] +grpcgcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] +grpcio-gcp = ["grpcio-gcp (>=0.2.2,<1.0.dev0)"] + +[[package]] +name = "google-api-python-client" +version = "2.97.0" +description = "Google API Client Library for Python" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +google-api-core = ">=1.31.5,<2.0.0 || >2.3.0,<3.0.0.dev0" +google-auth = ">=1.19.0,<3.0.0.dev0" +google-auth-httplib2 = ">=0.1.0" +httplib2 = ">=0.15.0,<1.dev0" +uritemplate = ">=3.0.1,<5" + +[[package]] +name = "google-auth" +version = "2.22.0" +description = "Google Authentication Library" +category = "main" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +cachetools = ">=2.0.0,<6.0" +pyasn1-modules = ">=0.2.1" +rsa = ">=3.1.4,<5" +six = ">=1.9.0" +urllib3 = "<2.0" + +[package.extras] +aiohttp = ["aiohttp (>=3.6.2,<4.0.0.dev0)", "requests (>=2.20.0,<3.0.0.dev0)"] +enterprise_cert = ["cryptography (==36.0.2)", "pyopenssl (==22.0.0)"] +pyopenssl = ["pyopenssl (>=20.0.0)", "cryptography (>=38.0.3)"] +reauth = ["pyu2f (>=0.1.5)"] +requests = ["requests (>=2.20.0,<3.0.0.dev0)"] + +[[package]] +name = "google-auth-httplib2" +version = "0.1.0" +description = "Google Authentication Library: httplib2 transport" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +google-auth = "*" +httplib2 = ">=0.15.0" +six = "*" + +[[package]] +name = "googleapis-common-protos" +version = "1.60.0" +description = "Common protobufs used in Google APIs" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +protobuf = ">=3.19.5,<3.20.0 || >3.20.0,<3.20.1 || >3.20.1,<4.21.1 || >4.21.1,<4.21.2 || >4.21.2,<4.21.3 || >4.21.3,<4.21.4 || >4.21.4,<4.21.5 || >4.21.5,<5.0.0.dev0" + +[package.extras] +grpc = ["grpcio (>=1.44.0,<2.0.0.dev0)"] + +[[package]] +name = "grpcio" +version = "1.57.0" +description = "HTTP/2-based RPC framework" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +protobuf = ["grpcio-tools (>=1.57.0)"] + +[[package]] +name = "httplib2" +version = "0.22.0" +description = "A comprehensive HTTP client library." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" + +[package.dependencies] +pyparsing = {version = ">=2.4.2,<3.0.0 || >3.0.0,<3.0.1 || >3.0.1,<3.0.2 || >3.0.2,<3.0.3 || >3.0.3,<4", markers = "python_version > \"3.0\""} + +[[package]] +name = "idna" +version = "3.4" +description = "Internationalized Domain Names in Applications (IDNA)" +category = "main" +optional = false +python-versions = ">=3.5" + +[[package]] +name = "importlib-metadata" +version = "5.0.0" +description = "Read metadata from Python packages" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["sphinx (>=3.5)", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "furo", "jaraco.tidelift (>=1.4)"] +perf = ["ipython"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "flake8 (<5)", "pytest-cov", "pytest-enabler (>=1.3)", "packaging", "pyfakefs", "flufl.flake8", "pytest-perf (>=0.9.2)", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)", "importlib-resources (>=1.3)"] + +[[package]] +name = "itsdangerous" +version = "2.1.2" +description = "Safely pass data to untrusted environments and back." +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "jedi" +version = "0.18.1" +description = "An autocompletion tool for Python that can be used for text editors." +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +parso = ">=0.8.0,<0.9.0" + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["Django (<3.1)", "colorama", "docopt", "pytest (<7.0.0)"] + +[[package]] +name = "jinja2" +version = "3.1.2" +description = "A very fast and expressive template engine." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +MarkupSafe = ">=2.0" + +[package.extras] +i18n = ["Babel (>=2.7)"] + +[[package]] +name = "markupsafe" +version = "2.1.1" +description = "Safely add untrusted strings to HTML/XML markup." +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "multidict" +version = "6.0.2" +description = "multidict implementation" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "numpy" +version = "1.23.4" +description = "NumPy is the fundamental package for array computing with Python." +category = "main" +optional = false +python-versions = ">=3.8" + +[[package]] +name = "openai" +version = "0.27.8" +description = "Python client library for the OpenAI API" +category = "main" +optional = false +python-versions = ">=3.7.1" + +[package.dependencies] +aiohttp = "*" +requests = ">=2.20" +tqdm = "*" + +[package.extras] +datalib = ["numpy", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "openpyxl (>=3.0.7)"] +dev = ["black (>=21.6b0,<22.0.0)", "pytest (>=6.0.0,<7.0.0)", "pytest-asyncio", "pytest-mock"] +embeddings = ["scikit-learn (>=1.0.2)", "tenacity (>=8.0.1)", "matplotlib", "plotly", "numpy", "scipy", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "openpyxl (>=3.0.7)"] +wandb = ["wandb", "numpy", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "openpyxl (>=3.0.7)"] + +[[package]] +name = "packaging" +version = "21.3" +description = "Core utilities for Python packages" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.dependencies] +pyparsing = ">=2.0.2,<3.0.5 || >3.0.5" + +[[package]] +name = "parso" +version = "0.8.3" +description = "A Python Parser" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +qa = ["flake8 (==3.8.3)", "mypy (==0.782)"] +testing = ["docopt", "pytest (<6.0.0)"] + +[[package]] +name = "pluggy" +version = "1.0.0" +description = "plugin and hook calling mechanisms for python" +category = "dev" +optional = false +python-versions = ">=3.6" + +[package.extras] +testing = ["pytest-benchmark", "pytest"] +dev = ["tox", "pre-commit"] + +[[package]] +name = "protobuf" +version = "4.24.2" +description = "" +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "pyasn1" +version = "0.5.0" +description = "Pure-Python implementation of ASN.1 types and DER/BER/CER codecs (X.208)" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" + +[[package]] +name = "pyasn1-modules" +version = "0.3.0" +description = "A collection of ASN.1-based protocols modules" +category = "main" +optional = false +python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7" + +[package.dependencies] +pyasn1 = ">=0.4.6,<0.6.0" + +[[package]] +name = "pyflakes" +version = "2.5.0" +description = "passive checker of Python programs" +category = "dev" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "pyjwt" +version = "2.8.0" +description = "JSON Web Token implementation in Python" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +crypto = ["cryptography (>=3.4.0)"] +dev = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface", "cryptography (>=3.4.0)", "pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)", "pre-commit"] +docs = ["sphinx (>=4.5.0,<5.0.0)", "sphinx-rtd-theme", "zope.interface"] +tests = ["pytest (>=6.0.0,<7.0.0)", "coverage[toml] (==5.0.4)"] + +[[package]] +name = "pyparsing" +version = "3.0.9" +description = "pyparsing module - Classes and methods to define and execute parsing grammars" +category = "main" +optional = false +python-versions = ">=3.6.8" + +[package.extras] +diagrams = ["railroad-diagrams", "jinja2"] + +[[package]] +name = "python-dotenv" +version = "1.0.0" +description = "Read key-value pairs from a .env file and set them as environment variables" +category = "main" +optional = false +python-versions = ">=3.8" + +[package.extras] +cli = ["click (>=5.0)"] + +[[package]] +name = "python-lsp-jsonrpc" +version = "1.0.0" +description = "JSON RPC 2.0 server library" +category = "dev" +optional = false +python-versions = "*" + +[package.dependencies] +ujson = ">=3.0.0" + +[package.extras] +test = ["coverage", "pytest-cov", "pytest", "pyflakes", "pycodestyle", "pylint"] + +[[package]] +name = "pytoolconfig" +version = "1.2.2" +description = "Python tool configuration" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +packaging = ">=21.3" +tomli = {version = ">=2.0", markers = "python_version < \"3.11\""} + +[package.extras] +validation = ["pydantic (>=1.7.4)"] +global = ["appdirs (>=1.4.4)"] +gen_docs = ["pytoolconfig", "sphinx-rtd-theme (>=1.0.0)", "sphinx-autodoc-typehints (>=1.18.1)", "sphinx (>=4.5.0)"] +doc = ["sphinx (>=4.5.0)", "tabulate (>=0.8.9)"] + +[[package]] +name = "pytz" +version = "2023.3" +description = "World timezone definitions, modern and historical" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "replit" +version = "3.2.4" +description = "A library for interacting with features of repl.it" +category = "main" +optional = false +python-versions = ">=3.8,<4.0" + +[package.dependencies] +aiohttp = ">=3.6.2,<4.0.0" +Flask = ">=2.0.0,<3.0.0" +requests = ">=2.25.1,<3.0.0" +typing_extensions = ">=3.7.4,<4.0.0" +Werkzeug = ">=2.0.0,<3.0.0" + +[[package]] +name = "replit-python-lsp-server" +version = "1.15.9" +description = "Python Language Server for the Language Server Protocol" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +jedi = ">=0.17.2,<0.19.0" +pluggy = ">=1.0.0" +pyflakes = {version = ">=2.5.0,<2.6.0", optional = true, markers = "extra == \"pyflakes\""} +python-lsp-jsonrpc = ">=1.0.0" +rope = {version = ">0.10.5", optional = true, markers = "extra == \"rope\""} +toml = ">=0.10.2" +ujson = ">=3.0.0" +whatthepatch = {version = ">=1.0.2,<2.0.0", optional = true, markers = "extra == \"yapf\""} +yapf = {version = "*", optional = true, markers = "extra == \"yapf\""} + +[package.extras] +all = ["autopep8 (>=1.6.0,<1.7.0)", "flake8 (>=5.0.0,<5.1.0)", "mccabe (>=0.7.0,<0.8.0)", "pycodestyle (>=2.9.0,<2.10.0)", "pydocstyle (>=2.0.0)", "pyflakes (>=2.5.0,<2.6.0)", "pylint (>=2.5.0)", "rope (>=0.10.5)", "yapf", "whatthepatch"] +autopep8 = ["autopep8 (>=1.6.0,<1.7.0)"] +flake8 = ["flake8 (>=5.0.0,<5.1.0)"] +mccabe = ["mccabe (>=0.7.0,<0.8.0)"] +pycodestyle = ["pycodestyle (>=2.9.0,<2.10.0)"] +pydocstyle = ["pydocstyle (>=2.0.0)"] +pyflakes = ["pyflakes (>=2.5.0,<2.6.0)"] +pylint = ["pylint (>=2.5.0)"] +rope = ["rope (>0.10.5)"] +test = ["pylint (>=2.5.0)", "pytest", "pytest-cov", "coverage", "numpy (<1.23)", "pandas", "matplotlib", "pyqt5", "flaky"] +websockets = ["websockets (>=10.3)"] +yapf = ["yapf", "whatthepatch (>=1.0.2,<2.0.0)"] + +[[package]] +name = "requests" +version = "2.28.1" +description = "Python HTTP for Humans." +category = "main" +optional = false +python-versions = ">=3.7, <4" + +[package.dependencies] +certifi = ">=2017.4.17" +charset-normalizer = ">=2,<3" +idna = ">=2.5,<4" +urllib3 = ">=1.21.1,<1.27" + +[package.extras] +socks = ["PySocks (>=1.5.6,!=1.5.7)"] +use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] + +[[package]] +name = "rope" +version = "1.3.0" +description = "a python refactoring library..." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +pytoolconfig = ">=1.1.2" + +[package.extras] +doc = ["sphinx-rtd-theme (>=1.0.0)", "sphinx-autodoc-typehints (>=1.18.1)", "sphinx (>=4.5.0)", "pytoolconfig"] +dev = ["build (>=0.7.0)", "pytest-timeout (>=2.1.0)", "pytest (>=7.0.1)"] + +[[package]] +name = "rsa" +version = "4.9" +description = "Pure-Python RSA implementation" +category = "main" +optional = false +python-versions = ">=3.6,<4" + +[package.dependencies] +pyasn1 = ">=0.1.3" + +[[package]] +name = "six" +version = "1.16.0" +description = "Python 2 and 3 compatibility utilities" +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "skills" +version = "0.3.0" +description = "Implementation of the TrueSkill, Glicko and Elo Ranking Algorithms" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "soupsieve" +version = "2.4.1" +description = "A modern CSS selector implementation for Beautiful Soup." +category = "main" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "tasks" +version = "2.8.0" +description = "A simple personal task queue to track todo items" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +datadispatch = "*" + +[[package]] +name = "toml" +version = "0.10.2" +description = "Python Library for Tom's Obvious, Minimal Language" +category = "dev" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" + +[[package]] +name = "tomli" +version = "2.0.1" +description = "A lil' TOML parser" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "tqdm" +version = "4.66.1" +description = "Fast, Extensible Progress Meter" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + +[package.extras] +dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] +notebook = ["ipywidgets (>=6)"] +slack = ["slack-sdk"] +telegram = ["requests"] + +[[package]] +name = "twilio" +version = "8.5.0" +description = "Twilio API client and TwiML generator" +category = "main" +optional = false +python-versions = ">=3.7.0" + +[package.dependencies] +aiohttp = ">=3.8.4" +aiohttp-retry = ">=2.8.3" +PyJWT = ">=2.0.0,<3.0.0" +pytz = "*" +requests = ">=2.0.0" + +[[package]] +name = "typing-extensions" +version = "3.10.0.2" +description = "Backported and Experimental Type Hints for Python 3.5+" +category = "main" +optional = false +python-versions = "*" + +[[package]] +name = "ujson" +version = "5.5.0" +description = "Ultra fast JSON encoder and decoder for Python" +category = "dev" +optional = false +python-versions = ">=3.7" + +[[package]] +name = "uritemplate" +version = "4.1.1" +description = "Implementation of RFC 6570 URI Templates" +category = "main" +optional = false +python-versions = ">=3.6" + +[[package]] +name = "urllib3" +version = "1.26.12" +description = "HTTP library with thread-safe connection pooling, file post, and more." +category = "main" +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4" + +[package.extras] +brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"] +secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "urllib3-secure-extra", "ipaddress"] +socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] + +[[package]] +name = "werkzeug" +version = "2.2.2" +description = "The comprehensive WSGI web application library." +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +MarkupSafe = ">=2.1.1" + +[package.extras] +watchdog = ["watchdog"] + +[[package]] +name = "whatthepatch" +version = "1.0.2" +description = "A patch parsing and application library." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "xds-protos" +version = "0.0.11" +description = "Generated Python code from envoyproxy/data-plane-api" +category = "main" +optional = false +python-versions = "*" + +[package.dependencies] +grpcio = "*" +protobuf = "*" + +[[package]] +name = "yapf" +version = "0.32.0" +description = "A formatter for Python code." +category = "dev" +optional = false +python-versions = "*" + +[[package]] +name = "yarl" +version = "1.8.1" +description = "Yet another URL library" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +idna = ">=2.0" +multidict = ">=4.0" + +[[package]] +name = "zipp" +version = "3.9.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["sphinx (>=3.5)", "jaraco.packaging (>=9)", "rst.linker (>=1.9)", "furo", "jaraco.tidelift (>=1.4)"] +testing = ["pytest (>=6)", "pytest-checkdocs (>=2.4)", "pytest-flake8", "flake8 (<5)", "pytest-cov", "pytest-enabler (>=1.3)", "jaraco.itertools", "func-timeout", "jaraco.functools", "more-itertools", "pytest-black (>=0.3.7)", "pytest-mypy (>=0.9.1)"] + +[metadata] +lock-version = "1.1" +python-versions = ">=3.8.0,<3.9" +content-hash = "c0a0faf8c1bfce437b0517d539ce578ac47df821f02234e0c3ad58c35c88ea34" + +[metadata.files] +aiohttp = [] +aiohttp-retry = [] +aiosignal = [] +async-timeout = [] +attrs = [] +beautifulsoup4 = [] +bs4 = [] +cachetools = [] +certifi = [] +charset-normalizer = [] +click = [] +colorama = [] +datadispatch = [] +debugpy = [] +flask = [] +frozenlist = [] +google-api-core = [] +google-api-python-client = [] +google-auth = [] +google-auth-httplib2 = [] +googleapis-common-protos = [] +grpcio = [] +httplib2 = [] +idna = [] +importlib-metadata = [] +itsdangerous = [] +jedi = [] +jinja2 = [] +markupsafe = [] +multidict = [] +numpy = [] +openai = [] +packaging = [] +parso = [] +pluggy = [] +protobuf = [] +pyasn1 = [] +pyasn1-modules = [] +pyflakes = [] +pyjwt = [] +pyparsing = [] +python-dotenv = [] +python-lsp-jsonrpc = [] +pytoolconfig = [] +pytz = [] +replit = [] +replit-python-lsp-server = [] +requests = [] +rope = [] +rsa = [] +six = [] +skills = [] +soupsieve = [] +tasks = [] +toml = [] +tomli = [] +tqdm = [] +twilio = [] +typing-extensions = [] +ujson = [] +uritemplate = [] +urllib3 = [] +werkzeug = [] +whatthepatch = [] +xds-protos = [] +yapf = [] +yarl = [] +zipp = [] diff --git a/classic/babyfoxagi/public/static/chat.js b/classic/babyfoxagi/public/static/chat.js new file mode 100644 index 00000000..a7872ef3 --- /dev/null +++ b/classic/babyfoxagi/public/static/chat.js @@ -0,0 +1,279 @@ + // Display message function + function displayMessage(content, role = "user") { + let messageHTML = createMessageHTML(content, role); + $("#chat-messages").append(messageHTML); + if (role === "ongoing") { + $(".bg-green-100:last").parent().addClass("ai-processing"); + } + } + + function createMessageHTML(content, role) { + if (role === "user") { + return `
+
+ ${content} +
+
`; + } else { + return `
+
+ ${content} +
+
`; + } + } + + // Send message function + function sendMessage() { + const userMessage = $("#user-input").val().trim(); + + if (!userMessage) { + alert("Message cannot be empty!"); + return; + } + + // Display the user's message + displayMessage(userMessage, "user"); + scrollToBottom(); + // Display processing message + displayMessage('...', 'ongoing'); + scrollToBottom(); + + // Clear the input and disable the button + $("#user-input").val(""); + toggleSendButton(); + + // Send the message to the backend for processing + $.ajax({ + type: 'POST', + url: '/determine-response', + data: JSON.stringify({ user_message: userMessage }), + contentType: 'application/json', + dataType: 'json', + success: handleResponseSuccess, + error: handleResponseError + }); + } + + function handleResponseSuccess(data) { + $(".ai-processing").remove(); + displayMessage(data.message, "assistant"); + scrollToBottom(); + + if (data.objective) { + displayTaskWithStatus(`Task: ${data.objective}`, "ongoing", data.skill_used, data.task_id); + } + + if (data.path !== "ChatCompletion") { + checkTaskCompletion(data.task_id); + } else { + + } + + } + + function handleResponseError(error) { + $(".ai-processing").remove(); + const errorMessage = error.responseJSON && error.responseJSON.error ? error.responseJSON.error : "Unknown error"; + displayMessage(`Error: ${errorMessage}`, "error"); + scrollToBottom(); + } + + // Other utility functions + function toggleSendButton() { + if ($("#user-input").val().trim()) { + $("button").prop("disabled", false); + } else { + $("button").prop("disabled", true); + } + } + +function scrollToBottom() { + setTimeout(() => { + const chatBox = document.getElementById("chat-messages"); + chatBox.scrollTop = chatBox.scrollHeight; + }, 100); // small delay to ensure content is rendered +} + + + + function checkTaskCompletion(taskId) { + $.ajax({ + type: 'GET', + url: `/check-task-status/${taskId}`, + dataType: 'json', + success(data) { + if (data.status === "completed") { updateTaskStatus(taskId, "completed"); + fetchTaskOutput(taskId); + displayMessage("Hey, I just finished a task!", "assistant"); + } else { + fetchTaskOutput(taskId); + setTimeout(() => { + checkTaskCompletion(taskId); + }, 5000); // Check every 5 seconds + } + }, + error(error) { + console.error(`Error checking task status for ${taskId}:`, error); + } + }); + } + +function fetchTaskOutput(taskId) { + $.ajax({ + type: 'GET', + url: `/fetch-task-output/${taskId}`, + dataType: 'json', + success(data) { + if (data.output) { + const $taskItem = $(`.task-item[data-task-id="${taskId}"]`); // Find the task item with the given task ID + console.log('taskItem:'+$taskItem) + const $outputContainer = $taskItem.find('.task-output'); + console.log('outputContainer:'+$outputContainer) + console.log('data.output:'+data.output) + + // Update the task's output content + $outputContainer.html(`

${data.output}

`); + } + }, + error(error) { + console.error(`Error fetching task output for ${taskId}:`, error); + } + }); +} + + + + +$(document).ready(function() { + toggleSendButton(); + loadPreviousMessages(); + loadAllTasks(); + $("#send-btn").on('click', function() { + sendMessage(); + }); + $("#user-input").on('keyup', toggleSendButton); + $("#user-input").on('keydown', function(e) { + if (e.key === 'Enter' && !e.shiftKey && !e.ctrlKey) { + e.preventDefault(); + sendMessage(); + } + }); + $("#task-search").on("keyup", function() { + let value = $(this).val().toLowerCase(); + $(".task-item").filter(function() { + $(this).toggle($(this).find(".task-title").text().toLowerCase().indexOf(value) > -1); + }); + }); + + +}); + + function loadPreviousMessages() { + $.ajax({ + type: 'GET', + url: '/get-all-messages', + dataType: 'json', + success(data) { + data.forEach(message => displayMessage(message.content, message.role)); + scrollToBottom(); + }, + error(error) { + console.error("Error fetching previous messages:", error); + } + }); + } + +function getStatusBadgeHTML(status) { + switch (status) { + case 'ongoing': + return `Ongoing`; + case 'completed': + return `Completed`; + case 'error': + return `Error`; + default: + return `Unknown`; + } +} + +function displayTaskWithStatus(taskDescription, status, skillType = "Unknown Skill", taskId, output = null) { + let statusBadgeHTML = getStatusBadgeHTML(status); + + let skillBadgeHTML = ''; + if (skillType) { + skillBadgeHTML = `${skillType}`; + } + + let outputHTML = output ? `

${output}

` : '

No output yet...

'; + + const taskHTML = ` +
+ ${taskDescription}
+ + ${statusBadgeHTML}${skillBadgeHTML} + +
`; + $("#task-list").prepend(taskHTML); +} + + +function updateTaskStatus(taskId, status, output) { + const $taskToUpdate = $(`#task-list > .task-item[data-task-id="${taskId}"]`); + + // Remove the existing status badge + $taskToUpdate.find(".status-badge").remove(); + + // Insert the new status badge right before the skill badge + $taskToUpdate.find(".toggle-output-icon").after(getStatusBadgeHTML(status)); + + // Update output, if available + const $outputContainer = $taskToUpdate.find(".task-output"); + if (output) { + $outputContainer.html(`

${output}

`); + } else { + $outputContainer.find("p").text("No output yet..."); + } +} + +function loadAllTasks() { + $.ajax({ + type: 'GET', + url: '/get-all-tasks', + dataType: 'json', + success(data) { + console.log("Debug: Received tasks:", data); // Debug print + data.forEach(task => { + const description = task.description || ''; + const status = task.status || ''; + const skill_used = task.skill_used || ''; + const task_id = task.task_id || ''; + const output = task.output || null; // Get the output, if it exists, otherwise set to null + displayTaskWithStatus(description, status, skill_used, task_id, output); + }); + }, + error(error) { + console.error("Error fetching all tasks:", error); + } + }); +} + + + + + + +$(document).on('click', '.toggle-output-icon', function() { + const $task = $(this).closest(".task-item"); + const $output = $task.find(".task-output"); + $output.toggleClass('hidden'); + // Change the icon when the output is toggled + const icon = $(this); + if ($output.hasClass('hidden')) { + icon.text('▶'); // Icon indicating the output is hidden + } else { + icon.text('▼'); // Icon indicating the output is shown + } +}); diff --git a/classic/babyfoxagi/public/static/style.css b/classic/babyfoxagi/public/static/style.css new file mode 100644 index 00000000..8e35631f --- /dev/null +++ b/classic/babyfoxagi/public/static/style.css @@ -0,0 +1,97 @@ +body::before { + content: ""; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: -1; + background-image: + linear-gradient(45deg, rgba(250, 243, 224, 0.1) 25%, transparent 25%, transparent 50%, rgba(250, 243, 224, 0.1) 50%, rgba(250, 243, 224, 0.1) 75%, transparent 75%, transparent); + background-size: 40px 40px; +} + +body { + background-color: #ffffff; + opacity: 0.8; + background-image: linear-gradient(135deg, #fff2ce 25%, transparent 25%), linear-gradient(225deg, #fff2ce 25%, transparent 25%), linear-gradient(45deg, #fff2ce 25%, transparent 25%), linear-gradient(315deg, #fff2ce 25%, #ffffff 25%); + background-position: 10px 0, 10px 0, 0 0, 0 0; + background-size: 10px 10px; + background-repeat: repeat; + font-family: 'Montserrat'; +} + + + + +.chat-box { + + background-color: #faf3e0; /* Light beige */ +} +.objectives-box { + background-color: #faf3e0; /* Light beige */ +} +#task-list { + background-color: #fff; /* Light beige */ +} + +.chat-messages { + overflow-y: scroll; + background-color: #fff + /* background-color: #faf3e0; /* Light beige */ +} + +/* Style for user's speech bubble tail */ +.bg-blue-200::before { + content: ""; + position: absolute; + top: 10px; + right: -10px; + border-width: 10px; + border-style: solid; + border-color: transparent transparent transparent; +} + +/* Style for assistant's speech bubble tail */ +.bg-gray-300::before { + content: ""; + position: absolute; + top: 10px; + left: -10px; + border-width: 10px; + border-style: solid; + border-color: transparent transparent transparent; +} + + +.task-item { + border: 1px solid #fffbe7; + border-radius: 8px; + margin-bottom: 10px; + padding: 10px; + position: relative; +} + +.task-title { + font-weight: bold; +} + +.task-output { + margin-top: 10px; +} + +.show-output .task-output { + display: block; +} + +#task-list { + overflow-y: auto; + max-height: calc(100vh - 150px); /* Adjust based on header and padding */ +} + +.task-output { + background-color: #fffdfd; /* Light gray */ + padding: 10px; + border-radius: 5px; + margin-top: 5px; +} diff --git a/classic/babyfoxagi/pyproject.toml b/classic/babyfoxagi/pyproject.toml new file mode 100644 index 00000000..bcb0297b --- /dev/null +++ b/classic/babyfoxagi/pyproject.toml @@ -0,0 +1,28 @@ +[tool.poetry] +name = "python-template" +version = "0.1.0" +description = "" +authors = ["Your Name "] + +[tool.poetry.dependencies] +python = ">=3.8.0,<3.9" +numpy = "^1.22.2" +replit = "^3.2.4" +Flask = "^2.2.0" +urllib3 = "^1.26.12" +openai = "^0.27.8" +tasks = "^2.8.0" +python-dotenv = "^1.0.0" +skills = "^0.3.0" +bs4 = "^0.0.1" +twilio = "^8.5.0" +google-api-python-client = "^2.97.0" +xds-protos = "^0.0.11" + +[tool.poetry.dev-dependencies] +debugpy = "^1.6.2" +replit-python-lsp-server = {extras = ["yapf", "rope", "pyflakes"], version = "^1.5.9"} + +[build-system] +requires = ["poetry-core>=1.0.0"] +build-backend = "poetry.core.masonry.api" \ No newline at end of file diff --git a/classic/babyfoxagi/skills/__init__.py b/classic/babyfoxagi/skills/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/classic/babyfoxagi/skills/__pycache__/__init__.cpython-38.pyc b/classic/babyfoxagi/skills/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9796b8b6f38ca30cd26d78602c7d71582321c0bd GIT binary patch literal 131 zcmWIL<>g`kf`B`BQ$X}%5P=LBfgA@QE@lA|DGb33nv8xc8Hzx{2;!H9enx(7s(w*v zUS4XEzEfgSWpYMhiGFc*W=>AAetdjpUS>&ryk0@&Ee@O9{FKt1R6CFfpMjVG0M{`b A&;S4c literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/airtable_search.cpython-310.pyc b/classic/babyfoxagi/skills/__pycache__/airtable_search.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..90dc146e0143040f68a929587f951cc08c6e6f60 GIT binary patch literal 4122 zcmZ`+TW=f372X?{7g1N!y4YztiQ+hFZ8AaPrV*N^k`)IAQlUu{G^LR)R-B=@(q5RE zT}j+7`%oLmLy;fQCPr=aDLDus(u2KA(U90tS_U0MTulnD=b9V ziY3Ym517kJ8*TQn68*$y{Yb(>AcB1%X~4-KVkd&du74yrizDXlM@fu%J%OOS5M4vB z*UoFxpmIAJiqQ3XQy>Trx~5H`f3<`r*6wN#^i0o;hX!p67OL-V7I|YlbYv2S zLUy*@{n4%HbnCs_Fs-7{^*aSzsxysw0%UtQl63^vq$mwEOM1F8g{=g?Jq@f!%b9-G za5015P5jh9bWO{&Lrqm^SqIwK%CP@Ord;AiW_*cEyKjywp~2B|eQam8dX!mmow5#) zSP%7bOsL~&H8!&9U1V;iJutbQ(X(n+MO(`%XzN8!<9_|2KCWff11qaN#OT=IGjS!W z-PKn#-dxd23x_tJUDL*OK9|+M)b1O6o-cf5j2nD$Jj0j9O@3iKo7Kj1aW$L0tB>b- zE1NymWWXYn8!v_EMks<$@wI0ko)W#RHi=3@#JKCq;A20J4 zG86M(EY`W6E%TSwH2Ac~E13;c4fzM4v>Mv{N@immVY^yj^>Vg~`>WYX(OUsNEuiuW zP-#7Zik-Fi#)|f)Hnu_ZU$? zDfl(!h8!bREL=E%S2YTwa1%1T z)@`g>c`9}%h+qXLUOZq20?c&dQi7sTu#-sg^HGl2zN;`gg2G_^{m2J|G@Jl{r%s#w zq{IY6{82Hel(GqOK-&f0Y8ZtISf#JAJ@-gpnWSO8UX&oB$@UZyS)weCy(m$o6ZZ<3 z+-|&k>IOq!U^+SeaH6i~#C56=PY5fSD@FP0Qz~x@Y!ZOrLlYO?f}4V z0{DP3VzcahGOC+V$P=W_RL!xyI!3Cqx`cKth@- zN*~tqy}*sd(_u`y8wDz9wNVC_u3WzJ)~mtgJFf@lGG&DQvM%3wHIOz0GOcdo*bC5 z(>L8;BAx#)H>q+sp&CMh0x;AoaWBmilP5LD#KSj&gs3Rj6D4FRB;qudE|RgJr4+t6 zCi0&T8F@%k2oVOi1CpOY6$+=PG!2D^pZwQBDf0RuD01ON~F>~Sv*@iRZc8$N3>d|kfw+*oaiTy*obz1 zpLm0i5*!1Pgb#Fz!FOD@(~Gn*>+4&$Kiu2ee*ayzv$eMca@5aQlU5ouAbsPO>-i-2 z6m?n8JwtJz$FUnjuX+&|urt3$dd`T8P-k?Iu@8kx{CE?EWaZfPJ)T>Kkr(EbA?mw0 zZ^hUz2ONMBQ9e0w6#2OgqTw9NO%+Q?m5rpzEw4#|V@bs-HxERd&tQ4*j@ewmDiL^1 zQe~doLszJv1!Zt0d;Gv{>6uuRLiWYqMR^oH3o8fMKhbo<{{6(cN5ubG$31+%6< zkKdeyr+U-GXw%l4#;V!U=XB$r3-cKJr`BytPnV~8Jt=J`jBf``1O``*#PZ+}Cdzr5r99P0H5KTT4zQOzp> zwhDL$IJ4XXq|Qt%0#x`&Nh$J1;GQCvk3^{Q3f@tiVZPWe(l_jl9C}0X<59jqCmz*f z(;oRI8MkE>fBD=^2+++lYUFlL3aB&U9YkIa9;*}Icx^#?4mYeK`{f%HC4nS6)I7Pt z@&)wy_^DMiHN*OkZ5sOdul=u9BVB)LbDl-hjTw4TqO5kDyx}+iB=PCG>3~nJKY3!& ze?G&7>LExI`p@$d`aaR9g=(vEGbG+u!R9IJJa3M^Yzy~ z_q*MehO7IhfAFv8HSO>8as0CI@e|x?9SzsG9%!LH&~ z9@x6}k;W}vd#v%A&>xwDIyXMnmh5jJNn6s3+Q++o5ZtA=hiRM$kNS^aq?;~v8;#Hg zI@h32hb{wpOp?v*Eo)HwR2%BN&Kr*#1DiK_>#;Vdiw19hsq+r+KDGwUPc=&$w4i;a zm|62>;%)}wW8q3~yO?#S-;OFfvfr&SMV7H-+fA4hiS)&uP>j2Y%Z4%zS)59CD1}8Z zU9nX8(H3)AC8N*oD=|z1HjE`Cgd*G&k_PN=$82A)#0_=@XGzT5%{Wak&l51pkI*&p zy?)X5VlEshTrT9GU7F)4cMl&u9(!phqQp)7IGWmA%qHR>alAMj1%moLlN>j_=$(8? zPjwPaqCL`{>bag9&kWj0_(i|FWE3@(j)W{4j^jsu;y6oYVJQ&|i-tS$on0|j#mNv> zIzb$doM9Syr0XlaXgZD;xJo(BSK9MyE8B4>R%9AQLay9&H^;;HVC|zj_wTHf3sEbt zc)ULv%O=d~l6?jmP0!{J{afu)|Ij4w5EL|Q0e2hsE!^s#x~ApYj;30)pe=1;5CU1VB zJu&z!pZn67wD^fho1dI?_^C-ZZ%k&Adft7cPiA>9@9x_2Z~S!Lg|0JC%*dj*$sC{0 zXV7ytpMxB0aso3>V8%(lkhjr)F7Kc{pUvLtEjH1kt=i@*7q zG3n*KofiN0uJO4p|D5-pwx4N})A?!67PNKklVoy+e8O!iHIXM5MXK- zVVaB(T$>_@_nE5@h(jSovo^*#8jHzBMrSOtz{IZ9skmE9X>Ccgj_k2zP zOJWhSd!o+pT%EfV^KDYt|u#H$|L@a!nxe>=ml_1#BHD+oY z#nCua4BQd=N{vJifIC#5Rk2VS_djx0S!OJ=jQ=W5nJWdPvb8%{wenTsPCbGZ?EA?! z+Y*RO@JGp5aU|G&EctOO$86J87#)LN5jz=brN8~{x#efEpWCm0fp%Rv?MO+5#1 zyYROf#gT%qvUk{qyDP9vqCC%wQ&=?Jo{~{3FN-}tPF1DEy@Yt~w>~^@iPJEh6yKYY z@i8twV4U&TXDow zK zf$mzGY{$~i%HFJl^(C_QrOSjMVDN3NlIm(2hP5*2>=1mFN0OHuFI_Q(d}#^V0S8_p&o@ zRRV90Z^trB1NXh#ckZsg{ajx{!BF2WGo$+EFJS(nj>rXoRW=LoOBQeLAd>w(v6{78 zWlxFP>~w1busFg?HEFf5E4|r+`y*udib%u(qK5FU(lB%plbd7aa!x&dgq)5&LE@{5 zfn?iPEE4;GJ6X|Zi;Ii5Qm_diKApbm{uW^VzmZ90#y%As5&-}~UghvgJjL;J+ol>` z&=IU6q^C;AibhZbNEc8ngHnn>>`~|+HyJ3TDdZ6bwcA8KB`1_h4`?bdNpkV;@rhXE zp}=KK->rOcESyQ~k?Z^H&X5#=h{!wf(cDtzGE+XHdW>FTh^Yma;ke6vb{F+jrJf`n zt)0p&7P@0vEmOcJER3e?$zwLAo!_LWfk`QnfF#;OFeQj~y!W%yv@wf|Yj^H#tlj+i zhirXqV-2CvJc^mD)}jH~ySH6GAhIW`tK#o9d;>N{2?n;JN)2*mH;By{MIyKv9VG0Y zP-&2?E_I69o*VeQuy$fUDrzGXgGteQrD$`KI1UOMA&D$jm@1KysvAjlThWjLNl7KD zFtHXhTyMgWl*1f4rY%=SN|7gDg*LSm2nD-iuJ;-Lb7Q35efkZ4jptT(@M}a z;#bL8a&Otpk?ZQ>KzL~)=tFIw(R5q4Ov`NOGX|c9ZJg5^`bD#4^vssk(2alFbxXG_ z+-Ac(XU^#j^G&^B&6pj$b&PXnOSg@Vc~9#oSVR^ec}vEDDC$$H$xdvIk}KEx*RLe0+>Ech^ZnheKmLKPzkcBV8r<~_ zZcS3NR4-~FHVWPjk+0k>0H>XZ5C;bUQi`G#x(7h=u834o!*RwL6(@!zcq4D@0NQSl zjEgzSc2tWU_Q)TSa2uebA$~D)3k-DY=s;809!^6EnYTzlTK1WMd34eK= z4E-@RSE+e0nUi`0;nMR$6p@=*Rc&as8i zF-a0cC1In1TXpC~2MYBL(AM~*{AZ~#=u`);e)*6DE4hZIFk&Sy(Mtzm6Zo5ba0YNe cL{m;ZrtL*^G;UODe4k*Th-(1y^4Br{3!2aj$^ZZW literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/call_babyagi.cpython-310.pyc b/classic/babyfoxagi/skills/__pycache__/call_babyagi.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2deb4e61c5fedd5d5dd6a76d062c70f747cdf55f GIT binary patch literal 1718 zcma)6&2HQ_5GM7v+8?L3Yotwq0z!Mq0`VF}(Hw%Ht?RM z=Z7hp?dC2F{TL>K#FUfR;S{&W9(N~nLSz308B^|Y|0;|F9`Mc;i90;xT^Pd`#3OMR zj_TR|PLk(eB*)WalG#BD20OmBX5Vc`8Uu*TNn#=;&Q)fFj*`eE`Yhr?rz)#VCd()* zjf|2qlEXpxVj`|wv%8(GRi*5GR*N;AauR2Ee(j2J~< ziM$%uc{G*^4%H%2>B-0jvJz#IrOVtA0-PhnIO=ui40%qj>734;8wWv#4C&#}vA(V= zp=`)lR%V8=p=&)1$A(Fju`@B%cD+cllI2oXY+RR+ge+fD+hr`x6RjD0N&b4cbs`I~ zrRuU2Y6~&jl^5H4``}v3GRe0ZwDeXR#8EX>9f+)pAv(~l)7eG~^3gJ+6cbrC+yQ(d zJMcE#nUiZWr#JM{x0MhjiZSPzhvs>8gTSiwoG(i9$ZZBc@RSsG@@8IBL`8Q8erKo-Eb$$a=LF3W)3}nG4%VWocmJY;97R&5uW) zY?@jf%gv7-o=txI0o!k%WxoSTuPA_{up4%)Uq~);8x}%q(4Dq@BMRVd0-J^H70CrN z@=TQ4`UX@`*|o6}Pis-8Q-<2h&D5?|sK_!E%P;CZbRHq)GtK zAl?(1rEp?raW)9jKx&J)pz057{q{v zuJ?D~I<)-+e{S?A(ry)j_j1^z7iax{R7Z ep&S~}o8^|ze+3pkzD?p&ynl`5WI#f?;rs)_&*qK* literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/code_reader.cpython-310.pyc b/classic/babyfoxagi/skills/__pycache__/code_reader.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8fba661e282449b0a6917dddf0b9f169b7b9acae GIT binary patch literal 3675 zcmbVPTW=f36`q-0lFOTJzNbaAh!eLg5;~3Hwh#ozvTUV(QG+O`VWRG0wKJktTJEl9 zhLX5g0x9Go?n6-Yt!W&9>YE?>5BdlC7Y2RHQ=aqG6is{1l9XsCMcVFi?mK7B^*h6) z*{l(GKEC%C{?QyEpW$Hkv0-ob1#gdjR1$Uk2M*nJJ1oNLpcGGv}17wB&;s&ls+Wel-s=Y*y)sc znO7c@PK7(X3a!Iyybf*kLt>Lo4GwIW+FHWJd&1{JntA|zPZophSF5fMm&@v(1{$le#~7dbYI2_K2&ay#9G8! zwM{umMeK*cR3wxP`=~)|)e=;i9Fq~vY0gd<>Tr`hqZ=(|N~)g%!;a^Lai~47Wf@zE zXxlh`8hU$Tpv+Rw4`VM%lGNMo#{qKtjGC(F1(C0m=RG4|-n_n(^u%@9k7FUP-|@Ew z>&emTJNJRKN@72{UMN6apHWLY9Y_ZlLoZMaswJA8J5ynM%20q}OdoI*QirkzMZHA{ z$;mEJB@`z|@yLj2Yu6r?boqqhJG6C$)7<`qe9Df|dBAN#u=Uqo>uD8i4QnCcrKk3= z#LL4nXTu7wD%4dLVgPF+0paqpBFoS%E%S+WJ~tG(HsE!$A={;_MB5-uwU zhQ3bwI-6eww;N=_oye5^c^v}J^<9;UAlwdvA^3=b$82G zg2PHNkLS5ufL_A1h~O;|C5LTyH9GVMY7(on)Z16DUVR@3p33W<%Ik&z3Ci70A|Rob z-CEYDJxu!2#Y4a{b2Zpod+*-nyFcI9ytkaa?*0$7Kt%(kMKAjr9IN1jghM6AG6041qe+?Ha$FXCX}A%I1CU=~s- z6wHX}jJyQIv&*aZUDfXbQ4RV>%dVj^TVBU;oB*^Y{g}7icOwCIa)TWa>=l+; zy$?wG4g1@zMN>NPqmY|RU7@{G!<;#yIfn}czo58Sdw8y4i86N4a{%L`anJLRG0!ul zREDvZpdVu9)&TR@P9wsU1K6NZ$va6HfRZW`bpa4-a3o_Rn1dxCb(lqzY$MU@DAzqH z6WN-RRU}(ZeaH{WT;u{i=g3$Q9b@NLrkZT+Lf!}uglU3QXYzC|h$A246NR0bo|{gO z33nEzW3We>#7clQG5jDi^-?XrgG_+zleKCgd7%ayyPZZcF>xe`s6DRcabHCoiJ-4V z69(!QRAp+@%WRJR5L%nQ${fp~_E%+_(b>2B_w&^`%2YE;)wu zf$8EmYz6KZFiY|h&JIgC8(6r<+9LX>ix70g_0q4nw^IXu{fW8XCe)f$|yS zS68|s7DuVP^%EqFC$JALlkE145&uIhwpZ}76scR#0noxc1KlY(IN$Q)L5oUssb{oP z54&+93rAP-Hq6Pw`)X&Kk%KQ#&TAz5@&9-J*Hng%wh3iM@k4MhPOgC3_n2Zi9lQz&vBD)5)rpfUELAMHsrx~WLO zctop`N~2I4ONq2qEAXQ;wp0OT@@BD5CW$sSfC9iX2CwQgg1#&ukB5OaH6#Taz`h85 zQp=ygzA;aCv4_f)SD|_fih2bq8v^w_dx_3bn_YxHj`3w5IJ8FDzicae<=eIXTqvWv zU|GX6@E}fytO}03OGltj2t5czZtXES&uzF9T0>f_Ssa4(gbmrg)hcfo@_u&hzq_$! zFMrE-8Edy&cA@-<+Chi0-xJ2(O~M$K5U|gbiE8XL!8CB|KY%oc26AF?IJU*}NGV^1 zt!Q0^Fkk~3BK9Fh0uKHo``!!6dm&4;>UgV1Rv;(UJP*=yzsOGw&)e_&(PYIgn61xT zWX$69>JPWA{XSe3d2v&h%fLlp;({^Locsd_L=<`6jC(flyTTt3axo$b0UQv)2FPw8 zz`wR|UgB!2WE$feT)Su_#$%B?$2!GtW=IzbD&p8#84Gj^;ZET{i3UOMI?w(Mgs={Q literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/code_reader.cpython-38.pyc b/classic/babyfoxagi/skills/__pycache__/code_reader.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f3308970969a34c6e0f510ed615e26c2391894cf GIT binary patch literal 3654 zcmbVO%Wot{8L#T;ndy1hYwz2zTD90DXwYW7A}k=wTGrV|v=)5*Nru@`?+4;1A(nXmLYA$|+}ZK>WU%u|2y<6hY5abv?Si`kue< zdv9i@$?*K^v(LmYXBqn^O{O0kle=j87CONMkJt|HaZY0^vU-+zS9+Cqn5YQ*hzVQr z!%EK<)*9tlEBZxBz8mmGb|+gv74%-ulya%M5tttC2HvUv2c~l1}fJ0P`g1A zXEM%ox2z?pjQub;5s4(DF=>$A`aHUf9kOFy@WOg-kq+0`3%=U1%BmiuVA%1zFb*@% z>r_fx%V?u?{515o?x+kmzB*4i77hx^5duaM5 zXRKgbOgBl8f&^p7R#w@vkE>bjIj7&q&gy~}_Q&iK>yX?A4im!4U-(s?HDG1zg-leR z*`uncjcURg)kVFie$0eZ)X+DII{GHj!wRRBPg)~Kv_xCXJhQ}X;n2HpjT)J=)!1r^ zb45eUiFt8eT)^+*Z|kFG(cEf@3kz(kEiNsv%a(Z%?1>*d>R@kDaQl z(zZqb46<~P<>yvnKN{xJoye5`?ID8A^njPSHf5A< z3?f{rVF*9Yx`=;UC6O%a6ErF9ftD(t>8IJ7Z*_0K2`5=k`hWftJ%9gE_>U@MpCa~{ zqOMaft2@BCsC0Iu)_z~=vV}W=Cq8*s+1&9Dye!$4v4*JtA89$iq2%sB#=+1d(2BBQ zIhQJ_u_8}a)D;lVudY0D^`H-;8T3zR>5@T~+>)z&=^@SI1aO)RV$pTqjU?>k2AeY2 zHkMj>1oZrp^WDyTS>5xaP?T5tGV@LibLNQU1==wDLUD2C(Y>XW=+ZVL2e=-MN1jKF zd0tsfRTyUq`eEhlJ>dMk(}*Z*0S;uO#$v4CE&ei3C z3nfk+-{Ce!uT>oG{KvLy+?xLVyVkhKXDy4jxno`9bKK$i`Keqn zAsd)9LS*q!Ua6r~(DYx?g=~b>js$=@K>*(Swcj8gQZ{_f@3SXIKd+1`BLKfi_(<_b zPGR3?he#fjEPiDz1MIQNiYl#+s)aSGWo*kjt`rr}u#Roasz^EYq7HiQ$SE8Z7f2l2 zb@l6z6}7@yHvIw{)plc|^Q1j$2wH0_v)w=LUN5L>X|Aey@T=y~m4M@P82E5$&+FCF zp^$q5??eJaOM6eodjUn^$wOn3`U0AM16?19@yT_P)X0Sj>d1Ktc@Oy^*Y;EP2W!{H zoz_cKNRO0u-y$kk(SgR?h(Be=jI{MLB$!7R*U+X^KZ1h^wgAO%TiP!8 zF=~+KTwyd(eZ|12F?n7=`hz$QB734tBU8yYUI``j0Jr>*ZlW5Bq-ewAJ2mw>rlq~_ zN85_bU)B}yo{+1m(wweJpb=#a%Ns^T|00K}=fvkJ&V4w{Ai7?2@ zCXvDc*qUHaW$GQA8}oLbMxr{R8SLEM%?TnMQ(Mw9ouMoqGu>A_IG4yZzW-jEkf%v?HLoyz!oU;_mJbrB2Fy#$I5>hCe>>= z`VdXiI`n4&_6Q!K$Nc&$`g$cnby`%!6C+4RP0vGS9+=eB_PpJJA5C^_!)j})hAHPy zDt#1My8~2-!j$KVVA@#JuvN}X3jRPwk>^c$Wt+Y;<|o9ZKrrl&8LwIGiu1a2QPEzf o+G~$Xx2}09f*X@G$NHpirU)D5m;iQ`$Ku=}l+zTxq!`EgFJH+83IG5A literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/create_google_slides.cpython-38.pyc b/classic/babyfoxagi/skills/__pycache__/create_google_slides.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..66a7f2bf5a661accdeff0f9651ae07b051766f99 GIT binary patch literal 1888 zcmaJ?&2J+$6u0Lqldo=fSyq&ScBH)!Bog3+s?~~8q^cYOQng5!gEbvHjaz4C)}Cp% zQF>~nJ@qd@NXc<8{6W5Q;sAdD2bSlVqzX&59@&2Wh~N9Y_kJ(WyWJLo_S>I-vBwRB zez%MD=ELG^=z0tTLkvqa!31L~JJOlB#I0mkdJ~`cmF&rY1pA2jEVxE2;P}cV4c1_x zz$D}i9y0IBAx++#x0nZCbK#OU@9_4Oeb+lgz1AO~4fU|`4^M@ZAhh=sXFM%LEVbP~ zDuraj#9mu(?sB1bK;VdAhKR#3ahXFr<{o3>zd|DnO8x7Q1T0{UYXruHtO;}Y3VDb$ ztpVl%D2xlfpJm6AA40(j~-W>R9CyxpLe4o%VeZ;o`88`%2_1RsJbiC<0y{m zN22}IvxdiQfv%S*A)tN{*jgurR(}JGpEKOEu zEoduy%UOQ{LV>Q(9MAB~SvVLK&fJ}0hPTiH&k*$(g3)IPusTBpkw?A2_`1xwG9jfR z6$PcnvucblvzP&T{gw?P9c88sIBx^v8c%aMGGUwxdde@fc`%7ZN@bSibX2B^#rztZ zCZ&mtwWjnn`unTFIGgZ+D$|s!!8h^I#VC8ZyMOTfV4!XE_24eI_wx(Y1hd=FbpQjx z8+a2>xA*=RxBd!Gg6+L!v$A#A9(4Ty3BC<%0ufrQwE9Ua1euxG!2rjPHyyZ(#BVq7&FXUpgc^N%0^UZh(VB}hP)(@=dh zLvv?=AZ1(zeyH}{8~4(id2?@pPJBxqfmv{a=fMJ9`ZJ$3w$Nq3!ddVO`qhDyY7W5x zHD{c&F94XuXZ%7Gl212>{5<+$jRe4F?ljKtWb1RPTdUW$}~l`pen`^)F3>zvM|-@Lr>Gy$SMI#S?8FF`o0(rZp{MeM+;k z$jd^TW_EPKA&Jj8*{JcYVoEye8pyr%6$N|#8&tIoMm>UFB_6^7_M8?D-Ay-i9OS%l z-n4?ypMH2pY*!YyEKSpR!hxnON+%gBrIkCBz9?h4Jn`&o+EBa_C34F?bAQE6s`!gi z0DCJQ)y}eZneLLE373-6HPi3Isj^9_Y!X@0X`8)am5YJX@x!1xsp`ozdiqmVDtfrA z8oSHN_1)q3LptV)KbtUI*g#srX^r7Vn}E0Gp!9;m`t(K38myZo80Bie3t Ru>C@3^HUo_&uu|L`WN754PyWR literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/crunchbase_search.cpython-310.pyc b/classic/babyfoxagi/skills/__pycache__/crunchbase_search.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..544fe804444524de9fd2fc5e1a484e112cd77ef2 GIT binary patch literal 2615 zcmZuzPjB1E6(5pAilSu4cHKDM1}OVy5$F&JZFkec>v-#RyNetev~c%O0xdD-j3n9= z$;=FGOQ22;u%`ujZ8r-z$NdOB^xmT1V6HvoBeYmx_dQCEoTe1`IDg-~dB5L#PsQ3= zK;ZfNzy8K29YX$%o!bwE&M)CrzW~7rw=y!ZBFnVrC$d(TPa z@s?-}ZSM2-a~k=d5lW&q92A(1BU$Ci_=G8OESOBjW;1y`602rt_M5{{p&_9jGaW7l zg$h?&LzeSU3SG%ug=s!4(8$U}BgR5~o=h~??58B+b^#(KO2w$W+$Ra+o5xgytA zZPoTm8{Gz0T2FhnaaC0cY5X`&^Hj%i&oNYqY-oH|rtz7WDzi0VX&z@qQN}~K6dLfA zW!iC^WK5|z{)+tfgTc6%hymO%7jp0sJDGl5TpoV%1XQacXW5{+ry9KFgMK-c9+-#l zD;va?HQQY|VBx#|jd2NH>E_vlMho5}c-4K2keZwl<>J+#WpQhl%xO)oZEn}r7r=)` z(l6bY*1Q35Wb+n6_re}oJOD^L+Ns?u3wqrb6yzFTe?jLSzf;pM$d~rKrRk|#w`z}X zd~VPE+CTO9=9w*@*Zy_uC7HK%>)Nl|px=X5yY{dJtGB?CfPZJ+se@mUKlbO{y34nB z33uWBF3hdn>feR_`m*mq`wrg$tJbfq9m4PJka`WiyE_EN?lfcX?2!3J-KjTzPUf9w z$93nJ=zxC@Z9|){niv7F?>{xc%KJ0^hoTCZ6u_|X@JR?brb@G1hnlG~!NZGGkHe7w zG=W|DC}d%gi}0e5e96=BStaB&M1pge0}6|9SY%mofy+_LUO4-6Km4sM&QmU6Nj7a3 zEoi>n&J<{tLX}0XL_hfTC7YC)2!G#fUw{uJ+LUp@ zr(lEI_|v#qI!X2Hqp{Yd+8Yeuy8RVw{iK-S(BS;N!A-gtypaVytdx)#wvQpr(NhS% zSO0(`jgObrsZfSQK`GI+rspuHdVa9a!m$*?#}94{xwyFaUqfym zJeX}g0MzA3=*JJ@lMKA~;9z!lf4~m*2aUo;C^#fLn62**7OlnH?9s<8%>Z3pV78N~ zI2R#jnuSAIOx^(cSTkLzFe$j$3ws@d_NQF>c*kumE_Dnp0tz9Bu`Y_tI7&l$lLGv! zv@*0Tlr|k8JLrMM;QM!Qv&*YV320;m$cyJ9+FF4XFB7N<8cfn0)*6b}GEOBkv}wV6<3X#+w7lOOw$K`R4PsM6 zb2bt3`!FT%;5-GCgzRF!5orAe z$iSyIhRVsIiiV$wum zC4~tl>=HO}CUO8+Lp3YS=CC0~o=jsvmSuWsHXyK-$P;lhBJn>*&odqbP0muYegxI} z=rzlXmq>wWpp}e^G=W1~%j2Q80+-7kZCE7VM?*hAF+lMqg7OD2hLh?Z2*BqX&#?iw z)zACSb})PQErk9b=i&bc)N|xb9Gf7H!7EjU?QR@Dt5~*}k(i52=T_EhDzQZR$RDCW zo6Op>&L<+v;`o-?Sh6J&PC6j4h%3C4PbhG&Yx~aKelr8E>n_TFzxhZck^B(~j68XW c0#)47y<)b%zp(xQAy#;1m-s+l-`cjn1#w>dfdBvi literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/crunchbase_search.cpython-38.pyc b/classic/babyfoxagi/skills/__pycache__/crunchbase_search.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1826909ca259623c9ae99bc44af51619dda8c6e2 GIT binary patch literal 3166 zcmZuzQEwZ^5#BofjA4eH$8CEfaS*iE{QsqY>qX}S~RaQ>@J2WkU4f(#r^K(qs5-GTQZ*>|ka z0L0a~|AY*D(cq1z4i9+qi8ly7Bpw+w;h@lTw&f^`5BHc7yMoF1&@9C-IFYif^4E8% zLPe-PWSUNxLebe)$}&!+&?A{Cnq>RA95S8c8D)FJAqtozT zEd$ZyF*$cCr*faU=r+*O+3C8bu11BBCWxXWOLP=OC|lDWSl)A<(6FY#}Hm5Yy^6~bRzAypgxzqvwycg6Ck z?RRYdTPtL|T(zp@pOA6u;XlEKmbDK({Ozg^9{x_%0{Xi3aI0$V;tjol_M@M0E#8d< z;CFY*>q;}Jlk9-bKr)SlvYF+Yp0G6GCY;Td{DXW%nG_Hl^zMBM5KKTwrm1G?Sa5op z=tFuS0EiG6AehlS6ZABfd@7he90_?wF*Z5OLG%f_pQmYlipx>T1}(qr(f4G2l5hb_ z(lfhg5~0)WOo3!6RFP*&^yoV|MRk)bPDilEo}a|_SVhz1SkQF{9u6nmpjxou4Jvfp zTelI;FYjHSMZ!7=q-Xg^&Q1z{@rVtJRM7XWqLWww{Uv>nX;u_q(>8>zW+~b>6V;H-TR@HSvJDS3KN3)nNIc-$Pi2FFqb;wLd7yEFbTk|Ff9&pnFwXtQ|ls~ z!_lU7D)v;OMKqG>AdoCOjv!kNYD~qDbOMl=@(3>0Qy|nd3n_Sl(o7KNU`CRuu4g=b z2D#}PMEanWLyzfP;wB$JF%s(e4`HV34Vo9yl&QllgRXUZ!=|1l!$g<&4z(`SM!yek z>CJ%Di}N9}`X@i`U*z8YrIdPSq=dvAcpGYlcck*{`QPA36QENxT1`w?NL0;^>Q*nX zMnIlUlkB)`?*Jz5p0Ffkd#NaIZSv$~Ym?DKDfVx#U+8sudiqtp=Jn{8%j*D3c_8%d z^=K~zXRdFRYny$xwb{1X_@4?iR@zRp_@rLNT6Etp=?H4OiO!;7U zG#oOSl;SIaajE_Q_@N4A>QqRobGs6q1uzBIE8U|H=0*zQ>D3;+KUctDkGeQ#!#KV2 z*1O;&z%EQc!2?L02+EmebYJGfOEBKmOpg?eb1pV$w`J;uOfqfI`3aW+UeGly9_a|~ z7vcnw3ZTqWQ&SoW-Bd$mydqcHv>C-UC9M!11AZ zwXnA81hoYxbgnt>e|$f1Lnm+-p)J6C0Q}H}buQ`~cUw#T%h&0b#s0D9Ze%Bu?OkRvV`zH}X^7}|IFeTb-7tTqZp+4B0=)Q#) MtO6htNC)o!0QsV&uK)l5 literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/directory_structure.cpython-310.pyc b/classic/babyfoxagi/skills/__pycache__/directory_structure.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f57ad2cfa57b0a75635fadbb755db30f3424e409 GIT binary patch literal 2419 zcmbtV&2Jnv6t_K|+1bs8R!P;gDIY5+g+*e6)N2~(2Q3IG5(?rlq8d%d-ej7|%*6Jl z-PMd#l%SR)|G|b^Zu~vIa^lPZi3_|pv)iU^Dk0&KpPxPU^LxMd;e2tiL!f=T`kT1X zCggXVoNgQ@UqII%0}({fl z-HdU1x;e9nUI#{!fC>^YLGgINpOPW1h5CUjT;Uy&z!NRu!`KpS(Sgx_N;nDHaMglc zx|1jwn_P{zj2gveq-3xx=4+vzFHXL>*)w^b_RLOXdilr{Bcov=d&4A^y(kmCVV(-9 zde4va@?kH*O?lBPA|*33+cJpf7N8->BTzW%B_M_zk|`}|$&ML9wM9~tsfOhxGTrnF6~807N&V#gu_u5n8bQ}xW)ebkfzS)!9Guj4nED;T3$ z^VR|eO}q_VUj<^wLpo)~Ii?ja$&{BII2Sm#bb<3qZ;L3D(Dy9MwdF-*cC4>tlr~%# zBJ^Qsm$#(}0p+kk83x$6f{SMu(QheK#d@$g+SB%;kcG?ycqcTG-VJB2we9@DLkNuI z0OBDYDRf_`W0e%9mLHsZ^}_xn&E?m-(MZB&hAIo@TdC6C>uB-XZsI{oqti#VfbY1SXs#zvOqwAuTzi z9r=l^L%P8#DP3Fz$XVrqGuYHA9gxAM9A@Z(R@nl1=PSST)$P(Ry#uN~Pe|#N{(4O> zlgit_j&kmMRa@X%d!6jx-Cro{MBW6~(ZOV49uN>f7AD0wj^Z8gT;LUBArAzMxio5Q z`GL$1VhYC18}b-)%h2^TAlsxQ_s^llwS8d!k^clmCwXONowQguSkmw}26}V(nSI z#9*x)1F3ol?`Y`=XP2pE!MA{~XkAmefRsLF71Y*};jVqB?`>N0-Q?ZZYX6t%5NiLd z=1*4M@PEPvgT7lkVFo{7nMHfjx`j$I!1iIDWPRS?{~TLHHCO~sMOa3gvg~K_QRAAI z3#7uA?^Edd0uW9a?bdj5hXcld>tB;=Z@}}FLfr4*6N%cv)6faSyf`n2Hd|dR(Co8 literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/documentation_search.cpython-310.pyc b/classic/babyfoxagi/skills/__pycache__/documentation_search.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..90db45fe8f5bce0c4e9276629e2393aadfbef36e GIT binary patch literal 6452 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 literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/game_generation.cpython-38.pyc b/classic/babyfoxagi/skills/__pycache__/game_generation.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b5a7685424b8ac50bf5eb1614499c51dbc2b1fac GIT binary patch literal 1651 zcmah}&2Jk;6rY*>@Y=Cc8bqrikftYOsd11Bmrx-}ghCO7L+F9MjMh72XPx!#c4jth zEayTZ^~7I5k&+YYzcg1)+~CTg;=Q$`Qh?`oSNdMf%hQhsRl- zAEdZkZ{BiYc44AOOeKLQY3xY`Pk3V*2QSHpN?!&SQ5?!pwk}BAl96n~7`-GuiQDi| z$8GK>Q?;)Or4yT#g}XI|wHRM-%)WZe4I07iQDXV>KpCEJlTE8!aa7>7$%--m`iJkn zeZcn)48D~6#03+X|h zxxmzw(k>DrD>5rY-*diE`N%~{m5F0@YTVW|$qJE|WhF*+k)kiJscQ?7=7}*vye5A? z+&LK2%VP#Xq<(AnL8$7`@MDQ(X2 z4m5NM`yO=j2!y1PN$(XyK<4x=VE&4Y*%`BBLMKdyb0%9qvomk*O*|RhBNJb??~%*k z3PaF2aG^OhR!wKW{aDsK(TYR#5fml4R3|Ej;A#$jX2vFk^z0YglKHAH>@ znEATQm20JCVO3$BkGE!@Et&Q|FOJGQFP|f$%Hc!-m?vtN&wAs^Za*A+x@~JcERhyd zFe`Tvm~WD?GMYlD^=%OSP(xM{x48$T-CKBp><&|{5{qUXm07BcYb|wwVit(n_jQDN z?dyNz4;xZo0M@cXtiE)SR;DToaIS}+H)0c?*jlBExPAZ+%{w3ni)i%Y1Pm-OqzyYpTJ~JCd70wVJww&?)^f3W#>?B=H!yiy(@a=!#PH|c_ZJ3;%2m3>V-;ct5CsoK@b{JpKf{`=(9UF!n3>#&ieh-Y<@FHd|N-NqU}-R)J~5L!K}Gp%Hk=#6jsKK$-_%gT|eJQrfk+zmL@ zh_FTk5Q)a#2(0yK$huzCYfkz>++CEFK{IG95DQka(ll4rUVY1(=+VNB&yma~1kt4t Gjo3e&j>_%; literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/google_jobs_api_search.cpython-310.pyc b/classic/babyfoxagi/skills/__pycache__/google_jobs_api_search.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9fada38230b4a3f41f27c677613430b8c8e8b110 GIT binary patch literal 3298 zcmZu!&2JmW72hu|mlP#`M75K;9|QPMGqotWw$)lL+9*!qDvssUb`X?CuwCv9$rYEo z>zP?gB&d@O9Iing1PqOdk<|mJw|U#5bs9XgXu#~JZg+vY53F|MC3dmewyHfCC25LI z>vo4Us1rp+$|<13p3mdPoMlOAsWHjFu zWcwkxvesFES?^9FSFd0FY3J%Xxpe1O5AS|YFx*15L6w%+;K74jhJM?9(CinYDRFSmLSpQ6L8u1&43L+!3h9pK{* z?19~_eQot^>e1S>pzBkg)}L9xDWDCs0d2BI-=Pb%^~~!wzqUN9y8vVs)jM+}KFxb# z^Yg7aAl-8V15dbv(J(5KP#B=5o0~*v1W7;VWImQ;1M`HX@KH!S3sMR_EQj#eC?Z-t zf&PxF1K0v8<0F@}XS%N`p<8hlLRwF)1AAuAoI^(k9j4mbZAaBaSun1GFif&UhGE-P zo?vNT1=`9Hn+SDw93@$p=6MnJ%Pf{jo_%AhMi|CvBt#f~WBuoY)uG0pms!U6>W5Kp z@=3n8d3y`0MV>|Js&P`Ro{U1LnD78LYCDCaJNEQ@C!p9lGBVb#9bGyv>bP&=7Jq=4 zA&w)LFHmRZ{28o%;qH4G#`e9LbpYniY>d`s9;E-Gc4+U{q)qG6nbn@!=fU&y)>EA~ zjahAG?YOi_7oL0jzVv4PQ~RKHXzkab$Dh>?>a;bm4jpI+jON8z0G&%OT#PhiaL}Nq z4xwq*0NAIul{NhxlE4I!vVvt4xjbIw^TE3%NsCF2vr7d=_<{7FI?ovV#{`4iS5ADy(>hO#@;(QopSM4XW*Bb!^; zS%7a*WfipSCUQuSvW!QXh9r8MeQ#RPiv(WEGh=5zPvMPWEnM6n)2}*Yt8b=^&*0NY z%#%2JL(OHlFH1hx0%tOr#8LXj5{=O$FF96u-GyURe5tdf?J1vSgCt|heyr*h`<0uO<8I5OEjBAtDWaimS;v55 zdtkW)gi$&X67d=LkV;jyR_~C!PsVsTK{X>fvFEAIM2FmGyF8I|TD-Jfe2A7q4W-8h1ute`=1$duqYk^|3Q|UXiq97 z(A+0kF3B)5D)hFONtvZg2oj+z&G!;SL=z%%vO<*K7NMZz=!1olz0a1eVK!BTz<0mI*f2~Bj#C!OZSix;=HKe>1R?&ia-d)ph+mKnPL z@#pvMKiqf;7gJ!^Zt{kH(Jvw%jfFb*Dse+i;&7h0LN)T<4w4el zQA=+wQ8mLmwKzv6tQ0LA-}Y=}Ov30ZQ~WvddlCPC)@@>7df}upTIq;&sXe-C=q%tiRkVlBz}k0a_D9xN@9(=a z7j@G%n%w ze+*~%Wi3{;=xDL3#hMmZwD<``+u<5dT#X{SUEj2 zDCEl3&h-_P zkWsIH{=b)-Cx`8%iqvlDbCsra7^-F%0-Z9|{Z<%0E~B)X@pNk_sm>f7x=;rmmkDRo z9E|w;u*cAfYf5o#o?5I9U@=S6Fg)f;OE1yYg6pKlp9M>th6q4KO~#fJxB)qL+RVbl z*8FVNF%8!>f%{tMipe)2l-m>6xTc#%4&f3~D2FrtLp@X&aqYP7WdH-M*;eED zdS=!#3F^s4^HB7yFKunWdd$zrWBvin6<&e{(JWq+I!^b#%D6m>l?jyu8{ljAm-`ft3>O2_;D8<@i7|A3T5&r+Cw{R zd|?f2>e0sYAoQtEo6jxa6wnsxfVF7*g-w@e=eZZQzpy+jTmmx7>a7}ykBh$8`t()} zh&nqo@I*Tpjp8zmgaK-{wMB$RkPHe=>ajE*njV(IMj_D`q!M~qj$pA7M6`GU`90MH zuq8w$2PSFDbX!wG*Wxq^X+5)E*>ijD>^s`&P}SbpvT7BaJ_=@|^Lt590nb8O3tV zm?qYax4lCy{t z&zIiZe`ddG>|1+H$nodRS54X(TKf(p1V-}mJb=uVmo7$HGI-UZC-xy}-U7rYx0N;f z1E_CYM_I{oiU=Sw7GqLWvaBTOv#i)9u^^lc7-xBc#RVC$tRxAK2UrZRCk5v$kqe5D z#}(sK!xBAN^5RKK8Lh>}{o)Dhkq?jCY*X32+4#3bMPklKoRh6v*ja#O@j@#|*-hn$ zAW#{PHTg*VCg+Z7(T@c_DsrRepvYj2;VMkrB(u+ZAH%LJb@4}e3<4;*^gCo!FlE8)g?y)V0>U_(3JL#A z`Us#!j4r+*#ehukaf%#9bU4=m&P0#gW4k<+H7&lyEX|PyCO{o%!q_6$jvq0RK4CDr zEU=+=4v2&91Wwo9$w7oTkq~;DvC{1G@ZKke94t;oz<-z*#@o|{5J(=7ypUuR8xeZj z%cROPCIpF*jOx7v5%H9Wf~=A>C%dCG8Ic6eCa^t2N`P16nw_;>gr?Mp-7WYSdQl>( z;gA8H0xac+5HS2rnb1U+eA;VYym;}}_OI^VyR-G+*4^#RS;q|B`{>iV_a1D%2J%+7 ztL&VCcPktT6e2d+@9qQ8Tngm=`+(Iu*3co#0$ z$V3Z4ONX~Ty-+4)^gxx+uaV~`uzg*e)QU9OQXKHUzBa@oGzo?swI@q^q>qUU<>)_bIYTJ4o%pK&!_O@!sRAx+dv@gNmD35`C zkQIrczH*=ce)5CK?pSI(1$+LwnDW){ed7 zP#32m?k)PxqHk}1Y-(@fRBN8Mc0AfTZ|M`Q_Jvm=XmAnmcmbC*lOhMSa&~H1%GImA z>#Io0{bKdX#(4ObKkMgjzexXyfaGsuXKo_1sTz}lvP=aNB#;;pua+d6AV0?Vn5p(8 zeu`Kf!(;F~ZV{zAHQ)>pk|)y$u*aF4s*@#<)~DaW2;aaCU0?ZyUXrSn8%Q@dzKMS2 zC!B#3P!cKiR>zRt{ zoWS*U%4AIbK3bvM7uUEZo5l^!5>hCKGyX$8)Eu#xGA@j`LpqqmWpY;V^V;n9HAlo5 T6dk*Rc+f9?u46f0d*1&5^d82n literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/image_generation.cpython-38.pyc b/classic/babyfoxagi/skills/__pycache__/image_generation.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..477e57cdf90339f5b8b12cb3adadf3f82a0a8a6b GIT binary patch literal 3417 zcmb_eNsrvd6=soaHhY;xlA|TaQcw;~BT`QiN0)&ucp?SD0D|n;Fr4s0a9Gvd>|wL$ zs_Gdv!8u8QPey>80@wydNB@WX5?*uiCAkJh5ag?3_hd9#PU;59T3)?+>-XL#TU&hx zzgvI(8~fvqT+yt4?RfH+Sa z=CjUAhjlo4;YD5Me(a2bZ^6kKk*4$UahB)LFbbD4FB7SFYPK%_y!FIh&z*=c2UikY z=i(}FN}|r^&V<-&!YE){tjofe1XuM~|D_X!Y`}*2_C9xfC+g$+?Plk2kxcm$UT~S{ zOcc#-W)|Y<#Y*-50cA2d&59}2LgZA>5>1ziEBZmni-W_r6@7g0^y&TI(1Q;TK|EBGYj^ z@)}?9eA0wTnZ?I^rJC!DBrD=vh%%lmi`3}x4QYCDoaTvAar}+*-LLj$V!`+1vVf+0 zzfO);6Y>1u$>Ar5d&*c|?OoM-T&`pYxrRnS7cYlYx35@hybQG;Ca<8ox~R ztO@w@Olj4Gi{zNIOkyPYXG?C@yeZexIHn1Y5(U>Sc;nASRy4lxWYam7ndVJ56@})7 z9);2<)_92AXfP2u<1)6FfTb!$p?K4`A)K3LC(HMxbX|H_?)XRdM9++|y}%eKF8&pf!tV>@@8 zxzBd*IOm;LCd#ipYXY@WnlGy6_hLyC$!S7oJTE89oFahXr=-wSl|0RoJgYdPS+U77 zla{IEh`JLTD;Q;5r7|n+yp&>5YDFhPw#j&LN)%&?%Ou@+h-Qi=`DwCJR1}=T>u_z# z>1n2Cv=B7UuzG}xj@6hRPJjZ)@1k7l_8>Oc3U})SzGe3)6F8)4I!lTv2=y45>`Rez z`2*Ou@qr(*nyzuj=-Y~VNzzs+QBV_t3_{r}5nZ{kSR{_-9I1Z7_vvH66Id`9QIuIy zKrpscTEbd1&Gf42U0AV_A92`FAxUWo)X>I$V)Fk^wj+lk>d70@rP`X7`u;oP z2lrw4BVh{KLNJ~Kc)3z2wMr6XgiVW4AOn*f+mBFUA0c&fZfkO2#W#RE6Iset(_NoM zafIOI$Zr9-caam(_uG8BRLA3s`+j8ZyMnbl)3)IAc%6u#Q6^O#Hzb$;!@v83fVjl{ zXV9sBd=+i4a7hiaZhi(~je3vps6XRXJ99_%O{Ld_k=pyx`PzMf;#E5+iyn%dk9D{9 zH|yG)`}0nXR>$IU@P&WYsXKFE^Br_mcN~;M!ooV((1TC64#202^K3 z>$3q+ImGU;4mZ1O%j};I>h3G@rNg$r_Re~s*r|IPin<5X-hEaLKKM5TRo6GFv$X~H z>9@wQQTM?we)$}a{2^u|zX@6cHw4vG0nDSF4Ft!C@P)@dr`2^+{8(H4 zY-Qc2;A1-u(>tO*NL_tT^4ykz*XXL-?`DfBRWg0=?rY7-UUmB}5MRG{cN6K1T6f-k zSPf=+k;f_Gbf2PY=$rsDS<@*2fLi_>dbDxcvGnq1SZI8fK#HH3J=4v}8)mT!kxX%y zw=o;_+b9jt3M32F++-Zh1Vf>Tb)t@AvD7FhO74L++P!K{X`(jjU05aBy=s%_#(yzD z)VomSYRGr_bDl0We+P`!Pw{d{Kzy_keLObAO_={92)Ea3yZF9Ew%zKM)q8C=!2fv7 zX+-2M*o+3ITQ&Vn95?+qM!ql6@Wjn9jz3!_`Fh8<9nj#SUZeTGwrOc&(~D)2Guwi; zX0>*I2>#91y0cArp2zVey-cqvO?xCw;&t&TljkAS@=4$hz5ccEhTR*5(XefN$Mz#l xKV-csZ<>+uVKYSZ<=N5L_HMQzv}Z3}Xi>yAYvH@Cnh#CTE0gQPHEs;ue*tmt`_%vd literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/news_search.cpython-310.pyc b/classic/babyfoxagi/skills/__pycache__/news_search.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..00577417180401af8c240e1f6142a11474916ae0 GIT binary patch literal 1628 zcmZuxPjBQj6t|r}lgZ3fOIZpd5IZa&F$X#l61NrFvWRLgBSdeJz)EAc%{G&yw$t5K znbWScUt$mKam82QBk&1)<-|8Y91!n0Gblwk%FpjP&hz{I{OoEni3v3G+ppr6kdWWe z={Fyo$MEVe00a@VAS;?siZ&})!VZb>g#U^NU(%Oe;t6&{=D{B@o6M;Vj?VLO6Q3kQSZZYBrF^cB zWU8_=J1W8E+Bw&c4`v#dpP94N%(_9T!ILwbHfwZVo;vris!j_z!$7kirEUr{JNV)0 zezr{$vqc-#z+UeH81jGB=#hJ?m>(oY*L>bAB$b zwB22$dC7~as`*J%W+t!7x6}?f&x%xQ&fk*1zFwSFE4ffjSxU9|Ha))jzIt(R_!L6x zs!WT8qox-GiwFsS{^^n^1-{whbqv{{&wB6+la#5gj0%macv9 zsGw7_4q9@r|yHZTEz%WhrE7xJmUnuNqS~M%!U^{7iyUm-64BMyiz^Y(%vY z1gxu4OFQnzS7V6Y!35VC$QLqej2wZZcK|}_Q=i3j?8Tl>uXlTbZKmES0~8ob!0Gi6 z41-0$Knt%0r38D!Ks67wg0Y|tKp$_Jc;#~qwLvcu&m6CyB*F@4rn#LQfp7V1T}1l2tlo5HJcJZ8{n_;>6BwY?s--o9j5 zD%%!-fj5Gq_cIu$WDN(irI$o~i3F$QJMz;*APYxzN&r2FzQe#_=y2dLayXn45xBd0k6mqYQ>m zD|Kau=B!yAgOCLj(#H@mq&`LX4B>MCxR=5d;*??@bTT^~T^?aqmPvP! zbk2Vjeg|S;MEwzfPa`^}Aq(l9=g)Bria+Qn{=dZ^<26yHE6KTyIbT(xDbOBse$k}G z=8KQL9SNy3mDi|}JLd|w+3mN&l+DSl?Q;Q-Bo@B$@_H7A}`vx r7rSq8e8=6zvhO;DwV^)T{@RP_s?MFL=uwyUHxvWo`52Rk-e>;+^M;W; literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/objective_saver.cpython-310.pyc b/classic/babyfoxagi/skills/__pycache__/objective_saver.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..dfdb40f53b2418b184bd54bc92293a0a1e6d1992 GIT binary patch literal 1678 zcmZWq&2QsG6rZujj*}++2-TME0a+^25~NK*Df_3A(DRwc{HLf_dxi+=1z^5r z=0DpT@mMDZd^bAa(kzIpIaPLH^5tfz?59ILjC5$%R2YRBKMeU%G|p2_uQkHGNO2a5 z49@Z}7Fo=5t-`((leU%|HM?bxzp->MBlqZ4$BOI$i9TJXPcX zF`zWb5>08xHAL~WZ-OXK=srJIrag|5jHW{5v|nW4m&jgXQ>QdeBc&*Pi7wt-ABr(w zmqnIwx&CpqcYI$QZ9aGirj^K|biHy{tzWx!javEu&PH1~Ali7c@^A9GSHNQH&Q+U( zK^>kgc+{^TN;E>svu>3TsZ}(EP?zL6g#U#*^%yBV=KbUh@YL6M#1D zvVz#+3Oa-I(@nNi2G7w8=LBY70nG&Zi8~wbqPLNHbO<}PKZeJi*sW%jZrf@-o(1QX zopVdb3j0aQ!z_ZeH~63%-jQ7KNXA2|hJvf^2=^?s*cpYTK`{JD1#3#@Yfgko5|`He7!0n z7OEnS0Ytw@)8mbB(%zO5n1BMa6vS|@+i4l^AW9Qvd||(xnE5RzLSPxn&Zf5UW62|J zc^HZWteM)SUr;MrCb@BhG9-`m(2$WxGDEB(;~mNbw7gs|LejOG=EIeS&Zs$XmX7~SCZp>o`cH4LnH~%7j`-1rXpAAf$$*upC zW5N3~7e25#tpX5D6yN|aJO2PZwAvQ{ literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/play_music.cpython-38.pyc b/classic/babyfoxagi/skills/__pycache__/play_music.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c80af970e69e1f10b79a5b9eba096326c6c30251 GIT binary patch literal 3957 zcmc&%&5s;M6|d^A>G{~tIO{k9(HV$FBiSPfCKOuiZAAw7591sT|5bss@c-IcxEHR_5?|Sv#tM`8I z)uW}Q4uRia2LE7xT_fag*tz_(pmPU4`4cD@VKgRlI-wNXMr=$BU7HhA*Ve?+wLP(+ zHe+YrnzShSm@tdkPYJU*JvApTGaizm^A%7eLt5Dn5285UftH(#I17a2Ax>TXnN3Ht z!O4U&0!LFg)_^0;Jvy;JBfC@|<4zp5!dlFIN+&JWW}T-5j_k4?)NQuJmZ9!^Ml3Sv z0voGU`#~HW|1_6TSamXJ`g3d-kM4RB8S~UEP+l&%gckQc;=JI(yZhk2r&3SyK!h`I zHx-`V0R^GO z`U{1P7K}W~Z;$kVvHs-#z%`J`{@V(=!7^q;f-%GZR~sDC^8F z={Y?yl~dAFdSdBytE zlTS(Mfbxu;$~c@3dzF#O%E|&Ys~mn5NhK?H9vpBM31|uaIOjNK?r|mXGAef(NVu*! zudMwvN-7I)ud)wCq!MaS6ODmELs{3x!Z#Vr;)9*I@UejNy`z6JC zuXAGHmF*jBxir|y?~M}^F3Du8*T}xb)~=B=`yB0ieW!Bdu~d8>!2OY5rnwgg?gieA z$JuTkdtkkAVL_rinei|R;;7)vixO{-gBgR>C3~hDP4Y-xc0{juhDqgN9 z!FjzO&7*|RtM&`66l;TeY;b*0Gd(D-==DmZSri7b2TVvlD&88rJOUfEM1zL@`|o?1 zNT)HM%i_(!r5-HQ@(zj>O*oiN`LQR!K#MDbiw-h?+8Pu~dzrfN-st8Hm5XVL_%%-% zkHP1-ln4+4ZklKbhmLSi(B*r8w)fI`7ITEI$_WJr&491X(kSGzYBhTS3zdknp;h}{ zJDu)xK)WZrdK26b}r3bfw{job6xgk*kY#}KVXOb5~t82h+(!HPaEj9Y`}bO5P(Y8R-Ch!qdEqFtcHJr${#Z};ni zZyZJpi1j@w!tMTov3l?_Z}rHS1>#^_6CD@79V-a0+81OW!aP&6?f!Q+H{b08T19&^ zwcUSjbF=U1W2dQrc(C1veh|m$A#6(|NXL`#c)PDe&YOwtJ`9QD3~=Da970m?ffvS6 zHcbP;Zs>^9zr8iCWw|}EwT~5_yguR3XUE0i|4E?A*7|&n0%>~ViLJryHPY4e#>Le~ z31nkgDiF6X1(9J(Y=F^kV!}YHX8#wBqNF+sbQ}MFk{URs~3Ry63r0vjKvQVLF{Wd z{1h4`*(b7tAfYIurTIB|W*~Hw1kytjK+b}3qqG*|%w*P6Qd(ywv(M>A4ibPRvhZv-I_d-+r zh=;l2qpdiFC@pV8W3lHsy*{)n`w1jFOnd-)1zrqISo@j`pblIK zi8ee@Fj?~bs^j|*^>fI0{i^5tkMkgIW-OfocNshsAd>5FzqWYCfK5NnBf*(|eTeS> zRV}*~THs@}T58@jA&+CPOE= zR|0K7ry{i%<7-T8K~eR<2jgfu(uur&9cTiV&eeH^&fMLtTIAbkT@v}Jouqb@{{Z}4 BX0QMN literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/producthunt_search.cpython-310.pyc b/classic/babyfoxagi/skills/__pycache__/producthunt_search.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5359b287a600948ee4c064f111756f210c1ad716 GIT binary patch literal 1774 zcmZ`)OKTiQ5bo}IY9G7y@1=*@X}YC?-U9u;YtjP)-A(X-&7ZBhAi?x_gAR znLWv%Q+`1lkdD6Q67mo77t*IdPW}slh*Zz6td$-0GBsV*)iqx|x9E0T2-a_>zw^HW zg#Nb7{BnW$8Xk2Y2uB=eXoO>o?Ve;LCQlJ}xcd@uSKt#Tb~xEaJ?{-@qaHTi_CcCu zTZv`omosD3C$JGH#vH|jV|zVzPth)JjE1qtJ?_6mv44tO6bE2BFw4(n!K*}nTjhFN zFqsU^N;wq`K}1y}8$a8Ol>K<5hfGIOXqk#5p`w|pigsl&iso!ZJ4^{46?w#>sg3Bc z67sm8T%xt0?cN5(zSRm4jZV-Ru5nGyi46;2#9KXLJXMuKnvl{oPc@}I$GA#lyC!61 zN)N=bGAkpN<}@pclI~V{qSGROjm-k3Nye0-^fmhD;b2&d#6VVgF67`5+c|zxyx4sD z3{0ycXW5|fQ4QV?K)*beKHv*2Py%EHkMGX8I}Kg`5?*4Rx?Ek@KsBJp@Tgyb)My_m z&pHP?%<(#!xSZ7P&*&FoQ}v2WAXO8e`!!xeJXk}MK$Cr^_G^!ab&z7d@RKtM>u}%Y ztpg&ztHU$z98DJX(4X0HN~`ldxuuDsylhNI?~jl{JNHkW2Zv%M_M zZ$7VN_P=*0?@6yquDGk6@fs%~vcXU=4#jF*8|pWIKrtDYGtFYJiH7kSe~oKh0}39P zUY@DeGetImTD|t6X?^XcblK}7sK=mgO zLk)?EVL*X9XNF$Tg<~AKqa6;CL-!@bV4RoamAj;ra0hOB1$c)mocj;R^RE`JZW&hx zOD4ZFE=V>Pp79JlCh}X|HpFVtTqaQP+vSJk7f3hD<#Hor7N*T@BH_@YKgCI_-ECy-knhh>lWjCiuR>XN(R`J2C zNOe{gb}Jo=bP`K(G|Qym_(oZ;l3AgxTO?Cqz2hX$xZPN(d3)D}891PHQl!GVd8V{& zDWMasrFBL^cYNzAksnB`t;D3;wQ1vYW5xkv)>pG3tgngOfE#amy>U4eJvl21A$#8^ z!_!CQ$^FOsm5{0|lDt>fRy9kvI<-L@XGNyt7%Qnh1L9MkhSa6oG@w2Ufe+xjN!bhb zPvGF{zL{UI`=z^joT85_aDnjv(Pi~BkP%rxKO7RMD-Mf}fUehMOm_3Q(X4|_XQHYaD?tOyVFdFt^o6Ns#LVi!dySsUr zCb`<{zJ1#9B#wn_Bf(*}PEtU%lByB~@3?Xu59-QZNA3e8wGZ3Kp(7G4-vCq}0&$@Z zA=D&<{0B94>HOCJx|HJM71SZXrXEssOXHsj{f%~5f8Kj|BGQ=_(NySh$)o8^>1ZgT zY+B`FDhe%llob)QS(+zGbvx8<^-H~fb^K7uQeK8+{VI{kR1LPdz)0bJtP?ef%bBib zS`FIe@KC_)9E&F0tGu^F literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/skill.cpython-38.pyc b/classic/babyfoxagi/skills/__pycache__/skill.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5dffc235a7a82cd7fd8ba0998ddbbaac515a85ed GIT binary patch literal 1569 zcmZ`(PjB2r6rUOYS+ARfwo!wEK(YiJRzjOf+^Q%lr2!(r3emt}rONVpX0z^kZD(d& zHSX+%jRYsY0IJj+`#ty+b3%e|z>O2{+1VtO%6Q+KfA78dy?^`T=4MD>{C)T*f9VtQ z7jAA22R09Z)Lmd-6HYii2U@W);p_<+I4PXJH5lw30;&7J1Q}CK#*EXk!`aw9A!Cm_ z+&w2_pL^VgxyO4vfH~lOuoqhI$vn&RCY&TnM5}@n1V23!(e?Q-MRe-{=t%(d zC6M|9m`3*?IDN1tKgS?CK+yzOm0Gv3( zz5kdz9WPzoGp-I+O#WtEIQeAh8PCwUFQ>Y1i1EOR|B4y69d*5YB;p&U;kD|FaQ_-}PQNtcY zUmcHwIH|H2PMh5*TdN5ksX9%CQqwxm&jz=wSIMl<)-95Su)(E@vUe9rR>XN(R`Ikh zQk|8B-A-pBoySs~)|nI>Z7b_N2RPi`S!-Th+b{!cN+(4stea;_+ny3S(OO#PMCgHU zT_y6VbRj{~17)_s97yc|Gvw*kk{Sx?{!au7as13}3SvI_H>HH1t;)&6c*r>L@ zeEHr^EfcOp{z446jrNp2UWdePsAIVV$Ia>?Bns3O;b< zCSJ6e+``p+$XX1Dy#*pM7rQ{}HZTHtzzpor4T%2+ZFc_`PKsvN1J@XvwzW54fvvrw zKf_-Ov#mtVjGUzph`^ajpwg0 kNUSI%g^>y%W|E=Kufz literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/skill_registry.cpython-310.pyc b/classic/babyfoxagi/skills/__pycache__/skill_registry.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..88cac4303190a7f125b1e6f1c58d1c28e83eee58 GIT binary patch literal 4214 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 S8Nz4C936salaPk=s`DQQs{XnF literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/startup_analysis.cpython-38.pyc b/classic/babyfoxagi/skills/__pycache__/startup_analysis.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..8ebaf903833f68ddf942c1f61d7955496be0c26b GIT binary patch literal 3508 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 b&SmosBAb?;K~vSM$Xe*4zhzqgIAi@26L-cz literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/__pycache__/web_search.cpython-38.pyc b/classic/babyfoxagi/skills/__pycache__/web_search.cpython-38.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d7159b761084575a7601c85f0faf398bc762cd3b GIT binary patch literal 6313 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 Jn(=4L`U@l@#rFUJ literal 0 HcmV?d00001 diff --git a/classic/babyfoxagi/skills/airtable_search.py b/classic/babyfoxagi/skills/airtable_search.py new file mode 100644 index 00000000..e61f1914 --- /dev/null +++ b/classic/babyfoxagi/skills/airtable_search.py @@ -0,0 +1,123 @@ +from skills.skill import Skill +import openai +import os +import requests +from urllib.parse import quote # Python built-in URL encoding function + +#This Airtable Skill is tuned to work with our Airtable set up, where we are seaeching a specific column in a specific table, in a specific base. ll three variables need to be set below (Lines 55-59) +class AirtableSearch(Skill): + name = 'airtable_search' + description = "A skill that retrieves data from our Airtable notes using a search. Useful for remembering who we talked to about a certain topic." + api_keys_required = ['code_reader','skill_saver','documentation_search','text_completion'] + + def __init__(self, api_keys, main_loop_function): + super().__init__(api_keys, main_loop_function) + + def execute(self, params, dependent_task_outputs, objective): + if not self.valid: + return + + # Initialize a list to keep track of queries tried + tried_queries = [] + + # Modify the query based on the dependent task output + if dependent_task_outputs != "": + dependent_task = f"Use the dependent task output below as reference to help craft the correct search query for the provided task above. Dependent task output:{dependent_task_outputs}." + else: + dependent_task = "." + + # Initialize output + output = '' + + while not output: + # If there are tried queries, add them to the prompt + tried_queries_prompt = f" Do not include search queries we have tried, and instead try synonyms or misspellings. Search queries we have tried: {', '.join(tried_queries)}." if tried_queries else "" + print(tried_queries_prompt) + query = self.text_completion_tool(f"You are an AI assistant tasked with generating a one word search query based on the following task: {params}. Provide only the search query as a response. {tried_queries_prompt} Take into account output from the previous task:{dependent_task}.\nExample Task: Retrieve data from Airtable notes using the skill 'airtable_search' to find people we have talked to about TED AI.\nExample Query:TED AI\nExample Task:Conduct a search in our Airtable notes to identify investors who have expressed interest in climate.\nExample Query:climate\nTask:{params}\nQuery:") + + # Add the query to the list of tried queries + tried_queries.append(query) + + print("\033[90m\033[3m"+"Search query: " +str(query)+"\033[0m") + + # Retrieve the Airtable API key + airtable_api_key = self.api_keys['airtable'] + + # Set the headers for the API request + headers = { + "Authorization": f"Bearer {airtable_api_key}", + "Content-Type": "application/json" + } + + + # Set base id + base_id = '' + table_name = '