import os import gradio as gr import requests import json import time # Test HF_TOKEN HF_TOKEN = os.getenv("HF_TOKEN") if not HF_TOKEN: print("Error: HF_TOKEN not set. Please set your Hugging Face API token.") else: print("HF_TOKEN loaded successfully.") # ๐ŸŒ Web search function (unchanged) def search_web(query): try: url = "https://api.duckduckgo.com/" params = {"q": query, "format": "json", "no_html": 1, "skip_disambig": 1} response = requests.get(url, params=params) data = response.json() if data.get("AbstractText"): return data["AbstractText"] elif data.get("RelatedTopics"): topics = [t.get("Text", "") for t in data["RelatedTopics"] if "Text" in t] return " ".join(topics[:3]) else: return "No useful information found." except Exception as e: return f"Search error: {e}" # ๐Ÿง  Memory setup (unchanged) MEMORY_FILE = "memory.json" def load_memory(): if os.path.exists(MEMORY_FILE): with open(MEMORY_FILE, "r") as f: return json.load(f) return [] def save_memory(memory): with open(MEMORY_FILE, "w") as f: json.dump(memory, f) memory = load_memory() # ๐Ÿ’ฌ Chat function (unchanged) def chat_with_model(message, history, context): if not isinstance(history, list): history = [] if message.lower().startswith("search "): query = message[7:] search_result = search_web(query) timestamp = time.strftime("%H:%M") history.append((f"{message} {timestamp}", f"๐Ÿ”Ž Here's what I found online:\n\n{search_result} {timestamp}")) save_memory(history) return history, history conversation = [ {"role": "system", "content": ( "You are EduAI, a multilingual educational AI assistant created by a Sri Lankan student named Wafa Fazly. " "When solving math, explain step-by-step like a professional tutor. " "Use Markdown and LaTeX formatting for equations (use \\[ and \\]). " "Keep answers neat, structured, and student-friendly." )} ] for past_user, past_bot in history[-5:]: conversation.append({"role": "user", "content": past_user.split(' {timestamp}", f"{reply} {timestamp}")) save_memory(history) return history, history except Exception as e: print("Backend Error:", e) timestamp = time.strftime("%H:%M") error_msg = f"๐Ÿ˜… EduAI is having trouble connecting right now. Check your HF_TOKEN or try again later! {timestamp}" history.append((f"{message} {timestamp}", error_msg)) return history, history # ๐Ÿ“˜ Sidebar context update (unchanged) def update_context(choice): if not choice: return "๐Ÿ“˜ **You are in General Mode.** Ask EduAI anything about your studies!" return f"๐Ÿ“˜ **You selected {choice} mode.** Ask anything related to this topic!" # ๐Ÿงน Clear chat memory (unchanged) def clear_memory(): if os.path.exists(MEMORY_FILE): os.remove(MEMORY_FILE) return [], "๐Ÿงน Chat memory cleared! Start fresh." # ๐Ÿ–Œ Custom CSS for light theme, full-screen, responsive, cyan buttons with animations custom_css = """ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&display=swap'); body { font-family: 'Inter', sans-serif; margin: 0; padding: 0; background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%); color: #334155; height: 100vh; overflow: hidden; } .gradio-container { background: transparent; box-shadow: none; height: 100vh; } .header { position: fixed; top: 0; left: 0; right: 0; background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(10px); padding: 15px 20px; display: flex; justify-content: space-between; align-items: center; z-index: 1000; border-bottom: 1px solid #e2e8f0; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .header-title { font-size: 24px; font-weight: 600; color: #00BCD4; text-shadow: 0 0 10px rgba(0, 188, 212, 0.3); } .toggle-btn { background: #00BCD4; color: white; border: none; border-radius: 8px; padding: 10px; cursor: pointer; font-size: 18px; transition: all 0.3s ease; box-shadow: 0 2px 6px rgba(0, 188, 212, 0.3); } .toggle-btn:hover { background: #0097a7; transform: scale(1.1); box-shadow: 0 4px 12px rgba(0, 188, 212, 0.4); } .sidebar { position: fixed; top: 70px; left: 0; width: 300px; height: calc(100vh - 70px); background: rgba(255, 255, 255, 0.95); backdrop-filter: blur(15px); padding: 20px; border-radius: 0 12px 12px 0; box-shadow: 4px 0 12px rgba(0,0,0,0.1); border: 1px solid #e2e8f0; border-left: none; z-index: 999; transform: translateX(-100%); transition: transform 0.3s ease; } .sidebar.visible { transform: translateX(0); } .menu-title { font-size: 18px; font-weight: 500; margin-bottom: 15px; color: #00BCD4; } .accordion { border-radius: 10px; margin-bottom: 10px; box-shadow: 0 2px 6px rgba(0,0,0,0.1); } .accordion-header { background: #f1f5f9; color: #334155; border-radius: 10px; padding: 12px; font-weight: 500; cursor: pointer; transition: all 0.3s ease; } .accordion-header:hover { background: #00BCD4; color: white; transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0, 188, 212, 0.3); } .main-chat { position: fixed; top: 70px; left: 0; right: 0; bottom: 0; padding: 20px; background: transparent; display: flex; flex-direction: column; z-index: 1; } .context-box { background: rgba(255, 255, 255, 0.9); padding: 15px; border-radius: 10px; box-shadow: 0 2px 6px rgba(0,0,0,0.1); margin-bottom: 15px; border: 1px solid #e2e8f0; color: #334155; font-size: 14px; } .chatbox { flex: 1; border-radius: 10px; box-shadow: 0 2px 6px rgba(0,0,0,0.1); background: rgba(255, 255, 255, 0.9); border: 1px solid #e2e8f0; padding: 15px; overflow-y: auto; } .message { display: flex; margin: 15px 0; animation: fadeIn 0.5s ease-in; } @keyframes fadeIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } } .message.user { justify-content: flex-end; } .message.ai { justify-content: flex-start; } .message .avatar { width: 40px; height: 40px; border-radius: 50%; margin: 0 10px; background: #00BCD4; display: flex; align-items: center; justify-content: center; color: white; font-weight: 600; font-size: 16px; } .message.ai .avatar { background: #e2e8f0; color: #334155; } .message .bubble { max-width: 70%; padding: 12px 16px; border-radius: 18px; position: relative; } .message.user .bubble { background: #00BCD4; color: white; } .message.ai .bubble { background: #f8fafc; color: #334155; border: 1px solid #e2e8f0; } .timestamp { font-size: 10px; opacity: 0.7; margin-top: 5px; display: block; } .typing { display: none; font-style: italic; color: #64748b; padding: 10px; animation: pulse 1.5s infinite; } @keyframes pulse { 0%, 100% { opacity: 0.5; } 50% { opacity: 1; } } .input-row { margin-top: 15px; } .chat-input { width: 100%; border-radius: 20px; padding: 12px 16px; border: 1px solid #cbd5e1; background: rgba(255, 255, 255, 0.9); color: #334155; transition: all 0.3s ease; font-size: 14px; } .chat-input:focus { border-color: #00BCD4; outline: none; box-shadow: 0 0 0 2px rgba(0, 188, 212, 0.2); } .btn-clear { background: #00BCD4; color: white; border-radius: 10px; padding: 10px 15px; border: none; font-weight: 500; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 2px 6px rgba(0, 188, 212, 0.3); } .btn-clear:hover { background: #0097a7; transform: scale(1.05); box-shadow: 0 4px 12px rgba(0, 188, 212, 0.4); } .about-text { font-size: 14px; color: #64748b; line-height: 1.5; } .radio { margin: 10px 0; } .radio label { color: #334155; } @media (max-width: 768px) { .sidebar { width: 250px; } .header-title { font-size: 20px; } } """ # ๐ŸŽจ Gradio Interface with light theme, full-screen, responsive, cyan buttons with animations with gr.Blocks(theme=gr.themes.Base(), css=custom_css) as iface: with gr.Row(elem_classes="header"): gr.Markdown("# ๐Ÿค– **EduAI**", elem_classes="header-title") toggle_btn = gr.Button("โ˜ฐ", elem_classes="toggle-btn") sidebar = gr.Column(scale=1, elem_classes="sidebar", visible=False) with sidebar: gr.Markdown("### ๐Ÿงญ **Menu**", elem_classes="menu-title") with gr.Accordion("๐Ÿ“š Subject Tutor", open=False): subj = gr.Radio( ["Science ๐Ÿงช", "ICT ๐Ÿ’ป", "English ๐Ÿ“˜", "Mathematics โž—"], label="Choose a subject", type="index" ) with gr.Accordion("๐Ÿ—“ Study Planner", open=False): planner = gr.Radio( ["View Plan ๐Ÿ“…", "Add Task โœ๏ธ", "Study Tips ๐Ÿ’ก"], label="Planner Options", type="index" ) with gr.Accordion("๐ŸŒ Languages", open=False): lang = gr.Radio( ["Learn Sinhala ๐Ÿ‡ฑ๐Ÿ‡ฐ", "Learn Tamil ๐Ÿ‡ฎ๐Ÿ‡ณ", "Learn English ๐Ÿ‡ฌ๐Ÿ‡ง", "Learn Spanish ๐Ÿ‡ช๐Ÿ‡ธ"], label="Language Options", type="index" ) with gr.Accordion("โš™๏ธ Settings", open=False): clear_btn = gr.Button("๐Ÿงน Clear Memory", elem_classes="btn-clear") with gr.Accordion("๐Ÿ‘ฉโ€๐ŸŽ“ About", open=False): gr.Markdown(""" EduAI โ€“ developed by **Wafa Fazly** using a pre-trained AI model. Helps learners understand **Science, ICT, English, and more** in a simple, friendly way! ๐Ÿ’ฌ """, elem_classes="about-text") with gr.Column(scale=4, elem_classes="main-chat"): context_display = gr.Markdown( "๐Ÿ“˜ **You are in General Mode.** Ask EduAI anything about your studies!", elem_classes="context-box" ) chatbot = gr.Chatbot( label="", height=500, render_markdown=True, type="messages", latex_delimiters=[{"left": "$$", "right": "$$", "display": True}, {"left": "\\[", "right": "\\]", "display": True}] ) typing_indicator = gr.Markdown("", elem_classes="typing") with gr.Row(elem_classes="input-row"): msg = gr.Textbox(placeholder="Message EduAI...", elem_classes="chat-input", show_label=False) # Event Handlers toggle_btn.click(lambda: gr.update(visible=not sidebar.visible), outputs=sidebar) subj.change(update_context, inputs=subj, outputs=context_display) planner.change(update_context, inputs=planner, outputs=context_display) lang.change(update_context, inputs=lang, outputs=context_display) msg.submit(lambda: "EduAI is typing...", outputs=typing_indicator).then( chat_with_model, inputs=[msg, chatbot, context_display], outputs=[chatbot, chatbot] ).then(lambda: "", outputs=typing_indicator) clear_btn.click(clear_memory, outputs=[chatbot, context_display]) iface.launch()