Spaces:
Sleeping
Sleeping
#!/usr/bin/env python3 | |
""" | |
Theorem Explanation Agent - Gradio Interface for Hugging Face Spaces | |
""" | |
import os | |
import sys | |
import json | |
import asyncio | |
import time | |
import random | |
from typing import Dict, Any, Tuple | |
from pathlib import Path | |
import gradio as gr | |
# Add project root to path | |
project_root = Path(__file__).parent | |
sys.path.insert(0, str(project_root)) | |
# Environment setup | |
DEMO_MODE = os.getenv("DEMO_MODE", "true").lower() == "true" | |
video_generator = None | |
CAN_IMPORT_DEPENDENCIES = True | |
def setup_environment(): | |
"""Setup environment for HF Spaces.""" | |
print("๐ Setting up Theorem Explanation Agent...") | |
gemini_keys = os.getenv("GEMINI_API_KEY", "") | |
if gemini_keys: | |
key_count = len([k.strip() for k in gemini_keys.split(',') if k.strip()]) | |
print(f"โ Found {key_count} Gemini API key(s)") | |
return True | |
else: | |
print("โ ๏ธ No Gemini API keys found - running in demo mode") | |
return False | |
def initialize_video_generator(): | |
"""Initialize video generator.""" | |
global video_generator, CAN_IMPORT_DEPENDENCIES | |
try: | |
if DEMO_MODE: | |
return "โ Demo mode enabled - No heavy dependencies loaded" | |
# Check if we have API keys before importing heavy dependencies | |
gemini_keys = os.getenv("GEMINI_API_KEY", "") | |
if not gemini_keys: | |
return "โ ๏ธ No API keys found - running in demo mode (prevents model downloads)" | |
# Try to import but handle missing dependencies gracefully | |
try: | |
from generate_video import VideoGenerator | |
from mllm_tools.litellm import LiteLLMWrapper | |
except ImportError as import_err: | |
print(f"Heavy dependencies not available: {import_err}") | |
return "โ ๏ธ Heavy dependencies not installed - using demo mode to prevent downloads" | |
planner_model = LiteLLMWrapper( | |
model_name="gemini/gemini-2.0-flash", | |
temperature=0.7, | |
print_cost=True, | |
verbose=False, | |
use_langfuse=False | |
) | |
video_generator = VideoGenerator( | |
planner_model=planner_model, | |
helper_model=planner_model, | |
scene_model=planner_model, | |
output_dir="output", | |
use_rag=False, | |
use_context_learning=False, | |
use_visual_fix_code=False, | |
verbose=False | |
) | |
return "โ Video generator initialized with full dependencies" | |
except Exception as e: | |
CAN_IMPORT_DEPENDENCIES = False | |
print(f"Initialization error: {e}") | |
return f"โ ๏ธ Running in demo mode to prevent model downloads: {str(e)[:100]}..." | |
def simulate_video_generation(topic: str, context: str, max_scenes: int, progress_callback=None): | |
"""Simulate video generation.""" | |
stages = [ | |
("๐ Analyzing topic", 15), | |
("๐ Planning structure", 30), | |
("๐ฌ Generating scenes", 50), | |
("โจ Creating animations", 75), | |
("๐ฅ Rendering video", 90), | |
("โ Finalizing", 100) | |
] | |
results = [] | |
for stage, progress in stages: | |
if progress_callback: | |
progress_callback(progress, stage) | |
time.sleep(random.uniform(0.3, 0.7)) | |
results.append(f"โข {stage}") | |
return { | |
"success": True, | |
"message": f"Demo video generated for: {topic}", | |
"scenes_created": max_scenes, | |
"processing_steps": results, | |
"demo_note": "This is a simulation for demo purposes." | |
} | |
async def generate_video_async(topic: str, context: str, max_scenes: int, progress_callback=None): | |
"""Generate video asynchronously.""" | |
global video_generator | |
if not topic.strip(): | |
return {"success": False, "error": "Please enter a topic"} | |
try: | |
if DEMO_MODE or not CAN_IMPORT_DEPENDENCIES: | |
return simulate_video_generation(topic, context, max_scenes, progress_callback) | |
if progress_callback: | |
progress_callback(10, "๐ Starting generation...") | |
result = await video_generator.generate_video_pipeline( | |
topic=topic, | |
description=context, | |
max_retries=3, | |
only_plan=False, | |
specific_scenes=list(range(1, max_scenes + 1)) | |
) | |
if progress_callback: | |
progress_callback(100, "โ Completed!") | |
return {"success": True, "message": f"Video generated for: {topic}", "result": result} | |
except Exception as e: | |
return {"success": False, "error": str(e)} | |
def generate_video_gradio(topic: str, context: str, max_scenes: int, progress=gr.Progress()) -> Tuple[str, str]: | |
"""Main Gradio function.""" | |
def progress_callback(percent, message): | |
progress(percent / 100, desc=message) | |
loop = asyncio.new_event_loop() | |
asyncio.set_event_loop(loop) | |
try: | |
result = loop.run_until_complete( | |
generate_video_async(topic, context, max_scenes, progress_callback) | |
) | |
finally: | |
loop.close() | |
if result["success"]: | |
output = f"""# ๐ Video Generation Complete! | |
**Topic:** {topic} | |
**Context:** {context if context else "None"} | |
**Scenes:** {max_scenes} | |
## โ Result | |
{result["message"]} | |
""" | |
if "processing_steps" in result: | |
output += "## ๐ Steps\n" | |
for step in result["processing_steps"]: | |
output += f"{step}\n" | |
if "demo_note" in result: | |
output += f"\nโ ๏ธ **{result['demo_note']}**" | |
status = "๐ฎ Demo completed" if DEMO_MODE else "โ Generation completed" | |
return output, status | |
else: | |
error_output = f"""# โ Generation Failed | |
{result.get("error", "Unknown error")} | |
## ๐ก Tips | |
- Check topic validity | |
- Verify API keys | |
- Try simpler topics | |
""" | |
return error_output, "โ Failed" | |
def get_examples(): | |
"""Example topics.""" | |
return [ | |
["Velocity", "Physics concept with real-world examples"], | |
["Pythagorean Theorem", "Mathematical proof with applications"], | |
["Derivatives", "Calculus concept with geometric interpretation"], | |
["Newton's Laws", "Three laws of motion with demonstrations"], | |
["Quadratic Formula", "Step-by-step derivation and usage"] | |
] | |
def create_interface(): | |
"""Create Gradio interface.""" | |
setup_environment() | |
init_status = initialize_video_generator() | |
with gr.Blocks( | |
title="๐ Theorem Explanation Agent", | |
theme=gr.themes.Soft() | |
) as demo: | |
gr.HTML(""" | |
<div style="text-align: center; padding: 20px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 10px; color: white; margin-bottom: 20px;"> | |
<h1>๐ Theorem Explanation Agent</h1> | |
<p>Generate educational videos using AI</p> | |
</div> | |
""") | |
if DEMO_MODE: | |
gr.HTML(""" | |
<div style="background: #fff3cd; padding: 15px; border-radius: 5px; margin: 10px 0;"> | |
<h3>โ ๏ธ Demo Mode - Preventing Model Downloads</h3> | |
<p>This prevents automatic downloading of Kokoro and other heavy models.</p> | |
<p>To enable full functionality:</p> | |
<ul> | |
<li>Set <code>GEMINI_API_KEY</code> (supports comma-separated keys)</li> | |
<li>Set <code>DEMO_MODE=false</code></li> | |
<li>Install full dependencies (manim, manim-voiceover, etc.)</li> | |
</ul> | |
<p><strong>Note:</strong> Full mode requires ~2GB of model downloads.</p> | |
</div> | |
""") | |
with gr.Row(): | |
with gr.Column(): | |
topic_input = gr.Textbox( | |
label="Topic", | |
placeholder="e.g., velocity, pythagorean theorem" | |
) | |
context_input = gr.Textbox( | |
label="Context (Optional)", | |
placeholder="Additional details or requirements", | |
lines=3 | |
) | |
max_scenes_slider = gr.Slider( | |
label="Max Scenes", | |
minimum=1, | |
maximum=6, | |
value=3, | |
step=1 | |
) | |
generate_btn = gr.Button("๐ Generate Video", variant="primary") | |
with gr.Column(): | |
status_display = gr.Textbox( | |
label="Status", | |
value=init_status, | |
interactive=False | |
) | |
gr.HTML(""" | |
<div style="background: #f8f9fa; padding: 15px; border-radius: 5px;"> | |
<h4>๐ API Setup</h4> | |
<p><strong>Multiple keys:</strong></p> | |
<code>GEMINI_API_KEY=key1,key2,key3</code> | |
<p><strong>Single key:</strong></p> | |
<code>GEMINI_API_KEY=your_key</code> | |
</div> | |
""") | |
examples = gr.Examples( | |
examples=get_examples(), | |
inputs=[topic_input, context_input] | |
) | |
output_display = gr.Markdown( | |
value="Ready to generate! Enter a topic and click Generate." | |
) | |
generate_btn.click( | |
fn=generate_video_gradio, | |
inputs=[topic_input, context_input, max_scenes_slider], | |
outputs=[output_display, status_display], | |
show_progress=True | |
) | |
return demo | |
if __name__ == "__main__": | |
demo = create_interface() | |
demo.launch( | |
server_name="0.0.0.0", | |
server_port=7860, | |
share=False | |
) |