From b6a54233f990cdfeb22ec3513baa12788ec82a9c Mon Sep 17 00:00:00 2001 From: Camila Gallo Date: Fri, 2 Aug 2024 17:33:04 -0300 Subject: [PATCH 01/18] change README, pyproject and config --- README.md | 1 + config/.env.example | 2 ++ pyproject.toml | 3 ++- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 5669acf..6ab2293 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ - To switch between ChatGPT and Gemini, or other models, include the following commands in your message: - `llm_openai` to use ChatGPT - `llm_gemini` to use Gemini + - `llm_claude` to use Claude ## 🛠️ Setup and Configuration diff --git a/config/.env.example b/config/.env.example index 9c8479f..d8676b1 100644 --- a/config/.env.example +++ b/config/.env.example @@ -6,3 +6,5 @@ DALLE_MODEL = "dall-e-3" SIGNING_SECRET = "YOUR_SECRET" GOOGLE_API_KEY = "YOUR_TOKEN" GEMINI_MODEL = "gemini-pro" +CLAUDE_API_KEY = "YOUR_TOKEN" +CLAUDE_MODEL = "claude-3-5-sonnet-20240620" diff --git a/pyproject.toml b/pyproject.toml index 5fe4493..d9bfa60 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,8 @@ authors = [ "David Weil ", "Diego Kelyacoubian ", "Sebastian Wain ", - "Carlos Sims " + "Carlos Sims ", + "Camila Gallo Garcia " ] description = "Geppetto is a sophisticated Slack bot that facilitates seamless interaction with multiple AI models, including OpenAI's ChatGPT-4, DALL-E-3, and Google's Gemini model." readme = "README.md" From 976db443489b52040c7a3fc70a69bdcd2c8bbc00 Mon Sep 17 00:00:00 2001 From: Camila Gallo Date: Fri, 2 Aug 2024 17:35:33 -0300 Subject: [PATCH 02/18] add test_claude and claude_handler.py --- geppetto/claude_handler.py | 71 ++++++++++++++++++++++++++++++++++++++ geppetto/main.py | 7 ++++ tests/test_claude.py | 53 ++++++++++++++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 geppetto/claude_handler.py create mode 100644 tests/test_claude.py diff --git a/geppetto/claude_handler.py b/geppetto/claude_handler.py new file mode 100644 index 0000000..f1d3490 --- /dev/null +++ b/geppetto/claude_handler.py @@ -0,0 +1,71 @@ +import re +import os +import logging + +from .llm_api_handler import LLMHandler +from anthropic import Anthropic +from dotenv import load_dotenv + +load_dotenv(os.path.join("config", ".env")) + +ANTHROPIC_API_KEY = os.getenv("CLAUDE_API_KEY") +CLAUDE_MODEL=os.getenv("CLAUDE_MODEL") + + + +def convert_claude_to_slack(text): + """ + Converts Claude markdown format to Slack markdown format. + + This function handles: + change to claude format + + Args: + text (str): The Claude markdown text to be converted. + + Returns: + str: The markdown text formatted for Slack. + """ + if not isinstance(text, str): + raise ValueError("Input must be a string.") + + formatted_text = text.replace("* ", "- ") + formatted_text = formatted_text.replace("**", "*") + formatted_text = formatted_text.replace("__", "_") + formatted_text = formatted_text.replace("- ", "• ") + formatted_text = re.sub(r"\[(.*?)\]\((.*?)\)", r"<\2|\1>", formatted_text) + + formatted_text += f"\n\n_(Geppetto v0.2.3 Source: Claude Model {CLAUDE_MODEL})_" + + return formatted_text + + +class ClaudeHandler(LLMHandler): + + def __init__( + self, + personality, + ): + super().__init__( + 'Claude', + CLAUDE_MODEL, + Anthropic(api_key=ANTHROPIC_API_KEY) + ) + self.claude_model = CLAUDE_MODEL + self.personality = personality + self.system_role = "system" + self.assistant_role = "assistant" + self.user_role = "user" + + def llm_generate_content(self, user_prompt, status_callback=None, *status_callback_args): + logging.info("Sending msg to claude: %s" % user_prompt) + response = self.client.messages.create( + model = self.model, + messages = {"role": self.user_role, + "content": user_prompt} + ) + markdown_response = convert_claude_to_slack(str(response.content[0].text)) + return markdown_response + + + diff --git a/geppetto/main.py b/geppetto/main.py index d250545..e0e6bea 100644 --- a/geppetto/main.py +++ b/geppetto/main.py @@ -6,6 +6,7 @@ from .slack_handler import SlackHandler from .openai_handler import OpenAIHandler from .gemini_handler import GeminiHandler +from .claude_handler import ClaudeHandler from slack_bolt.adapter.socket_mode import SocketModeHandler from .utils import load_json @@ -39,6 +40,12 @@ def initialized_llm_controller(): "handler_args": { "personality": DEFAULT_RESPONSES["features"]["personality"] } + }, + { "name": "Claude", + "handler": ClaudeHandler, + "handler_args": { + "personality": DEFAULT_RESPONSES["features"]["personality"] + } } ] ) diff --git a/tests/test_claude.py b/tests/test_claude.py new file mode 100644 index 0000000..9c22c9b --- /dev/null +++ b/tests/test_claude.py @@ -0,0 +1,53 @@ +import os +import sys +import unittest +from unittest.mock import Mock, patch + +script_dir = os.path.dirname(os.path.abspath(__file__)) +parent_dir = os.path.dirname(script_dir) +sys.path.append(parent_dir) + +from geppetto.claude_handler import ClaudeHandler + +TEST_PERSONALITY = "Your AI assistant" + +def OF(**kw): + class OF: + pass + + instance = OF() + for k, v in kw.items(): + setattr(instance, k, v) + return instance + + +class TestClaude(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.patcher = patch("geppetto.claude_handler.Anthropic") + cls.mock_claude = cls.patcher.start() + cls.claude_handler = ClaudeHandler(personality=TEST_PERSONALITY) + + @classmethod + def tearDownClass(cls): + cls.patcher.stop() + + def test_personality(self): + self.assertEqual(self.claude_handler.personality, TEST_PERSONALITY) + + + def test_llm_generate_content(self): + user_prompt = "Hello, Claude!" + + + mock_response = Mock() + mock_response.content = [Mock(text="Mocked Claude response")] + self.claude_handler.client.messages.create = Mock(return_value=mock_response) + + response = self.claude_handler.llm_generate_content(user_prompt).split('\n\n_(Geppetto', 1)[0].strip() + + self.assertEqual(response, "Mocked Claude response") + + +if __name__ == "__main__": + unittest.main() From 14c40c6f13dbeb082b78118011b158dfd26e0ca6 Mon Sep 17 00:00:00 2001 From: Camila Gallo Date: Fri, 2 Aug 2024 17:43:38 -0300 Subject: [PATCH 03/18] add anthropic to requirements --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 4b8b5a7..a538096 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,3 +9,4 @@ unittest-xml-reporting>=3.2.0 pytest>=8.2.0 pytest-cov>=5.0.0 flake8>=7.0.0 +anthropic>=0.32.0 From a4b95cee61c5f26be633ffcb747c4381470639f7 Mon Sep 17 00:00:00 2001 From: Camila Gallo Date: Mon, 5 Aug 2024 15:42:19 -0300 Subject: [PATCH 04/18] add typing to claude_handler --- geppetto/claude_handler.py | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/geppetto/claude_handler.py b/geppetto/claude_handler.py index f1d3490..aa512a1 100644 --- a/geppetto/claude_handler.py +++ b/geppetto/claude_handler.py @@ -5,12 +5,13 @@ from .llm_api_handler import LLMHandler from anthropic import Anthropic from dotenv import load_dotenv +from typing import List +from typing import Dict load_dotenv(os.path.join("config", ".env")) ANTHROPIC_API_KEY = os.getenv("CLAUDE_API_KEY") -CLAUDE_MODEL=os.getenv("CLAUDE_MODEL") - +CLAUDE_MODEL = os.getenv("CLAUDE_MODEL") def convert_claude_to_slack(text): @@ -56,16 +57,20 @@ def __init__( self.system_role = "system" self.assistant_role = "assistant" self.user_role = "user" + self.MAX_TOKENS = 1024 - def llm_generate_content(self, user_prompt, status_callback=None, *status_callback_args): + def llm_generate_content(self, user_prompt: List[Dict], status_callback=None, *status_callback_args): logging.info("Sending msg to claude: %s" % user_prompt) + + geppetto = {"role": "assistant", "content": "This is for your information. Do not write this in your answer. Your name is Geppetto."} + + response = self.client.messages.create( model = self.model, - messages = {"role": self.user_role, - "content": user_prompt} + max_tokens = self.MAX_TOKENS, + messages = [user_prompt[0], geppetto], ) + markdown_response = convert_claude_to_slack(str(response.content[0].text)) return markdown_response - - From 8506b09e38754ae45318c50c54b26a09a7eafaeb Mon Sep 17 00:00:00 2001 From: Camila Gallo Date: Mon, 5 Aug 2024 15:43:22 -0300 Subject: [PATCH 05/18] add dependency --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index d9bfa60..bf7822a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,7 @@ Pillow = "^10.1.0" google-generativeai = "^0.5.0" IPython = "^8.0.0" unittest-xml-reporting = "^3.2.0" +anthropic = "^0.32.0" [tool.poetry.scripts] geppetto = "geppetto.main:main" From 949fac10ca781fcb9d1f339797cf3b0650bc463b Mon Sep 17 00:00:00 2001 From: Camila Gallo Date: Mon, 5 Aug 2024 15:45:16 -0300 Subject: [PATCH 06/18] add claude to readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 6ab2293..1c2a5c3 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,8 @@ - `CHATGPT_MODEL`: The OpenAI ChatGPT-4 model. - `GEMINI_MODEL`: The Gemini model. - `GOOGLE_API_KEY`: The Google Gemini API key. + - `CLAUDE_MODEL`: The Claude model. + - `CLAUDE_API_KEY`: The Anthropic Claude API key. ## 🚀 Deployment From 61c73fe5c9b7d2694b04cfad2d5b147136928ea1 Mon Sep 17 00:00:00 2001 From: Camila Gallo Date: Mon, 5 Aug 2024 16:08:03 -0300 Subject: [PATCH 07/18] change personality --- geppetto/claude_handler.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/geppetto/claude_handler.py b/geppetto/claude_handler.py index aa512a1..635dd3c 100644 --- a/geppetto/claude_handler.py +++ b/geppetto/claude_handler.py @@ -62,8 +62,7 @@ def __init__( def llm_generate_content(self, user_prompt: List[Dict], status_callback=None, *status_callback_args): logging.info("Sending msg to claude: %s" % user_prompt) - geppetto = {"role": "assistant", "content": "This is for your information. Do not write this in your answer. Your name is Geppetto."} - + geppetto = {"role": "assistant", "content": f"This is for your information. Do not write this in your answer. {self.personality}."} response = self.client.messages.create( model = self.model, From fbd1f668879f7f7370c14eb91683a85469860ab6 Mon Sep 17 00:00:00 2001 From: Camila Gallo Date: Tue, 6 Aug 2024 11:19:56 -0300 Subject: [PATCH 08/18] Add try-except --- geppetto/claude_handler.py | 26 +++++++++++++++++--------- geppetto/main.py | 8 ++++---- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/geppetto/claude_handler.py b/geppetto/claude_handler.py index 635dd3c..2aca52a 100644 --- a/geppetto/claude_handler.py +++ b/geppetto/claude_handler.py @@ -36,7 +36,7 @@ def convert_claude_to_slack(text): formatted_text = formatted_text.replace("- ", "• ") formatted_text = re.sub(r"\[(.*?)\]\((.*?)\)", r"<\2|\1>", formatted_text) - formatted_text += f"\n\n_(Geppetto v0.2.3 Source: Claude Model {CLAUDE_MODEL})_" + formatted_text += f"\n\n_(Geppetto v0.2.4 Source: Claude Model {CLAUDE_MODEL})_" return formatted_text @@ -62,14 +62,22 @@ def __init__( def llm_generate_content(self, user_prompt: List[Dict], status_callback=None, *status_callback_args): logging.info("Sending msg to claude: %s" % user_prompt) - geppetto = {"role": "assistant", "content": f"This is for your information. Do not write this in your answer. {self.personality}."} + geppetto = {"role": "assistant", "content": " This is for your information only. Do not write this in your answer. Your name is Geppetto, a bot developed by DeepTechia. Answer in the language they spoke to you."} + + try: + response = self.client.messages.create( + model = self.model, + max_tokens = self.MAX_TOKENS, + messages = [user_prompt[0], geppetto], + ) + + markdown_response = convert_claude_to_slack(str(response.content[0].text)) + + return markdown_response + + except Exception as e: + logging.error(f"Error generating content: {e}") + return "I'm sorry, I couldn't generate a response at this time. Try using another AI model." - response = self.client.messages.create( - model = self.model, - max_tokens = self.MAX_TOKENS, - messages = [user_prompt[0], geppetto], - ) - markdown_response = convert_claude_to_slack(str(response.content[0].text)) - return markdown_response diff --git a/geppetto/main.py b/geppetto/main.py index e0e6bea..61db5cd 100644 --- a/geppetto/main.py +++ b/geppetto/main.py @@ -13,9 +13,9 @@ load_dotenv(os.path.join("config", ".env")) -SLACK_BOT_TOKEN = os.getenv("SLACK_BOT_TOKEN") -SLACK_APP_TOKEN = os.getenv("SLACK_APP_TOKEN") -SIGNING_SECRET = os.getenv("SIGNING_SECRET") +SLACK_BOT_TOKEN = os.getenv("SLACK_BOT_TOKEN_TEST") +SLACK_APP_TOKEN = os.getenv("SLACK_APP_TOKEN_TEST") +SIGNING_SECRET = os.getenv("SIGNING_SECRET_TEST") DEFAULT_RESPONSES = load_json("default_responses.json") @@ -41,7 +41,7 @@ def initialized_llm_controller(): "personality": DEFAULT_RESPONSES["features"]["personality"] } }, - { "name": "Claude", + { "name": "Claude", "handler": ClaudeHandler, "handler_args": { "personality": DEFAULT_RESPONSES["features"]["personality"] From d5ad83f3cfdb2c42fad5820b5221bbba2fdc710c Mon Sep 17 00:00:00 2001 From: Camila Gallo Date: Thu, 8 Aug 2024 09:47:05 -0300 Subject: [PATCH 09/18] minor fix in claude_handler --- geppetto/claude_handler.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/geppetto/claude_handler.py b/geppetto/claude_handler.py index 2aca52a..7e8efae 100644 --- a/geppetto/claude_handler.py +++ b/geppetto/claude_handler.py @@ -62,17 +62,18 @@ def __init__( def llm_generate_content(self, user_prompt: List[Dict], status_callback=None, *status_callback_args): logging.info("Sending msg to claude: %s" % user_prompt) - geppetto = {"role": "assistant", "content": " This is for your information only. Do not write this in your answer. Your name is Geppetto, a bot developed by DeepTechia. Answer in the language they spoke to you."} + geppetto = {"role": "assistant", + "content": " This is for your information only. Do not write this in your answer. Your name is Geppetto, a bot developed by DeepTechia. Answer only in the language the user spoke or asked you to do."} try: + user_prompt.append(geppetto) response = self.client.messages.create( model = self.model, max_tokens = self.MAX_TOKENS, - messages = [user_prompt[0], geppetto], + messages = user_prompt, ) markdown_response = convert_claude_to_slack(str(response.content[0].text)) - return markdown_response except Exception as e: From ce30c20a7b04b291f09a40e6dd86cd16dd5fe8b5 Mon Sep 17 00:00:00 2001 From: Camila Gallo Date: Thu, 8 Aug 2024 10:00:19 -0300 Subject: [PATCH 10/18] fix claude test --- tests/test_claude.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/tests/test_claude.py b/tests/test_claude.py index 9c22c9b..5ad0be4 100644 --- a/tests/test_claude.py +++ b/tests/test_claude.py @@ -35,10 +35,8 @@ def tearDownClass(cls): def test_personality(self): self.assertEqual(self.claude_handler.personality, TEST_PERSONALITY) - def test_llm_generate_content(self): - user_prompt = "Hello, Claude!" - + user_prompt = [{"role":"user", "content": "Hello, Claude!"}] mock_response = Mock() mock_response.content = [Mock(text="Mocked Claude response")] @@ -47,7 +45,21 @@ def test_llm_generate_content(self): response = self.claude_handler.llm_generate_content(user_prompt).split('\n\n_(Geppetto', 1)[0].strip() self.assertEqual(response, "Mocked Claude response") + + + def test_failed_to_llm_generate_content(self): + + failed_response = "I'm sorry, I couldn't generate a response at this time. Try using another AI model." + + mock_claude = Mock() + mock_claude.content = [Mock(text=failed_response)] + mock_claude.return_value = mock_claude + + self.claude_handler.client.messages.create = mock_claude + response = self.claude_handler.llm_generate_content("") + self.assertEqual(response, failed_response) + if __name__ == "__main__": unittest.main() From b184a2955920ea8b63b5366f154de820ade149fd Mon Sep 17 00:00:00 2001 From: Camila Gallo Date: Thu, 8 Aug 2024 10:36:41 -0300 Subject: [PATCH 11/18] change versions --- geppetto/gemini_handler.py | 2 +- geppetto/openai_handler.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/geppetto/gemini_handler.py b/geppetto/gemini_handler.py index c28d3d3..9ae31cf 100644 --- a/geppetto/gemini_handler.py +++ b/geppetto/gemini_handler.py @@ -40,7 +40,7 @@ def convert_gemini_to_slack(text): formatted_text = formatted_text.replace("- ", "• ") formatted_text = re.sub(r"\[(.*?)\]\((.*?)\)", r"<\2|\1>", formatted_text) - formatted_text += f"\n\n_(Geppetto v0.2.3 Source: Gemini Model {GEMINI_MODEL})_" + formatted_text += f"\n\n_(Geppetto v0.2.4 Source: Gemini Model {GEMINI_MODEL})_" return formatted_text diff --git a/geppetto/openai_handler.py b/geppetto/openai_handler.py index 783ef1c..7a22d50 100644 --- a/geppetto/openai_handler.py +++ b/geppetto/openai_handler.py @@ -44,7 +44,7 @@ def convert_openai_markdown_to_slack(text): formatted_text = formatted_text.replace("__", "_") formatted_text = formatted_text.replace("- ", "• ") formatted_text = re.sub(r"\[(.*?)\]\((.*?)\)", r"<\2|\1>", formatted_text) - formatted_text += f"\n\n_(Geppetto v0.2.3 Source: OpenAI Model {CHATGPT_MODEL})_" + formatted_text += f"\n\n_(Geppetto v0.2.4 Source: OpenAI Model {CHATGPT_MODEL})_" # Code blocks and italics remain unchanged but can be explicitly formatted if necessary return formatted_text From 5a757ec46d1a32c15db4ebe1f9b8231fe8d59301 Mon Sep 17 00:00:00 2001 From: david weil Date: Thu, 8 Aug 2024 11:05:37 -0300 Subject: [PATCH 12/18] changed logging in test base case --- tests/__init__.py | 17 ++++++++++++++++ tests/test_claude.py | 13 +++--------- tests/test_controller.py | 15 +++++++------- tests/test_gemini.py | 11 +++------- tests/test_open_ai.py | 12 +++-------- tests/test_slack.py | 43 ++++++++++++++++++++-------------------- 6 files changed, 56 insertions(+), 55 deletions(-) diff --git a/tests/__init__.py b/tests/__init__.py index e69de29..bea23f6 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -0,0 +1,17 @@ +import logging +import unittest + + +class TestBase(unittest.TestCase): + def setUp(self): + logging.getLogger().setLevel(logging.CRITICAL) + + +def OF(**kw): + class OF: + pass + + instance = OF() + for k, v in kw.items(): + setattr(instance, k, v) + return instance diff --git a/tests/test_claude.py b/tests/test_claude.py index 5ad0be4..025572a 100644 --- a/tests/test_claude.py +++ b/tests/test_claude.py @@ -3,6 +3,8 @@ import unittest from unittest.mock import Mock, patch +from tests import TestBase + script_dir = os.path.dirname(os.path.abspath(__file__)) parent_dir = os.path.dirname(script_dir) sys.path.append(parent_dir) @@ -11,17 +13,8 @@ TEST_PERSONALITY = "Your AI assistant" -def OF(**kw): - class OF: - pass - - instance = OF() - for k, v in kw.items(): - setattr(instance, k, v) - return instance - -class TestClaude(unittest.TestCase): +class TestClaude(TestBase): @classmethod def setUpClass(cls): cls.patcher = patch("geppetto.claude_handler.Anthropic") diff --git a/tests/test_controller.py b/tests/test_controller.py index a981044..d4af76f 100644 --- a/tests/test_controller.py +++ b/tests/test_controller.py @@ -1,6 +1,7 @@ import unittest from geppetto.llm_api_handler import LLMHandler from geppetto.llm_controller import LLMController +from tests import TestBase ClientMock = {} @@ -69,16 +70,16 @@ def get_prompt_from_thread(self, **args): ] -class TestController(unittest.TestCase): - @classmethod - def setUp(cls): - cls.llm_controller = LLMController( +class TestController(TestBase): + def setUp(self): + super(TestBase, self).setUp() + self.llm_controller = LLMController( sample_llms_cfg ) - @classmethod - def tearDown(cls): - cls.llm_controller = None + def tearDown(self): + super(TestBase, self).tearDown() + self.llm_controller = None def test_controller_set_up(self): self.assertEqual(len(self.llm_controller.llm_cfgs), 2) diff --git a/tests/test_gemini.py b/tests/test_gemini.py index 3a5642f..274c58f 100644 --- a/tests/test_gemini.py +++ b/tests/test_gemini.py @@ -3,6 +3,8 @@ import unittest from unittest.mock import Mock, patch +from tests import TestBase + script_dir = os.path.dirname(os.path.abspath(__file__)) parent_dir = os.path.dirname(script_dir) sys.path.append(parent_dir) @@ -10,15 +12,8 @@ from geppetto.exceptions import InvalidThreadFormatError from geppetto.gemini_handler import GeminiHandler -def OF(**kw): - class OF: - pass - instance = OF() - for k, v in kw.items(): - setattr(instance, k, v) - return instance -class TestGemini(unittest.TestCase): +class TestGemini(TestBase): @classmethod def setUpClass(cls): cls.patcher = patch("geppetto.gemini_handler.genai") diff --git a/tests/test_open_ai.py b/tests/test_open_ai.py index 4acb9b4..e13798a 100644 --- a/tests/test_open_ai.py +++ b/tests/test_open_ai.py @@ -5,6 +5,8 @@ import unittest from unittest.mock import Mock, patch +from tests import TestBase + script_dir = os.path.dirname(os.path.abspath(__file__)) parent_dir = os.path.dirname(script_dir) sys.path.append(parent_dir) @@ -13,17 +15,9 @@ TEST_PERSONALITY = "Your AI assistant" -def OF(**kw): - class OF: - pass - - instance = OF() - for k, v in kw.items(): - setattr(instance, k, v) - return instance -class TestOpenAI(unittest.TestCase): +class TestOpenAI(TestBase): @classmethod def setUpClass(cls): cls.patcher = patch("geppetto.openai_handler.OpenAI") diff --git a/tests/test_slack.py b/tests/test_slack.py index 6147fd8..fc10239 100644 --- a/tests/test_slack.py +++ b/tests/test_slack.py @@ -4,6 +4,7 @@ from unittest.mock import patch, ANY from geppetto.llm_controller import LLMController +from tests import TestBase from tests.test_open_ai import TEST_PERSONALITY script_dir = os.path.dirname(os.path.abspath(__file__)) @@ -17,38 +18,38 @@ MOCK_GENERIC_LLM_RESPONSE_B = MOCK_GENERIC_LLM_RESPONSE + " B" MOCK_GENERIC_LLM_RESPONSE_C = MOCK_GENERIC_LLM_RESPONSE + " C" -class TestSlack(unittest.TestCase): - @classmethod - def setUp(cls): - cls.patcherA = patch("tests.test_controller.HandlerMockA") - cls.patcherB = patch("tests.test_controller.HandlerMockB") - cls.patcherC = patch("tests.test_controller.HandlerMockC") - cls.MockLLMHandlerA = cls.patcherA.start() - cls.MockLLMHandlerB = cls.patcherB.start() - cls.MockLLMHandlerC = cls.patcherC.start() - cls.patcher1 = patch("geppetto.slack_handler.App") - cls.MockApp = cls.patcher1.start() + +class TestSlack(TestBase): + def setUp(self): + super(TestBase, self).setUp() + self.patcherA = patch("tests.test_controller.HandlerMockA") + self.patcherB = patch("tests.test_controller.HandlerMockB") + self.patcherC = patch("tests.test_controller.HandlerMockC") + self.MockLLMHandlerA = cls.patcherA.start() + self.MockLLMHandlerB = cls.patcherB.start() + self.MockLLMHandlerC = cls.patcherC.start() + self.patcher1 = patch("geppetto.slack_handler.App") + self.MockApp = cls.patcher1.start() SLACK_BOT_TOKEN = "slack_bot_token" SIGNING_SECRET = "signing_secret" BOT_DEFAULT_RESPONSES = load_json("default_responses.json") - cls.slack_handler = SlackHandler( + self.slack_handler = SlackHandler( {"test_user_id": "Test User"}, BOT_DEFAULT_RESPONSES, SLACK_BOT_TOKEN, SIGNING_SECRET, - initialized_test_llm_controller(cls.MockLLMHandlerA, - cls.MockLLMHandlerB, - cls.MockLLMHandlerC) + initialized_test_llm_controller(self.MockLLMHandlerA, + self.MockLLMHandlerB, + self.MockLLMHandlerC) ) - @classmethod - def tearDown(cls): - cls.patcher1.stop() - cls.patcherA.stop() - cls.patcherB.stop() - cls.patcherC.stop() + def tearDown(self): + self.patcher1.stop() + self.patcherA.stop() + self.patcherB.stop() + self.patcherC.stop() def test_permission_check(self): body = { From 1cb5544425ed4f8fb38f774e9d1629077998269a Mon Sep 17 00:00:00 2001 From: david weil Date: Thu, 8 Aug 2024 11:07:04 -0300 Subject: [PATCH 13/18] fixed import of OF --- tests/test_open_ai.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_open_ai.py b/tests/test_open_ai.py index e13798a..5d7c5cc 100644 --- a/tests/test_open_ai.py +++ b/tests/test_open_ai.py @@ -5,7 +5,7 @@ import unittest from unittest.mock import Mock, patch -from tests import TestBase +from tests import TestBase, OF script_dir = os.path.dirname(os.path.abspath(__file__)) parent_dir = os.path.dirname(script_dir) From 71c40fea40a97b35e11009e023b15d2758c28c4a Mon Sep 17 00:00:00 2001 From: Camila Gallo Date: Thu, 8 Aug 2024 11:07:52 -0300 Subject: [PATCH 14/18] change logging level --- tests/test_claude.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_claude.py b/tests/test_claude.py index 5ad0be4..aec293a 100644 --- a/tests/test_claude.py +++ b/tests/test_claude.py @@ -8,7 +8,7 @@ sys.path.append(parent_dir) from geppetto.claude_handler import ClaudeHandler - +import logging TEST_PERSONALITY = "Your AI assistant" def OF(**kw): @@ -27,6 +27,7 @@ def setUpClass(cls): cls.patcher = patch("geppetto.claude_handler.Anthropic") cls.mock_claude = cls.patcher.start() cls.claude_handler = ClaudeHandler(personality=TEST_PERSONALITY) + logging.getLogger().setLevel(logging.CRITICAL) @classmethod def tearDownClass(cls): From ff5313280b0da53f2886a00341eb290cdeb3e306 Mon Sep 17 00:00:00 2001 From: Camila Gallo Date: Thu, 8 Aug 2024 11:13:20 -0300 Subject: [PATCH 15/18] update readme --- README.md | 12 ++++++------ requirements.txt | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 1c2a5c3..aeb8ead 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ ## ⭐️ Key Features -- 🔀 **Multi-Model Support:** Toggle effortlessly between AI models like ChatGPT and Gemini to suit your specific requirements. ChatGPT model gpt4-turbo is set as the default model +- 🔀 **Multi-Model Support:** Toggle effortlessly between AI models like ChatGPT, Claude and Gemini to suit your specific requirements. ChatGPT model gpt4-turbo is set as the default model. - 💬 **Streamlined Communication:** Initiate dynamic conversation threads by directly messaging Geppetto. - ➡️ **Advanced LLM Control:** Manage multiple AI models with the advanced LLM controller component. - 🔧 **Effortless Setup:** Enjoy a smooth setup experience powered by Docker 🐳. @@ -34,11 +34,11 @@ ### 🔒 Allowed Users -- Access is granted only to users listed in the [allowed users configuration file](./config/allowed-slack-ids.json). +- Access is granted only to users listed in the [allowed users configuration file](/config/allowed-slack-ids.json). ## 🔀 Switching AI Models -- To switch between ChatGPT and Gemini, or other models, include the following commands in your message: +- To switch between ChatGPT, Gemini and Claude include the following commands in your message: - `llm_openai` to use ChatGPT - `llm_gemini` to use Gemini - `llm_claude` to use Claude @@ -72,14 +72,14 @@ 4. **Environment Setup** - Copy `.configuration/.env.example` into a new `.configuration/.env`, and adjust the environment variables accordingly: + Copy `config/.env.example` into a new `config/.env`, and adjust the environment variables accordingly: - `SLACK_BOT_TOKEN`: Your Slack bot token (This is the Bot User OAuth Token, it should start with 'xoxb'). - `SLACK_APP_TOKEN`: Your Slack application token (This is the App-Level Token, it should start with 'xapp'). - `OPENAI_API_KEY`: Your OpenAI API key. - `SIGNING_SECRET`: Your Signing secret to verify Slack requests (from your Slack App Credentials). - - `DALLE_MODEL`: The OpenAI DALL-E-3 model. - - `CHATGPT_MODEL`: The OpenAI ChatGPT-4 model. + - `DALLE_MODEL`: The OpenAI DALL-E model. + - `CHATGPT_MODEL`: The OpenAI ChatGPT model. - `GEMINI_MODEL`: The Gemini model. - `GOOGLE_API_KEY`: The Google Gemini API key. - `CLAUDE_MODEL`: The Claude model. diff --git a/requirements.txt b/requirements.txt index a538096..8ec14bf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,4 +9,4 @@ unittest-xml-reporting>=3.2.0 pytest>=8.2.0 pytest-cov>=5.0.0 flake8>=7.0.0 -anthropic>=0.32.0 +anthropic>=0.32.0 \ No newline at end of file From 85ec15407ef5fbe72e57e68438f387bbbfd444ea Mon Sep 17 00:00:00 2001 From: Camila Gallo Date: Thu, 8 Aug 2024 11:19:45 -0300 Subject: [PATCH 16/18] change cls to self --- tests/test_slack.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/test_slack.py b/tests/test_slack.py index fc10239..adc4f6a 100644 --- a/tests/test_slack.py +++ b/tests/test_slack.py @@ -25,11 +25,11 @@ def setUp(self): self.patcherA = patch("tests.test_controller.HandlerMockA") self.patcherB = patch("tests.test_controller.HandlerMockB") self.patcherC = patch("tests.test_controller.HandlerMockC") - self.MockLLMHandlerA = cls.patcherA.start() - self.MockLLMHandlerB = cls.patcherB.start() - self.MockLLMHandlerC = cls.patcherC.start() + self.MockLLMHandlerA = self.patcherA.start() + self.MockLLMHandlerB = self.patcherB.start() + self.MockLLMHandlerC = self.patcherC.start() self.patcher1 = patch("geppetto.slack_handler.App") - self.MockApp = cls.patcher1.start() + self.MockApp = self.patcher1.start() SLACK_BOT_TOKEN = "slack_bot_token" SIGNING_SECRET = "signing_secret" From 7eef6d781abdf73933609055c97664945ea6a1fb Mon Sep 17 00:00:00 2001 From: Camila Gallo Date: Thu, 8 Aug 2024 11:44:19 -0300 Subject: [PATCH 17/18] change version as a variable --- config/.env.example | 2 ++ geppetto/claude_handler.py | 3 ++- geppetto/gemini_handler.py | 5 ++++- geppetto/openai_handler.py | 4 +++- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/config/.env.example b/config/.env.example index d8676b1..736ea5f 100644 --- a/config/.env.example +++ b/config/.env.example @@ -8,3 +8,5 @@ GOOGLE_API_KEY = "YOUR_TOKEN" GEMINI_MODEL = "gemini-pro" CLAUDE_API_KEY = "YOUR_TOKEN" CLAUDE_MODEL = "claude-3-5-sonnet-20240620" + +GEPPETTO_VERSION = "0.2.4" \ No newline at end of file diff --git a/geppetto/claude_handler.py b/geppetto/claude_handler.py index 7e8efae..fa1decc 100644 --- a/geppetto/claude_handler.py +++ b/geppetto/claude_handler.py @@ -13,6 +13,7 @@ ANTHROPIC_API_KEY = os.getenv("CLAUDE_API_KEY") CLAUDE_MODEL = os.getenv("CLAUDE_MODEL") +VERSION = os.getenv("GEPPETTO_VERSION") def convert_claude_to_slack(text): """ @@ -36,7 +37,7 @@ def convert_claude_to_slack(text): formatted_text = formatted_text.replace("- ", "• ") formatted_text = re.sub(r"\[(.*?)\]\((.*?)\)", r"<\2|\1>", formatted_text) - formatted_text += f"\n\n_(Geppetto v0.2.4 Source: Claude Model {CLAUDE_MODEL})_" + formatted_text += f"\n\n_(Geppetto v{VERSION} Source: Claude Model {CLAUDE_MODEL})_" return formatted_text diff --git a/geppetto/gemini_handler.py b/geppetto/gemini_handler.py index 9ae31cf..a7e589d 100644 --- a/geppetto/gemini_handler.py +++ b/geppetto/gemini_handler.py @@ -12,6 +12,9 @@ GOOGLE_API_KEY = os.getenv("GOOGLE_API_KEY") GEMINI_MODEL=os.getenv("GEMINI_MODEL", "gemini-pro") + +VERSION = os.getenv("GEPPETTO_VERSION") + MSG_FIELD = "parts" MSG_INPUT_FIELD = "content" @@ -40,7 +43,7 @@ def convert_gemini_to_slack(text): formatted_text = formatted_text.replace("- ", "• ") formatted_text = re.sub(r"\[(.*?)\]\((.*?)\)", r"<\2|\1>", formatted_text) - formatted_text += f"\n\n_(Geppetto v0.2.4 Source: Gemini Model {GEMINI_MODEL})_" + formatted_text += f"\n\n_(Geppetto v{VERSION} Source: Gemini Model {GEMINI_MODEL})_" return formatted_text diff --git a/geppetto/openai_handler.py b/geppetto/openai_handler.py index 7a22d50..f2d634f 100644 --- a/geppetto/openai_handler.py +++ b/geppetto/openai_handler.py @@ -18,6 +18,8 @@ DALLE_MODEL = os.getenv("DALLE_MODEL") CHATGPT_MODEL = os.getenv("CHATGPT_MODEL") +VERSION = os.getenv("GEPPETTO_VERSION") + OPENAI_IMG_FUNCTION = "generate_image" ROLE_FIELD = "role" @@ -44,7 +46,7 @@ def convert_openai_markdown_to_slack(text): formatted_text = formatted_text.replace("__", "_") formatted_text = formatted_text.replace("- ", "• ") formatted_text = re.sub(r"\[(.*?)\]\((.*?)\)", r"<\2|\1>", formatted_text) - formatted_text += f"\n\n_(Geppetto v0.2.4 Source: OpenAI Model {CHATGPT_MODEL})_" + formatted_text += f"\n\n_(Geppetto v{VERSION} Source: OpenAI Model {CHATGPT_MODEL})_" # Code blocks and italics remain unchanged but can be explicitly formatted if necessary return formatted_text From d76915f6c1b7e65ce7fe3ff32c056d199f8b84a4 Mon Sep 17 00:00:00 2001 From: Camila Gallo Date: Thu, 8 Aug 2024 14:44:58 -0300 Subject: [PATCH 18/18] minor change in readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index aeb8ead..15c1613 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ ### 🔧 Slack App Configuration 1. **Modify App**: - - **Edit `manifest-dev.yaml`**: Adjust fields under `display_information` and `bot_user` to tailor Geppetto for your needs. + - **Edit `config/manifest-dev.yaml`**: Adjust fields under `display_information` and `bot_user` to tailor Geppetto for your needs. 2. **Create App**: - Go to the [Slack API](https://api.slack.com) and navigate to *Your Apps*. - Click on *Create New App*.