""" AI Science Lab – Hugging Face Space NCERT Class 10 Science: 75 experiments with fal.ai AI videos, audio narration, AI explanations and smart quizzes. """ import os import gradio as gr from experiment_data import EXPERIMENTS from ai_engine import generate_experiment_explanation, generate_quiz_questions, text_to_speech_explanation from fal_video_generator import generate_experiment_video # ── Helpers ─────────────────────────────────────────────────────────────────── def get_chapter_labels(): return [f"Chapter {c}" for c in sorted(set(e["chapter"] for e in EXPERIMENTS))] def get_experiments_for_chapter(chapter_label): ch = int(chapter_label.split(" ")[1]) return [e["title"] for e in EXPERIMENTS if e["chapter"] == ch] def get_experiment(title): return next((e for e in EXPERIMENTS if e["title"] == title), None) # ── Tab 1: Details ──────────────────────────────────────────────────────────── def load_experiment(title): exp = get_experiment(title) if not exp: return "### Select an experiment", "", "", "", "" mats = "\n".join(f"• {m.strip()}" for m in exp["materials"].split(",")) parts = [s.strip() for s in exp["steps"].split(".") if s.strip()] steps = "\n".join(f"{i+1}. {s}" for i, s in enumerate(parts)) return ( f"### {exp['title']}\n*Page {exp['page']} · Chapter {exp['chapter']}*", mats, steps, exp["safety"], exp["outcome"] ) def on_chapter_change(chapter_label): exps = get_experiments_for_chapter(chapter_label) first = exps[0] if exps else None det = load_experiment(first) if first else ("", "", "", "", "") return gr.Dropdown(choices=exps, value=first), *det # ── Tab 2: fal.ai Video ─────────────────────────────────────────────────────── def generate_video(title, progress=gr.Progress()): exp = get_experiment(title) if not exp: return None, "⚠️ Please select an experiment first." progress(0.05, desc="Generating video prompt with Groq LLM...") progress(0.15, desc="Submitting to fal.ai MiniMax Hailuo-02...") video_path, status = generate_experiment_video(exp) progress(1.0, desc="Done!") return video_path, status # ── Tab 3: Audio ────────────────────────────────────────────────────────────── def get_audio(title): exp = get_experiment(title) if not exp: return None, "⚠️ Please select an experiment first." audio = text_to_speech_explanation(exp) status = f"🔊 Audio ready for: **{exp['title']}**" if audio else "❌ Audio generation failed." return audio, status # ── Tab 4: AI Explanation ───────────────────────────────────────────────────── def get_explanation(title): exp = get_experiment(title) if not exp: return "⚠️ Please select an experiment first." try: return generate_experiment_explanation(exp) except Exception as e: return f"❌ Error: {e}" # ── Tab 5: Quiz ─────────────────────────────────────────────────────────────── _quiz = {} def start_quiz(title): exp = get_experiment(title) if not exp: empty = gr.update(visible=False) return "⚠️ Select an experiment first.", empty, empty, empty, empty, "" try: qs = generate_quiz_questions(exp) except Exception as e: empty = gr.update(visible=False) return f"❌ {e}", empty, empty, empty, empty, "" _quiz.update({"questions": qs, "current": 0, "score": 0}) return _render_q() def _render_q(): qs = _quiz.get("questions", []) idx = _quiz.get("current", 0) if idx >= len(qs): s = _quiz.get("score", 0); t = len(qs) pct = int(s / t * 100) if t else 0 em = "🏆" if pct == 100 else ("👍" if pct >= 70 else "📚") msg = f"## {em} Quiz Complete!\n\n**Score: {s}/{t} ({pct}%)**\n\n" msg += "Perfect!" if pct==100 else ("Great job!" if pct>=70 else "Review and try again!") return msg, gr.update(visible=False), gr.update(visible=False), \ gr.update(visible=False), gr.update(visible=False), "" q = qs[idx]; opts = q["options"] hdr = f"**Question {idx+1} of {len(qs)}**\n\n{q['question']}" btns = [gr.update(visible=True, value=opts[i]) if i < len(opts) else gr.update(visible=False) for i in range(4)] return hdr, *btns, "" def answer(choice): qs = _quiz.get("questions", []) idx = _quiz.get("current", 0) if idx >= len(qs): return _render_q() q = qs[idx]; correct = q["answer"].strip() if choice.strip() == correct: _quiz["score"] = _quiz.get("score", 0) + 1 fb = f"✅ **Correct!** {q.get('explanation','')}" else: fb = f"❌ **Incorrect.** Answer: **{correct}**\n\n{q.get('explanation','')}" _quiz["current"] = idx + 1 q_text, b1, b2, b3, b4, _ = _render_q() return q_text, b1, b2, b3, b4, fb # ── CSS ─────────────────────────────────────────────────────────────────────── CSS = """ @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;600;700&display=swap'); *, body, .gradio-container { font-family: 'Space Grotesk', sans-serif !important; } .banner { background: linear-gradient(135deg, #0f0c29, #302b63, #24243e); border-radius: 20px; padding: 2.5rem 2rem; text-align: center; margin-bottom: 1.5rem; border: 1px solid rgba(255,255,255,0.08); } .banner h1 { color: #fff; font-size: 2.4rem; font-weight: 700; margin: 0; } .banner h1 span { color: #7c3aed; } .banner p { color: #a5b4fc; margin: 0.5rem 0 0; } .chip { display: inline-block; background: rgba(124,58,237,0.15); border: 1px solid rgba(124,58,237,0.4); border-radius: 999px; padding: 0.2rem 0.8rem; font-size: 0.82rem; color: #c4b5fd; margin: 0.4rem 0.2rem 0; } .info-box { background: rgba(124,58,237,0.08); border: 1px solid rgba(124,58,237,0.3); border-radius: 10px; padding: 0.8rem 1rem; margin-bottom: 1rem; font-size: 0.9rem; } """ # ── Build UI ────────────────────────────────────────────────────────────────── initial_chapter = "Chapter 1" initial_exps = get_experiments_for_chapter(initial_chapter) initial_exp = initial_exps[0] init_det = load_experiment(initial_exp) with gr.Blocks(title="AI Science Lab") as demo: gr.HTML("""
""") with gr.Row(): chapter_dd = gr.Dropdown(choices=get_chapter_labels(), value=initial_chapter, label="📚 Chapter", scale=1, interactive=True) exp_dd = gr.Dropdown(choices=initial_exps, value=initial_exp, label="🧪 Experiment", scale=4, interactive=True) with gr.Tabs(): # ── Tab 1: Details ──────────────────────────────────────────────────── with gr.Tab("📋 Experiment Details"): title_md = gr.Markdown(init_det[0]) with gr.Row(): mat_box = gr.Textbox(label="🧰 Materials", value=init_det[1], lines=7, interactive=False) step_box = gr.Textbox(label="📝 Steps", value=init_det[2], lines=7, interactive=False) with gr.Row(): safe_box = gr.Textbox(label="⚠️ Safety", value=init_det[3], interactive=False) out_box = gr.Textbox(label="✅ Outcome", value=init_det[4], interactive=False) # ── Tab 2: fal.ai Video ─────────────────────────────────────────────── with gr.Tab("🎬 AI Experiment Video"): gr.HTML("""