ManojINaik commited on
Commit
25bd165
Β·
verified Β·
1 Parent(s): c93e2aa

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +92 -92
app.py CHANGED
@@ -1,148 +1,167 @@
1
- # Theorem Explain Agent - Video Generation App
2
- # Updated: 2025-06-12 - Fixed Gradio interface
3
  import gradio as gr
4
  import uuid
5
  import subprocess
6
  import threading
7
  import os
8
  import time
9
- from fastapi import FastAPI
10
- from fastapi.responses import FileResponse
11
- import asyncio
12
  import sys
 
 
13
 
14
-
15
- # A simple in-memory dictionary to track task status.
16
- # For a production system, you'd use a database or Redis.
17
- tasks = {}
18
  sys.path.insert(0, os.getcwd())
 
19
  # --- Download Kokoro models if they don't exist ---
20
  model_dir = "models"
21
- if not os.path.exists(os.path.join(model_dir, "kokoro-v0_19.onnx")):
 
 
 
22
  print("Downloading Kokoro TTS models...")
23
  os.makedirs(model_dir, exist_ok=True)
24
- os.system(f"wget -P {model_dir} https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files/kokoro-v0_19.onnx")
25
- os.system(f"wget -P {model_dir} https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files/voices.bin")
 
26
  print("Model download complete.")
27
 
 
 
 
28
  def run_video_generation(task_id: str, topic: str, context: str, model: str):
29
  """
30
- This function runs the main generation script in a separate process.
31
  """
32
  tasks[task_id]['status'] = 'running'
 
33
 
34
- # Sanitize topic to create a valid directory name
35
- file_prefix = "".join(c if c.isalnum() else "_" for c in topic.lower())
 
36
  output_dir = os.path.join("output", file_prefix)
37
 
 
38
  command = [
39
- "python", "generate_video.py",
40
  "--model", model,
41
  "--topic", topic,
42
  "--context", context,
43
- "--output_dir", "output",
44
- # "--use_langfuse"
45
  ]
46
 
47
  try:
48
- # Using subprocess to run the existing script
49
- process = subprocess.run(command, check=True, capture_output=True, text=True)
 
 
 
 
 
 
 
 
 
 
 
 
50
 
51
- # Look for the output video in the directory
52
- video_path = None
53
- if os.path.exists(output_dir):
54
- for file in os.listdir(output_dir):
55
- if file.endswith("_combined.mp4"):
56
- video_path = os.path.join(output_dir, file)
57
- break
58
-
59
- if video_path and os.path.exists(video_path):
60
- tasks[task_id]['status'] = 'completed'
61
- tasks[task_id]['video_path'] = video_path
 
 
 
62
  else:
63
  tasks[task_id]['status'] = 'failed'
64
- tasks[task_id]['error'] = "Video file not found after generation."
65
-
66
- except subprocess.CalledProcessError as e:
67
- tasks[task_id]['status'] = 'failed'
68
- tasks[task_id]['error'] = str(e)
69
  except Exception as e:
 
70
  tasks[task_id]['status'] = 'failed'
71
  tasks[task_id]['error'] = str(e)
 
72
 
73
  def start_generation(topic: str, context: str, model: str):
74
  if not all([topic, context, model]):
75
  return "Topic, Context, and Model cannot be empty.", ""
76
 
77
  task_id = str(uuid.uuid4())
78
- tasks[task_id] = {'status': 'queued', 'model': model}
79
 
80
- # Use a background thread to run the time-consuming task
81
  thread = threading.Thread(
82
  target=run_video_generation,
83
  args=(task_id, topic, context, model)
84
  )
85
  thread.start()
86
 
87
- return f"Task started with model {model}. Your Task ID is: {task_id}", task_id
88
 
89
  def check_status(task_id: str):
90
  if not task_id:
91
- return "Please provide a Task ID.", None
92
 
93
  task = tasks.get(task_id)
94
  if not task:
95
- return "Task not found.", None
96
 
