|
|
|
|
|
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 |
|
|
|
|
|
from open_tts import OpenTTSClient |
|
|
|
|
|
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. |
|
""" |
|
|
|
audio_bytes = opentts.synthesize(text=script, voice="alloy", format="mp3") |
|
|
|
out_path = os.path.join(tempfile.gettempdir(), "voiceover.mp3") |
|
with open(out_path, "wb") as f: |
|
f.write(audio_bytes) |
|
return out_path |
|
|
|
|
|
|
|
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." |
|
) |
|
) |
|
|
|
|
|
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." |
|
) |
|
) |
|
|
|
|
|
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." |
|
) |
|
) |
|
|
|
|
|
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." |
|
) |
|
) |
|
|
|
|
|
voice_agent = Agent( |
|
name="Voiceover Agent", |
|
instructions=( |
|
f"{RECOMMENDED_PROMPT_PREFIX}\n" |
|
"Create a 1-2 minute voiceover script. Return JSON with keys 'script'." |
|
) |
|
) |
|
|
|
|
|
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() |
|
|
|
|
|
def build_workshop_bundle(topic: str, audience: str): |
|
prompt = f"Create a {topic} workshop for {audience}." |
|
results = runner.run(document_orchestrator, prompt).outputs |
|
|
|
|
|
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']) |
|
|
|
|
|
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)) |
|
|
|
|
|
tmpdir = tempfile.mkdtemp() |
|
zip_path = os.path.join(tmpdir, 'workshop_bundle.zip') |
|
with ZipFile(zip_path, 'w') as zipf: |
|
|
|
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_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_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') |
|
|
|
|
|
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 |
|
|
|
|
|
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() |