Retrieval-Augmented-Generation / _helper_functions.py
arif97's picture
Couple of Changes
4f3a66e
import json
import os
import time
import random
import string
import shutil
import base64
from pathlib import Path
from dotenv import load_dotenv
load_dotenv()
import streamlit as st
from streamlit_option_menu import option_menu
from streamlit_lottie import st_lottie
import torch
from langchain_community.embeddings import HuggingFaceInstructEmbeddings
from langchain_community.document_loaders import PyPDFLoader, TextLoader, WikipediaLoader
from langchain_community.vectorstores import Chroma
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.document_loaders.merge import MergedDataLoader
from langchain_groq import ChatGroq
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
from langchain.chains import create_retrieval_chain
# langchain-openai==0.1.6
# langchain-text-splitters==0.0.1
# langdetect==1.0.9
# langsmith==0.1.53
# --------------------------------------------------------- Helper Function/Class 1
def loadLottieFile(filePath: str):
with open(file=filePath, mode = "r") as f:
return json.load(f)
# --------------------------------------------------------- Helper Function/Class 2
def loadItOnce(container, animation, height, quality = 'high'):
with container.container():
st_lottie(animation_source=animation, height=height, quality=quality)
# --------------------------------------------------------- Helper Function/Class 3
def initialRampUp(llamaAnimation):
with st.lottie_spinner(llamaAnimation, height = 700):
if 'filesUploadedRecords' not in st.session_state: # To Maintain Active File Records
st.session_state.filesUploadedRecords = None
if 'vdbBuilt' not in st.session_state: # To Maintain Active Vector Database records for corresponding files
st.session_state.vdbBuilt = {}
if 'vectorDatabase' not in st.session_state: # To Maintain or hold vector database connection
st.session_state.vectorDatabase = {}
if 'collectionName' not in st.session_state:
st.session_state.collectionName = str(''.join(random.choices(string.ascii_letters, k=25)))
try:
shutil.rmtree('Dump')
except:
pass
os.makedirs('Dump', exist_ok=True)
time.sleep(3)
# --------------------------------------------------------- Helper Function/Class 4
def image_to_base64(image_path):
with open(image_path, "rb") as img_file:
encoded_string = base64.b64encode(img_file.read()).decode("utf-8")
return encoded_string
def set_background_image(base64_image, opacity):
# Define custom CSS for setting background image
custom_css = f"""
<style>
[data-testid = 'stApp'] {{
background-image: linear-gradient(rgba(255, 255, 255, {opacity}), rgba(255, 255, 255, {opacity})), url('data:image/jpeg;base64,{base64_image}');
background-size: cover;
background-repeat: no-repeat;
background-attachment: fixed; /* Ensures the background image remains fixed */
}}
[data-testid = 'stHeader']{{
background-color: rgba(0,0,0,0);
}}
</style>
"""
# Display custom CSS using markdown
st.markdown(custom_css, unsafe_allow_html=True)
# --------------------------------------------------------- Helper Function/Class 5
def navigationBar():
# Use the following link to get whichever icon you'd like:
# https://getbootstrap.com/
options = [
{"label": "Retrieval Augmented Generation", "icon": "bezier"},
{"label": "Fine Tuning LLMs (Coming Soon)", "icon": "gpu-card"},
{"label": "Forecasting LLMs (Coming Soon)", "icon": "graph-up-arrow"}
]
selected = option_menu(
menu_title= None, #"Ask Me Anything", # Menu title
options=[option["label"] for option in options],
icons=[option["icon"] for option in options],
menu_icon="lightbulb-fill",
default_index=0,
orientation="horizontal",
styles={
"container": {
"display": "flex",
"flex-direction": "column",
"justify-content": "center",
"padding": "20px 40px 20px 40px", # Increased top and bottom padding
"background-color": "#222", # Dark background color
"border-radius": "20px",
"width":"100%",
"box-shadow": "0px 2px 10px rgba(0, 0, 0, 0.2)", # Shadow effect
"margin": "auto", # Center align the navigation bar
"overflow-x": "auto", # Allow horizontal scrolling for small screens
},
"menu-title": {
"font-size": "36px",
"font-weight": "bold",
"background-color": "#222",
"color": "#FFFFFF", # White text color
"margin-bottom": "20px", # Spacing below the menu title
},
"menu-icon": {
"color": "#FFD700", # Golden yellow icon color
"font-size": "36px",
"margin-right": "10px",
},
"icon": {
"color": "#FFD700", # Golden yellow icon color
"font-size": "36px",
"margin-right": "10px",
},
"nav-link": {
"font-size": "20px",
"text-align": "center",
"color": "#FFFFFF", # White text color
"padding": "10px 20px",
"border-radius": "15px",
"transition": "background-color 0.3s ease",
},
"nav-link-selected": {
"background-color": "#FF6347", # Tomato red when selected
"color": "#FFFFFF", # White text color when selected
}
},
)
return selected
# --------------------------------------------------------- Helper Function/Class 6
@st.cache_resource(show_spinner=False)
def loadEmbeddingsModels():
DEVICE = "cuda:0" if torch.cuda.is_available() else "cpu"
# Constructing Embeddings function to pass as an argument in ChromaDB for Calculating Embeddings
return HuggingFaceInstructEmbeddings(
model_name = "hkunlp/instructor-base",
query_instruction = "Represent the query for retrieval: ",
model_kwargs = {"device": DEVICE})
# --------------------------------------------------------- Helper Function/Class 7
def removedOrAdded(files):
removed = {}
# In the beginning, when there are no files
# In the end when there are no files
if len(files) == 0:
if (st.session_state.filesUploadedRecords is not None) and (len(st.session_state.filesUploadedRecords) > 0):
removed = {st.session_state.filesUploadedRecords[0].file_id : st.session_state.filesUploadedRecords[0]}
st.session_state.filesUploadedRecords = None
return removed
# Files that were just removed
currentFileIds = [file_obj.file_id for file_obj in files]
for file in st.session_state.filesUploadedRecords:
if file.file_id not in currentFileIds:
removed[file.file_id] = file
# Removing the crossed off files from active files directory that we maintained
for toRemove in removed.values():
st.session_state.filesUploadedRecords.remove(toRemove)
return removed
# --------------------------------------------------------- Helper Function/Class 8
# To parse PDF and turn to vector embeddings
def buildVectorDatabase(files, query, addOrRemove):
"""
files: Either a list of fileUploader Objects with file details
or that one filename which was just removed by the user and should be from the vector Database as well
addOrRemove: if true, add else remove
"""
# Create Embeddings and Add to the Vector Store
if addOrRemove:
embeddings = loadEmbeddingsModels()
collName = st.session_state.collectionName
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1024, chunk_overlap=100)
loader_map = {
'.pdf': PyPDFLoader,# Add more mappings as needed
'.txt': TextLoader,
'wiki' : WikipediaLoader
}
docsMergedPDF = [
loader_map[Path(doc).suffix.lower()](file_path=doc)
for doc in files
]
loadersPDF = MergedDataLoader(loaders=docsMergedPDF)
pagesPDF = loadersPDF.load_and_split(text_splitter) # Split into pages
pagesWiki = []
for wikiQ in query:
loader = WikipediaLoader(query=wikiQ, load_max_docs=2)
try:
pagesW = loader.load_and_split(text_splitter)
if len(pagesW) > 0:
references = list(set(list(map(lambda x: x.metadata['source'], pagesW))))
st.session_state.vdbBuilt['Keyword ; ' + wikiQ.strip()] = references
pagesWiki += pagesW
except Exception as e:
message = str(e) + '\n' + f'Looks like we could not search for your key word {wikiQ}'
st.toast(body=message, icon="⚠️")
_ = st.session_state.vdbBuilt.pop('Keyword ; ' + wikiQ.strip(), None)
pages = pagesPDF + pagesWiki
st.session_state.vectorDatabase = Chroma.from_documents(documents= pages,
embedding= embeddings,
collection_name= collName,
) # Load the pages into vector database (Build ChromaDB)
# Delete corresponding embeddings from the vector store
else:
if files is not None:
st.session_state.vectorDatabase._collection.delete(where={"source": {'$eq':files}})
for src in query:
st.session_state.vectorDatabase._collection.delete(where={"source": {'$eq':src}})
# --------------------------------------------------------- Helper Function/Class 9
os.environ["TOKENIZERS_PARALLELISM"] = "false"
class RetrievalChainGenerator:
def __init__(self, model_name, vectorStore):
self.model_name = model_name
self.groq_api_key = os.environ['GROQ_API_KEY']#os.getenv('GROQ_API_KEY')
self.vectorStore = vectorStore
self.chain = None
self.generate_retrieval_chain()
def generate_retrieval_chain(self):
llm = ChatGroq(groq_api_key=self.groq_api_key, model_name=self.model_name)
prompt = ChatPromptTemplate.from_template("""
Answer the following question based only on the provided context.
Think step by step before providing a detailed answer.
If using finance terms, briefly explain them for clarity.
Always return your complete response in html format.
I will tip you $200 if the user finds the answer helpful.
<context>
{context}
</context>
Question: {input}""")
promptChain = create_stuff_documents_chain(llm= llm, prompt= prompt)
retrieverORreferrence = self.vectorStore.as_retriever()
retrievalChain = create_retrieval_chain(retrieverORreferrence, promptChain)
self.chain = retrievalChain
# ---------------------------------------------------------------------------------------xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx-----------------------------------------------------------
# --------------------------------------------------------- Front-End Functions/Class 1
import streamlit as st
def display_main_title(title_text, container):
# Define custom CSS styles for animations and title box
custom_css = """
<style>
@keyframes pulse {
0% { transform: scale(1); box-shadow: 0 0 10px rgba(255, 99, 71, 0.7); }
50% { transform: scale(1.05); box-shadow: 0 0 20px rgba(255, 99, 71, 0.9); }
100% { transform: scale(1); box-shadow: 0 0 10px rgba(255, 99, 71, 0.7); }
}
@keyframes shimmer {
0% { background-position: -100%; }
25% { background-position: -50%; }
50% { background-position: 0%; }
75% { background-position: 50%; }
100% { background-position: 100%; }
}
@keyframes wiggle {
0% { transform: rotate(0); }
25% { transform: rotate(2deg); }
50% { transform: rotate(-2deg); }
75% { transform: rotate(2deg); }
100% { transform: rotate(0); }
}
@keyframes wavy {
0% { transform: translateY(0); }
25% { transform: translateY(-2px); }
50% { transform: translateY(-1px); }
75% { transform: translateY(-2px); }
100% { transform: translateY(0); }
}
@keyframes vibration {
0% { transform: translate(0, 0); }
25% { transform: translate(-1px, 1px); }
50% { transform: translate(1px, -1px); }
75% { transform: translate(-1px, -1px); }
100% { transform: translate(1px, 1px); }
}
.main-title-container {
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 20px rgba(0,0,0,0.2);
background-color: #ff6347;
width: fit-content;
margin: 20px auto;
text-align: center;
font-size: 36px;
font-weight: bold;
color: white;
animation: pulse 2s infinite, vibration 0.1s infinite linear alternate;
position: relative;
overflow: hidden;
}
.main-title-text {
animation: wavy 10s infinite, shimmer 1s linear infinite, wiggle 1s infinite;
background-image: linear-gradient(to right, transparent 0%, white 50%, transparent 100%);
background-size: 200% 100%;
color: transparent;
-webkit-background-clip: text;
background-clip: text;
}
</style>
"""
# Embed custom CSS into the Streamlit app
st.markdown(custom_css, unsafe_allow_html=True)
# Display main title in a Markdown object with custom CSS class
container.markdown(f'<div class="main-title-container"><span class="main-title-text">{title_text}</span></div>', unsafe_allow_html=True)
# --------------------------------------------------------- Front-End Functions/Class 2
import streamlit as st
def display_alert_note(message, container):
# Define custom CSS styles for the alert message
custom_css = """
<style>
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.03); }
100% { transform: scale(1); }
}
@keyframes wiggle {
0% { transform: rotate(0deg); }
50% { transform: rotate(6deg); }
100% { transform: rotate(0deg); }
}
.alert-box {
position: relative;
padding: 20px;
border-radius: 10px;
background-image: linear-gradient(45deg, #ff4d4d, #ff416c);
color: white;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
animation: pulse 2s infinite alternate;
transition: transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
overflow:visible;
}
.alert-box:hover {
transform: scale(1.05);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
animation: wiggle 0.3s infinite alternate;
}
.alert-icon {
position: absolute;
top: 40px;
right: 10px;
font-size: 24px;
}
.alert-flare {
}
</style>
"""
# Embed custom CSS into the Streamlit app
st.markdown(custom_css, unsafe_allow_html=True)
# Display the alert message in a styled text box with icons and visual effects
container.markdown(f'<div class="alert-flare"></div><div class="alert-box">{message}<span class="alert-icon">&#9888;</span></div>', unsafe_allow_html=True)
# --------------------------------------------------------- Front-End Functions/Class 3
import streamlit as st
def display_attention_text(text, container):
# Define custom CSS styles for animations and text box
custom_css = """
<style>
@keyframes slide-in {
0% {
transform: translateX(-50px);
opacity: 0;
}
100% {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slide-in-top {
0% {
transform: translateY(-100%); /* Start off the screen above */
opacity: 0; /* Hidden initially */
}
100% {
transform: translateY(0); /* Slide in to the top */
opacity: 1; /* Fully visible */
}
}
.build-text-container {
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
border-radius: 10px;
background: linear-gradient(to bottom right, #ffd700, #ffdf80);
width: fit-content;
margin: 20px auto;
text-align: center;
font-size: 28px;
font-weight: bold;
color: black;
overflow: hidden;
}
.animated-text-in-attention {
display: inline-block;
opacity: 0;
animation: slide-in-top 2s ease-in-out forwards infinite;
}
/* Preserve spaces */
.animated-text-in-attention.space {
width: 2ch; /* Adjust width to match character width */
visibility: hidden;
}
</style>
"""
# Embed custom CSS into the Streamlit app
st.markdown(custom_css, unsafe_allow_html=True)
# Display text in a Markdown object with custom CSS class
animated_text_html = ''.join(f'<span class="animated-text-in-attention" style="animation-delay: {i * 0.0005}s;">{char}</span>' if char != ' ' else '<span class="animated-text-in-attention space"></span>' for i, char in enumerate(text))
text_html = f"""
<div class="build-text-container">
{animated_text_html}
</div>
"""
container.markdown(text_html, unsafe_allow_html=True)
# --------------------------------------------------------- Front-End Functions/Class 4
import streamlit as st
def display_custom_arrow(direction, container):
# Define custom CSS styles for arrow animation
translateX_value = '70px' if direction.lower() == 'right' else '70px'
custom_css = f"""
<style>
.arrow-container {{
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
border-radius: 10px;
border: 0px solid #ffaa00; /* Border style */
background: linear-gradient(45deg, rgba(255, 204, 0, 0), rgba(255, 102, 0, 0));
width: fit-content;
overflow: visible;
transition: transform 0.3s ease-in-out; /* Transition effect */
}}
.arrow-svg {{
width: 100;
height: 30px;
fill: white;
animation: arrow-bounce 1s infinite;
}}
@keyframes arrow-bounce {{
0%, 100% {{ transform: translateX(0); }}
50% {{ transform: translateX({translateX_value}); }}
}}
</style>
"""
# Embed custom CSS into the Streamlit app
container.markdown(custom_css, unsafe_allow_html=True)
# Define arrow SVG path based on direction
if direction.lower() == 'left':
arrow_svg = '''
<svg class="arrow-svg" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M14 5l-9 9 9 9" stroke="currentColor" stroke-width="4" fill="none"/>
</svg>
'''
elif direction.lower() == 'right':
arrow_svg = '''
<svg class="arrow-svg" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path d="M10 19l9-9-9-9" stroke="currentColor" stroke-width="4" fill="none"/>
</svg>
'''
else:
pass
# Display arrow container with arrow SVG
container.markdown(f'<div class="arrow-container">{arrow_svg}</div>', unsafe_allow_html=True)
# --------------------------------------------------------- Front-End Functions/Class 5
import streamlit as st
def display_heading_box(message, container):
# Define custom CSS for the styled text box with updated gradients, height, and animations
css = f"""
<style>
.styled-text-container {{
padding: 10px 10px;
border-radius: 10px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
animation: slide-in 1s ease-out forwards; /* Slide in only once */
display: flex;
justify-content: center; /* Center horizontally */
align-items: center; /* Center vertically */
background: linear-gradient(135deg, #fdd835, #4caf50); /* Updated gradient colors */
color: white;
width: fit-content;
height: auto; /* Adjusted height */
transition: transform 0.3s ease-in-out; /* Smooth transition for transform */
margin-left: auto; /* Center horizontally */
margin-right: auto;
}}
@keyframes slide-in {{
from {{
transform: translateY(-10px);
opacity: 0;
}}
to {{
transform: translateY(0);
opacity: 1;
}}
}}
.styled-text-container:hover {{
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
transform: scale(1.05);
animation: wiggle 0.5s ease-in-out infinite alternate; /* Wiggle on hover */
}}
@keyframes wiggle {{
0% {{
transform: rotate(0deg);
}}
25% {{
transform: rotate(-5deg);
}}
50% {{
transform: rotate(5deg);
}}
75% {{
transform: rotate(-5deg);
}}
100% {{
transform: rotate(0deg);
}}
}}
</style>
"""
# Render the Markdown with the styled container and embedded text
styled_html = f"""
<div class="styled-text-container">
<h3>{message}</h3>
</div>
"""
# Display the Markdown using Streamlit
st.markdown(css, unsafe_allow_html=True)
container.markdown(styled_html, unsafe_allow_html=True)
# --------------------------------------------------------- Front-End Functions/Class 6
import streamlit as st
def display_error_message(message, container):
# Define custom CSS for the error message box
custom_css = """
<style>
.error-container {
display: flex;
justify-content: center;
align-items: center;
height: auto;
background: linear-gradient(135deg, #ff3864, #d81e5b);
padding: 20px;
border-radius: 15px;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
overflow: hidden;
width: auto;
position: relative;
animation: shake 0.8s cubic-bezier(0.36, 0.07, 0.19, 0.97) infinite;
}
.error-message {
color: #ffffff;
font-size: 16px;
font-weight: bold;
text-align: center;
opacity: 0.9;
}
@keyframes shake {
0%, 100% { transform: translateX(0); }
10%, 30%, 50%, 70%, 90% { transform: translateX(-5px); }
20%, 40%, 60%, 80% { transform: translateX(5px); }
}
</style>
"""
# Display custom error message using HTML with the defined CSS
st.markdown(custom_css, unsafe_allow_html=True)
container.markdown(f"""
<div class="error-container">
<div class="error-message">{message}</div>
</div>
""", unsafe_allow_html=True)
# --------------------------------------------------------- Front-End Functions/Class 7
import streamlit as st
def display_small_text(text, container):
# Define custom CSS styles for animations and text box
custom_css = """
<style>
@keyframes flash {
0% { left: -100%; }
50% { left: 100%; }
100% { left: 100%; }
}
@keyframes wiggle {
0% { transform: rotate(0); }
50% { transform: rotate(2deg); }
100% { transform: rotate(0); }
}
@keyframes vibrate {
0% { transform: translate(0); }
25% { transform: translate(-2px, 2px); }
50% { transform: translate(2px, -2px); }
75% { transform: translate(-2px, -2px); }
100% { transform: translate(2px, 2px); }
}
.animated-text-container {
position: relative;
display: block;
padding: 10px 50px 10px 50px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
background-image: linear-gradient(45deg, #0044ff, #0099ff);
transition: box-shadow 0.3s ease-in-out;
width: fit-content;
margin: 20px auto;
text-align: center;
}
.animated-text-container:hover {
box-shadow: 0 0 20px rgba(255,255,255,0.5);
animation: vibrate 0.3s ease infinite;
}
.animated-text {
font-size: 24px;
font-weight: bold;
color: white;
display: inline-block;
}
.animated-text-container:hover .animated-text {
animation: wiggle 0.5s infinite alternate;
}
</style>
"""
# Embed custom CSS into the Streamlit app
st.markdown(custom_css, unsafe_allow_html=True)
# Display text in a Markdown object with custom CSS class
container.markdown(f'<div class="animated-text-container"><span class="animated-text">{text}</span></div>', unsafe_allow_html=True)
# --------------------------------------------------------- Front-End Functions/Class 8
import streamlit as st
def display_response_message(message, container):
# Define custom CSS for the response message box
custom_css = """
<style>
.response-container {
display: flex;
justify-content: center;
align-items: center;
background-color: #ff6347;
padding: 20px;
border-radius: 15px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
animation: pulse 1s ease infinite alternate;
}
.response-message {
color: white;
font-size: 24px;
text-align: center;
opacity: 0;
animation: fade-in 0.5s ease forwards, scale-up 0.5s ease forwards;
}
@keyframes pulse {
0% { transform: scale(1); }
100% { transform: scale(1.05); }
}
@keyframes fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}
@keyframes scale-up {
0% { transform: scale(0.8); }
100% { transform: scale(1); }
}
</style>
"""
# Display custom CSS for the response message
st.markdown(custom_css, unsafe_allow_html=True)
# Create a response container
response_container = f'<div class="response-container">'
# Create a container for the response message
response_container += '<div class="response-message">'
response_container += message # Add the message directly
response_container += '</div>' # Close the response message container
response_container += '</div>' # Close the response container
# Display the response container with the message
container.markdown(response_container, unsafe_allow_html=True)
#--------------------------------------------------------- Front-End Functions/Class 9
def display_question_box(container):
# Define custom CSS for the question box
custom_css = """
<style>
.question-box {
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
overflow: hidden;
height: 100px; /* Adjust height based on icon size */
}
.question-icon {
width: 75px;
height: 75px;
fill: black;
animation: float-vibrate 1s infinite ease-in-out alternate,
rotate 2s infinite linear,
pulse 1s infinite alternate,
change-color 3s infinite,
scale 1.5s infinite alternate;
}
@keyframes float-vibrate {
0% { transform: translateY(0); }
50% { transform: translateY(-5px); }
100% { transform: translateY(0); }
}
@keyframes rotate {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.7; }
100% { opacity: 1; }
}
@keyframes change-color {
0% { fill: black; }
25% { fill: red; }
50% { fill: green; }
75% { fill: blue; }
100% { fill: black; }
}
@keyframes scale {
0% { transform: scale(1); }
100% { transform: scale(1.2); }
}
</style>
"""
# Display custom CSS for the question box within the specified container
container.markdown(custom_css, unsafe_allow_html=True)
# Create the question box HTML structure with a question mark SVG icon
question_box = f"""
<div class="question-box">
<svg class="question-icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<text x="12" y="12" font-size="24" text-anchor="middle" alignment-baseline="central">?</text>
</svg>
<span style="color: black; font-size: 48px;">Llama3</span>
</div>
"""
# Display the question box HTML within the specified container
container.markdown(question_box, unsafe_allow_html=True)
# --------------------------------------------------------- Front-End Functions/Class 10
import markdown
import random
def display_citations(heading, contents, container):
unique_id = str(random.randint(10000000, 99999999))
css_styles = f"""
<style>
@keyframes move-in-from-left {{
0% {{
transform: translateX(-100%);
opacity: 0;
}}
100% {{
transform: translateX(0);
opacity: 1;
}}
}}
.fancy-container-{unique_id} {{
display: flex;
justify-content: center;
align-items: center;
margin: 30px auto;
animation: move-in-from-left 1s ease-out forwards;
}}
.fancy-card-{unique_id} {{
width: 500px;
height: 200px;
perspective: 1000px;
position: relative;
border-radius: 20px;
overflow: hidden; /* Ensure contents do not overflow */
}}
.fancy-card-inner-{unique_id} {{
width: 100%;
height: 100%;
text-align: center;
transition: transform 1.25s;
transform-style: preserve-3d;
border-radius: 20px;
}}
.fancy-card-{unique_id}:hover .fancy-card-inner-{unique_id} {{
transform: rotateY(180deg);
}}
.fancy-card-front-{unique_id}, .fancy-card-back-{unique_id} {{
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden;
display: flex;
flex-direction: column; /* Stack elements vertically */
justify-content: center;
align-items: center;
font-size: 24px;
padding: 20px;
border-radius: 20px;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
overflow: hidden; /* Ensure contents do not overflow */
}}
.fancy-card-front-{unique_id} {{
background: linear-gradient(to bottom right, #000, #222);
color: white;
animation: fancy-wiggle 0.5s ease-in-out infinite;
}}
.fancy-card-front-{unique_id} h2 {{
font-size: 20px;
font-style: italic;
color: white;
margin: 0; /* Remove default margin */
padding: 10px; /* Add padding */
text-align: center; /* Center align text */
word-wrap: break-word; /* Ensure long words wrap */
overflow-wrap: break-word; /* Ensure long words wrap */
word-break: break-all; /* Ensure words break if too long */
}}
@keyframes fancy-wiggle {{
0% {{ transform: translateX(-4px); }}
50% {{ transform: translateX(4px); }}
100% {{ transform: translateX(-4px); }}
}}
.fancy-card-back-{unique_id} {{
background: linear-gradient(to bottom right, #ffd700, #ffff00);
background-color: #f7f7f7;
color: #333;
transform: rotateY(180deg);
overflow: auto; /* Enable vertical and horizontal scrolling */
}}
.fancy-card-back-{unique_id} p {{
margin: 10px 0px; /* Add margin to separate paragraphs */
word-break: normal; /* Ensure words break if too long */
}}
.fancy-card-back-{unique_id} a {{
color: blue; /* Make links blue */
text-decoration: underline; /* Underline links */
word-wrap: break-word; /* Ensure long links wrap */
overflow-wrap: break-word; /* Ensure long links wrap */
word-break: normal; /* Ensure words break if too long */
display: inline-block;
}}
</style>
"""
# Convert markdown contents to HTML
html_contents = markdown.markdown(contents)
# Define the HTML structure for the fancy card
html_content = f"""
<div class="fancy-container-{unique_id}" id="{unique_id}">
<div class="fancy-card-{unique_id}" id="{unique_id}">
<div class="fancy-card-inner-{unique_id}" id="{unique_id}">
<div class="fancy-card-front-{unique_id}" id="{unique_id}">
<h2>{heading}</h2>
</div>
<div class="fancy-card-back-{unique_id}" id="{unique_id}">
{html_contents}
</div>
</div>
</div>
</div>
"""
# Display the fancy card using Streamlit Markdown
st.markdown(css_styles, unsafe_allow_html=True)
container.markdown(html_content, unsafe_allow_html=True)
def display_allCitations(resp, contain):
for res in resp['context']:
if res.metadata['source'].endswith('.pdf') or res.metadata['source'].endswith('.txt'):
headingText = f"{res.metadata['source'].split("---")[-1]} &nbsp;&nbsp Page No.&nbsp;&nbsp;{res.metadata['page']}"
else:
headingText = f"{res.metadata['source']}"
contents_text = f"A <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> <br> Content <br><br> {res.page_content}"
display_citations(headingText, contents_text, contain)
time.sleep(0.5)