97
  status = task.get('status')
98
  model = task.get('model', 'Unknown')
 
99
 
100
  if status == 'completed':
101
  video_path = task.get('video_path')
102
- return f"Status: {status} (Model: {model})", video_path
 
103
  elif status == 'failed':
104
  error = task.get('error', 'Unknown error')
105
- return f"Status: {status} (Model: {model})\nError: {error}", None
 
106
 
107
- return f"Status: {status} (Model: {model})", None
 
108
 
109
  # Create the Gradio interface
110
- with gr.Blocks(title="Theorem Explain Agent") as demo:
111
- gr.Markdown("# πŸŽ“ Theorem-Explain-Agent Video Generation")
112
- gr.Markdown("Generate educational videos explaining mathematical theorems and concepts.")
113
 
114
  with gr.Tab("πŸš€ Start Generation"):
115
- gr.Markdown("### Enter the details for your video:")
116
- model_input = gr.Textbox(
117
  label="Model",
118
- placeholder="e.g., gemini/gemini-1.5-flash, openai/gpt-4o",
119
- value="gemini/gemini-1.5-flash"
120
- )
121
- topic_input = gr.Textbox(
122
- label="Topic",
123
- placeholder="e.g., The Pythagorean Theorem"
124
- )
125
- context_input = gr.Textbox(
126
- label="Context",
127
- placeholder="A short explanation of the theorem.",
128
- lines=3
129
  )
 
 
130
  start_button = gr.Button("🎬 Generate Video", variant="primary")
131
 
 
132
  with gr.Row():
133
  status_output = gr.Textbox(label="Status", interactive=False)
134
  task_id_output = gr.Textbox(label="Task ID", interactive=False)
135
 
136
- with gr.Tab("πŸ“Š Check Status"):
137
- gr.Markdown("### Check the status of your video generation:")
138
- task_id_input = gr.Textbox(
139
- label="Task ID",
140
- placeholder="Enter the Task ID you received"
141
- )
142
- check_button = gr.Button("πŸ” Check Status", variant="secondary")
143
 
144
- status_display = gr.Textbox(label="Status", interactive=False)
145
- video_output = gr.Video(label="Generated Video")
 
146
 
147
  # Connect the functions to the interface
