# app.py
# Pakistan AI Career Roadmap Bot
# Interactive Streamlit Application β Dark Blue/Red Theme
import streamlit as st
from groq import Groq
from dotenv import load_dotenv
import os
from prompts import (
SYSTEM_PROMPT,
get_career_prompt,
is_safe_input,
is_safe_output
)
from domains import (
get_all_domains,
get_domain_info
)
# =====================================
# LOAD API KEY
# =====================================
load_dotenv()
try:
api_key = st.secrets["GROQ_API_KEY"]
except:
api_key = os.getenv("GROQ_API_KEY")
client = Groq(api_key=api_key)
# =====================================
# PAGE CONFIG
# =====================================
st.set_page_config(
page_title="Pakistan AI Career Bot",
page_icon="π€",
layout="wide"
)
# =====================================
# GLOBAL CSS
# =====================================
st.markdown("""
""", unsafe_allow_html=True)
# =====================================
# DOMAIN ICONS
# =====================================
DOMAIN_ICONS = {
"Machine Learning Engineer": "π§ ",
"Deep Learning Engineer": "β‘",
"Computer Vision Engineer": "ποΈ",
"NLP Engineer": "π¬",
"MLOps Engineer": "βοΈ",
"Data Scientist": "π",
"Generative AI Engineer": "β¨",
"AI Research Engineer": "π¬"
}
# =====================================
# SESSION STATE
# =====================================
defaults = {
"messages": [{"role": "system", "content": SYSTEM_PROMPT}],
"roadmap_generated": False,
"selected_domain": None,
"roadmap_text": "",
"current_step": 1,
"student_name": "",
"form_data": {}
}
for k, v in defaults.items():
if k not in st.session_state:
st.session_state[k] = v
# =====================================
# HERO
# =====================================
st.markdown("""
β Pakistan AI Career Roadmap Bot
Find Your Path in Artificial Intelligence
AI-powered personalized learning roadmaps for students β by Athar Abbas
""", unsafe_allow_html=True)
# =====================================
# STEP TRACKER
# =====================================
step = st.session_state.current_step
labels = ["Domain", "Profile", "Generating", "Roadmap"]
def sc(n): # circle class
if n < step: return "done"
if n == step: return "active"
return "idle"
def lc(n): # line class
return "done-line" if n < step else ""
nodes_html = ""
for i, lbl in enumerate(labels, 1):
nodes_html += f''
if i < len(labels):
nodes_html += f''
st.markdown(f'{nodes_html}
', unsafe_allow_html=True)
st.markdown("
", unsafe_allow_html=True)
# ======================================================
# STEP 1 β CHOOSE DOMAIN
# ======================================================
if step == 1:
st.markdown('Choose Your AI Domain
', unsafe_allow_html=True)
st.markdown('Select the specialisation that excites you most β we\'ll build a roadmap around it.
', unsafe_allow_html=True)
all_domains = get_all_domains()
cols = st.columns(4)
for i, dname in enumerate(all_domains):
dinfo = get_domain_info(dname)
icon = DOMAIN_ICONS.get(dname, "π€")
chips = "".join([f'{s}'
for s in dinfo["skills_required"][:3]])
with cols[i % 4]:
st.markdown(f"""
{icon}
{dname}
{dinfo['description']}
{chips}
""", unsafe_allow_html=True)
if st.button(f"Select {icon}", key=f"d{i}", use_container_width=True):
st.session_state.selected_domain = dname
st.session_state.current_step = 2
st.rerun()
# ======================================================
# STEP 2 β STUDENT PROFILE
# ======================================================
elif step == 2:
dinfo = get_domain_info(st.session_state.selected_domain)
icon = DOMAIN_ICONS.get(st.session_state.selected_domain, "π€")
# Banner
st.markdown(f"""
{icon}
{st.session_state.selected_domain}
{dinfo['description']}
""", unsafe_allow_html=True)
# Stats (no salary, no duration)
st.markdown(f"""
{len(dinfo['skills_required'])}
Core Skills
{len(dinfo['free_resources'])}
Free Resources
""", unsafe_allow_html=True)
# Form
st.markdown('", unsafe_allow_html=True)
st.markdown("
", unsafe_allow_html=True)
c1, c2 = st.columns([1, 3])
with c1:
if st.button("β Back", use_container_width=True):
st.session_state.current_step = 1
st.rerun()
with c2:
if st.button("π Generate My Roadmap", use_container_width=True):
if not name.strip():
st.warning("β οΈ Please enter your name to continue.")
else:
st.session_state.student_name = name.strip()
st.session_state.form_data = {
"name": name.strip(),
"education": education,
"skills": skills,
"experience": experience
}
st.session_state.current_step = 3
st.rerun()
# ======================================================
# STEP 3 β GENERATING
# ======================================================
elif step == 3:
st.markdown("""
β‘
Building Your Roadmap
Analysing profile Β· cross-checking recommendations Β· writing plan
""", unsafe_allow_html=True)
progress_bar = st.progress(0, text="Initialisingβ¦")
form = st.session_state.form_data
career_prompt = get_career_prompt(
name = form["name"],
education = form["education"],
skills = ", ".join(form["skills"]) if form["skills"] else "No skills yet",
domain = st.session_state.selected_domain,
experience = form["experience"]
)
if not is_safe_input(career_prompt):
st.error("β οΈ Invalid input detected.")
st.session_state.current_step = 2
st.rerun()
st.session_state.messages.append({"role": "user", "content": career_prompt})
responses = []
for i in range(3):
pct = (i + 1) * 30
msgs = [f"Generating roadmap β pass {i+1}/3β¦",
"Cross-checking domain knowledgeβ¦",
"Selecting best responseβ¦"]
progress_bar.progress(pct, text=msgs[i])
resp = client.chat.completions.create(
model = "llama-3.1-8b-instant",
messages = st.session_state.messages,
temperature= 0.3,
max_tokens = 2000
)
responses.append(resp.choices[0].message.content)
progress_bar.progress(100, text="β
Done!")
best = max(responses, key=len)
if not is_safe_output(best):
st.error("β οΈ Something went wrong. Please try again.")
st.session_state.current_step = 2
else:
st.session_state.messages.append({"role": "assistant", "content": best})
st.session_state.roadmap_text = best
st.session_state.roadmap_generated= True
st.session_state.current_step = 4
st.rerun()
# ======================================================
# STEP 4 β ROADMAP DISPLAY
# ======================================================
elif step == 4:
icon = DOMAIN_ICONS.get(st.session_state.selected_domain, "π€")
dinfo = get_domain_info(st.session_state.selected_domain)
name = st.session_state.student_name
# Top row: title + download
col_hdr, col_dl = st.columns([3, 1])
with col_hdr:
st.markdown(f"""
{icon}
{st.session_state.selected_domain}
Roadmap for {name}
""", unsafe_allow_html=True)
with col_dl:
download_content = (
f"AI CAREER ROADMAP\n"
f"For: {name}\n"
f"Domain: {st.session_state.selected_domain}\n"
f"{'β'*50}\n\n"
f"{st.session_state.roadmap_text}\n\n"
f"{'β'*50}\n"
f"Generated by AI Career Roadmap Bot\n"
)
st.markdown("
", unsafe_allow_html=True)
st.download_button(
label = "β¬οΈ Download Roadmap",
data = download_content,
file_name= f"AI_Roadmap_{name.replace(' ','_')}.txt",
mime = "text/plain",
use_container_width=True
)
# Stats (no salary, no duration)
st.markdown(f"""
{len(dinfo['skills_required'])}
Skills in Roadmap
{len(dinfo['pakistani_companies'])}
Hiring Companies
""", unsafe_allow_html=True)
# Roadmap content β styled like Claude output
st.markdown('', unsafe_allow_html=True)
st.markdown(st.session_state.roadmap_text)
st.markdown("
", unsafe_allow_html=True)
st.divider()
# ββ Follow-up chat ββ
st.markdown('π¬ Ask Follow-up Questions
', unsafe_allow_html=True)
st.markdown('Have questions about your roadmap? Ask anything below.
', unsafe_allow_html=True)
# show only follow-up messages (skip system + initial roadmap exchange)
for msg in st.session_state.messages[3:]:
with st.chat_message(msg["role"]):
st.write(msg["content"])
user_q = st.chat_input("Ask about resources, skills, companiesβ¦")
if user_q:
if not is_safe_input(user_q):
st.error("β οΈ Please ask career-related questions only.")
else:
st.session_state.messages.append({"role": "user", "content": user_q})
with st.spinner("Thinkingβ¦"):
resp = client.chat.completions.create(
model = "llama-3.1-8b-instant",
messages = st.session_state.messages,
temperature= 0.3,
max_tokens = 1000
)
reply = resp.choices[0].message.content
if not is_safe_output(reply):
st.error("β οΈ Something went wrong.")
else:
st.session_state.messages.append({"role": "assistant", "content": reply})
st.rerun()
st.divider()
# Action buttons
c1, c2 = st.columns(2)
with c1:
if st.button("π New Roadmap", use_container_width=True):
for k, v in defaults.items():
st.session_state[k] = v
st.rerun()
with c2:
if st.button("π Change Domain", use_container_width=True):
st.session_state.messages = [{"role": "system", "content": SYSTEM_PROMPT}]
st.session_state.roadmap_generated = False
st.session_state.roadmap_text = ""
st.session_state.selected_domain = None
st.session_state.current_step = 1
st.rerun()
# ββ FOOTER ββ
st.markdown("""
""", unsafe_allow_html=True)