TheCenterforUnity commited on
Commit
9c8a058
·
1 Parent(s): 450b76b

Initial version

Browse files
.DS_Store ADDED
Binary file (6.15 kB). View file
 
.gitignore ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ ./logs
2
+ __pychache__
3
+ ./tests
4
+ .env
5
+ ./*.txt
AskJesus_RAG.py ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import openai
2
+ import pandas as pd
3
+ import os
4
+ import logging
5
+
6
+ from logging_utils import setup_logging_to_file, sanitize_filename, close_logging_handlers
7
+
8
+ # support for the .env file
9
+ from dotenv import load_dotenv
10
+ load_dotenv()
11
+
12
+ # ChromaDB's client library for Python
13
+ import chromadb
14
+ from chromadb.utils.embedding_functions import OpenAIEmbeddingFunction
15
+ from chroma_datasets.utils import (
16
+ load_huggingface_dataset,
17
+ import_chroma_exported_hf_dataset
18
+ )
19
+
20
+ # LangChain's client library for Python
21
+ import langchain
22
+ from langchain.vectorstores import Chroma
23
+ from langchain.embeddings import OpenAIEmbeddings
24
+ from langchain.chains import (
25
+ RetrievalQA,
26
+ LLMChain
27
+ )
28
+ from langchain.chat_models import ChatOpenAI
29
+ from langchain.prompts import PromptTemplate
30
+ from langchain.prompts.chat import (
31
+ ChatPromptTemplate,
32
+ HumanMessagePromptTemplate,
33
+ )
34
+
35
+
36
+
37
+ #
38
+ # OpenAI
39
+ #
40
+
41
+
42
+ # Test that your OpenAI API key is correctly set as an environment variable
43
+ if os.getenv("OPENAI_API_KEY") is not None:
44
+ openai.api_key = os.getenv("OPENAI_API_KEY")
45
+ print ("OPENAI_API_KEY is ready")
46
+ else:
47
+ print ("OPENAI_API_KEY environment variable not found")
48
+
49
+ # set up both gpt-3.5 and gpt-4 so we can compare them
50
+ chat_gpt_temperature = os.getenv("CHAT_GPT_TEMPERATURE")
51
+
52
+ # This code is used both in a private local deployment, with a local persisted ChromaDB
53
+ # and on HuggingFace, by loading the ChromaDB from a dataset.
54
+ # In either case, the ChromaDB is used to create a Chroma LangChain.
55
+ private_local_deployemnt = os.getenv("PRIVATE_LOCAL_DEPLOYMENT") == "True"
56
+
57
+
58
+ #
59
+ # ChromaDB
60
+ #
61
+
62
+ # open a pre-existing ChromaDB collection of UB paragraphs from disk (created by a different process)
63
+ chroma_embedding_function = OpenAIEmbeddingFunction(
64
+ openai.api_key,
65
+ model_name=os.getenv("EMBEDDING_MODEL")
66
+ )
67
+
68
+ if private_local_deployemnt :
69
+ # the entire UB is under UBVectorDB/entire_book
70
+ chroma_db_path = "../../Python/UBVectorDB/entire_book"
71
+ chroma_client = chromadb.PersistentClient(path=chroma_db_path)
72
+ chroma_collection_name = "entire_book_paragraph_content"
73
+
74
+ paragraph_content_collection = chroma_client.get_collection(
75
+ name=chroma_collection_name,
76
+ embedding_function=chroma_embedding_function
77
+ )
78
+ print(f"\nChroma has opened the persisted collection {chroma_collection_name} at {chroma_db_path} with {paragraph_content_collection.count()} paragraphs.")
79
+ else :
80
+ UB_paragraphs_dataset_name = os.getenv("UB_PARAGRAPH_DATASET_NAME")
81
+ UB_paragraphs_dataset = load_huggingface_dataset(UB_paragraphs_dataset_name)
82
+
83
+ #
84
+ # create a Chroma Collection from the HF dataset
85
+ #
86
+ chroma_client = chromadb.Client()
87
+ chroma_collection_name_from_HF = "entire_book_paragraph_content-from-HF"
88
+
89
+
90
+ """
91
+ NOTE: this code works only on HF due to batch sizes.
92
+
93
+ ValueError: Batch size 14585 exceeds maximum batch size 166
94
+ """
95
+
96
+ paragraph_content_collection_from_HF = import_chroma_exported_hf_dataset(
97
+ chroma_client=chroma_client,
98
+ dataset=UB_paragraphs_dataset['data'],
99
+ collection_name=chroma_collection_name_from_HF,
100
+ embedding_function=chroma_embedding_function
101
+ )
102
+ print(paragraph_content_collection_from_HF.count())
103
+
104
+
105
+
106
+ #
107
+ # LangChain
108
+ #
109
+
110
+
111
+ # create the open-source embedding function
112
+ langchain_embedding_function = OpenAIEmbeddings(
113
+ openai_api_key=openai.api_key
114
+ )
115
+
116
+ # Create a ChromaDB client to be used in LangChain
117
+ langchain_chroma = Chroma(
118
+ client=chroma_client,
119
+ collection_name=chroma_collection_name if private_local_deployemnt else chroma_collection_name_from_HF,
120
+ embedding_function=langchain_embedding_function
121
+ )
122
+ print(f"\nLangChain has imported the collection.\nThere are {langchain_chroma._collection.count()} UB paragraphs.\n")
123
+
124
+
125
+ # Use LangChain to run a similiraty query on the collection
126
+ def similarity_test(question, k=20) :
127
+ docs = langchain_chroma.similarity_search(question, k)
128
+
129
+ print(f"\nSimilarity Search for: {question}\n")
130
+ #logging.info(f"\nSimilarity Search for: {question}\n")
131
+
132
+ for doc in docs:
133
+ print(f"{doc.metadata['Standard Reference']} {doc.page_content}\n")
134
+ #logging.info(f"{doc.metadata['Standard Reference']} {doc.page_content}\n")
135
+
136
+
137
+
138
+ #
139
+ # Define the LangChain RetrievalQA chains
140
+ #
141
+ similarity_k = int(os.getenv("SIMILARITY_K"))
142
+
143
+
144
+
145
+ def run_question(question, system_message, gpt_model, use_RAG):
146
+ # Creating log filename based on first 50 characters of the question
147
+ log_filename = f"{sanitize_filename(question[:50])}.txt"
148
+
149
+ # Setting up logging to the file
150
+ setup_logging_to_file(log_filename)
151
+
152
+ print(f"Question: {question}\nSystem Message: {system_message}\nChatGPT model: {gpt_model}\nUse references: {use_RAG}\n")
153
+ #logging.info(f"Question: {question}\nSystem Message: {system_message}\nChatGPT model: {gpt_model}\nUse references: {use_RAG}\n")
154
+
155
+ if use_RAG is True :
156
+ response = run_retrievalQA_chains(question, system_message, gpt_model)
157
+ else :
158
+ response = run_regular_chain(question, system_message, gpt_model)
159
+
160
+ print(f"Results logged to file: {log_filename}\n\n")
161
+ close_logging_handlers()
162
+
163
+ return response
164
+
165
+
166
+ def run_retrievalQA_chains(question, system_message, gpt_model):
167
+ result = ""
168
+
169
+ llm = ChatOpenAI(model_name=gpt_model, temperature=chat_gpt_temperature)
170
+
171
+ template = system_message + "Use the following pieces of context to answer the question at the end.\n{context}\nQuestion: {question}\Masterful and inspiring answer:"
172
+
173
+ QA_CHAIN_PROMPT = PromptTemplate.from_template(template)
174
+
175
+ qa_chain_with_prompt = RetrievalQA.from_chain_type(
176
+ llm,
177
+ retriever=langchain_chroma.as_retriever(search_kwargs={'k': similarity_k}),
178
+ chain_type_kwargs={"prompt": QA_CHAIN_PROMPT},
179
+ return_source_documents=True
180
+ )
181
+
182
+ result = qa_chain_with_prompt({"query": question})
183
+
184
+ print(f"\n\nWith UB references (model: {gpt_model}, with {len(result['source_documents'])} references)\n\n{result['result']}\n\n")
185
+ #logging.info(f"\n\nWith UB references (model: {gpt_model}, with {len(result['source_documents'])} references)\n\n{result['result']}\n\n")
186
+
187
+ return result['result']
188
+
189
+
190
+ def run_regular_chain(question, system_message, gpt_model) :
191
+ result = ""
192
+
193
+ llm = ChatOpenAI(model_name=gpt_model, temperature=chat_gpt_temperature)
194
+
195
+ template = system_message + "\nQuestion: {question}\Masterful and inspiring answer:"
196
+
197
+ # create AskJesus system message for non-RAG chains
198
+ human_message_prompt = HumanMessagePromptTemplate(
199
+ prompt=PromptTemplate(
200
+ template=template,
201
+ input_variables=["question"]
202
+ )
203
+ )
204
+ prompt_template = ChatPromptTemplate.from_messages([human_message_prompt])
205
+
206
+ # run chain, log to file and to console
207
+ chain = LLMChain(llm=llm, prompt=prompt_template)
208
+ result = chain.run(question)
209
+
210
+ print(f"\n\nWithout UB references (model: {gpt_model})\n\n{result}\n")
211
+ #logging.info(f"\n\nWithout UB references (model: {gpt_model})\n\n{result}\n")
212
+
213
+ return result
Tell me everything about Adam and Eve.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK"
2
+ HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
Tell me everything about your relationship with yo.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ HTTP Request: POST https://api.openai.com/v1/embeddings "HTTP/1.1 200 OK"
2
+ HTTP Request: POST https://api.openai.com/v1/chat/completions "HTTP/1.1 200 OK"
__pycache__/AskJesus_RAG.cpython-310.pyc ADDED
Binary file (4.51 kB). View file
 
__pycache__/logging_utils.cpython-310.pyc ADDED
Binary file (949 Bytes). View file
 
__pycache__/social_sharing.cpython-310.pyc ADDED
Binary file (2.39 kB). View file
 
app.py ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # AskJesus HF v0.32
3
+ # Feb 22, 2024
4
+ #
5
+ #
6
+
7
+ # Import the required libraries
8
+ import os
9
+ import gradio as gr
10
+
11
+ # Import the LangChain and ChatOpenAI classes
12
+ from langchain.chat_models import ChatOpenAI
13
+ from langchain.prompts.chat import (
14
+ ChatPromptTemplate,
15
+ SystemMessagePromptTemplate,
16
+ HumanMessagePromptTemplate,
17
+ )
18
+ from langchain.schema import HumanMessage, SystemMessage
19
+
20
+
21
+ # Import the required libraries
22
+ from dotenv import load_dotenv
23
+ load_dotenv()
24
+
25
+ from social_sharing import send_email_mailchimp, send_to_airtable
26
+ from AskJesus_RAG import run_question
27
+
28
+ # This code is used both in a private local deployment, with a local persisted ChromaDB
29
+ # and on HuggingFace, by loading the ChromaDB from a dataset.
30
+ # In either case, the ChromaDB is used to create a Chroma LangChain.
31
+ private_local_deployemnt = os.getenv("PRIVATE_LOCAL_DEPLOYMENT") == "True"
32
+
33
+
34
+ #
35
+ # ChatGPT SYSTEM MESSAGE and Model selection
36
+ #
37
+ current_system_message = os.getenv("CHAT_GPT_DEFAULT_SYSTEM_MESSAGE")
38
+ current_gpt_model = os.getenv("CHAT_GPT_3_5_MODEL")
39
+
40
+ # Use RAG for the answer generation
41
+ current_RAG_option = True
42
+
43
+ # Initialize an empty list to store the chat history
44
+ chat_history = []
45
+
46
+ with gr.Blocks(title=f"AskJesus Chatbot {os.getenv('ASKJESUS_CHATBOT_VERSION')}") as demo:
47
+
48
+ # Gradio UI
49
+ with gr.Row():
50
+ msg = gr.Textbox(
51
+ label="Question:",
52
+ placeholder="Enter question here",
53
+ scale=4
54
+ )
55
+
56
+ with gr.Row():
57
+ chatbot = gr.Chatbot(
58
+ label=f"AskJesus Chatbot {os.getenv('ASKJESUS_CHATBOT_VERSION')}",
59
+ scale=8
60
+ )
61
+
62
+ with gr.Row():
63
+
64
+ email_input = gr.Textbox(
65
+ label="Email:",
66
+ placeholder="Enter your email here",
67
+ scale=8
68
+ )
69
+ send_email_button = gr.Button(
70
+ value="Send email",
71
+ variant="primary",
72
+ scale=1
73
+ )
74
+
75
+
76
+ def user(user_message, history):
77
+ # Append the user's message to the chat history
78
+ chat_history.append({"Question": user_message, "AskJesus chatbot": None})
79
+ return gr.update(value="", interactive=False), history + [[user_message, None]]
80
+
81
+ def bot(history):
82
+
83
+ # Get a chat completion from the formatted messages and the current system message
84
+ global current_system_message
85
+
86
+ # assuming response is an str
87
+ bot_message = run_question(
88
+ question=history[-1][0],
89
+ system_message=current_system_message,
90
+ gpt_model=current_gpt_model,
91
+ use_RAG=current_RAG_option
92
+ )
93
+
94
+ # Update the chat history with the bot's message
95
+ chat_history[-1]["AskJesus chatbot"] = bot_message
96
+
97
+ history[-1][1] = bot_message
98
+ yield history
99
+
100
+ # Send the question and answer to Airtable
101
+ send_to_airtable(
102
+ history[-1][0],
103
+ history[-1][1],
104
+ current_system_message,
105
+ current_gpt_model,
106
+ current_RAG_option
107
+ )
108
+
109
+ def email_session(email):
110
+ if not email:
111
+ print("No email address provided. Cannot send email.")
112
+ raise gr.Error("Please provide email.")
113
+ return
114
+
115
+ # Convert the chat history to a string format
116
+ history_str = "\n".join([f"Question: {entry['Question']}\nAskJesus chatbot: {entry['AskJesus chatbot']}\n\n" for entry in chat_history])
117
+ send_email_mailchimp(email, history_str)
118
+
119
+ def email_last_qa(email):
120
+ if not email:
121
+ print("No email address provided. Cannot send email.")
122
+ raise gr.Error("Please provide email.")
123
+ return
124
+
125
+ # Convert the last question and answer to a string format
126
+ history_str = "\n".join([f"Question: {chat_history[-1]['Question']}\nAskJesus chatbot: {chat_history[-1]['AskJesus chatbot']}\n\n"])
127
+ if send_email_mailchimp(email, history_str) == True :
128
+ gr.Info("Email sent.")
129
+ else :
130
+ raise gr.Error("Error: email not sent.")
131
+
132
+ response = msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False).then(
133
+ bot, chatbot, chatbot
134
+ )
135
+ response.then(lambda: gr.update(interactive=True), None, [msg], queue=False)
136
+ send_email_button.click(email_last_qa, [email_input])
137
+
138
+
139
+ demo.queue()
140
+ demo.launch(show_error=True)
logging_utils.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import logging
2
+
3
+ def setup_logging_to_file(filename):
4
+ logging.basicConfig(
5
+ filename=filename,
6
+ level=logging.INFO,
7
+ #format='%(asctime)s - %(levelname)s - %(message)s'
8
+ format='%(message)s'
9
+
10
+ )
11
+
12
+
13
+ def sanitize_filename(filename):
14
+ invalid_chars = '\/:*?"<>|'
15
+ return ''.join('_' if c in invalid_chars else c for c in filename)
16
+
17
+
18
+ def close_logging_handlers():
19
+ for handler in logging.root.handlers[:]:
20
+ handler.close()
21
+ logging.root.removeHandler(handler)
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ openai
2
+ langchain
3
+ gradio
4
+ dotenv
social_sharing.py ADDED
@@ -0,0 +1,92 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #
2
+ # Ask Jesus v0.3
3
+ # Sep 28, 2023
4
+ #
5
+
6
+ import os
7
+ import tweepy
8
+ import requests # To make API calls to Airtable
9
+
10
+
11
+ #
12
+ # Mandrill
13
+ #
14
+ import mailchimp_transactional as MailchimpTransactional
15
+ from mailchimp_transactional.api_client import ApiClientError
16
+
17
+ # NOTE: There is a Live or Test key
18
+ mailchimp = MailchimpTransactional.Client(os.getenv("MAILCHIMP_LIVE_KEY"))
19
+
20
+ #
21
+ # Send all questions to an Airtable
22
+ #
23
+
24
+ def send_to_airtable(question, answer, current_system_message, current_gpt_model, current_RAG_option):
25
+ # Replace with your Airtable API URL and API Key
26
+ url = os.getenv("AIRTABLE_URL")
27
+ headers = {"Authorization": f"Bearer {os.getenv('AIRTABLE_PERSONAL_TOKEN')}"}
28
+ data = {
29
+ "fields": {
30
+ "question": question,
31
+ "answer": answer,
32
+ "system prompt":current_system_message,
33
+ "chatbot version":os.getenv("ASKJESUS_CHATBOT_VERSION"),
34
+ "added UB references":str(current_RAG_option),
35
+ "gpt_model":current_gpt_model
36
+ }
37
+ }
38
+ requests.post(url, headers=headers, json=data)
39
+
40
+ #
41
+ # Send last Q&A to via email
42
+ #
43
+
44
+ def send_email_mailchimp(email, history_str):
45
+
46
+ # Email content
47
+ subject = "👉 A chatbot session from AskJesus"
48
+ #body = "Someone that cares about you share their AskJesus chatbot session with you:\n\n" + history_str + "\n\n Click here to try the AskJesus chatbot."
49
+
50
+ html_intro = "<html><body><p>Someone that cares about you shared their AskJesus chatbot session with you:</p>"
51
+ html_link_to_askjesus_cfu_page = f"<p>Click <a href={os.getenv('ASKJESUS_CFU_PAGE')}>here</a> to try the AskJesus chatbot."
52
+ html_link_to_matching_fund_cfu_page = f"<br/>AskJesus will always be free and ad-free. Will you pay-it-foward so that others can enjoy it? Click <a href={os.getenv('MATCHING_FUND_CFU_PAGE')}>here</a> to donate.</p></body></html>"
53
+
54
+ # Replace newline characters with HTML line breaks
55
+ history_html = history_str.replace("\n", "<br/>")
56
+
57
+ # Combine everything
58
+ body = html_intro + history_html + html_link_to_askjesus_cfu_page + html_link_to_matching_fund_cfu_page
59
+
60
+ message = {
61
+ "from_email": os.getenv("FROM_EMAIL"),
62
+ "subject": subject,
63
+ "html": body,
64
+ "to": [
65
+ {
66
+ "email": email,
67
+ "type": "to"
68
+ }
69
+ ]
70
+ }
71
+
72
+ try:
73
+ response = mailchimp.messages.send({"message":message})
74
+ print('Mailchimp API called successfully: {}'.format(response))
75
+ #print (message)
76
+ return True
77
+
78
+ except ApiClientError as error:
79
+ print('An exception occurred: {}'.format(error.text))
80
+ return False
81
+
82
+
83
+ def send_tweet(tweet_link):
84
+
85
+ try:
86
+ twitter_client.create_tweet(text=tweet_link)
87
+ print (f"Successfully tweeted the last Q&A. {tweet_link}")
88
+ return True
89
+
90
+ except Exception as e:
91
+ print (f"Failed to tweet. Error: {e}")
92
+ return False