Spaces:
Sleeping
Sleeping
import sys | |
import os | |
import streamlit as st | |
import configparser | |
from datetime import datetime | |
import atexit | |
import pickle | |
import uuid # Import the uuid module | |
import re | |
import base64 | |
import sqlite3 | |
import gspread | |
import streamlit.components.v1 as components | |
from langchain_community.vectorstores import Chroma | |
from langchain.chains import ConversationalRetrievalChain | |
from langchain.text_splitter import CharacterTextSplitter | |
from langchain.memory import ConversationBufferMemory | |
from langchain_community.llms import OpenAI | |
from langchain_community.chat_models import ChatOpenAI | |
from langchain_community.embeddings import OpenAIEmbeddings | |
from langchain.chains import RetrievalQA | |
from langchain.prompts import PromptTemplate | |
from langchain.prompts.prompt import PromptTemplate | |
from langchain.prompts import SystemMessagePromptTemplate | |
from langchain.prompts import HumanMessagePromptTemplate | |
from langchain.prompts import ChatMessagePromptTemplate | |
from langchain.prompts import ChatPromptTemplate | |
config = configparser.ConfigParser() | |
# Set page to wide mode | |
st.set_page_config(layout="wide") | |
# Connect to Google Sheets | |
from oauth2client.service_account import ServiceAccountCredentials | |
# Define the scope | |
scope = ['https://spreadsheets.google.com/feeds','https://www.googleapis.com/auth/drive'] | |
# Add credentials to the account | |
creds = ServiceAccountCredentials.from_json_keyfile_name('./boreal-matrix-401021-3df47b4efb3d.json', scope) | |
# Authorize the clientsheet | |
client = gspread.authorize(creds) | |
google_sheet_url = os.getenv("Google_Sheet") | |
sheet = client.open_by_url(google_sheet_url) | |
worksheet = sheet.get_worksheet(0) | |
# Function to get base64 encoding of an image | |
def get_image_base64(path): | |
with open(path, "rb") as image_file: | |
encoded_string = base64.b64encode(image_file.read()).decode() | |
return encoded_string | |
icon_base64 = get_image_base64("clipboard.png") | |
# Function to create a copy-to-clipboard button | |
def create_copy_button(text_to_copy): | |
button_uuid = str(uuid.uuid4()).replace("-", "") | |
button_id = re.sub('\D', '', button_uuid) | |
copy_js = f""" | |
<div style="text-align: right;"> | |
<script> | |
function copyToClipboard{button_id}() {{ | |
const str = `{text_to_copy}`; | |
const el = document.createElement('textarea'); | |
el.value = str; | |
document.body.appendChild(el); | |
el.select(); | |
document.execCommand('copy'); | |
document.body.removeChild(el); | |
}} | |
</script> | |
<button | |
onmouseover="this.style.transform='scale(1.3)'" | |
onmouseout="this.style.transform='scale(1.0)'" | |
onclick="copyToClipboard{button_id}()" | |
class="copy-button" | |
title="Copy to clipboard" | |
style="border: none; background: none; cursor: pointer; transition: transform 0.3s ease;"> | |
<img src="data:image/png;base64,{icon_base64}" style="width: 24px; height: 24px;"/> | |
</button> | |
</div> | |
""" | |
return copy_js | |
# Retrieve the API key from the environment variables | |
api_key = os.getenv("OPENAI_API_KEY") | |
# Check if the API key is available, if not, raise an error | |
if api_key is None: | |
raise ValueError("API key not found. Ensure that the OPENAI_API_KEY environment variable is set.") | |
# Create a Chroma database instance from the SQLite file | |
vectordb = Chroma(persist_directory="./data", embedding_function=OpenAIEmbeddings()) | |
# Define the system message template | |
system_template = """Use only the following pieces of context to answer the question at the end. | |
If you don't know the answer, just say that you don't know. Don't try to make up an answer. | |
Always answer in Englsih. Split the answer into easily readable paragraphs. Use bullet points and number points where possible. | |
Include any useful URLs and/or contact details from the context provided whereever possible. | |
Always end by adding a carrage return and then say: | |
Thank you for your query! If you need detailed info please visit https://www.citizensinformation.ie. | |
---------------- | |
{context}""" | |
# Create the chat prompt templates | |
messages = [ | |
SystemMessagePromptTemplate.from_template(system_template), | |
HumanMessagePromptTemplate.from_template("{question}") | |
] | |
qa_prompt = ChatPromptTemplate.from_messages(messages) | |
pdf_qa = ConversationalRetrievalChain.from_llm( | |
ChatOpenAI(temperature=0.9, model_name="gpt-3.5-turbo"), | |
vectordb.as_retriever(),return_source_documents=True,verbose=False,combine_docs_chain_kwargs={"prompt": qa_prompt}) | |
chat_history = [] | |
user_query = "" | |
answer = "" # Initialize ai_response with a default value | |
def ask_alans_ai(query, vectordb, chat_history): | |
# Filter out chat history turns where the answer is None | |
filtered_chat_history = [(q, a) for q, a in chat_history if a is not None] | |
# Call pdf_qa with the filtered chat history | |
result = pdf_qa( | |
{"question": query, "chat_history": filtered_chat_history, "vectordb": vectordb}) | |
answer = result["answer"] | |
# Append the new query and its answer to the original chat history | |
chat_history.append((query, answer)) | |
return answer | |
def clear_input_box(): | |
st.session_state["new_item"] = "" | |
# Clean and prepare data for appending | |
def clean_string(s): | |
return s.replace("\n", " ").replace("\t", " ") | |
# Streamlit app | |
def main(): | |
answer = "" # Initialize ai_response with a default value | |
# Sidebar | |
st.sidebar.title("About Citizens Information Chatbot") | |
st.sidebar.write("""**Health, Social Welfare, Employment, Money and Tax, Moving Country, Returning to Ireland, Housing, Education and Training, Travel and Recreation, Environment, Government in Ireland, Consumer, Death and Bereavement, Family and Relationships, Justice** | |
<br><br> | |
**General Info Only:** | |
This chatbot gives basic information, not legal or professional advice.<br><br> | |
**No Liability:** | |
We're not responsible for decisions made based on this chatbot's info. For personal advice, please consult a professional. | |
<br><br> | |
**No Personal Data:** | |
Don't share private or sensitive info with the chatbot. We aim to keep your data safe and secure. | |
<br><br> | |
**Automated Responses:** | |
The chatbot's answers are automatically created and might not be completely accurate. Double-check the info provided. | |
<br><br> | |
**External Links:** | |
We might give links to other websites for more info. These are just for help and not endorsed by us. | |
<br><br> | |
**Changes and Updates:** | |
We can change the chatbot's information anytime without notice. | |
<br><br> | |
**Using this chatbot means you accept these terms. For more detailed advice, consult the <a href="https://www.citizensinformation.ie/" target="_blank">Citizens Information Website</a>**""", unsafe_allow_html=True) | |
# Base64-encoded images | |
facebook_icon = get_image_base64("facebook.png") | |
twitter_icon = get_image_base64("twitter.png") | |
linkedin_icon = get_image_base64("linkedin.png") | |
instagram_icon = get_image_base64("Instagram.png") | |
# HTML for social media links with base64-encoded images | |
social_media_html = f""" | |
<p>Find us on social media:</p> | |
<a href="https://www.facebook.com/citizensinformation/" target="_blank"> | |
<img src="data:image/png;base64,{facebook_icon}" alt="Facebook" style="height: 40px; margin: 2px"> | |
</a> | |
<a href="https://twitter.com/citizensinfo" target="_blank"> | |
<img src="data:image/png;base64,{twitter_icon}" alt="Twitter" style="height: 40px; margin: 2px"> | |
</a> | |
<a href="https://ie.linkedin.com/company/citizens-information-board" target="_blank"> | |
<img src="data:image/png;base64,{linkedin_icon}" alt="LinkedIn" style="height: 40px; margin: 2px"> | |
</a> | |
<a href="https://www.instagram.com/citizensinformation/" target="_blank"> | |
<img src="data:image/png;base64,{instagram_icon}" alt="Instagram" style="height: 40px; margin: 2px"> | |
</a> | |
""" | |
# Add social media links to sidebar | |
st.sidebar.markdown(social_media_html, unsafe_allow_html=True) | |
st.markdown(""" | |
<style> | |
@media (max-width: 768px) { | |
.main .block-container { | |
padding: 2rem 1rem; | |
max-width: 100%; | |
} | |
} | |
</style> | |
""", unsafe_allow_html=True) | |
st.markdown(""" | |
<style> | |
.stChatMessage { | |
padding-left: 0px; /* Reduces padding on the left */ | |
padding-top: 0px; /* Reduces padding on the left */ | |
} | |
</style> | |
""", unsafe_allow_html=True) | |
hide_decoration_bar_style = ''' | |
<style> | |
header {visibility: hidden;} | |
</style> | |
''' | |
st.markdown(hide_decoration_bar_style, unsafe_allow_html=True) | |
# Apply custom CSS to reduce top margin | |
st.markdown(""" | |
<style> | |
.block-container { | |
padding-top: 1rem; | |
padding-bottom: 0rem; | |
padding-left: 5rem; | |
padding-right: 5rem; | |
} | |
</style> | |
""", unsafe_allow_html=True) | |
# Custom CSS to change the focus style of st.text_area | |
custom_css = """ | |
<style> | |
/* Target the st.text_area input element on focus */ | |
.st-d0:focus { | |
border-color: #4fd64d !important; | |
box-shadow: 0 0 0.25rem rgba(255, 75, 75, 0.25) !important; | |
} | |
</style> | |
""" | |
# Inject custom CSS with markdown | |
st.markdown(custom_css, unsafe_allow_html=True) | |
# Get the current date and time | |
current_datetime = datetime.now() | |
# Format the date in the desired format, for example, "January 20, 2024" | |
date_string = current_datetime.strftime("%B %d, %Y") | |
# Initialize last_question and last_answer | |
last_question, last_answer = "", "" | |
# Initialize session state variables | |
if 'chat_history' not in st.session_state: | |
st.session_state['chat_history'] = [] | |
# Display the welcome message and chat history | |
with st.container(): | |
with st.chat_message("assistant", avatar='./ci.png'): | |
st.write("**Welcome to Citizens Information chat. How can we help you today?**") | |
# Custom CSS to add some space between columns | |
st.markdown(""" | |
<style> | |
.reportview-container .main .block-container{ | |
padding-top: 2rem; /* Adjust top padding */ | |
padding-bottom: 2rem; /* Adjust bottom padding */ | |
} | |
.reportview-container .main { | |
flex-direction: column; | |
} | |
</style> | |
""", unsafe_allow_html=True) | |
st.markdown( | |
""" | |
<style> | |
div[data-testid="stAppViewContainer"]{ | |
position:fixed; | |
bottom:20%; | |
padding: 10px; | |
} | |
div[data-testid="stForm"]{ | |
position: fixed; | |
right: 10%; | |
left: 10%; | |
bottom: 2%; | |
border: 1px solid #d3d3d3; | |
padding: 5px; | |
z-index: 100; | |
} | |
</style> | |
""", unsafe_allow_html=True | |
) | |
with st.form("input_form"): | |
col1, col2 = st.columns([6, 1]) | |
with col1: | |
message = st.text_input("message", label_visibility="collapsed") | |
with col2: | |
submitted = st.form_submit_button(label="Ask", use_container_width=True) | |
if submitted and message: | |
# Process the query and get the response | |
with st.spinner('Thinking...'): | |
response = ask_alans_ai(message, 'vectordb_placeholder', st.session_state.chat_history) | |
# Display chat history | |
for question, answer in st.session_state.chat_history: | |
with st.chat_message("user", avatar='./user.png'): | |
st.write(question) | |
with st.chat_message("assistant", avatar='./ci.png'): | |
st.write(answer) | |
# Your combined string with the current date included | |
combined_string = f"Question: {message}\n\nAnswer: {answer}\n\nDate: {date_string}\n\nhttps://www.citizensinformation.ie/" | |
# Create a list with the three strings | |
message_clean = clean_string(message) | |
answer_clean = clean_string(answer) | |
date_string_clean = clean_string(date_string) | |
# Check length (Google Sheets cells have a limit, typically 50000 characters) | |
max_length = 50000 | |
message_clean = message_clean[:max_length] | |
answer_clean = answer_clean[:max_length] | |
date_string_clean = date_string_clean[:max_length] | |
# Append the cleaned data to the worksheet | |
data_to_append = [message_clean, answer_clean, current_datetime] | |
# Create and display the copy button only if answer has content | |
if answer: | |
# Create and display the copy button | |
copy_button_html = create_copy_button(combined_string) | |
components.html(copy_button_html, height=40) | |
# Input fields to Google Sheet | |
# Split the answer into lines | |
answer_lines = answer.split("\n") | |
# Limit the number of lines to avoid exceeding cell limits | |
max_lines = 10 | |
truncated_answer_lines = answer_lines[:max_lines] | |
# Create a list with the message, each line of the answer, and the date | |
worksheet.append_row(data_to_append) | |
# Run the Streamlit app | |
if __name__ == "__main__": | |
main() | |
# print("system_template is:", system_template, end="\n") | |
# print("pdf_qa is:", pdf_qa, end="\n") | |
# print("messages is:", messages, end="\n") | |
# print("qa_prompt is:", qa_prompt, end="\n") | |