-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathapp.py
More file actions
489 lines (408 loc) Β· 21.9 KB
/
app.py
File metadata and controls
489 lines (408 loc) Β· 21.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
import streamlit as st
import time
import os
from script_generator import script_generator
from manim_code_generator import manim_generator
# from v0 import manim_generator
from animation_creator import create_animation_from_code
st.set_page_config(
page_title="EduGen",
page_icon="π",
)
# Initialize session state
if "messages" not in st.session_state:
st.session_state.messages = []
if "processing" not in st.session_state:
st.session_state.processing = False
# Custom CSS for minimal ChatGPT-like interface
st.markdown("""
<style>
/* Hide default streamlit elements */
#MainMenu {visibility: hidden;}
footer {visibility: hidden;}
header {visibility: hidden;}
/* Main container styling */
.main .block-container {
padding-top: 2rem;
padding-bottom: 2rem;
max-width: 48rem;
margin: 0 auto;
}
/* Chat messages styling */
.stChatMessage {
background: transparent;
border: none;
padding: 1rem 0;
}
/* User message styling */
.stChatMessage[data-testid="user-message"] {
background-color: transparent;
}
/* Assistant message styling */
.stChatMessage[data-testid="assistant-message"] {
background-color: #f7f7f8;
border-radius: 0.75rem;
margin: 1rem 0;
padding: 1.5rem;
}
/* Input styling */
.stChatInputContainer {
background: white;
border: 1px solid #d1d5db;
border-radius: 0.75rem;
padding: 0.75rem;
margin-top: 1rem;
}
/* Sidebar styling */
.css-1d391kg {
background-color: #f9fafb;
border-right: 1px solid #e5e7eb;
}
/* Status container minimal styling */
.element-container div[data-testid="stStatus"] {
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 0.5rem;
}
/* Button styling */
.stButton > button {
background: #ffffff;
color: #374151;
border: 1px solid #d1d5db;
border-radius: 0.5rem;
padding: 0.5rem 1rem;
font-weight: 500;
}
.stButton > button:hover {
background: #f9fafb;
border-color: #9ca3af;
}
/* Expander styling */
.streamlit-expanderHeader {
background: transparent;
border: none;
color: #6b7280;
font-size: 0.875rem;
}
/* Minimal video container */
.video-container {
margin: 1rem 0;
border-radius: 0.75rem;
overflow: hidden;
border: 1px solid #e5e7eb;
}
/* Title styling */
h1 {
font-size: 2rem;
font-weight: 600;
color: #111827;
text-align: center;
margin-bottom: 0.5rem;
}
/* Subtitle styling */
.subtitle {
color: #6b7280;
text-align: center;
font-size: 1rem;
margin-bottom: 2rem;
}
</style>
""", unsafe_allow_html=True)
# Minimal header
st.title(" 𧬠EduGen π¬")
st.markdown('<div class="subtitle">Create educational science videos with AI</div>', unsafe_allow_html=True)
# Minimal sidebar with settings
with st.sidebar:
st.markdown("### Settings")
# Complexity level
complexity = st.selectbox(
"Complexity:",
["elementary", "middle-school", "high-school", "undergraduate", "advanced"],
index=2,
label_visibility="collapsed"
)
# Scientific domain (auto-detect from query, but user can override)
domain = st.selectbox(
"Domain:",
["auto-detect", "physics", "chemistry", "biology", "earth-science", "mathematics", "computer-science"],
index=0,
label_visibility="collapsed"
)
# Video quality
video_quality = st.selectbox(
"Quality:",
["medium", "high", "low"],
index=0,
label_visibility="collapsed"
)
st.markdown("---")
# Minimal example section
st.markdown("**Examples by Domain:**")
with st.expander("π¬ Physics"):
st.markdown("β’ Newton's laws of motion\nβ’ Wave properties and behavior\nβ’ Electromagnetic induction\nβ’ Quantum mechanics basics")
with st.expander("π§ͺ Chemistry"):
st.markdown("β’ Atomic structure and bonding\nβ’ Chemical reactions and equilibrium\nβ’ Periodic table trends\nβ’ Molecular geometry")
with st.expander("𧬠Biology"):
st.markdown("β’ Cell structure and function\nβ’ DNA replication and transcription\nβ’ Photosynthesis process\nβ’ Evolution and natural selection")
with st.expander("π Earth Science"):
st.markdown("β’ Plate tectonics\nβ’ Water cycle and weather\nβ’ Rock cycle and formation\nβ’ Climate change mechanisms")
with st.expander("π Mathematics"):
st.markdown("β’ Algebra and equations\nβ’ Geometric theorems\nβ’ Calculus fundamentals\nβ’ Statistics and probability")
with st.expander("π» Computer Science"):
st.markdown("β’ Algorithm complexity\nβ’ Data structures\nβ’ Programming concepts\nβ’ Machine learning basics")
if st.button("Clear chat", use_container_width=True):
st.session_state.messages = []
st.session_state.processing = False
st.rerun()
# Display chat messages
for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])
# Display video if available
if message.get("video_path"):
st.video(message["video_path"])
# Minimal download button
try:
with open(message["video_path"], 'rb') as video_file:
st.download_button(
label="Download video",
data=video_file.read(),
file_name=f"math_animation_{int(time.time())}.mp4",
mime="video/mp4",
key=f"download_{message.get('timestamp', 0)}",
use_container_width=False
)
except FileNotFoundError:
st.error("Video file not found. Please try regenerating.")
# Minimal structured content display
if message.get("educational_breakdown"):
with st.expander("View educational details"):
educational_breakdown = message["educational_breakdown"]
manim_structure = message.get("manim_structure", {})
# Educational content details
st.write(f"**Title:** {educational_breakdown.get('title', 'Math Concept')}")
st.write(f"**Target Audience:** {educational_breakdown.get('target_audience', 'N/A')}")
st.write(f"**Duration:** {educational_breakdown.get('estimated_total_duration', 'N/A')} seconds")
# Learning objectives
if educational_breakdown.get('learning_objectives'):
st.write("**Learning Objectives:**")
for obj in educational_breakdown['learning_objectives']:
st.write(f"β’ {obj}")
# Educational steps summary
if educational_breakdown.get('educational_steps'):
st.write("**Educational Steps:**")
for i, step in enumerate(educational_breakdown['educational_steps'][:3], 1):
st.write(f"{i}. **{step.get('step_title', f'Step {i}')}** ({step.get('duration_seconds', 'N/A')}s)")
st.write(f" {step.get('description', '')[:100]}...")
# Prerequisites and applications
col1, col2 = st.columns(2)
with col1:
if educational_breakdown.get('prerequisites'):
st.write("**Prerequisites:**")
for prereq in educational_breakdown['prerequisites']:
st.write(f"β’ {prereq}")
with col2:
if educational_breakdown.get('real_world_applications'):
st.write("**Applications:**")
for app in educational_breakdown['real_world_applications']:
st.write(f"β’ {app}")
# Common misconceptions
if educational_breakdown.get('common_misconceptions'):
st.write("**Common Misconceptions:**")
for misconception in educational_breakdown['common_misconceptions']:
st.write(f"β οΈ {misconception}")
# Animation details
if manim_structure and manim_structure.get('animation_steps'):
st.write("**Animation Steps:**")
for step in manim_structure['animation_steps'][:3]:
objects = ', '.join(step.get('manim_objects', [])[:3])
animations = ', '.join(step.get('animations', [])[:3])
st.write(f"β’ {step.get('description', 'Animation step')}")
st.write(f" Objects: {objects}")
st.write(f" Animations: {animations}")
# Chat input - simplified
if prompt := st.chat_input("What scientific concept would you like me to explain?"):
if not st.session_state.processing:
# Add user message to chat history
st.session_state.messages.append({
"role": "user",
"content": prompt,
"timestamp": time.time()
})
# Display user message
with st.chat_message("user"):
st.markdown(prompt)
# Process the request
with st.chat_message("assistant"):
message_placeholder = st.empty()
status_container = st.container()
st.session_state.processing = True
try:
# Step 1: Generate complete video plan (both educational and Manim structure)
with status_container:
status = st.status("Understanding your request...", expanded=False)
with status:
st.write("Analyzing scientific concept...")
enhanced_prompt = f"""
Create an educational video animation about: {prompt}
Requirements:
- Complexity level: {complexity}
- Scientific domain: {domain}
- Generate comprehensive educational content with clear step-by-step explanations
- Include detailed visual descriptions and animation plans
- Create engaging learning objectives and real-world applications
- Design content suitable for animated video format with proper timing
- Include scientific equations, diagrams, models, and interactive elements
- Use appropriate scientific terminology and concepts
"""
# Use the complete video plan generator
video_plan = script_generator.generate_complete_video_plan(enhanced_prompt)
# Print video plan to terminal for debugging
if video_plan:
print("\n" + "="*60)
print("π VIDEO PLAN RECEIVED IN APP.PY")
print("="*60)
print(f"Topic: {video_plan.get('topic', 'N/A')}")
educational_breakdown = video_plan.get("educational_breakdown", {})
print(f"Title: {educational_breakdown.get('title', 'N/A')}")
print(f"Educational Steps: {len(educational_breakdown.get('educational_steps', []))}")
print(f"Learning Objectives: {len(educational_breakdown.get('learning_objectives', []))}")
manim_structure = video_plan.get("manim_structure", {})
if manim_structure:
print(f"Animation Steps: {len(manim_structure.get('animation_steps', []))}")
generation_metadata = video_plan.get("generation_metadata", {})
print(f"Generation Metadata: {generation_metadata}")
print("="*60)
if video_plan:
educational_breakdown = video_plan.get("educational_breakdown", {})
manim_structure = video_plan.get("manim_structure", {})
generation_metadata = video_plan.get("generation_metadata", {})
st.write("β Educational content generated")
st.write(f"β Generated {len(educational_breakdown.get('educational_steps', []))} learning steps")
if manim_structure:
st.write(f"β Generated {len(manim_structure.get('animation_steps', []))} animation steps")
# Show completion status
stages_completed = generation_metadata.get("stages_completed", [])
if "educational_breakdown" in stages_completed:
st.write("β Stage 1: Educational breakdown completed")
if "manim_structure" in stages_completed:
st.write("β Stage 2: Manim structure completed")
else:
st.write("β Failed to generate content")
raise Exception("Content generation failed")
# Step 2: Generate Manim code
with status_container:
status.update(label="Creating animation code...", state="running")
with status:
st.write("Converting to Manim animation code...")
# Pass the complete video plan directly to Manim generator
st.write("Using complete video plan...")
if educational_breakdown.get('educational_steps'):
st.write(f"π Educational steps: {len(educational_breakdown.get('educational_steps', []))}")
if educational_breakdown.get('learning_objectives'):
st.write(f"π― Learning objectives: {len(educational_breakdown.get('learning_objectives', []))}")
st.write(f"π·οΈ Domain: {domain}")
# Pass the complete video plan to Manim generator
manim_code = manim_generator.generate_3b1b_manim_code(video_plan)
# Print Manim code info to terminal
if manim_code:
print("\n" + "="*60)
print("π MANIM CODE GENERATED IN APP.PY")
print("="*60)
print(f"Code length: {len(manim_code)} characters")
newline_char = '\n'
print(f"Lines of code: {len(manim_code.split(newline_char))}")
print(f"Contains 'def construct': {'β
' if 'def construct' in manim_code else 'β'}")
print(f"Contains 'from manim import': {'β
' if 'from manim import' in manim_code else 'β'}")
print("="*60)
if manim_code:
st.write("β Animation code generated")
st.write(f"β Code length: {len(manim_code)} characters")
# Show code analysis
if 'def construct' in manim_code:
st.write("β Contains construct method")
if 'from manim import' in manim_code:
st.write("β Contains proper imports")
if any(color in manim_code for color in ['BLUE_E', 'TEAL', 'YELLOW', 'RED_B']):
st.write("β Uses 3Blue1Brown color palette")
# Check for syntax validation indicators in the code
if "SYNTAX ERROR DETECTED" in manim_code:
st.warning("β οΈ Code has syntax issues but will attempt to render")
elif "# REMOVED:" in manim_code:
st.info("βΉοΈ Code was automatically cleaned (removed invalid elements)")
else:
st.success("β
Code passed all validation checks")
else:
st.write("β Failed to generate animation code")
raise Exception("Animation code generation failed")
# Step 3: Render animation
with status_container:
status.update(label="Rendering animation...", state="running")
with status:
st.write("Rendering video...")
try:
video_path = create_animation_from_code(manim_code)
if video_path and os.path.exists(video_path):
st.write("β Animation rendered successfully")
status.update(label="β Complete", state="complete")
else:
st.error("β Failed to render animation")
st.error("This may be due to syntax errors in the generated code.")
st.info("π‘ The system will automatically improve code generation for future requests.")
raise Exception("Animation rendering failed")
except Exception as render_error:
st.error(f"β Rendering error: {str(render_error)}")
st.error("The generated code had issues that prevented successful rendering.")
st.info("π‘ This feedback helps improve the AI code generator.")
raise
# Success response - enhanced with more details
# Extract clean topic title - remove the enhanced prompt part
clean_prompt = prompt.split('\n')[0].strip() # Get just the first line (original user prompt)
title = educational_breakdown.get('title', clean_prompt)
abstract = educational_breakdown.get('abstract', educational_breakdown.get('summary', 'The animation includes step-by-step visual explanations with mathematical notation and smooth transitions.'))
learning_objectives = educational_breakdown.get('learning_objectives', [])
duration = educational_breakdown.get('estimated_total_duration', 'N/A')
target_audience = educational_breakdown.get('target_audience', 'general')
response_content = f"""I've created an animated explanation of **{title}**.
{abstract}
**Learning Objectives:**
{chr(10).join('β’ ' + obj for obj in learning_objectives[:3])}
**Target Audience:** {target_audience}
**Duration:** {duration} seconds
The animation includes {len(educational_breakdown.get('educational_steps', []))} educational steps with corresponding visual animations."""
# Add prerequisites if any
if educational_breakdown.get('prerequisites'):
prereq_text = ', '.join(educational_breakdown['prerequisites'][:2])
response_content += f"\n\n**Prerequisites:** {prereq_text}"
# Add applications if any
if educational_breakdown.get('real_world_applications'):
app_text = ', '.join(educational_breakdown['real_world_applications'][:2])
response_content += f"\n\n**Real-world applications:** {app_text}"
message_placeholder.markdown(response_content)
# Add assistant message to chat history
st.session_state.messages.append({
"role": "assistant",
"content": response_content,
"video_path": video_path,
"educational_breakdown": educational_breakdown,
"manim_structure": manim_structure,
"video_plan": video_plan,
"timestamp": time.time()
})
# Force app refresh to show the new video immediately
st.rerun()
except Exception as e:
error_message = f"""I encountered an issue while creating your animation: {str(e)}
Please try rephrasing your question or ensure the mathematical concept is specific enough."""
message_placeholder.markdown(error_message)
# Add error message to chat history
st.session_state.messages.append({
"role": "assistant",
"content": error_message,
"timestamp": time.time()
})
finally:
st.session_state.processing = False
# Minimal footer
st.markdown("---")
st.markdown("*EduGen - AI-powered science education*")