File size: 5,371 Bytes
c679f41
07976be
9fef5b9
 
 
 
 
 
 
 
 
c679f41
 
07976be
c679f41
 
 
 
 
 
 
07976be
 
 
c679f41
07976be
 
c679f41
 
 
 
07976be
9fef5b9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c679f41
9fef5b9
 
 
 
 
 
 
07976be
9fef5b9
 
 
 
 
 
 
 
 
 
 
 
07976be
9fef5b9
 
 
 
07976be
9fef5b9
 
07976be
9fef5b9
 
 
 
 
 
 
 
07976be
 
 
 
 
 
 
 
 
 
c679f41
 
 
07976be
c679f41
07976be
 
 
 
 
 
 
 
 
 
 
 
 
9fef5b9
07976be
 
 
9fef5b9
 
 
c679f41
 
9fef5b9
07976be
 
 
 
9fef5b9
07976be
 
 
 
 
 
 
 
9fef5b9
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
## 1. `app.py`

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

# === Coqui TTS Integration ===
from TTS.api import TTS

# Initialize Coqui TTS (choose a model)
# Using Tacotron2-DDC model from LJSpeech
tts = TTS(
    model_name="tts_models/en/ljspeech/tacotron2-DDC",
    progress_bar=False,
    gpu=False
)

def generate_tts_audio(script: str) -> str:
    """
    Synthesize speech using Coqui TTS and save to a WAV file.
    Returns the local file path to the audio.
    """
    # Determine output path in temp directory
    out_path = os.path.join(tempfile.gettempdir(), "voiceover.wav")
    # Render the speech to the file
    tts.tts_to_file(text=script, file_path=out_path)
    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 key '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 JSON & HTML
        slide_json_file = os.path.join(tmpdir, 'slides.json')
        with open(slide_json_file, 'w') as sf:
            sf.write(json.dumps(slides_json, indent=2))
        zipf.write(slide_json_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):
    slide_html, audio_path, zip_path = build_workshop_bundle(topic, audience)
    return slide_html, audio_path, zip_path

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()