|
import streamlit as st |
|
from openai import OpenAI |
|
from elevenlabs.client import ElevenLabs |
|
from stability_sdk import client |
|
import stability_sdk.interfaces.gooseai.generation.generation_pb2 as generation |
|
import pandas as pd |
|
from datetime import datetime |
|
from transformers import pipeline |
|
|
|
|
|
st.set_page_config( |
|
page_title="AfriLearn AI Tutor", |
|
page_icon="π", |
|
layout="wide" |
|
) |
|
|
|
|
|
|
|
try: |
|
AIML_API_KEY = st.secrets["AIML_API_KEY"] |
|
ELEVENLABS_API_KEY = st.secrets["ELEVENLABS_API_KEY"] |
|
STABILITY_KEY = st.secrets["STABILITY_KEY"] |
|
ADMIN_PASS = st.secrets["ADMIN_PASS"] |
|
except KeyError as e: |
|
st.error(f"Missing secret: {e}. Please add it via the Hugging Face Space settings.") |
|
st.stop() |
|
|
|
|
|
@st.cache_resource |
|
def initialize_apis(): |
|
base_url = "https://api.aimlapi.com/v1" |
|
api = OpenAI(api_key=AIML_API_KEY, base_url=base_url) |
|
elevenlabs_client = ElevenLabs(api_key=ELEVENLABS_API_KEY) |
|
stability_client = client.StabilityInference( |
|
key=STABILITY_KEY, |
|
engine="stable-diffusion-xl-1024-v1-0" |
|
) |
|
return api, elevenlabs_client, stability_client |
|
|
|
api, elevenlabs_client, stability_client = initialize_apis() |
|
|
|
|
|
LANGUAGES = { |
|
"English": "en", |
|
"French": "fr", |
|
"Swahili": "sw" |
|
} |
|
|
|
|
|
if "language" not in st.session_state: |
|
st.session_state.language = "English" |
|
|
|
if "offline_mode" not in st.session_state: |
|
st.session_state.offline_mode = False |
|
|
|
if "messages" not in st.session_state: |
|
st.session_state.messages = [] |
|
|
|
|
|
TRANSLATIONS = { |
|
"English": { |
|
"chat_header": "π€ Chat with the AI Tutor", |
|
"chat_placeholder": "Ask your question...", |
|
"enable_voice": "Enable Voice", |
|
"select_voice": "π€ Select Voice", |
|
}, |
|
"French": { |
|
"chat_header": "π€ Discutez avec le tuteur IA", |
|
"chat_placeholder": "Posez votre question...", |
|
"enable_voice": "Activer la voix", |
|
"select_voice": "π€ SΓ©lectionnez une voix", |
|
}, |
|
"Swahili": { |
|
"chat_header": "π€ Zungumza na Mwalimu wa AI", |
|
"chat_placeholder": "Uliza swali lako...", |
|
"enable_voice": "Washa Sauti", |
|
"select_voice": "π€ Chagua sauti", |
|
} |
|
} |
|
|
|
translations = TRANSLATIONS.get(st.session_state.language, TRANSLATIONS["English"]) |
|
|
|
|
|
LANGUAGE = st.sidebar.selectbox( |
|
"π Choose Language / Choisir la langue / Chagua lugha", |
|
list(LANGUAGES.keys()), |
|
index=list(LANGUAGES.keys()).index(st.session_state.language) |
|
) |
|
if LANGUAGE != st.session_state.language: |
|
st.session_state.language = LANGUAGE |
|
st.session_state.messages = [] |
|
st.rerun() |
|
|
|
|
|
VOICE_MAPPING = { |
|
"English": ["George", "Alice"], |
|
"French": ["Georges", "Alice"], |
|
"Swahili": ["George", "Alice"] |
|
} |
|
|
|
voices = VOICE_MAPPING.get(st.session_state.language, ["Emily"]) |
|
|
|
selected_voice = st.sidebar.selectbox( |
|
translations["select_voice"], |
|
voices |
|
) |
|
|
|
|
|
SYSTEM_PROMPT = f""" |
|
You are a friendly tutor for African students. Follow these rules: |
|
1. Respond in {st.session_state.language} |
|
2. Use examples from local culture (markets, farming, traditions) |
|
3. Keep answers under 3 sentences |
|
4. For math/science, use simple terms |
|
""" |
|
|
|
|
|
tabs = ["Home", "About Me", "Tutoring Request", "Career Guidance"] |
|
selected_tab = st.sidebar.radio("Go to / Aller Γ / Nenda kwa", tabs) |
|
|
|
|
|
@st.cache_resource |
|
def load_offline_model(): |
|
return pipeline("text-generation", model="castorini/afriberta_base") |
|
|
|
st.session_state.offline_mode = st.sidebar.checkbox( |
|
"Enable Offline Mode", |
|
value=st.session_state.offline_mode |
|
) |
|
|
|
st.sidebar.markdown("---") |
|
st.sidebar.write("Developed with β€οΈ by Motsim") |
|
st.sidebar.write("Powered by **AI for Connectivity Hackathon II**") |
|
|
|
|
|
if selected_tab == "Home": |
|
st.header(translations["chat_header"]) |
|
|
|
if not st.session_state.messages: |
|
st.session_state.messages = [{ |
|
"role": "assistant", |
|
"content": { |
|
"English": "Jambo! I'm your AI tutor. Ask me anything about school subjects!", |
|
"French": "Bonjour! Je suis votre tuteur IA. Posez-moi des questions sur les sujets scolaires!", |
|
"Swahili": "Habari! Mimi ni mwalimu wako wa AI. Niulize chochote kuhusu masomo!" |
|
}.get(st.session_state.language, "Jambo! I'm your AI tutor. Ask me anything about school subjects!") |
|
}] |
|
|
|
for message in st.session_state.messages: |
|
with st.chat_message(message["role"]): |
|
st.write(message["content"]) |
|
|
|
if prompt := st.chat_input(translations["chat_placeholder"]): |
|
st.session_state.messages.append({"role": "user", "content": prompt}) |
|
|
|
if st.session_state.offline_mode: |
|
try: |
|
offline_model = load_offline_model() |
|
ai_response = offline_model(prompt, max_length=50)[0]['generated_text'] |
|
except Exception as e: |
|
ai_response = f"β οΈ Error: {str(e)}" |
|
else: |
|
try: |
|
completion = api.chat.completions.create( |
|
model="mistralai/Mistral-7B-Instruct-v0.2", |
|
messages=[ |
|
{"role": "system", "content": SYSTEM_PROMPT}, |
|
{"role": "user", "content": prompt}, |
|
], |
|
temperature=0.7, |
|
max_tokens=256, |
|
) |
|
ai_response = completion.choices[0].message.content |
|
except Exception as e: |
|
ai_response = f"β οΈ Error: {str(e)}" |
|
|
|
st.session_state.messages.append({"role": "assistant", "content": ai_response}) |
|
st.rerun() |
|
|
|
if st.checkbox(translations["enable_voice"], key="voice_toggle"): |
|
if st.session_state.messages[-1]["role"] == "assistant": |
|
try: |
|
|
|
audio_generator = elevenlabs_client.generate( |
|
text=st.session_state.messages[-1]["content"], |
|
voice=selected_voice, |
|
model="eleven_multilingual_v2" |
|
) |
|
audio_bytes = b"".join(audio_generator) |
|
st.audio(audio_bytes, format="audio/mp3") |
|
except Exception as e: |
|
st.error(f"β οΈ Error generating audio: {str(e)}") |
|
|
|
elif selected_tab == "About Me": |
|
st.header("About Me π¨βπ»") |
|
st.write("Meet the AfriLearn AI Team. We are a diverse group of professionals and students specializing in AI, software engineering, pedagogy, cybersecurity, and machine learning.") |
|
|
|
elif selected_tab == "Tutoring Request": |
|
st.header("π Tutoring Request Form") |
|
st.write("Fill out the form below to request a tutor.") |
|
MENTORSHIP_DB = "mentorship_requests.csv" |
|
|
|
def load_requests(): |
|
try: |
|
return pd.read_csv(MENTORSHIP_DB) |
|
except FileNotFoundError: |
|
return pd.DataFrame(columns=[ |
|
"timestamp", "name", "country", "interests", |
|
"contact", "status", "notes" |
|
]) |
|
|
|
def save_request(request): |
|
df = load_requests() |
|
new_row = pd.DataFrame([request]) |
|
df = pd.concat([df, new_row], ignore_index=True) |
|
df.to_csv(MENTORSHIP_DB, index=False) |
|
|
|
with st.form("mentor_form"): |
|
col1, col2 = st.columns(2) |
|
with col1: |
|
name = st.text_input("Full Name*", help="Required field") |
|
country = st.selectbox("Country*", ["Nigeria", "Kenya", "Ghana", "South Africa", "Other"]) |
|
with col2: |
|
contact = st.text_input("Email/Phone*", placeholder="e.g. +2348123456789") |
|
education_level = st.selectbox("Education Level", ["Primary School", "Secondary School", "University", "Graduate"]) |
|
|
|
interests = st.multiselect("Areas of Need*", ["Math", "Science", "Coding", "University Applications", "Career Advice"]) |
|
preferred_comms = st.radio("Preferred Communication", ["WhatsApp", "SMS", "Email"], horizontal=True) |
|
|
|
if st.form_submit_button("Submit Request"): |
|
if not name or not contact or not interests: |
|
st.error("Please fill required fields (*)") |
|
else: |
|
request = { |
|
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), |
|
"name": name, |
|
"country": country, |
|
"contact": contact, |
|
"interests": ", ".join(interests), |
|
"education_level": education_level, |
|
"preferred_comms": preferred_comms, |
|
"status": "Pending", |
|
"notes": "" |
|
} |
|
save_request(request) |
|
st.success("Request submitted! Mentor will contact you within 48hrs.") |
|
|
|
if st.sidebar.text_input("Admin Password", type="password") == ADMIN_PASS: |
|
st.sidebar.success("Admin Mode") |
|
st.header("π Mentor Request Dashboard") |
|
df = load_requests() |
|
if not df.empty: |
|
status_filter = st.multiselect("Filter Status", options=df["status"].unique(), default=["Pending"]) |
|
filtered_df = df[df["status"].isin(status_filter)] |
|
st.dataframe(filtered_df.sort_values("timestamp", ascending=False), |
|
column_config={"timestamp": "Date", "contact": "Contact Info", "preferred_comms": "Preferred Channel"}, |
|
use_container_width=True) |
|
selected_id = st.number_input("Enter Request ID to Update", min_value=0, max_value=len(df)-1) |
|
new_status = st.selectbox("New Status", ["Pending", "Contacted", "Resolved"]) |
|
new_notes = st.text_area("Add Notes") |
|
if st.button("Update Request"): |
|
df.at[selected_id, "status"] = new_status |
|
df.at[selected_id, "notes"] = new_notes |
|
df.to_csv(MENTORSHIP_DB, index=False) |
|
st.success("Request updated!") |
|
else: |
|
st.info("No mentorship requests yet") |
|
|
|
elif selected_tab == "Career Guidance": |
|
st.header("π Career Guidance") |
|
st.write("Explore career options and connect with mentors.") |
|
CAREER_DB = "career_requests.csv" |
|
|
|
def load_career_requests(): |
|
try: |
|
return pd.read_csv(CAREER_DB) |
|
except FileNotFoundError: |
|
return pd.DataFrame(columns=["timestamp", "name", "email", "career_interest"]) |
|
|
|
def save_career_request(request): |
|
df = load_career_requests() |
|
new_row = pd.DataFrame([request]) |
|
df = pd.concat([df, new_row], ignore_index=True) |
|
df.to_csv(CAREER_DB, index=False) |
|
|
|
with st.form("career_form"): |
|
name = st.text_input("Your Name*") |
|
email = st.text_input("Your Email*") |
|
career_interest = st.selectbox("Career Interest", ["Engineering", "Medicine", "Business", "Technology", "Arts"]) |
|
|
|
if st.form_submit_button("Submit"): |
|
if not name or not email: |
|
st.error("Please fill required fields (*)") |
|
else: |
|
request = { |
|
"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M:%S"), |
|
"name": name, |
|
"email": email, |
|
"career_interest": career_interest |
|
} |
|
save_career_request(request) |
|
st.success("Thank you! A mentor will contact you soon.") |
|
|
|
if st.sidebar.text_input("Admin Password", type="password") == ADMIN_PASS: |
|
st.sidebar.success("Admin Mode") |
|
st.header("π Career Guidance Dashboard") |
|
df = load_career_requests() |
|
if not df.empty: |
|
st.dataframe(df.sort_values("timestamp", ascending=False), |
|
column_config={"timestamp": "Date", "email": "Email", "career_interest": "Career Interest"}, |
|
use_container_width=True) |
|
else: |
|
st.info("No career guidance requests yet") |
|
|