naso / app.py
mgbam's picture
Update app.py
07976be verified
raw
history blame
5.41 kB
# app.py: Workshop-in-a-Box Space with OpenTTS & Live Slide Previews
import os
import json
import tempfile
from zipfile import ZipFile
import gradio as gr
from agents import Agent, AgentRunner, handoff
from agents.extensions.handoff_prompt import RECOMMENDED_PROMPT_PREFIX
# OpenTTS client
from open_tts import OpenTTSClient
# Initialize OpenTTS with your server URL (set via env var or default localhost)
opentts = OpenTTSClient(api_url=os.getenv("OPENTTS_API_URL", "http://localhost:5500"))
def generate_tts_audio(script: str) -> str:
"""
Synthesize speech using OpenTTS and save to a temp MP3 file.
Returns the local file path to the audio.
"""
# Request synthesis (voice and format can be configured)
audio_bytes = opentts.synthesize(text=script, voice="alloy", format="mp3")
# Write to a temp file
out_path = os.path.join(tempfile.gettempdir(), "voiceover.mp3")
with open(out_path, "wb") as f:
f.write(audio_bytes)
return out_path
# --- Multi-Agent Scaffold ---
# 1. Topic Agent
topic_agent = Agent(
name="Topic Agent",
instructions=(
f"{RECOMMENDED_PROMPT_PREFIX}\n"
"You are given a workshop topic and audience. Draft a structured learning path: goals, 4 modules, and a hands-on exercise for each."
)
)
# 2. Content Agent
content_agent = Agent(
name="Content Agent",
instructions=(
f"{RECOMMENDED_PROMPT_PREFIX}\n"
"Convert the outline into detailed module scripts, speaker notes, and 3 quiz questions per module."
)
)
# 3. Slide Agent
slide_agent = Agent(
name="Slide Agent",
instructions=(
f"{RECOMMENDED_PROMPT_PREFIX}\n"
"Given module content, produce slide JSON with title, bullet points, and design hints."
)
)
# 4. Code Agent
code_agent = Agent(
name="Code Agent",
instructions=(
f"{RECOMMENDED_PROMPT_PREFIX}\n"
"Generate runnable Python code snippets or a Colab notebook for hands-on labs in each module."
)
)
# 5. Voiceover Agent (Optional)
voice_agent = Agent(
name="Voiceover Agent",
instructions=(
f"{RECOMMENDED_PROMPT_PREFIX}\n"
"Create a 1-2 minute voiceover script. Return JSON with keys 'script'."
)
)
# Orchestrator: sequences agents
document_orchestrator = Agent(
name="Workshop Orchestrator",
instructions=(
"Invoke: topic_agent, content_agent, slide_agent, code_agent, voice_agent (optional); collect outputs."
),
handoffs=[
handoff(topic_agent, name="outline"),
handoff(content_agent, name="content"),
handoff(slide_agent, name="slides"),
handoff(code_agent, name="code_labs"),
handoff(voice_agent, name="voiceover", optional=True),
]
)
runner = AgentRunner()
# --- Helper: Run pipeline & bundle outputs ---
def build_workshop_bundle(topic: str, audience: str):
prompt = f"Create a {topic} workshop for {audience}."
results = runner.run(document_orchestrator, prompt).outputs
# Synthesize voiceover if script present
voice_info = results.get('voiceover', {})
audio_path = None
if isinstance(voice_info, dict) and 'script' in voice_info:
audio_path = generate_tts_audio(voice_info['script'])
# Render slides to HTML
slides_json = results.get('slides', {})
with open('static/slides_template.html') as f:
template = f.read()
slide_html = template.replace('{{SLIDES_JSON}}', json.dumps(slides_json))
# Create a ZIP bundle
tmpdir = tempfile.mkdtemp()
zip_path = os.path.join(tmpdir, 'workshop_bundle.zip')
with ZipFile(zip_path, 'w') as zipf:
# JSON outputs
out_json = os.path.join(tmpdir, 'workshop_outputs.json')
with open(out_json, 'w') as jf:
jf.write(json.dumps(results, indent=2))
zipf.write(out_json, 'workshop_outputs.json')
# Slides
slides_file = os.path.join(tmpdir, 'slides.json')
with open(slides_file, 'w') as sf:
sf.write(json.dumps(slides_json, indent=2))
zipf.write(slides_file, 'slides.json')
slide_html_file = os.path.join(tmpdir, 'slides.html')
with open(slide_html_file, 'w') as hf:
hf.write(slide_html)
zipf.write(slide_html_file, 'slides.html')
# Code labs
code_file = os.path.join(tmpdir, 'code_labs.py')
with open(code_file, 'w') as cf:
cf.write(results.get('code_labs', ''))
zipf.write(code_file, 'code_labs.py')
# Audio
if audio_path and os.path.exists(audio_path):
zipf.write(audio_path, os.path.basename(audio_path))
return slide_html, audio_path, zip_path
# --- Gradio UI ---
def run_app(topic, audience):
return build_workshop_bundle(topic, audience)
with gr.Blocks(title='πŸš€ Workshop in a Box') as demo:
gr.Markdown('# Workshop in a Box')
topic = gr.Textbox(label='Workshop Topic', placeholder='e.g., AI Agents 101')
audience = gr.Textbox(label='Audience', placeholder='e.g., Product Managers')
btn = gr.Button('Generate Workshop')
slide_preview = gr.HTML(label='Slide Preview')
audio_player = gr.Audio(label='Voiceover Preview', interactive=True)
download = gr.File(label='Download ZIP')
btn.click(
fn=run_app,
inputs=[topic, audience],
outputs=[slide_preview, audio_player, download]
)
if __name__ == '__main__':
demo.launch()