|
|
import os |
|
|
import streamlit as st |
|
|
from langchain.embeddings import HuggingFaceEmbeddings |
|
|
from langchain.chains import RetrievalQA |
|
|
from langchain_community.vectorstores import FAISS |
|
|
from langchain_core.prompts import PromptTemplate |
|
|
from langchain_community.llms import HuggingFaceEndpoint |
|
|
import time |
|
|
import translators as ts |
|
|
from huggingface_hub import hf_hub_download |
|
|
|
|
|
|
|
|
st.set_page_config(layout="wide") |
|
|
|
|
|
|
|
|
HF_TOKEN = os.getenv("HF_TOKEN") |
|
|
VECTORSTORE_REPO_ID = "vashu2425/bhagavad-geeta-faiss-vectordb" |
|
|
MODEL_REPO_ID = "mistralai/Mistral-7B-Instruct-v0.3" |
|
|
|
|
|
|
|
|
CUSTOM_PROMPT_TEMPLATE = """ |
|
|
Use The Pieces Of Information Provided In The Context To Answer User's Question. |
|
|
If You Don't Know The Answer, Just Say "I Don't Have Information",except this do not say anything. |
|
|
Don't Try To Make Up An Answer. Don't Provide Anything Out Of The Given Context. |
|
|
|
|
|
Context: {context} |
|
|
Question: {question} |
|
|
|
|
|
Start The Answer Directly., Please. The Answer Should Contain All 3 Contexts. |
|
|
Consider Yourself As God Krishna And Answer The Question Result Should Not Start With "Answer" |
|
|
""" |
|
|
|
|
|
|
|
|
def initialize_session_states(): |
|
|
session_defaults = { |
|
|
"messages": [], |
|
|
"selected_question": None, |
|
|
"show_predefined": True, |
|
|
"last_response": None, |
|
|
"translation_done": False, |
|
|
"last_prompt": None |
|
|
} |
|
|
for key, val in session_defaults.items(): |
|
|
if key not in st.session_state: |
|
|
st.session_state[key] = val |
|
|
|
|
|
def render_chat_messages(): |
|
|
for message in st.session_state.messages: |
|
|
with st.chat_message(message["role"], avatar="🐿" if message["role"] == "user" else "🪈"): |
|
|
content = message["content"] |
|
|
if "hindi-text" in content: |
|
|
st.markdown(content, unsafe_allow_html=True) |
|
|
else: |
|
|
st.markdown(content) |
|
|
|
|
|
def render_predefined_questions(): |
|
|
predefined_questions = [ |
|
|
"Meaning of Dharma?", |
|
|
"What is the purpose of life?", |
|
|
"How to find inner peace?", |
|
|
"How can I be a better person?", |
|
|
"What is the meaning of life?", |
|
|
"How can I be a better friend?" |
|
|
] |
|
|
st.markdown("### Or, try one of these:") |
|
|
buttons = st.columns(len(predefined_questions)) |
|
|
for idx, question in enumerate(predefined_questions): |
|
|
if buttons[idx].button(question, key=f"predefined_{idx}"): |
|
|
st.session_state.selected_question = question |
|
|
st.session_state.show_predefined = False |
|
|
|
|
|
|
|
|
def translate_text(text, dest_language="hi"): |
|
|
try: |
|
|
|
|
|
return ts.translate_text( |
|
|
text, |
|
|
to_language=dest_language, |
|
|
translator='google' |
|
|
) |
|
|
except Exception as e: |
|
|
st.error(f"Translation failed: {str(e)}") |
|
|
return text |
|
|
|
|
|
@st.cache_resource |
|
|
def get_vectorstore(): |
|
|
try: |
|
|
embedding_model = HuggingFaceEmbeddings(model_name='sentence-transformers/all-MiniLM-L6-v2') |
|
|
os.makedirs("vectorstore/db_faiss", exist_ok=True) |
|
|
|
|
|
faiss_files = ["index.faiss", "index.pkl"] |
|
|
for filename in faiss_files: |
|
|
if not os.path.exists(f"vectorstore/db_faiss/{filename}"): |
|
|
hf_hub_download( |
|
|
repo_id=VECTORSTORE_REPO_ID, |
|
|
filename=filename, |
|
|
local_dir="vectorstore/db_faiss", |
|
|
token=HF_TOKEN, |
|
|
repo_type="dataset" |
|
|
) |
|
|
|
|
|
return FAISS.load_local("vectorstore/db_faiss", embedding_model, allow_dangerous_deserialization=True) |
|
|
except Exception as e: |
|
|
st.error(f"Vectorstore initialization failed: {str(e)}") |
|
|
st.stop() |
|
|
|
|
|
def set_custom_prompt(custom_prompt_template): |
|
|
return PromptTemplate(template=custom_prompt_template, input_variables=["context", "question"]) |
|
|
|
|
|
def load_llm(huggingface_repo_id, hf_token): |
|
|
return HuggingFaceEndpoint( |
|
|
repo_id=huggingface_repo_id, |
|
|
temperature=0.4, |
|
|
huggingfacehub_api_token=hf_token, |
|
|
model_kwargs={"max_length": 512} |
|
|
) |
|
|
|
|
|
def handle_translation(): |
|
|
if "last_response" in st.session_state and st.session_state.last_response: |
|
|
try: |
|
|
if not st.session_state.get("translation_done", False): |
|
|
translated_text = translate_text(st.session_state.last_response, "hi") |
|
|
|
|
|
|
|
|
for i in range(len(st.session_state.messages) - 1, -1, -1): |
|
|
if st.session_state.messages[i]["role"] == "assistant": |
|
|
st.session_state.messages[i]["content"] = f'<div class="hindi-text">{translated_text}</div>' |
|
|
break |
|
|
|
|
|
|
|
|
st.session_state.translation_done = True |
|
|
st.rerun() |
|
|
|
|
|
except Exception as e: |
|
|
st.error(f"Translation error: {str(e)}") |
|
|
|
|
|
|
|
|
def format_source_docs(source_documents): |
|
|
formatted_docs = [] |
|
|
for idx, doc in enumerate(source_documents, start=1): |
|
|
content = doc.page_content.replace('\t', ' ').replace('\n', ' ').strip() |
|
|
formatted_doc = f"**Source {idx}** (Page {doc.metadata['page']}):\n\n{content[:500]}..." |
|
|
formatted_docs.append(formatted_doc) |
|
|
return "\n\n".join(formatted_docs) |
|
|
|
|
|
def handle_user_input(prompt, qa_chain): |
|
|
if prompt: |
|
|
|
|
|
if st.session_state.get("last_prompt") == prompt: |
|
|
return |
|
|
|
|
|
|
|
|
st.session_state.last_prompt = prompt |
|
|
|
|
|
with st.chat_message("user", avatar="🐿"): |
|
|
st.markdown(prompt) |
|
|
st.session_state.messages.append({"role": "user", "content": prompt}) |
|
|
|
|
|
try: |
|
|
|
|
|
with st.chat_message("assistant", avatar="🪈"): |
|
|
response_placeholder = st.empty() |
|
|
|
|
|
|
|
|
response = qa_chain.invoke({"query": prompt}) |
|
|
result = response["result"] |
|
|
source_documents = response["source_documents"] |
|
|
|
|
|
|
|
|
accumulated_text = "" |
|
|
for char in result: |
|
|
accumulated_text += char |
|
|
response_placeholder.markdown(f'<div class="english-text">{accumulated_text}</div>', unsafe_allow_html=True) |
|
|
time.sleep(0.01) |
|
|
|
|
|
|
|
|
st.session_state.messages.append({ |
|
|
"role": "assistant", |
|
|
"content": f'<div class="english-text">{accumulated_text}</div>', |
|
|
"original": accumulated_text |
|
|
}) |
|
|
|
|
|
st.session_state.last_response = accumulated_text |
|
|
st.session_state.show_predefined = False |
|
|
st.session_state.translation_done = False |
|
|
|
|
|
if "don't have information" not in result.lower(): |
|
|
with st.expander("Source Documents"): |
|
|
st.markdown(format_source_docs(source_documents)) |
|
|
|
|
|
except Exception as e: |
|
|
st.error(f"Error: {str(e)}") |
|
|
|
|
|
if st.session_state.messages and st.session_state.messages[-1]["role"] == "assistant": |
|
|
st.session_state.messages.pop() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def render_chat_messages(): |
|
|
for message in st.session_state.messages: |
|
|
with st.chat_message(message["role"], avatar="🐿" if message["role"] == "user" else "🪈"): |
|
|
content = message.get("original", message["content"]) |
|
|
if "hindi-text" in message["content"]: |
|
|
st.markdown(message["content"], unsafe_allow_html=True) |
|
|
else: |
|
|
st.markdown(content) |
|
|
|
|
|
def main(): |
|
|
st.markdown( """ |
|
|
<style> |
|
|
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+Devanagari:wght@400;700&display=swap'); |
|
|
.hindi-text { |
|
|
font-family: 'Noto Sans Devanagari', sans-serif; |
|
|
font-size: 16px; |
|
|
line-height: 1.8; |
|
|
direction: ltr; |
|
|
text-align: left; |
|
|
} |
|
|
|
|
|
.english-text { |
|
|
font-family: Arial, sans-serif; |
|
|
font-size: 16px; |
|
|
line-height: 1.6; |
|
|
} |
|
|
|
|
|
.translate-btn { |
|
|
background-color: #4CAF50 !important; |
|
|
color: white !important; |
|
|
border-radius: 20px; /* Reduced from 25px */ |
|
|
padding: 6px 20px; /* Reduced from 8px 25px */ |
|
|
margin: 6px 0; /* Reduced from 10px 0 */ |
|
|
border: none; |
|
|
transition: all 0.3s ease; |
|
|
font-size: 14px; /* Added font-size control */ |
|
|
min-width: 120px; /* Added for better proportions */ |
|
|
} |
|
|
|
|
|
.translate-btn:hover { |
|
|
background-color: #45a049 !important; |
|
|
transform: scale(1.03); /* Reduced from 1.05 */ |
|
|
} |
|
|
|
|
|
.top-left-button { |
|
|
position: auto; |
|
|
top: 50px; |
|
|
left: 20px; |
|
|
z-index: 100; |
|
|
padding: 10px 20px; |
|
|
background-color: #e0162e; |
|
|
color: white !important; |
|
|
text-decoration: none !important; |
|
|
border-radius: 50px; |
|
|
margin-top: 10px; |
|
|
font-size: 16px; |
|
|
text-align: center; |
|
|
} |
|
|
.top-left-button:hover { |
|
|
background-color: #f7525a; |
|
|
} |
|
|
|
|
|
/* Fullscreen styles */ |
|
|
body { |
|
|
margin: 0; |
|
|
padding: 0; |
|
|
width: 100vw; |
|
|
height: 100vh; |
|
|
display: flex; |
|
|
justify-content: center; |
|
|
align-items: center; |
|
|
background-color: #1e1e30; /* Change the background color to #1e1e30 */ |
|
|
} |
|
|
|
|
|
[data-testid="stAppViewContainer"] > .main { |
|
|
background-size: cover; |
|
|
background-position: center center; |
|
|
background-repeat: no-repeat; |
|
|
background-attachment: local; |
|
|
} |
|
|
|
|
|
/* Header background */ |
|
|
[data-testid="stHeader"] { |
|
|
background: #1e1e30; |
|
|
} |
|
|
|
|
|
/* Apply background color to the whole Streamlit app */ |
|
|
.stApp { |
|
|
width: 100%; |
|
|
max-width: 100vw; |
|
|
display: flex; |
|
|
justify-content: center; |
|
|
align-items: flex-start; |
|
|
padding: 20px; |
|
|
background-color: #1e1e30; /* This will apply the background color to the entire app */ |
|
|
} |
|
|
|
|
|
.custom-paragraph { |
|
|
font-size: 20px !important; |
|
|
line-height: 0.2; |
|
|
color: #666666; |
|
|
} |
|
|
|
|
|
/* Apply background color to stBottomBlockContainer */ |
|
|
[data-testid="stBottomBlockContainer"] { |
|
|
background-color: #1e1e30; /* Set the same color for bottom block */ |
|
|
} |
|
|
|
|
|
/* Hover effect for textarea (optional) */ |
|
|
.stTextArea>div>textarea:hover { |
|
|
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.3); /* Change shadow on hover */ |
|
|
</style> |
|
|
<a href="https://iskconmangaluru.com/wp-content/uploads/2021/04/English-Bhagavad-gita-His-Divine-Grace-AC-Bhaktivedanta-Swami-Prabhupada.pdf" target="_blank" class="top-left-button"> |
|
|
Source Bhagavad Gita PDF |
|
|
</a> |
|
|
""", |
|
|
unsafe_allow_html=True |
|
|
) |
|
|
|
|
|
st.title("Ask Krishna! 🦚") |
|
|
st.markdown('<p class="hindi-text" style="color:#666666; font-size:20px;">शांति स्वीकृति से शुरू होती है</p>', |
|
|
unsafe_allow_html=True) |
|
|
|
|
|
initialize_session_states() |
|
|
render_chat_messages() |
|
|
|
|
|
if st.session_state.show_predefined: |
|
|
render_predefined_questions() |
|
|
|
|
|
prompt = st.chat_input("What's your curiosity?") or st.session_state.selected_question |
|
|
st.session_state.selected_question = None |
|
|
|
|
|
try: |
|
|
vectorstore = get_vectorstore() |
|
|
qa_chain = RetrievalQA.from_chain_type( |
|
|
llm=load_llm(MODEL_REPO_ID, HF_TOKEN), |
|
|
chain_type="stuff", |
|
|
retriever=vectorstore.as_retriever(search_kwargs={"k": 3}), |
|
|
return_source_documents=True, |
|
|
chain_type_kwargs={"prompt": set_custom_prompt(CUSTOM_PROMPT_TEMPLATE)} |
|
|
) |
|
|
|
|
|
if prompt: |
|
|
handle_user_input(prompt, qa_chain) |
|
|
|
|
|
if st.session_state.get("last_response"): |
|
|
col1, col2 = st.columns([1, 3]) |
|
|
with col1: |
|
|
if st.button("🌐 Translate to Hindi", key="translate_btn"): |
|
|
handle_translation() |
|
|
with col2: |
|
|
if st.session_state.get("translation_done"): |
|
|
st.success("Translation to Hindi completed!") |
|
|
|
|
|
except Exception as e: |
|
|
st.error(f"Initialization error: {str(e)}") |
|
|
|
|
|
if __name__ == "__main__": |
|
|
main() |