From 82802c4dd7ea5de002881b6d085fea1c1a82b295 Mon Sep 17 00:00:00 2001 From: kksahay Date: Mon, 4 Sep 2023 03:04:51 +0530 Subject: [PATCH 1/6] feat: Palm-AI integration --- examples/palmai-bot/main.py | 41 +++++++++++++++++++++++++ textbase/models.py | 60 +++++++++++++++++++++++++++++++------ 2 files changed, 92 insertions(+), 9 deletions(-) create mode 100644 examples/palmai-bot/main.py diff --git a/examples/palmai-bot/main.py b/examples/palmai-bot/main.py new file mode 100644 index 00000000..4aee8e72 --- /dev/null +++ b/examples/palmai-bot/main.py @@ -0,0 +1,41 @@ +from textbase import bot, Message +from textbase.models import Palm +from typing import List + +Palm.api_key = "" + +SYSTEM_PROMPT = """You are chatting with an AI. There are no specific prefixes for responses, so you can ask or talk about anything you like. +The AI will respond in a natural, conversational manner. Feel free to start the conversation with any question or topic, and let's have a +pleasant chat! +""" + +@bot() +def on_message(message_history: List[Message], state: dict = None): + + # Generate Palm-model-chat-bison-001 response + bot_response = Palm.generate( + system_prompt=SYSTEM_PROMPT, + message_history=message_history + ) + + response = { + "data": { + "messages": [ + { + "data_type": "STRING", + "value": bot_response + } + ], + "state": state + }, + "errors": [ + { + "message": "" + } + ] + } + + return { + "status_code": 200, + "response": response + } diff --git a/textbase/models.py b/textbase/models.py index 814ed533..beea36ac 100644 --- a/textbase/models.py +++ b/textbase/models.py @@ -4,6 +4,7 @@ import time import typing import traceback +import google.generativeai as palm from textbase import Message @@ -91,9 +92,11 @@ def generate( for message in message_history: if message["role"] == "user": - inputs["past_user_inputs"].extend(extract_content_values(message)) + inputs["past_user_inputs"].extend( + extract_content_values(message)) else: - inputs["generated_responses"].extend(extract_content_values(message)) + inputs["generated_responses"].extend( + extract_content_values(message)) inputs["text"] = inputs["past_user_inputs"].pop(-1) @@ -106,22 +109,27 @@ def generate( } data = json.dumps(payload) - response = requests.request("POST", API_URL, headers=headers, data=data) + response = requests.request( + "POST", API_URL, headers=headers, data=data) response = json.loads(response.content.decode("utf-8")) if response.get("error", None) == "Authorization header is invalid, use 'Bearer API_TOKEN'.": print("Hugging Face API key is not correct.") if response.get("estimated_time", None): - print(f"Model is loading please wait for {response.get('estimated_time')}") + print( + f"Model is loading please wait for {response.get('estimated_time')}") time.sleep(response.get("estimated_time")) - response = requests.request("POST", API_URL, headers=headers, data=data) + response = requests.request( + "POST", API_URL, headers=headers, data=data) response = json.loads(response.content.decode("utf-8")) return response["generated_text"] except Exception: - print(f"An exception occured while using this model, please try using another model.\nException: {traceback.format_exc()}.") + print( + f"An exception occured while using this model, please try using another model.\nException: {traceback.format_exc()}.") + class BotLibre: application = None @@ -139,8 +147,42 @@ def generate( "instance": cls.instance, "message": most_recent_message } - response = requests.post('https://www.botlibre.com/rest/json/chat', json=request) - data = json.loads(response.text) # parse the JSON data into a dictionary + response = requests.post( + 'https://www.botlibre.com/rest/json/chat', json=request) + # parse the JSON data into a dictionary + data = json.loads(response.text) message = data['message'] - return message \ No newline at end of file + return message + + +class Palm: + api_key = None + + @classmethod + def generate( + cls, + system_prompt: str, + message_history: list[Message], + model="models/chat-bison-001", + temperature=0.7 + ): + assert cls.api_key is not None, "Palm API key is not set." + palm.configure(api_key=cls.api_key) + filtered_messages = [] + + for message in message_history: + # list of all the contents inside a single message + contents = get_contents(message, "STRING") + if contents: + filtered_messages.extend(contents) + + filtered_messages = [{'author': '0' if message['role'] == 'user' else '1', + 'content': message['content']} for message in filtered_messages] + + response = palm.chat( + model=model, + messages=[*map(dict, filtered_messages)], + temperature=temperature, + ) + return response.last \ No newline at end of file From da5be7d846038aaa5bc56270f7f46b54cfae8138 Mon Sep 17 00:00:00 2001 From: kksahay Date: Mon, 4 Sep 2023 07:16:17 +0530 Subject: [PATCH 2/6] feat: palm ai chromadb setup --- stores/palmai-bot/main.py | 90 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 stores/palmai-bot/main.py diff --git a/stores/palmai-bot/main.py b/stores/palmai-bot/main.py new file mode 100644 index 00000000..bbad7d30 --- /dev/null +++ b/stores/palmai-bot/main.py @@ -0,0 +1,90 @@ +from textbase import bot, Message +import google.generativeai as palm +from typing import List +import chromadb +from chromadb.api.types import Document, Embeddings + +palm.configure(api_key='') + +DOCUMENT = "Textbase is an awesome app" + + +def embed_function(texts: Document) -> Embeddings: + return [palm.generate_embeddings(model='models/embedding-gecko-001', text=text)['embedding'] + for text in texts] + + +def create_chroma_db(documents, name): + chroma_client = chromadb.Client() + db = chroma_client.create_collection( + name=name, embedding_function=embed_function) + for i, d in enumerate(documents): + db.add( + documents=d, + ids=str(i) + ) + return db + + +db = create_chroma_db([DOCUMENT], "textbasedb") + + +def get_relevant_passage(query, db): + passage = db.query(query_texts=[query], n_results=1)['documents'][0][0] + return passage + + +def make_prompt(query, relevant_passage): + escaped = relevant_passage.replace( + "'", "").replace('"', "").replace("\n", " ") + prompt = ("""You are a helpful and informative bot that answers questions using text from the reference passage included below. \ + Be sure to respond in a complete sentence, being comprehensive, including all relevant background information. \ + However, you are talking to a non-technical audience, so be sure to break down complicated concepts and \ + strike a friendly and converstional tone. \ + If the passage is irrelevant to the answer, you may ignore it. + QUESTION: '{query}' + PASSAGE: '{relevant_passage}' + + ANSWER: + """).format(query=query, relevant_passage=escaped) + + return prompt + + +@bot() +def on_message(message_history: List[Message], state: dict = None): + + query = message_history[-1]['content'][0]['value'] + passage = get_relevant_passage(query, db) + prompt = make_prompt(query, passage) + + bot_response = palm.generate_text( + prompt=prompt, + model="models/text-bison-001", + temperature=0.65, + max_output_tokens=1000 + ) + + bot_response = bot_response.candidates[0]['output'] + + response = { + "data": { + "messages": [ + { + "data_type": "STRING", + "value": bot_response + } + ], + "state": state + }, + "errors": [ + { + "message": "" + } + ] + } + + return { + "status_code": 200, + "response": response + } From bdbc860422682eec086ae8a1d01c02e1240a76fa Mon Sep 17 00:00:00 2001 From: kksahay Date: Mon, 4 Sep 2023 07:42:49 +0530 Subject: [PATCH 3/6] test: bot.py --- tests/test_bot.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/test_bot.py diff --git a/tests/test_bot.py b/tests/test_bot.py new file mode 100644 index 00000000..2cb016f6 --- /dev/null +++ b/tests/test_bot.py @@ -0,0 +1,33 @@ +import pytest +from textbase import bot + +class MockRequest: + def __init__(self, method, json_data): + self.method = method + self.json_data = json_data + +def mock_bot_function(*args): + request = args[0] + response = ('', 204, {'Access-Control-Allow-Origin': '*'}) + return response + +def test_bot(): + mock_request = MockRequest("OPTIONS", { + "data": { + "message_history": [], + "state": {} + } + }) + result = bot()(mock_bot_function)(mock_request) + assert isinstance(result, tuple) + assert len(result) == 3 + expected_headers = { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Methods': 'GET', + 'Access-Control-Allow-Headers': 'Content-Type', + 'Access-Control-Max-Age': '3600' + } + assert result[2] == expected_headers + assert result[0] == '' + assert result[1] == 204 + From 7b0680ec62223bd228d2088042165067d966a499 Mon Sep 17 00:00:00 2001 From: kksahay Date: Mon, 4 Sep 2023 09:53:19 +0530 Subject: [PATCH 4/6] feat: chromadb integration with openai --- stores/openai-bot/main.py | 85 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 stores/openai-bot/main.py diff --git a/stores/openai-bot/main.py b/stores/openai-bot/main.py new file mode 100644 index 00000000..9d8820ed --- /dev/null +++ b/stores/openai-bot/main.py @@ -0,0 +1,85 @@ +from textbase import bot, Message +from textbase.models import OpenAI +from typing import List +import chromadb +from chromadb.api.types import Document, Embeddings +from chromadb.utils.embedding_functions import OpenAIEmbeddingFunction + +OpenAI.api_key = "" + +DOCUMENT = "Textbase is an awesome app" + +embed_function = OpenAIEmbeddingFunction(OpenAI.api_key) + + +def create_chroma_db(documents, name): + chroma_client = chromadb.Client() + db = chroma_client.create_collection( + name=name, embedding_function=embed_function) + for i, d in enumerate(documents): + db.add( + documents=d, + ids=str(i) + ) + return db + + +db = create_chroma_db([DOCUMENT], "textbasedb") + + +def get_relevant_passage(query, db): + passage = db.query(query_texts=[query], n_results=1)['documents'][0][0] + return passage + + +def make_prompt(query, relevant_passage): + escaped = relevant_passage.replace( + "'", "").replace('"', "").replace("\n", " ") + prompt = ("""You are a helpful and informative bot that answers questions using text from the reference passage included below. \ + Be sure to respond in a complete sentence, being comprehensive, including all relevant background information. \ + However, you are talking to a non-technical audience, so be sure to break down complicated concepts and \ + strike a friendly and converstional tone. \ + If the passage is irrelevant to the answer, you may ignore it. + QUESTION: '{query}' + PASSAGE: '{relevant_passage}' + + ANSWER: + """).format(query=query, relevant_passage=escaped) + + return prompt + + +@bot() +def on_message(message_history: List[Message], state: dict = None): + + query = message_history[-1]['content'][0]['value'] + passage = get_relevant_passage(query, db) + prompt = make_prompt(query, passage) + + bot_response = OpenAI.generate( + system_prompt=prompt, + message_history=message_history, + model="gpt-3.5-turbo", + ) + + response = { + "data": { + "messages": [ + { + "data_type": "STRING", + "value": bot_response + } + ], + "state": state + }, + "errors": [ + { + "message": "" + } + ] + } + + return { + "status_code": 200, + "response": response + } From 5a0a2265ebbe48cd0069163fdb239295179d2c72 Mon Sep 17 00:00:00 2001 From: kksahay Date: Mon, 4 Sep 2023 09:57:47 +0530 Subject: [PATCH 5/6] chore: palmai doc --- docs/docs/examples/palmai-bot.md | 53 ++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 docs/docs/examples/palmai-bot.md diff --git a/docs/docs/examples/palmai-bot.md b/docs/docs/examples/palmai-bot.md new file mode 100644 index 00000000..2e209d2b --- /dev/null +++ b/docs/docs/examples/palmai-bot.md @@ -0,0 +1,53 @@ +--- +sidebar_position: 4 +--- + +# Open AI bot + +This bot makes an API call to Palm AI and processes the user input. It uses chat-bison-001 + +```py +from textbase import bot, Message +from textbase.models import Palm +from typing import List + +# Load your PalmAI API key +Palm.api_key = "" + +# Prompt for chat-bison-001 +SYSTEM_PROMPT = """You are chatting with an AI. There are no specific prefixes for responses, so you can ask or talk about anything you like. +The AI will respond in a natural, conversational manner. Feel free to start the conversation with any question or topic, and let's have a +pleasant chat! +""" + +@bot() +def on_message(message_history: List[Message], state: dict = None): + + # Generate Palm-model-chat-bison-001 response + bot_response = Palm.generate( + system_prompt=SYSTEM_PROMPT, + message_history=message_history + ) + + response = { + "data": { + "messages": [ + { + "data_type": "STRING", + "value": bot_response + } + ], + "state": state + }, + "errors": [ + { + "message": "" + } + ] + } + + return { + "status_code": 200, + "response": response + } +``` \ No newline at end of file From f47c03105dddff2e26982112ec5f52730e77b2f7 Mon Sep 17 00:00:00 2001 From: kksahay Date: Mon, 4 Sep 2023 11:47:36 +0530 Subject: [PATCH 6/6] chore: doc title update --- docs/docs/examples/palmai-bot.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/examples/palmai-bot.md b/docs/docs/examples/palmai-bot.md index 2e209d2b..debef6b5 100644 --- a/docs/docs/examples/palmai-bot.md +++ b/docs/docs/examples/palmai-bot.md @@ -2,7 +2,7 @@ sidebar_position: 4 --- -# Open AI bot +# Palm AI bot This bot makes an API call to Palm AI and processes the user input. It uses chat-bison-001