Update app.py
Browse files
app.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1 |
-
|
2 |
-
|
3 |
-
# app.py: Workshop-in-a-Box Hugging Face Space with TTS and Slide Previews
|
4 |
import os
|
5 |
import json
|
6 |
import tempfile
|
@@ -10,8 +9,24 @@ import gradio as gr
|
|
10 |
from agents import Agent, AgentRunner, handoff
|
11 |
from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX
|
12 |
|
13 |
-
#
|
14 |
-
from
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
15 |
|
16 |
# --- Multi-Agent Scaffold ---
|
17 |
# 1. Topic Agent
|
@@ -55,7 +70,7 @@ voice_agent = Agent(
|
|
55 |
name="Voiceover Agent",
|
56 |
instructions=(
|
57 |
f"{RECOMMENDED_PROMPT_PREFIX}\n"
|
58 |
-
"Create a 1-2 minute voiceover script. Return JSON with keys 'script'
|
59 |
)
|
60 |
)
|
61 |
|
@@ -63,7 +78,7 @@ voice_agent = Agent(
|
|
63 |
document_orchestrator = Agent(
|
64 |
name="Workshop Orchestrator",
|
65 |
instructions=(
|
66 |
-
"Invoke
|
67 |
),
|
68 |
handoffs=[
|
69 |
handoff(topic_agent, name="outline"),
|
@@ -76,15 +91,15 @@ document_orchestrator = Agent(
|
|
76 |
|
77 |
runner = AgentRunner()
|
78 |
|
79 |
-
# --- Helper: Run pipeline
|
80 |
def build_workshop_bundle(topic: str, audience: str):
|
81 |
prompt = f"Create a {topic} workshop for {audience}."
|
82 |
results = runner.run(document_orchestrator, prompt).outputs
|
83 |
|
84 |
-
#
|
85 |
voice_info = results.get('voiceover', {})
|
86 |
audio_path = None
|
87 |
-
if 'script' in voice_info:
|
88 |
audio_path = generate_tts_audio(voice_info['script'])
|
89 |
|
90 |
# Render slides to HTML
|
@@ -93,37 +108,56 @@ def build_workshop_bundle(topic: str, audience: str):
|
|
93 |
template = f.read()
|
94 |
slide_html = template.replace('{{SLIDES_JSON}}', json.dumps(slides_json))
|
95 |
|
96 |
-
# Create ZIP
|
97 |
-
|
98 |
-
|
99 |
-
with ZipFile(
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
(
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
109 |
if audio_path and os.path.exists(audio_path):
|
110 |
-
|
111 |
-
|
|
|
112 |
|
113 |
# --- Gradio UI ---
|
114 |
def run_app(topic, audience):
|
115 |
-
|
116 |
-
return slide_html, audio_path, zip_path
|
117 |
|
118 |
-
with gr.Blocks(title='Workshop in a Box') as demo:
|
119 |
-
gr.Markdown('#
|
120 |
-
topic = gr.Textbox(label='Workshop Topic')
|
121 |
-
audience = gr.Textbox(label='Audience')
|
122 |
btn = gr.Button('Generate Workshop')
|
123 |
-
slide_preview = gr.HTML()
|
124 |
-
audio_player = gr.Audio(interactive=True)
|
125 |
-
download = gr.File()
|
126 |
-
btn.click(
|
|
|
|
|
|
|
|
|
127 |
|
128 |
if __name__ == '__main__':
|
129 |
demo.launch()
|
|
|
1 |
+
|
2 |
+
# app.py: Workshop-in-a-Box Space with OpenTTS & Live Slide Previews
|
|
|
3 |
import os
|
4 |
import json
|
5 |
import tempfile
|
|
|
9 |
from agents import Agent, AgentRunner, handoff
|
10 |
from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX
|
11 |
|
12 |
+
# OpenTTS client
|
13 |
+
from open_tts import OpenTTSClient
|
14 |
+
|
15 |
+
# Initialize OpenTTS with your server URL (set via env var or default localhost)
|
16 |
+
opentts = OpenTTSClient(api_url=os.getenv("OPENTTS_API_URL", "http://localhost:5500"))
|
17 |
+
|
18 |
+
def generate_tts_audio(script: str) -> str:
|
19 |
+
"""
|
20 |
+
Synthesize speech using OpenTTS and save to a temp MP3 file.
|
21 |
+
Returns the local file path to the audio.
|
22 |
+
"""
|
23 |
+
# Request synthesis (voice and format can be configured)
|
24 |
+
audio_bytes = opentts.synthesize(text=script, voice="alloy", format="mp3")
|
25 |
+
# Write to a temp file
|
26 |
+
out_path = os.path.join(tempfile.gettempdir(), "voiceover.mp3")
|
27 |
+
with open(out_path, "wb") as f:
|
28 |
+
f.write(audio_bytes)
|
29 |
+
return out_path
|
30 |
|
31 |
# --- Multi-Agent Scaffold ---
|
32 |
# 1. Topic Agent
|
|
|
70 |
name="Voiceover Agent",
|
71 |
instructions=(
|
72 |
f"{RECOMMENDED_PROMPT_PREFIX}\n"
|
73 |
+
"Create a 1-2 minute voiceover script. Return JSON with keys 'script'."
|
74 |
)
|
75 |
)
|
76 |
|
|
|
78 |
document_orchestrator = Agent(
|
79 |
name="Workshop Orchestrator",
|
80 |
instructions=(
|
81 |
+
"Invoke: topic_agent, content_agent, slide_agent, code_agent, voice_agent (optional); collect outputs."
|
82 |
),
|
83 |
handoffs=[
|
84 |
handoff(topic_agent, name="outline"),
|
|
|
91 |
|
92 |
runner = AgentRunner()
|
93 |
|
94 |
+
# --- Helper: Run pipeline & bundle outputs ---
|
95 |
def build_workshop_bundle(topic: str, audience: str):
|
96 |
prompt = f"Create a {topic} workshop for {audience}."
|
97 |
results = runner.run(document_orchestrator, prompt).outputs
|
98 |
|
99 |
+
# Synthesize voiceover if script present
|
100 |
voice_info = results.get('voiceover', {})
|
101 |
audio_path = None
|
102 |
+
if isinstance(voice_info, dict) and 'script' in voice_info:
|
103 |
audio_path = generate_tts_audio(voice_info['script'])
|
104 |
|
105 |
# Render slides to HTML
|
|
|
108 |
template = f.read()
|
109 |
slide_html = template.replace('{{SLIDES_JSON}}', json.dumps(slides_json))
|
110 |
|
111 |
+
# Create a ZIP bundle
|
112 |
+
tmpdir = tempfile.mkdtemp()
|
113 |
+
zip_path = os.path.join(tmpdir, 'workshop_bundle.zip')
|
114 |
+
with ZipFile(zip_path, 'w') as zipf:
|
115 |
+
# JSON outputs
|
116 |
+
out_json = os.path.join(tmpdir, 'workshop_outputs.json')
|
117 |
+
with open(out_json, 'w') as jf:
|
118 |
+
jf.write(json.dumps(results, indent=2))
|
119 |
+
zipf.write(out_json, 'workshop_outputs.json')
|
120 |
+
|
121 |
+
# Slides
|
122 |
+
slides_file = os.path.join(tmpdir, 'slides.json')
|
123 |
+
with open(slides_file, 'w') as sf:
|
124 |
+
sf.write(json.dumps(slides_json, indent=2))
|
125 |
+
zipf.write(slides_file, 'slides.json')
|
126 |
+
|
127 |
+
slide_html_file = os.path.join(tmpdir, 'slides.html')
|
128 |
+
with open(slide_html_file, 'w') as hf:
|
129 |
+
hf.write(slide_html)
|
130 |
+
zipf.write(slide_html_file, 'slides.html')
|
131 |
+
|
132 |
+
# Code labs
|
133 |
+
code_file = os.path.join(tmpdir, 'code_labs.py')
|
134 |
+
with open(code_file, 'w') as cf:
|
135 |
+
cf.write(results.get('code_labs', ''))
|
136 |
+
zipf.write(code_file, 'code_labs.py')
|
137 |
+
|
138 |
+
# Audio
|
139 |
if audio_path and os.path.exists(audio_path):
|
140 |
+
zipf.write(audio_path, os.path.basename(audio_path))
|
141 |
+
|
142 |
+
return slide_html, audio_path, zip_path
|
143 |
|
144 |
# --- Gradio UI ---
|
145 |
def run_app(topic, audience):
|
146 |
+
return build_workshop_bundle(topic, audience)
|
|
|
147 |
|
148 |
+
with gr.Blocks(title='🚀 Workshop in a Box') as demo:
|
149 |
+
gr.Markdown('# Workshop in a Box')
|
150 |
+
topic = gr.Textbox(label='Workshop Topic', placeholder='e.g., AI Agents 101')
|
151 |
+
audience = gr.Textbox(label='Audience', placeholder='e.g., Product Managers')
|
152 |
btn = gr.Button('Generate Workshop')
|
153 |
+
slide_preview = gr.HTML(label='Slide Preview')
|
154 |
+
audio_player = gr.Audio(label='Voiceover Preview', interactive=True)
|
155 |
+
download = gr.File(label='Download ZIP')
|
156 |
+
btn.click(
|
157 |
+
fn=run_app,
|
158 |
+
inputs=[topic, audience],
|
159 |
+
outputs=[slide_preview, audio_player, download]
|
160 |
+
)
|
161 |
|
162 |
if __name__ == '__main__':
|
163 |
demo.launch()
|