AISCienceLab / ai_engine.py
swapnakumbar12's picture
Update ai_engine.py
36177f4 verified
Raw
History Blame Contribute Delete
6.75 kB
"""
AI Engine – uses Groq (free tier) for LLM calls + gTTS for audio.
Setup on Hugging Face Spaces:
Settings β†’ Repository secrets β†’ Add:
Name : GROQ_API_KEY
Value: your key from https://console.groq.com (free, no credit card)
Free Groq model used: llama-3.3-70b-versatile
"""
import os
import json
import re
from groq import Groq
# ── Client ───────────────────────────────────────────────────────────────────
def get_client() -> Groq:
api_key = os.environ.get("GROQ_API_KEY", "")
if not api_key:
raise ValueError(
"GROQ_API_KEY secret not set.\n"
"Go to: Space Settings β†’ Repository secrets β†’ add GROQ_API_KEY\n"
"Get a free key at: https://console.groq.com"
)
return Groq(api_key=api_key)
def _chat(prompt: str, max_tokens: int = 1024) -> str:
"""Single-turn chat with llama-3.3-70b on Groq."""
client = get_client()
resp = client.chat.completions.create(
model="llama-3.3-70b-versatile",
messages=[{"role": "user", "content": prompt}],
max_tokens=max_tokens,
temperature=0.7,
)
return resp.choices[0].message.content
# ── Experiment Explanation ────────────────────────────────────────────────────
def generate_experiment_explanation(exp: dict) -> str:
prompt = f"""You are an enthusiastic science teacher for Class 10 students (age ~15).
Experiment: {exp['title']}
Materials: {exp['materials']}
Steps: {exp['steps']}
Outcome: {exp['outcome']}
Write a clear, engaging explanation (250–350 words) covering:
1. **What is happening scientifically** – the core concept
2. **Why it works** – the chemistry/biology/physics behind it
3. **Real-world connection** – where students see this in daily life
4. **Key formula or equation** if applicable (use simple notation)
Use friendly, enthusiastic language suitable for a 15-year-old."""
return _chat(prompt, max_tokens=700)
# ── Quiz Generator ────────────────────────────────────────────────────────────
def generate_quiz_questions(exp: dict) -> list:
prompt = f"""You are a science quiz creator for Class 10 students.
Experiment: {exp['title']}
Materials: {exp['materials']}
Steps: {exp['steps']}
Outcome: {exp['outcome']}
Generate exactly 5 multiple-choice questions testing understanding of this experiment.
Return ONLY a valid JSON array β€” no markdown fences, no extra text β€” in this exact format:
[
{{
"question": "Question text here?",
"options": ["A) Option 1", "B) Option 2", "C) Option 3", "D) Option 4"],
"answer": "A) Option 1",
"explanation": "Brief reason why this answer is correct."
}}
]
Cover: observation, concept, reasoning, safety, and real-world application."""
raw = _chat(prompt, max_tokens=1400)
raw = re.sub(r"```json|```", "", raw).strip()
# Find the JSON array inside the response
match = re.search(r"\[.*\]", raw, re.DOTALL)
if match:
raw = match.group(0)
try:
questions = json.loads(raw)
if isinstance(questions, list) and questions:
return questions
except json.JSONDecodeError:
pass
# Fallback single question
return [{
"question": f"What is the main observation in '{exp['title']}'?",
"options": [
f"A) {exp['outcome'][:70]}",
"B) No reaction takes place",
"C) The mixture turns blue",
"D) Heat is always absorbed",
],
"answer": f"A) {exp['outcome'][:70]}",
"explanation": exp["outcome"],
}]
# ── Video Script Generator ────────────────────────────────────────────────────
def generate_video_script(exp: dict) -> str:
prompt = f"""You are a scriptwriter creating a 2-minute science video for Class 10 students.
Experiment: {exp['title']}
Materials: {exp['materials']}
Procedure: {exp['steps']}
Safety: {exp['safety']}
Outcome: {exp['outcome']}
Write a complete VIDEO SCRIPT with these six scenes:
🎬 SCENE 1 – HOOK (0:00–0:15)
[Camera/action]
Narrator: "..."
🎬 SCENE 2 – MATERIALS (0:15–0:30)
[Visual: each material shown]
Narrator: "..."
🎬 SCENE 3 – SAFETY BRIEFING (0:30–0:40)
[Safety icons / lab coat]
Narrator: "..."
🎬 SCENE 4 – STEP-BY-STEP PROCEDURE (0:40–1:20)
[Camera angle + action per step]
Narrator: "..."
🎬 SCENE 5 – OBSERVATION & RESULT (1:20–1:45)
[Close-up of the change/outcome]
Narrator: "..."
🎬 SCENE 6 – SCIENCE EXPLANATION (1:45–2:00)
[Animation or diagram]
Narrator: "..."
Include [ANIMATION: ...], [CLOSE-UP: ...], and [TEXT ON SCREEN: ...] cues.
Narrator text must be enthusiastic and clear for 15-year-old students."""
return _chat(prompt, max_tokens=1200)
# ── Text-to-Speech ────────────────────────────────────────────────────────────
def text_to_speech_explanation(exp: dict, script: str = None):
"""
Converts narrator lines from the script to MP3 using gTTS (free, no API key).
Returns the path to the MP3 file, or None on failure.
"""
try:
from gtts import gTTS
import tempfile
if script:
lines = []
for line in script.split("\n"):
line = line.strip()
if line.lower().startswith("narrator:"):
text = line[9:].strip().strip('"').strip("'")
if text:
lines.append(text)
audio_text = " ".join(lines) if lines else script
else:
audio_text = (
f"Welcome to the AI Science Lab. Today we explore: {exp['title']}. "
f"Materials needed: {exp['materials']}. "
f"Procedure: {exp['steps']}. "
f"Safety: {exp['safety']}. "
f"Expected outcome: {exp['outcome']}."
)
audio_text = audio_text[:3000] # gTTS limit
tts = gTTS(text=audio_text, lang="en", slow=False)
tmp = tempfile.NamedTemporaryFile(delete=False, suffix=".mp3")
tts.save(tmp.name)
return tmp.name
except ImportError:
return None
except Exception as e:
print(f"TTS error: {e}")
return None