import streamlit as st
import requests
import time
# Set page config for wide layout and custom favicon
st.set_page_config(
page_title="Agentic AI Demo",
page_icon="π",
layout="wide",
initial_sidebar_state="collapsed"
)
# Custom CSS for modern, compact design
st.markdown("""
<style>
/* Import Google Fonts */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
/* Hide Streamlit branding and padding */
#MainMenu {visibility: hidden;}
footer {visibility: hidden;}
header {visibility: hidden;}
.block-container {padding-top: 1rem !important; padding-bottom: 1rem !important;}
/* Main container styling */
.stApp {
background: #1e3c72;
font-family: 'Inter', sans-serif;
min-height: 100vh;
}
/* Floating particles */
.stApp::before {
content: '';
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image:
radial-gradient(2px 2px at 20px 30px, rgba(255,255,255,0.3), transparent),
radial-gradient(2px 2px at 40px 70px, rgba(255,255,255,0.2), transparent),
radial-gradient(1px 1px at 90px 40px, rgba(255,255,255,0.3), transparent),
radial-gradient(1px 1px at 130px 80px, rgba(255,255,255,0.2), transparent),
radial-gradient(2px 2px at 160px 30px, rgba(255,255,255,0.1), transparent);
background-repeat: repeat;
background-size: 200px 100px;
z-index: -1;
animation: floatParticles 20s linear infinite;
}
@keyframes floatParticles {
from { transform: translateY(100vh); }
to { transform: translateY(-100px); }
}
/* Header with stats */
.header-stats {
background: rgba(0, 0, 0, 0.2);
backdrop-filter: blur(10px);
padding: 0.5rem 1rem;
margin-bottom: 1rem;
border-radius: 0;
display: flex;
justify-content: space-between;
align-items: center;
color: white;
font-size: 0.9rem;
}
/* Main content grid */
.main-grid {
display: grid;
grid-template-columns: 1fr 2fr 1fr;
gap: 1rem;
max-width: 1400px;
margin: 0 auto;
padding: 1rem;
}
/* Left sidebar */
.left-sidebar {
display: flex;
flex-direction: column;
gap: 1rem;
}
/* Right sidebar */
.right-sidebar {
display: flex;
flex-direction: column;
gap: 1rem;
}
/* Main content area */
.main-content {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(20px);
border-radius: 20px;
padding: 1.5rem;
box-shadow: 0 8px 32px rgba(31, 38, 135, 0.37);
border: 1px solid rgba(255, 255, 255, 0.18);
animation: slideUp 0.8s ease-out;
}
@keyframes slideUp {
from { opacity: 0; transform: translateY(30px); }
to { opacity: 1; transform: translateY(0); }
}
/* Sidebar cards */
.sidebar-card {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(15px);
border-radius: 15px;
padding: 1rem;
border: 1px solid rgba(255, 255, 255, 0.2);
color: white;
transition: all 0.3s ease;
animation: fadeIn 0.6s ease-out;
}
.sidebar-card:hover {
transform: translateY(-2px);
background: rgba(255, 255, 255, 0.15);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}
.sidebar-card h4 {
margin: 0 0 0.5rem 0;
color: #ffd93d;
font-size: 1.1rem;
font-weight: 600;
}
.sidebar-card p {
margin: 0;
font-size: 0.9rem;
opacity: 0.9;
line-height: 1.4;
}
/* Stats cards */
.stat-number {
font-size: 1.5rem;
font-weight: 700;
color: #6bcf7f;
}
/* Title styling */
.main-title {
text-align: center;
color: white;
font-size: 2.5rem;
font-weight: 700;
margin-bottom: 0.5rem;
text-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
animation: glow 2s ease-in-out infinite alternate;
}
@keyframes glow {
from { text-shadow: 0 0 20px rgba(255, 255, 255, 0.5); }
to { text-shadow: 0 0 30px rgba(255, 255, 255, 0.8); }
}
.subtitle {
text-align: center;
color: rgba(255, 255, 255, 0.9);
font-size: 1rem;
margin-bottom: 1.5rem;
font-weight: 400;
}
/* Feature cards in main content */
.feature-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1rem;
margin-bottom: 1.5rem;
}
.feature-card {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border-radius: 12px;
padding: 1rem;
border: 1px solid rgba(255, 255, 255, 0.2);
transition: all 0.3s ease;
text-align: center;
}
.feature-card:hover {
transform: scale(1.02);
background: rgba(255, 255, 255, 0.15);
}
.feature-icon {
font-size: 2rem;
margin-bottom: 0.5rem;
}
.feature-title {
color: white;
font-size: 1rem;
font-weight: 600;
margin-bottom: 0.3rem;
}
.feature-description {
color: rgba(255, 255, 255, 0.8);
font-size: 0.85rem;
line-height: 1.4;
}
/* Radio button styling */
.stRadio > div {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border-radius: 10px;
padding: 0.75rem;
border: 1px solid rgba(255, 255, 255, 0.2);
margin-bottom: 1rem;
}
.stRadio > div > label {
color: white !important;
font-weight: 600;
}
/* Text input styling */
.stTextInput > div > div > input {
background: rgba(0, 0, 0, 0.3) !important;
backdrop-filter: blur(10px) !important;
border: 2px solid rgba(255, 255, 255, 0.3) !important;
border-radius: 12px !important;
color: white !important;
padding: 0.75rem !important;
font-size: 1rem !important;
transition: all 0.3s ease !important;
}
.stTextInput > div > div > input:focus {
border-color: #ffd93d !important;
box-shadow: 0 0 15px rgba(255, 217, 61, 0.3) !important;
}
.stTextInput > div > div > input::placeholder {
color: rgba(255, 255, 255, 0.6) !important;
}
.stTextInput > label {
color: white !important;
font-weight: 600 !important;
margin-bottom: 0.5rem !important;
}
/* Button styling */
.stButton > button {
background: linear-gradient(45deg, #ff6b6b, #ffd93d, #6bcf7f) !important;
background-size: 200% 200% !important;
color: white !important;
border: none !important;
border-radius: 15px !important;
padding: 0.75rem 2rem !important;
font-size: 1.1rem !important;
font-weight: 600 !important;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2) !important;
transition: all 0.3s ease !important;
text-transform: uppercase !important;
letter-spacing: 1px !important;
width: 100% !important;
animation: gradientButton 3s ease infinite !important;
}
@keyframes gradientButton {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
.stButton > button:hover {
transform: translateY(-2px) !important;
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3) !important;
}
/* Loading animation */
.loading-container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
padding: 1rem;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-top: 3px solid #ffd93d;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 0.5rem;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* Output styling */
.output-container {
background: rgba(0, 0, 0, 0.3);
backdrop-filter: blur(15px);
border-radius: 12px;
padding: 1rem;
margin-top: 1rem;
border: 1px solid rgba(255, 255, 255, 0.2);
animation: fadeIn 0.6s ease-out;
}
@keyframes fadeIn {
from { opacity: 0; transform: scale(0.95); }
to { opacity: 1; transform: scale(1); }
}
/* Progress indicator */
.progress-bar {
width: 100%;
height: 4px;
background: rgba(255, 255, 255, 0.2);
border-radius: 2px;
overflow: hidden;
margin: 1rem 0;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #ff6b6b, #ffd93d, #6bcf7f);
border-radius: 2px;
animation: progressMove 2s ease infinite;
}
@keyframes progressMove {
0% { width: 0%; }
50% { width: 70%; }
100% { width: 100%; }
}
/* Responsive design */
@media (max-width: 768px) {
.main-grid {
grid-template-columns: 1fr;
gap: 0.5rem;
}
.feature-grid {
grid-template-columns: 1fr;
}
.main-title {
font-size: 2rem;
}
.header-stats {
flex-direction: column;
gap: 0.5rem;
}
}
</style>
""", unsafe_allow_html=True)
# API endpoints
CUSTOM_TASK_URL = "http://localhost:8000/task"
PUBLIC_ASK_URL = "http://localhost:8001/ask"
# --- Fetch real-time stats from backend ---
def fetch_stats():
try:
resp = requests.get("http://localhost:8000/stats", timeout=5)
if resp.status_code == 200:
return resp.json()
except Exception:
pass
return {
"active_sessions": None,
"queries_processed": None,
"response_time": None,
"success_rate": None,
"todays_queries": None,
"uptime": None
}
# (Auto-refresh code removed due to TypeError)
# Mode selection (move up so we know which backend is selected)
st.markdown("### π― Choose Your AI Backend")
mode = st.radio("", ["Custom MCP", "Public MCP"], horizontal=True, key="ai_backend_radio")
# Fetch stats for selected backend
if mode == "Custom MCP":
stats = fetch_stats()
else:
def fetch_public_stats():
try:
resp = requests.get("http://localhost:8001/stats", timeout=5)
if resp.status_code == 200:
return resp.json()
except Exception:
pass
return {
"active_sessions": None,
"queries_processed": None,
"response_time": None,
"success_rate": None,
"todays_queries": None,
"uptime": None
}
stats = fetch_public_stats()
# Header with stats (real-time)
st.markdown(f"""
<div class="header-stats">
<div>π AI Processing Active</div>
<div>β‘ Response Time: {stats['response_time'] if stats['response_time'] is not None else 'N/A'}s</div>
<div>π Secure Connection</div>
<div>π Uptime: {stats['uptime'] if stats['uptime'] is not None else 'N/A'}%</div>
</div>
""", unsafe_allow_html=True)
# Create main grid layout
col1, col2, col3 = st.columns([1, 2, 1])
with col1:
# Left sidebar content with real-time stats for selected backend
st.markdown(f"""
<div class="sidebar-card">
<h4>π Real-time Stats ({mode})</h4>
<p>Active Sessions: <span class="stat-number">{stats['active_sessions'] if stats['active_sessions'] is not None else 'N/A'}</span></p>
<p>Queries Processed: <span class="stat-number">{stats['queries_processed'] if stats['queries_processed'] is not None else 'N/A'}</span></p>
<p>Response Time: <span class="stat-number">{stats['response_time'] if stats['response_time'] is not None else 'N/A'}s</span></p>
</div>
""", unsafe_allow_html=True)
st.markdown("""
<div class="sidebar-card">
<h4>π― AI Capabilities</h4>
<p>β Natural Language Processing</p>
<p>β Code Generation</p>
<p>β Data Analysis</p>
<p>β Creative Writing</p>
<p>β Problem Solving</p>
</div>
""", unsafe_allow_html=True)
st.markdown("""
<div class="sidebar-card">
<h4>π§ System Status</h4>
<p>π’ Custom MCP: Online</p>
<p>π’ Public MCP: Online</p>
<p>π’ Database: Connected</p>
<p>π’ API Gateway: Active</p>
</div>
""", unsafe_allow_html=True)
with col2:
# Main content area
st.markdown('<div class="main-content">', unsafe_allow_html=True)
# Title and subtitle
st.markdown('<h1 class="main-title">π Agentic AI Demo</h1>', unsafe_allow_html=True)
st.markdown('<p class="subtitle">Powered by Gemini β’ Experience the Future of AI Intelligence</p>', unsafe_allow_html=True)
# Feature cards
st.markdown("""
<div class="feature-grid">
<div class="feature-card">
<div class="feature-icon">π§</div>
<div class="feature-title">Custom MCP</div>
<div class="feature-description">Advanced task processing with specialized tools</div>
</div>
<div class="feature-card">
<div class="feature-icon">π</div>
<div class="feature-title">Public MCP</div>
<div class="feature-description">General-purpose AI with broad knowledge</div>
</div>
</div>
""", unsafe_allow_html=True)
# User input
st.markdown("### π¬ Enter Your Query")
user_input = st.text_input("", placeholder="Ask me anything... I'm powered by advanced AI!", label_visibility="collapsed")
# Run button
if st.button("π Process Query"):
if not user_input:
st.error("β οΈ Please enter a query to continue.")
else:
# Use a placeholder for the loading animation
loading_placeholder = st.empty()
loading_placeholder.markdown("""
<div class="loading-container">
<div class="loading-spinner"></div>
<div style="color: white; font-size: 0.9rem;">Processing your request...</div>
</div>
<div class="progress-bar">
<div class="progress-fill"></div>
</div>
""", unsafe_allow_html=True)
time.sleep(0.5)
try:
if mode == "Custom MCP":
with st.spinner("Creating task..."):
res = requests.post(
CUSTOM_TASK_URL,
json={"input": user_input, "tools": ["sample_tool"]},
timeout=30
)
if res.status_code != 201:
loading_placeholder.empty()
st.error(f"β Error creating task: {res.text}")
else:
task_id = res.json().get("task_id")
with st.spinner("Executing task..."):
run_res = requests.post(f"{CUSTOM_TASK_URL}/{task_id}/run", timeout=60)
loading_placeholder.empty()
if run_res.status_code == 200:
st.markdown('<div class="output-container">', unsafe_allow_html=True)
st.success("β
Custom MCP Processing Complete!")
output = run_res.json().get("output", "No output received")
st.markdown(f"""
<div style="color: white; line-height: 1.6; margin-top: 0.5rem;">
{output}
</div>
""", unsafe_allow_html=True)
st.markdown('</div>', unsafe_allow_html=True)
else:
st.error(f"β Error running task: {run_res.text}")
else:
with st.spinner("Querying Public MCP..."):
res = requests.post(PUBLIC_ASK_URL, json={"query": user_input}, timeout=60)
loading_placeholder.empty()
if res.status_code == 200:
st.markdown('<div class="output-container">', unsafe_allow_html=True)
st.success("β
Public MCP Processing Complete!")
response = res.json().get("response", "No response received")
st.markdown(f"""
<div style="color: white; line-height: 1.6; margin-top: 0.5rem;">
{response}
</div>
""", unsafe_allow_html=True)
st.markdown('</div>', unsafe_allow_html=True)
else:
st.error(f"β Error from Public MCP: {res.text}")
except requests.exceptions.Timeout:
loading_placeholder.empty()
st.error("β±οΈ Request timed out. Please try again.")
except requests.exceptions.ConnectionError:
loading_placeholder.empty()
st.error("π Connection error. Please check if the backend services are running.")
except Exception as e:
loading_placeholder.empty()
st.error(f"π₯ Unexpected error: {str(e)}")
st.markdown('</div>', unsafe_allow_html=True)
with col3:
# Right sidebar content
st.markdown("""
<div class="sidebar-card">
<h4>π Recent Activity</h4>
<p>β’ Code generation completed</p>
<p>β’ Data analysis in progress</p>
<p>β’ Creative writing finished</p>
<p>β’ API integration successful</p>
</div>
""", unsafe_allow_html=True)
st.markdown("""
<div class="sidebar-card">
<h4>π‘ Pro Tips</h4>
<p>π Be specific in your queries</p>
<p>π― Use clear, concise language</p>
<p>π Try different AI backends</p>
<p>β‘ Experiment with complex tasks</p>
</div>
""", unsafe_allow_html=True)
st.markdown(f"""
<div class="sidebar-card">
<h4>π Usage Analytics ({mode})</h4>
<p>Today's Queries: <span class="stat-number">{stats['todays_queries'] if stats['todays_queries'] is not None else 'N/A'}</span></p>
<p>Success Rate: <span class="stat-number">{stats['success_rate'] if stats['success_rate'] is not None else 'N/A'}%</span></p>
<p>Avg. Response: <span class="stat-number">{stats['response_time'] if stats['response_time'] is not None else 'N/A'}s</span></p>
</div>
""", unsafe_allow_html=True)
st.markdown("""
<div class="sidebar-card">
<h4>π Features</h4>
<p>π High-speed processing</p>
<p>π Enterprise security</p>
<p>π Real-time monitoring</p>
<p>π¨ Custom workflows</p>
</div>
""", unsafe_allow_html=True)
# Footer
st.markdown("""
<div style="text-align: center; color: rgba(255, 255, 255, 0.8); margin-top: 1rem; padding: 1rem; background: rgba(0, 0, 0, 0.2); backdrop-filter: blur(10px);">
<p>Powered by Prodigal AI Pvt. Ltd. β’ Β© 2025 β’ Built with β€οΈ using Streamlit β’ Real-time Processing Active</p>
</div>
""", unsafe_allow_html=True)