Upload app.py
Browse files
app.py
ADDED
@@ -0,0 +1,276 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import sqlite3
|
3 |
+
from hashlib import sha256
|
4 |
+
import streamlit as st
|
5 |
+
from datetime import datetime
|
6 |
+
from langchain_community.embeddings import LlamaCppEmbeddings
|
7 |
+
from langchain.text_splitter import CharacterTextSplitter
|
8 |
+
from langchain_community.document_loaders import TextLoader
|
9 |
+
from langchain_community.vectorstores import FAISS
|
10 |
+
from langchain.embeddings import HuggingFaceEmbeddings
|
11 |
+
from langchain.chains.llm import LLMChain
|
12 |
+
from langchain_community.llms import LlamaCpp
|
13 |
+
from langchain.chains import LLMChain
|
14 |
+
from langchain_community.llms import OpenAI
|
15 |
+
from langchain_core.prompts import PromptTemplate, ChatPromptTemplate
|
16 |
+
from langchain_core.runnables import RunnablePassthrough
|
17 |
+
from langchain_core.documents import Document
|
18 |
+
from langchain.chains import create_retrieval_chain
|
19 |
+
from langchain.chains.combine_documents import create_stuff_documents_chain
|
20 |
+
|
21 |
+
|
22 |
+
# Create a SQLite database named 'user_credentials.db'. If it already exists, connect to it.
|
23 |
+
# This database is used for storing user credentials.
|
24 |
+
conn = sqlite3.connect("user_credentials.db")
|
25 |
+
|
26 |
+
# Create a cursor object using the connection.
|
27 |
+
# The cursor is used to execute SQL commands.
|
28 |
+
cursor = conn.cursor()
|
29 |
+
|
30 |
+
# Execute an SQL command using the cursor.
|
31 |
+
# This command attempts to create a new table named 'users' if it doesn't already exist.
|
32 |
+
# The table is designed to store usernames and passwords.
|
33 |
+
# It has two columns: 'username' and 'password'.
|
34 |
+
# 'username' is of type TEXT and is set as the PRIMARY KEY, ensuring that each username is unique.
|
35 |
+
# 'password' is also of type TEXT to store the password associated with each username.
|
36 |
+
cursor.execute('''
|
37 |
+
CREATE TABLE IF NOT EXISTS users (
|
38 |
+
username TEXT PRIMARY KEY,
|
39 |
+
password TEXT
|
40 |
+
)
|
41 |
+
''')
|
42 |
+
|
43 |
+
# Commit the changes made by the cursor.execute command to the database.
|
44 |
+
# This ensures that the creation of the table is saved in the database.
|
45 |
+
conn.commit()
|
46 |
+
|
47 |
+
|
48 |
+
# Check if the embeddings model is not already stored in Streamlit's session state
|
49 |
+
if 'embeddings' not in st.session_state:
|
50 |
+
# Initialize and store the embeddings model in session state for efficient reuse
|
51 |
+
st.session_state.embeddings = HuggingFaceEmbeddings(
|
52 |
+
model_name="sentence-transformers/all-MiniLM-L6-v2", # Specify the model name to use
|
53 |
+
model_kwargs={"device": "cpu"}, # Force the model to run on CPU
|
54 |
+
)
|
55 |
+
|
56 |
+
|
57 |
+
def get_similar_docs(query):
|
58 |
+
# Load the FAISS index from local storage using the embeddings model stored in session state
|
59 |
+
db = FAISS.load_local('faiss_index', st.session_state.embeddings)
|
60 |
+
# Perform a similarity search in the FAISS database for the given query, returning top 100 similar documents with scores
|
61 |
+
docs = db.similarity_search_with_score(query, 100)
|
62 |
+
# Return the list of similar documents and their similarity scores
|
63 |
+
return docs
|
64 |
+
|
65 |
+
def format_docs(docs):
|
66 |
+
# Join the page content of each document in docs with a space, and return the concatenated string
|
67 |
+
return " ".join(doc.page_content for doc in docs)
|
68 |
+
|
69 |
+
def get_advice_from_llm(query):
|
70 |
+
# Load the FAISS index and initialize it with embeddings from session state for document retrieval
|
71 |
+
db = FAISS.load_local('faiss_index', st.session_state.embeddings)
|
72 |
+
# Convert the FAISS index into a retriever for fetching documents based on queries
|
73 |
+
retriever = db.as_retriever()
|
74 |
+
# Initialize the LlamaCpp model with specified model path and context size
|
75 |
+
llm = LlamaCpp(model_path="./tinyllama-1.1b-chat-v1.0.Q8_0.gguf", n_ctx=2048)
|
76 |
+
# Create a chat history string from session state for inclusion in the prompt template
|
77 |
+
chat_history_str = "\n".join(["" + entry[0] + entry[1] + "\n" for entry in st.session_state['chat_history']])
|
78 |
+
# Define the prompt template with placeholders for dynamic context and user input
|
79 |
+
template = """"
|
80 |
+
system
|
81 |
+
{context}""" + \
|
82 |
+
chat_history_str +\
|
83 |
+
"""
|
84 |
+
user{input}
|
85 |
+
|
86 |
+
assistant
|
87 |
+
"""
|
88 |
+
# Initialize a PromptTemplate with variables for dynamic insertion into the template
|
89 |
+
prompt = PromptTemplate(input_variables=["input", "context"], template=template)
|
90 |
+
# Chain the LlamaCpp model with the prompt for generating responses
|
91 |
+
llm_chain = LLMChain(llm=llm, prompt=prompt)
|
92 |
+
# Define the RAG chain combining retriever and LLM chain for generating advice based on the query
|
93 |
+
rag_chain = ({"context": retriever | format_docs, "input": RunnablePassthrough()} | llm_chain)
|
94 |
+
# Invoke the RAG chain with the user query to get advice
|
95 |
+
answer = rag_chain.invoke(query)
|
96 |
+
# Return the generated advice
|
97 |
+
return answer
|
98 |
+
|
99 |
+
|
100 |
+
def vectordb_entry():
|
101 |
+
# Load text documents from a specified file
|
102 |
+
loader = TextLoader("./output.txt")
|
103 |
+
documents = loader.load()
|
104 |
+
# Split loaded documents into smaller chunks for vectorization
|
105 |
+
text_splitter = CharacterTextSplitter(chunk_size=100, chunk_overlap=10)
|
106 |
+
docs = text_splitter.split_documents(documents)
|
107 |
+
# Load or create a FAISS vector database using embeddings from session state
|
108 |
+
db = FAISS.load_local('faiss_index', st.session_state.embeddings)
|
109 |
+
# Add the document chunks to the FAISS database
|
110 |
+
db.add_documents(docs)
|
111 |
+
# Save the updated database locally
|
112 |
+
db.save_local('faiss_index')
|
113 |
+
|
114 |
+
|
115 |
+
def save_into_text_file(file_path, text):
|
116 |
+
# Open the specified file path for writing and save the provided text string into it
|
117 |
+
with open(file_path, 'w') as file:
|
118 |
+
file.write(text)
|
119 |
+
# Print a confirmation message indicating where the text was saved
|
120 |
+
print(f"String saved to {file_path}")
|
121 |
+
|
122 |
+
|
123 |
+
def journal():
|
124 |
+
# Create a container to display chat messages with a specified height
|
125 |
+
messages = st.container(height=600)
|
126 |
+
# Create a chat input box for users to enter their queries
|
127 |
+
query = st.chat_input("Need some advice?")
|
128 |
+
|
129 |
+
# Initialize an input_key in session state if it doesn't exist, to track input changes
|
130 |
+
if 'input_key' not in st.session_state:
|
131 |
+
st.session_state.input_key = 0
|
132 |
+
|
133 |
+
# Initialize chat_history in session state if it doesn't exist, to store chat interactions
|
134 |
+
if 'chat_history' not in st.session_state:
|
135 |
+
st.session_state.chat_history = []
|
136 |
+
|
137 |
+
# If the user has entered a query
|
138 |
+
if query:
|
139 |
+
# Get advice from the language model for the given query
|
140 |
+
answer = get_advice_from_llm(query)
|
141 |
+
# Append the user's query and the model's response to the chat history
|
142 |
+
st.session_state.chat_history.append(("user", query))
|
143 |
+
st.session_state.chat_history.append(("assistant", answer['text']))
|
144 |
+
# Increment the input_key to trigger updates
|
145 |
+
st.session_state.input_key += 1
|
146 |
+
|
147 |
+
# If there is chat history, display it in the messages container
|
148 |
+
if 'chat_history' in st.session_state and st.session_state.chat_history:
|
149 |
+
for speaker, message in st.session_state.chat_history:
|
150 |
+
# Assign a display name based on the speaker
|
151 |
+
who = "You" if speaker == "user" else "JournaLLM"
|
152 |
+
# Display each message in the chat container
|
153 |
+
messages.chat_message(speaker).write(who + ': '+ str(message))
|
154 |
+
|
155 |
+
# Provide a button to reset the chat
|
156 |
+
if st.button('Reset Chat'):
|
157 |
+
# Clear the chat history
|
158 |
+
st.session_state.chat_history = []
|
159 |
+
# Increment the input_key to trigger updates
|
160 |
+
st.session_state.input_key += 1
|
161 |
+
# Rerun the Streamlit app to reflect changes
|
162 |
+
st.experimental_rerun()
|
163 |
+
|
164 |
+
|
165 |
+
|
166 |
+
# Function to hash passwords
|
167 |
+
def hash_password(password):
|
168 |
+
return sha256(password.encode()).hexdigest()
|
169 |
+
|
170 |
+
# Function to check login credentials
|
171 |
+
def authenticate(username, password):
|
172 |
+
hashed_password = hash_password(password)
|
173 |
+
cursor.execute("SELECT * FROM users WHERE username=? AND password=?", (username, hashed_password))
|
174 |
+
return cursor.fetchone() is not None
|
175 |
+
|
176 |
+
# Function to add a new user to the database
|
177 |
+
def add_user(username, password):
|
178 |
+
hashed_password = hash_password(password)
|
179 |
+
try:
|
180 |
+
cursor.execute("INSERT INTO users (username, password) VALUES (?, ?)", (username, hashed_password))
|
181 |
+
conn.commit()
|
182 |
+
return True # User added successfully
|
183 |
+
except sqlite3.IntegrityError:
|
184 |
+
return False # Username already exists
|
185 |
+
|
186 |
+
# Streamlit Login Page
|
187 |
+
def login_page():
|
188 |
+
st.title("Login Page")
|
189 |
+
|
190 |
+
st.session_state['username'] = st.text_input("Username:")
|
191 |
+
st.session_state['password'] = st.text_input("Password:", type="password")
|
192 |
+
|
193 |
+
if st.button("Login"):
|
194 |
+
if not st.session_state['username'] or not st.session_state['password']:
|
195 |
+
st.error("Both username and password are required.")
|
196 |
+
elif authenticate(st.session_state['username'], st.session_state['password']):
|
197 |
+
st.success("Login successful!")
|
198 |
+
else:
|
199 |
+
st.error("Invalid credentials. Please try again.")
|
200 |
+
|
201 |
+
# Streamlit Signup Page
|
202 |
+
def signup_page():
|
203 |
+
st.title("Signup Page")
|
204 |
+
new_username = st.text_input("New Username:")
|
205 |
+
new_password = st.text_input("New Password:", type="password")
|
206 |
+
|
207 |
+
if st.button("Signup"):
|
208 |
+
if not new_username or not new_password:
|
209 |
+
st.error("Both username and password are required.")
|
210 |
+
else:
|
211 |
+
result = add_user(new_username, new_password)
|
212 |
+
if result:
|
213 |
+
st.success("Signup successful! You can now login.")
|
214 |
+
#create db
|
215 |
+
else:
|
216 |
+
st.error("Username already exists. Please choose a different username.")
|
217 |
+
|
218 |
+
|
219 |
+
def entry():
|
220 |
+
st.title('JournaLLM') # Set the title of the Streamlit app
|
221 |
+
# Display a welcome message to the user
|
222 |
+
st.write('Welcome to JournaLLM, your personal space for mindful reflection and goal tracking! This app is designed to help you seamlessly capture your daily thoughts, set meaningful goals, and track your progress.')
|
223 |
+
|
224 |
+
# Initialize an input key in session state if it's not present, to track submissions
|
225 |
+
if 'input_key' not in st.session_state:
|
226 |
+
st.session_state.input_key = 0
|
227 |
+
|
228 |
+
file_path = "output.txt" # Define the path for the output file where entries will be saved
|
229 |
+
|
230 |
+
# Create a text area in the app for users to write their journal entry
|
231 |
+
text = st.text_area("Today's Entry")
|
232 |
+
|
233 |
+
# Prepare a template with questions for the journal entry, including the current date
|
234 |
+
template = f'''Question: What happened on {datetime.today().strftime("%B %d, %Y")}?
|
235 |
+
How did I feel on {datetime.today().strftime("%B %d, %Y")}?
|
236 |
+
What were the events that happened on {datetime.today().strftime("%B %d, %Y")}?
|
237 |
+
Describe your day, {datetime.today().strftime("%B %d, %Y")}. \n Answer: '''
|
238 |
+
|
239 |
+
text = template + text # Append the user's text to the template
|
240 |
+
|
241 |
+
# Save the journal entry to a file and update the vector database when the 'Pen down' button is pressed
|
242 |
+
if st.button('Pen down') and text:
|
243 |
+
save_into_text_file(file_path, text) # Call function to save the text into the specified file
|
244 |
+
vectordb_entry() # Call function to add the entry to the vector database
|
245 |
+
st.write('Entry saved') # Confirm the entry has been saved
|
246 |
+
st.write(text) # Display the saved text to the user
|
247 |
+
st.session_state.input_key += 1 # Increment the session state's input key to track changes
|
248 |
+
|
249 |
+
|
250 |
+
|
251 |
+
# Main Streamlit App
|
252 |
+
def main():
|
253 |
+
st.set_page_config(layout="wide")
|
254 |
+
st.sidebar.title("Navigation")
|
255 |
+
page = st.sidebar.radio("Go to", ["Login", "Signup","Entry","Advice"])
|
256 |
+
|
257 |
+
if page == "Login":
|
258 |
+
login_page()
|
259 |
+
elif page == "Signup":
|
260 |
+
signup_page()
|
261 |
+
elif page == "Journal":
|
262 |
+
if st.session_state.username == "":
|
263 |
+
st.write('Please login to continue.')
|
264 |
+
else:
|
265 |
+
st.write(f"Logged in as {st.session_state.username}")
|
266 |
+
entry()
|
267 |
+
elif page == "Advice":
|
268 |
+
if st.session_state.username == "":
|
269 |
+
st.write('Please login to continue.')
|
270 |
+
else:
|
271 |
+
st.write(f"Logged in as {st.session_state.username}")
|
272 |
+
journal()
|
273 |
+
|
274 |
+
if __name__ == "__main__":
|
275 |
+
main()
|
276 |
+
|