""" 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("""
🎬 How it works: Groq AI auto-generates a cinematic video prompt from the experiment data → fal.ai MiniMax Hailuo-02 renders a real 6-second HD experiment video.

💰 Cost: ~$0.27 per video  |  🎁 Free credits: $15 on signup at fal.ai (~55 free videos)  |  ⏱ Wait time: ~3-4 minutes
""") vid_btn = gr.Button("🎬 Generate AI Experiment Video (~3-4 min)", variant="primary") vid_status = gr.Markdown() video_out = gr.Video(label="🎬 AI Generated Experiment Video", interactive=False) vid_btn.click(generate_video, inputs=exp_dd, outputs=[video_out, vid_status]) # ── Tab 3: Audio ────────────────────────────────────────────────────── with gr.Tab("🔊 Audio Narration"): gr.Markdown("Generate a **spoken audio explanation** of the experiment (free, instant via gTTS).") audio_btn = gr.Button("🔊 Generate Audio Narration", variant="primary") audio_status = gr.Markdown() audio_out = gr.Audio(label="🔊 Audio Narration", type="filepath") audio_btn.click(get_audio, inputs=exp_dd, outputs=[audio_out, audio_status]) # ── Tab 4: AI Explanation ───────────────────────────────────────────── with gr.Tab("🤖 AI Explanation"): gr.Markdown("Get a **detailed AI explanation** of the science behind this experiment (via Groq).") exp_btn = gr.Button("🤖 Explain This Experiment", variant="primary") exp_md = gr.Markdown("Click the button above to generate an explanation.") exp_btn.click(get_explanation, inputs=exp_dd, outputs=exp_md) # ── Tab 5: Quiz ─────────────────────────────────────────────────────── with gr.Tab("📝 Quiz"): gr.Markdown("Test your understanding with **5 AI-generated MCQs** (via Groq).") quiz_btn = gr.Button("🚀 Start Quiz", variant="primary") q_md = gr.Markdown("Click **Start Quiz** to begin!") fb_md = gr.Markdown() with gr.Row(): b_a = gr.Button(visible=False) b_b = gr.Button(visible=False) with gr.Row(): b_c = gr.Button(visible=False) b_d = gr.Button(visible=False) qouts = [q_md, b_a, b_b, b_c, b_d, fb_md] quiz_btn.click(start_quiz, inputs=exp_dd, outputs=qouts) b_a.click(answer, inputs=b_a, outputs=qouts) b_b.click(answer, inputs=b_b, outputs=qouts) b_c.click(answer, inputs=b_c, outputs=qouts) b_d.click(answer, inputs=b_d, outputs=qouts) # ── Tab 6: All Experiments ──────────────────────────────────────────── with gr.Tab("📊 All Experiments"): summary = "" for ch in sorted(set(e["chapter"] for e in EXPERIMENTS)): exps = [e for e in EXPERIMENTS if e["chapter"] == ch] summary += f"\n### Chapter {ch} — {len(exps)} experiments\n" for e in exps: summary += f"- {e['title']} *(p.{e['page']})*\n" gr.Markdown(summary) gr.HTML("""
🔬 AI Science Lab · NCERT Class 10 · Videos by fal.ai + MiniMax Hailuo-02 · Prompts by Groq Llama 3.3-70B · Audio by gTTS
""") chapter_dd.change(on_chapter_change, inputs=chapter_dd, outputs=[exp_dd, title_md, mat_box, step_box, safe_box, out_box]) exp_dd.change(load_experiment, inputs=exp_dd, outputs=[title_md, mat_box, step_box, safe_box, out_box]) if __name__ == "__main__": demo.launch(share=False, css=CSS, theme=gr.themes.Soft())