import os
import json
import requests
import streamlit as st
# --- Configuration ---
API_URL = "http://127.0.0.1:8000/chat"
IMAGES_DIR = "images"
# --- Page Setup ---
st.set_page_config(
page_title="Tharushika | AI Portfolio",
page_icon="👋",
layout="centered",
initial_sidebar_state="collapsed"
)
st.markdown("""
""", unsafe_allow_html=True)
# --- Helper Functions ---
def render_projects(data):
st.markdown("### Featured Projects")
if not data:
st.info("No projects data received.")
return
cols = st.columns(2)
for i, proj in enumerate(data):
with cols[i % 2]:
with st.container(border=True):
img_path = proj.get("image_path", "")
if img_path and os.path.exists(img_path):
st.image(img_path, use_container_width=True)
else:
st.markdown(f"""
No Image
""", unsafe_allow_html=True)
st.markdown(f"#### {proj.get('title', 'Untitled')}")
st.caption(proj.get('type', 'Project').upper())
with st.expander("View Details"):
st.write(proj.get('description', ''))
st.markdown(
f"**Tech Stack:** {proj.get('technologies', '')}")
links = []
if proj.get('github_url'):
links.append(f"[GitHub]({proj.get('github_url')})")
if proj.get('demo_url'):
links.append(f"[Live Demo]({proj.get('demo_url')})")
if links:
st.markdown(" • ".join(links))
def render_skills(data):
st.markdown("### Skills & Expertise")
if not data:
st.info("No skills data received.")
return
for category, skills in data.items():
with st.container(border=True):
st.markdown(f"**{category}**")
badges = "".join(
[f"{s}" for s in skills])
st.markdown(badges, unsafe_allow_html=True)
def render_articles(data):
st.markdown("### Articles")
if not data:
st.info("No articles found.")
return
for item in data:
with st.container(border=True):
st.markdown(f"**{item.get('title', 'Untitled')}**")
st.markdown(
f"{item.get('description', '')}
", unsafe_allow_html=True)
if item.get('url'):
st.markdown(f"[Read Article ›]({item['url']})")
def render_videos(data):
st.markdown("### Video Tutorials")
if not data:
st.info("No videos found.")
return
cols = st.columns(2)
for i, item in enumerate(data):
with cols[i % 2]:
with st.container(border=True):
thumb = item.get('thumbnail_url', "")
if thumb and os.path.exists(thumb):
st.image(thumb, use_container_width=True)
st.markdown(f"**{item.get('title', 'Untitled')}**")
st.markdown(
f"{item.get('description', '')}
", unsafe_allow_html=True)
if item.get('url'):
st.markdown(f"[Watch on YouTube ›]({item['url']})")
def render_research(data):
st.markdown("### Research")
if not data:
st.info("No research found.")
return
for item in data:
with st.container(border=True):
st.markdown(f"**{item.get('title', 'Untitled')}**")
st.markdown(
f"{item.get('description', '')}
", unsafe_allow_html=True)
if item.get('url'):
st.markdown(f"[View Publication ›]({item['url']})")
def render_certifications(data):
st.markdown("### Certifications")
if not data:
st.info("No certifications found.")
return
for item in data:
st.markdown(f"""
🎖️
{item}
""", unsafe_allow_html=True)
# --- NEW: Resume Renderer ---
def render_resume(data):
st.markdown("### 📄 Resume / CV")
col1, col2 = st.columns([1, 2])
with col1:
preview_path = data.get("preview_image", "")
if preview_path and os.path.exists(preview_path):
st.image(preview_path, caption="Preview", use_container_width=True)
else:
st.markdown("""
📄
""", unsafe_allow_html=True)
with col2:
st.markdown(f"#### {data.get('title', 'Resume')}")
st.write(data.get('description', ''))
pdf_path = data.get("file_path", "")
if pdf_path and os.path.exists(pdf_path):
with open(pdf_path, "rb") as pdf_file:
pdf_bytes = pdf_file.read()
st.download_button(
label="📥 Download Resume (PDF)",
data=pdf_bytes,
file_name="Tharushika_Abedheera_Resume.pdf",
mime="application/pdf",
)
else:
st.error("Resume file not found.")
def render_content(data):
st.markdown("### Content & Research")
if not data:
return
tab1, tab2, tab3 = st.tabs(["Articles", "Videos", "Research"])
with tab1:
render_articles(data.get('articles', []))
with tab2:
render_videos(data.get('videos', []))
with tab3:
render_research(data.get('research', []))
# --- Centralized Chat Logic Function ---
def process_chat_message(prompt):
with st.spinner("Processing..."):
try:
response = requests.post(API_URL, json={"message": prompt})
if response.status_code == 200:
api_data = response.json()
st.session_state.last_exchange = {
"user_query": prompt,
"ai_response": api_data.get("response", ""),
"tool_code": api_data.get("tool_code"),
"tool_data": api_data.get("tool_data")
}
else:
st.error(f"Backend Error: {response.status_code}")
except Exception as e:
st.error(f"Connection Failed: {e}")
st.rerun()
# --- Main Layout ---
if "last_exchange" not in st.session_state:
st.session_state.last_exchange = None
# --- Top Section: Profile and Introduction ---
if not st.session_state.last_exchange:
st.markdown("", unsafe_allow_html=True)
profile_pic_path = "images/profile.png"
if os.path.exists(profile_pic_path):
st.image(profile_pic_path, width=160)
else:
st.markdown(f"""
Add profile.png
""", unsafe_allow_html=True)
st.markdown("
", unsafe_allow_html=True)
st.markdown("", unsafe_allow_html=True)
st.markdown("
Hey, I'm Tharushika 👋
", unsafe_allow_html=True)
st.markdown("Machine Learning Engineer
", unsafe_allow_html=True)
st.markdown("", unsafe_allow_html=True)
st.markdown("", unsafe_allow_html=True)
if st.button("Me"):
process_chat_message("Tell me about yourself")
if st.button("Projects"):
process_chat_message("Show me your projects")
if st.button("Skills"):
process_chat_message("What are your skills?")
if st.button("Contact"):
process_chat_message("How can I contact you?")
st.markdown("
", unsafe_allow_html=True)
if prompt := st.chat_input("Ask me anything..."):
process_chat_message(prompt)
# --- Conversation Area ---
if st.session_state.last_exchange:
exchange = st.session_state.last_exchange
with st.chat_message("user"):
st.write(exchange["user_query"])
with st.chat_message("assistant"):
st.write(exchange["ai_response"])
tool_code = exchange.get("tool_code")
tool_data = exchange.get("tool_data")
if tool_code == "show_projects":
render_projects(tool_data)
elif tool_code == "show_skills":
render_skills(tool_data)
elif tool_code == "show_content":
render_content(tool_data)
elif tool_code == "show_videos":
render_videos(tool_data)
elif tool_code == "show_articles":
render_articles(tool_data)
elif tool_code == "show_research":
render_research(tool_data)
elif tool_code == "show_certifications":
render_certifications(tool_data)
elif tool_code == "show_resume":
render_resume(tool_data) # <--- RESUME HANDLER ADDED
if prompt := st.chat_input("Ask for more details..."):
process_chat_message(prompt)