148
  start_button.click(
@@ -154,29 +173,10 @@ with gr.Blocks(title="Theorem Explain Agent") as demo:
154
  check_button.click(
155
  fn=check_status,
156
  inputs=[task_id_input],
157
- outputs=[status_display, video_output]
 
 
158
  )
159
 
160
- gr.Markdown("""
161
- ### πŸ“‹ How to Use:
162
- 1. **Start Generation**: Enter a Model, Topic, and Context, then click 'Generate Video'
163
- 2. **Copy the Task ID** that appears
164
- 3. **Check Status**: Go to the 'Check Status' tab, paste your Task ID, and click 'Check Status'
165
- 4. **Wait**: Video generation can take several minutes. Check periodically until complete
166
- 5. **Download**: When complete, the video will appear and can be downloaded
167
-
168
- ### πŸ€– Supported Models:
169
- - `gemini/gemini-1.5-flash` (recommended)
170
- - `gemini/gemini-1.5-pro`
171
- - `openai/gpt-4o`
172
- - `openai/o3-mini`
173
- - `anthropic/claude-3-opus-20240229`
174
- """)
175
-
176
  # Launch the app
177
- if __name__ == "__main__":
178
- demo.launch(
179
- server_name="0.0.0.0",
180
- server_port=7860,
181
- show_error=True
182
- )
 
1
+ # app.py (Final version with live logging and corrections)
2
+
3
  import gradio as gr
4
  import uuid
5
  import subprocess
6
  import threading
7
  import os
8
  import time
 
 
 
9
  import sys
10
+ import re
11
+ import traceback
12
 
13
+ # Add project root to Python path to fix any import issues
 
 
 
14
  sys.path.insert(0, os.getcwd())
15
+
16
  # --- Download Kokoro models if they don't exist ---
17
  model_dir = "models"
18
+ kokoro_model_path = os.path.join(model_dir, "kokoro-v0_19.onnx")
19
+ kokoro_voices_path = os.path.join(model_dir, "voices.bin")
20
+
21
+ if not os.path.exists(kokoro_model_path) or not os.path.exists(kokoro_voices_path):
22
  print("Downloading Kokoro TTS models...")
23
  os.makedirs(model_dir, exist_ok=True)
24
+ # Using specific wget commands for clarity and robustness
25
+ os.system(f"wget -O {kokoro_model_path} https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files/kokoro-v0_19.onnx")
26
+ os.system(f"wget -O {kokoro_voices_path} https://github.com/thewh1teagle/kokoro-onnx/releases/download/model-files/voices.bin")
27
  print("Model download complete.")
28
 
29
+ # In-memory dictionary to track task status.
30
+ tasks = {}
31
+
32
  def run_video_generation(task_id: str, topic: str, context: str, model: str):
33
  """
34
+ Runs the main generation script in a separate process and captures output in real-time.
35
  """
36
  tasks[task_id]['status'] = 'running'
37
+ tasks[task_id]['log'] = 'Process started...\n'
38
 
39
+ # Sanitize topic name to create a valid directory/file prefix
40
+ file_prefix = re.sub(r'[^a-z0-9_]+', '_', topic.lower())
41
+ # The generate_video.py script will create this directory inside the general 'output' folder
42
  output_dir = os.path.join("output", file_prefix)
43
 
44
+ # --- IMPORTANT: Command points to the specific output directory for this topic ---
45
  command = [
46
+ "python", "-u", "generate_video.py", # '-u' for unbuffered output
47
  "--model", model,
48
  "--topic", topic,
49
  "--context", context,
50
+ "--output_dir", output_dir
51
+ # Langfuse is disabled by not including the --use_langfuse flag
52
  ]
53
 
54
  try:
55
+ # Use Popen to run the process in the background and stream output
56
+ process = subprocess.Popen(
57
+ command,
58
+ stdout=subprocess.PIPE,
59
+ stderr=subprocess.STDOUT,
60
+ text=True,
61
+ bufsize=1,
62
+ universal_newlines=True,
63
+ )
64
+
65
+ # Read output line-by-line in real-time
66
+ for line in iter(process.stdout.readline, ''):
67
+ print(line, end='') # Print to Hugging Face console logs
68
+ tasks[task_id]['log'] += line
69
 
70
+ process.wait() # Wait for the process to complete
71
+
72
+ if process.returncode == 0:
73
+ # Check for the final combined video file
74
+ final_video_path = os.path.join(output_dir, f"{file_prefix}_combined.mp4")
75
+
76
+ if os.path.exists(final_video_path):
77
+ tasks[task_id]['status'] = 'completed'
78
+ tasks[task_id]['video_path'] = final_video_path
79
+ tasks[task_id]['log'] += f"\nβœ… Success! Video available at: {final_video_path}"
80
+ else:
81
+ tasks[task_id]['status'] = 'failed'
82
+ tasks[task_id]['error'] = "Script finished, but the final combined video file was not found."
83
+ tasks[task_id]['log'] += f"\n❌ Error: Output video not found at {final_video_path}"
84
  else:
85
  tasks[task_id]['status'] = 'failed'
86
+ tasks[task_id]['error'] = f"Process failed with return code {process.returncode}."
87
+ tasks[task_id]['log'] += f"\n❌ Error: Process failed. Check logs for details."
88
+
 
 
89
  except Exception as e:
90
+ print(f"Caught an exception: {e}")
91
  tasks[task_id]['status'] = 'failed'
92
  tasks[task_id]['error'] = str(e)
93
+ tasks[task_id]['log'] += f"\n❌ An exception occurred: {traceback.format_exc()}"
94
 
95
  def start_generation(topic: str, context: str, model: str):
96
  if not all([topic, context, model]):
97
  return "Topic, Context, and Model cannot be empty.", ""
98
 
99
  task_id = str(uuid.uuid4())
100
+ tasks[task_id] = {'status': 'queued', 'model': model, 'log': ''}
101
 
 
102
  thread = threading.Thread(
103
  target=run_video_generation,
104
  args=(task_id, topic, context, model)
105
  )
106
  thread.start()
107
 
108
+ return f"βœ… Task started with ID: {task_id}. Go to 'Check Status' tab to monitor progress.", task_id
109
 
110
  def check_status(task_id: str):
111
  if not task_id:
112
+ return "Please provide a Task ID.", None, "Please enter a Task ID above and click 'Check Status'."
113
 
114
  task = tasks.get(task_id)
115
  if not task:
116
+ return "Task not found.", None, f"No task found with ID: {task_id}"
117
 
118
  status = task.get('status')
119
  model = task.get('model', 'Unknown')
120
+ log = task.get('log', 'No logs yet...')
121
 
122
  if status == 'completed':
123
  video_path = task.get('video_path')
124
+ status_message = f"βœ… Status: {status} (Model: {model})"
125
+ return status_message, video_path, log
126
  elif status == 'failed':
127
  error = task.get('error', 'Unknown error')
128
+ status_message = f"❌ Status: {status} (Model: {model})"
129
+ return status_message, None, log
130
 
131
+ status_message = f"πŸ”„ Status: {status} (Model: {model})"
132
+ return status_message, None, log
133
 
134
  # Create the Gradio interface
135
+ with gr.Blocks(css="footer {display: none !important}", title="Theorem Explain Agent") as demo:
136
+ gr.Markdown("# πŸŽ“ Theorem Explain Agent: Video Generation")
137
+ gr.Markdown("Generate educational videos explaining mathematical theorems and concepts. This may take several minutes.")
138
 
139
  with gr.Tab("πŸš€ Start Generation"):
140
+ gr.Markdown("### 1. Enter the details for your video")
141
+ model_input = gr.Dropdown(
142
  label="Model",
143
+ choices=["gemini/gemini-1.5-flash-001", "gemini/gemini-1.5-pro-002"],
144
+ value="gemini/gemini-1.5-flash-001",
145
+ info="Select the AI model for content generation."
 
 
 
 
 
 
 
 
146
  )
147
+ topic_input = gr.Textbox(label="Topic", placeholder="e.g., The Pythagorean Theorem")
148
+ context_input = gr.Textbox(label="Context", placeholder="A short explanation of the theorem.", lines=3)
149
  start_button = gr.Button("🎬 Generate Video", variant="primary")
150
 
151
+ gr.Markdown("### 2. Monitor your task")
152
  with gr.Row():
153
  status_output = gr.Textbox(label="Status", interactive=False)
154
  task_id_output = gr.Textbox(label="Task ID", interactive=False)
155
 
156
+ with gr.Tab("πŸ“Š Check Status & View Video"):
157
+ gr.Markdown("### Paste your Task ID to check progress and view the final video")
158
+ with gr.Row():
159
+ task_id_input = gr.Textbox(label="Task ID", placeholder="Enter the Task ID you received")
160
+ check_button = gr.Button("πŸ” Check Status", variant="secondary")
 
 
161
 
162
+ status_display = gr.Textbox(label="Current Status", interactive=False)
163
+ video_output = gr.Video(label="Generated Video", interactive=False)
164
+ log_display = gr.Textbox(label="Live Generation Logs", lines=15, interactive=False)
165
 
166
  # Connect the functions to the interface
167
  start_button.click(
 
173
  check_button.click(
174
  fn=check_status,
175
  inputs=[task_id_input],
176
+ outputs=[status_display, video_output, log_display],
177
+ # Every 2 seconds, poll the status
178
+ every=2
179
  )
180
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
181
  # Launch the app
182
+ demo.launch()