Sachin-Hansaka's picture
Update src/langgraphagenticai/ui/streamlitui/loadui4.py
b4d55ff verified
Raw
History Blame Contribute Delete
13.9 kB
import streamlit as st
import os
from langgraphagenticai.ui.uiconfigfile import Config
class LoadStreamlitUI:
def __init__(self):
self.config = Config()
self.user_controls = {}
def _inject_custom_css(self):
"""Inject custom CSS with light purple-green background and white fonts"""
st.markdown("""
<style>
/* Main container styling - light purple to light green gradient */
.main {
background: linear-gradient(135deg, #e9d5ff 0%, #ddd6fe 25%, #c4b5fd 40%, #d1fae5 60%, #a7f3d0 80%, #6ee7b7 100%);
background-attachment: fixed;
}
/* Header styling - dark elegant background with main topic in gradient color */
.custom-header {
background: linear-gradient(135deg, #1e293b 0%, #334155 100%);
padding: 2.5rem 3rem 2rem 3rem;
border-radius: 16px;
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.3);
margin-bottom: 2.5rem;
text-align: center;
border: 1px solid rgba(100, 116, 139, 0.3);
position: relative;
overflow: hidden;
}
.custom-header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, transparent, #a78bfa, #6ee7b7, transparent);
opacity: 0.8;
}
.custom-header h1 {
font-size: 2.4rem;
font-weight: 600;
margin: 0 0 0.5rem 0;
letter-spacing: 0.3px;
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}
.header-text {
background: linear-gradient(135deg, #a78bfa 0%, #8b5cf6 30%, #6ee7b7 70%, #34d399 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
}
/* Built by Sachin text styling - like the reference image */
.built-by {
color: #94a3b8;
font-size: 0.8rem;
font-weight: 400;
letter-spacing: 0.3px;
margin-top: 0.5rem;
display: flex;
align-items: center;
justify-content: center;
gap: 0.4rem;
}
.built-by .sparkle {
font-size: 0.9rem;
color: #a78bfa;
}
/* Robot emoji animation - UP AND DOWN (floating) */
@keyframes float {
0%, 100% {
transform: translateY(0px);
}
50% {
transform: translateY(-10px);
}
}
.robot-icon {
display: inline-block;
animation: float 2s ease-in-out infinite;
font-size: 2.2rem;
}
/* Chatbot icon styling with animation */
.chatbot-icon {
position: fixed;
bottom: 2rem;
right: 2rem;
width: 60px;
height: 60px;
background: linear-gradient(135deg, #7c3aed 0%, #8b5cf6 100%);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
box-shadow: 0 4px 20px rgba(124, 58, 237, 0.4);
cursor: pointer;
z-index: 1000;
transition: all 0.3s ease;
animation: pulse 2s ease-in-out infinite;
}
.chatbot-icon:hover {
transform: scale(1.1);
box-shadow: 0 6px 30px rgba(124, 58, 237, 0.6);
}
@keyframes pulse {
0%, 100% {
transform: scale(1);
box-shadow: 0 4px 20px rgba(124, 58, 237, 0.4);
}
50% {
transform: scale(1.05);
box-shadow: 0 6px 25px rgba(124, 58, 237, 0.6);
}
}
/* Sidebar styling - deep dark */
[data-testid="stSidebar"] {
background: linear-gradient(180deg, #1a1625 0%, #0f0d15 100%);
border-right: 1px solid rgba(167, 139, 250, 0.2);
}
[data-testid="stSidebar"] .css-1d391kg {
padding: 2rem 1rem;
}
/* Selectbox styling - white text */
.stSelectbox label {
color: #ffffff !important;
font-weight: 500;
font-size: 0.95rem;
margin-bottom: 0.5rem;
}
.stSelectbox > div > div {
background-color: #2d2438;
border: 1px solid #4c3d5f;
border-radius: 8px;
color: #ffffff !important;
transition: all 0.3s ease;
}
.stSelectbox > div > div:hover {
border-color: #7c3aed;
background-color: #3d3250;
box-shadow: 0 0 0 1px rgba(167, 139, 250, 0.3);
}
/* Text input styling - white text */
.stTextInput label {
color: #ffffff !important;
font-weight: 500;
font-size: 0.95rem;
}
.stTextInput > div > div > input {
background-color: #2d2438;
border: 1px solid #4c3d5f;
border-radius: 8px;
color: #ffffff !important;
padding: 0.65rem;
transition: all 0.3s ease;
}
.stTextInput > div > div > input:focus {
border-color: #7c3aed;
background-color: #3d3250;
box-shadow: 0 0 0 2px rgba(167, 139, 250, 0.25);
outline: none;
}
.stTextInput > div > div > input::placeholder {
color: #a78bfa;
opacity: 0.7;
}
/* Warning box styling - white text */
.stAlert {
background: linear-gradient(135deg, #6d28d9 0%, #7c3aed 100%);
border-radius: 8px;
border: 1px solid #8b5cf6;
padding: 1rem;
color: #ffffff !important;
font-weight: 400;
box-shadow: 0 4px 15px rgba(124, 58, 237, 0.2);
}
/* Link styling - white */
a {
color: #ffffff !important;
text-decoration: none;
font-weight: 500;
transition: color 0.2s ease;
}
a:hover {
color: #f3f4f6 !important;
text-decoration: underline;
}
/* Clean section dividers */
.section-divider {
height: 1px;
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.3), transparent);
margin: 2rem 0;
}
/* Section headers - white */
.section-header {
color: #ffffff !important;
font-size: 0.85rem;
font-weight: 600;
margin-bottom: 1rem;
margin-top: 1.5rem;
display: flex;
align-items: center;
gap: 0.5rem;
text-transform: uppercase;
letter-spacing: 1px;
}
/* Remove extra spacing */
.stSelectbox, .stTextInput {
margin-bottom: 1.5rem;
}
/* Toggle/Checkbox styling - white */
.stCheckbox label {
color: #ffffff !important;
}
/* Overall text color - white */
p, span, div, label {
color: #ffffff !important;
}
/* Dropdown options styling */
[role="listbox"] {
background-color: #2d2438 !important;
}
[role="option"] {
color: #ffffff !important;
}
/* Button styling */
.stButton > button {
background: linear-gradient(135deg, #7c3aed 0%, #8b5cf6 100%);
color: #ffffff !important;
border: 1px solid #a78bfa;
border-radius: 8px;
padding: 0.5rem 1.5rem;
font-weight: 500;
transition: all 0.3s ease;
}
.stButton > button:hover {
background: linear-gradient(135deg, #8b5cf6 0%, #a78bfa 100%);
box-shadow: 0 0 20px rgba(167, 139, 250, 0.4);
border-color: #c4b5fd;
}
</style>
""", unsafe_allow_html=True)
def load_streamlit_ui(self):
# Page config
st.set_page_config(
page_title="πŸ€– " + self.config.get_page_title(),
layout="wide",
initial_sidebar_state="expanded"
)
st.session_state.timeframe = ''
st.session_state.IsFetchButtonClicked = False
# Inject custom CSS
self._inject_custom_css()
# Custom header with animated robot icon (bouncing up and down)
st.markdown(f"""
<div class="custom-header">
<h1>
<span class="robot-icon">πŸ€–</span>
<span class="header-text">{self.config.get_page_title()}</span>
</h1>
<p class="built-by"><span class="sparkle">✨</span> Built by Sachin Ranasing</p>
</div>
""", unsafe_allow_html=True)
# Animated chatbot icon at bottom right
st.markdown("""
<div class="chatbot-icon">
πŸ€–
</div>
""", unsafe_allow_html=True)
with st.sidebar:
# Get options from config
llm_options = self.config.get_llm_options()
usecase_options = self.config.get_usecase_options()
# AI Configuration section header
st.markdown("<div class='section-header'>βš™οΈ AI Configuration</div>", unsafe_allow_html=True)
# LLM selection
self.user_controls["selected_llm"] = st.selectbox(
"Select LLM",
llm_options,
help="Choose your preferred Language Model"
)
if self.user_controls["selected_llm"] == 'Groq':
# Model selection
model_options = self.config.get_groq_model_options()
self.user_controls["selected_groq_model"] = st.selectbox(
"Select Model",
model_options,
help="Choose a Groq model variant"
)
self.user_controls["GROQ_API_KEY"] = st.session_state["GROQ_API_KEY"] = st.text_input(
"API Key",
type="password",
placeholder="Enter your Groq API key"
)
# Validate API key
if not self.user_controls["GROQ_API_KEY"]:
st.warning("⚠️ Please enter your GROQ API key to proceed. Don't have one? Get it here: [Groq Console](https://console.groq.com/keys)")
# Divider
st.markdown("<div class='section-divider'></div>", unsafe_allow_html=True)
# Use Case section header
st.markdown("<div class='section-header'>🎯 Use Case</div>", unsafe_allow_html=True)
# Usecase selection
self.user_controls["selected_usecase"] = st.selectbox(
"Select Usecases",
usecase_options,
help="Choose your application use case"
)
if self.user_controls["selected_usecase"] =="Chatbot with Web Search" or self.user_controls["selected_usecase"] =="AI News":
os.environ["TAVILY_API_KEY"]=self.user_controls["TAVILY_API_KEY"]=st.session_state["TAVILY_API_KEY"]=st.text_input("TAVILY API KEY",type="password", placeholder="Enter your TAVILY API key")
# Validate API key
if not self.user_controls["TAVILY_API_KEY"]:
st.warning("⚠️ Please enter your TAVILY_API_KEY key to proceed. Don't have? refer : https://app.tavily.com/home")
if self.user_controls['selected_usecase']=="AI News":
st.subheader("πŸ“° AI News Explorer ")
with st.sidebar:
time_frame = st.selectbox(
"πŸ“… Select Time Frame",
["Daily", "Weekly", "Monthly"],
index=0
)
if st.button("πŸ” Fetch Latest AI News", use_container_width=True):
st.session_state.IsFetchButtonClicked = True
st.session_state.timeframe = time_frame
elif self.user_controls["selected_usecase"] == "Research Assistant":
st.info("πŸ”¬ **Research Assistant Features:**\n\n"
"β€’ **ArXiv Search**: Access academic papers and research\n"
"β€’ **Web Search**: Complement research with general web results\n"
"β€’ **No API Key Required**: ArXiv is free to use\n\n"
"Perfect for literature reviews, academic research, and finding relevant papers!")
# Still need TAVILY for web search component
os.environ["TAVILY_API_KEY"]=self.user_controls["TAVILY_API_KEY"]=st.session_state["TAVILY_API_KEY"]=st.text_input("TAVILY API KEY",type="password", placeholder="Enter your TAVILY API key for web search")
# Validate API key
if not self.user_controls["TAVILY_API_KEY"]:
st.warning("⚠️ Please enter your TAVILY_API_KEY for comprehensive research. Don't have? refer : https://app.tavily.com/home")
return self.user_controls