Update app.py
Browse files
app.py
CHANGED
@@ -14,8 +14,9 @@ from dotenv import load_dotenv
|
|
14 |
# Load environment variables
|
15 |
load_dotenv()
|
16 |
|
17 |
-
# Initialize
|
18 |
openai.api_key = os.getenv("OPENAI_API_KEY")
|
|
|
19 |
|
20 |
# =============================
|
21 |
# ENHANCED AGENT IMPLEMENTATION
|
@@ -292,12 +293,69 @@ class DesignAgent:
|
|
292 |
st.error(f"Design generation error: {str(e)}")
|
293 |
return None
|
294 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
295 |
# Initialize agents
|
296 |
topic_agent = TopicAgent()
|
297 |
content_agent = ContentAgent()
|
298 |
slide_agent = SlideAgent()
|
299 |
code_agent = CodeAgent()
|
300 |
design_agent = DesignAgent()
|
|
|
301 |
|
302 |
# =====================
|
303 |
# STREAMLIT APPLICATION
|
@@ -337,6 +395,35 @@ st.markdown("""
|
|
337 |
border-radius: 10px;
|
338 |
padding: 15px;
|
339 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
340 |
</style>
|
341 |
""", unsafe_allow_html=True)
|
342 |
|
@@ -346,33 +433,108 @@ with col1:
|
|
346 |
st.image("https://cdn-icons-png.flaticon.com/512/1995/1995485.png", width=100)
|
347 |
with col2:
|
348 |
st.title("π€ Workshop in a Box Pro")
|
349 |
-
st.caption("Generate Premium Corporate AI Training Workshops
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
350 |
|
351 |
# Sidebar configuration
|
352 |
with st.sidebar:
|
353 |
st.header("βοΈ Workshop Configuration")
|
354 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
355 |
duration = st.slider("Duration (hours)", 1.0, 8.0, 3.0, 0.5)
|
356 |
difficulty = st.selectbox("Difficulty Level",
|
357 |
["Beginner", "Intermediate", "Advanced", "Expert"])
|
358 |
include_code = st.checkbox("Include Code Labs", True)
|
359 |
include_design = st.checkbox("Generate Visual Designs", True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
360 |
|
361 |
if st.button("β¨ Generate Workshop", type="primary", use_container_width=True):
|
362 |
st.session_state.generating = True
|
|
|
363 |
|
364 |
# Generation pipeline
|
365 |
-
if
|
366 |
-
with st.spinner("π Creating your
|
367 |
start_time = time.time()
|
368 |
|
369 |
# Agent pipeline
|
370 |
-
outline = topic_agent.generate_outline(workshop_topic, duration, difficulty)
|
371 |
content = content_agent.generate_content(outline)
|
372 |
slides = slide_agent.generate_slides(content)
|
373 |
code_labs = code_agent.generate_code(content) if include_code else None
|
374 |
design_url = design_agent.generate_design(slides) if include_design else None
|
375 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
376 |
# Prepare download package
|
377 |
zip_buffer = io.BytesIO()
|
378 |
with zipfile.ZipFile(zip_buffer, "a") as zip_file:
|
@@ -387,6 +549,9 @@ if hasattr(st.session_state, 'generating'):
|
|
387 |
zip_file.writestr("slide_design.png", img_data)
|
388 |
except:
|
389 |
pass
|
|
|
|
|
|
|
390 |
|
391 |
# Store results
|
392 |
st.session_state.outline = outline
|
@@ -394,20 +559,21 @@ if hasattr(st.session_state, 'generating'):
|
|
394 |
st.session_state.slides = slides
|
395 |
st.session_state.code_labs = code_labs
|
396 |
st.session_state.design_url = design_url
|
|
|
397 |
st.session_state.zip_buffer = zip_buffer
|
398 |
st.session_state.gen_time = round(time.time() - start_time, 2)
|
399 |
st.session_state.generated = True
|
400 |
st.session_state.generating = False
|
401 |
|
402 |
# Results display
|
403 |
-
if
|
404 |
-
st.success(f"β
|
405 |
|
406 |
# Download button
|
407 |
st.download_button(
|
408 |
label="π₯ Download Workshop Package",
|
409 |
data=st.session_state.zip_buffer.getvalue(),
|
410 |
-
file_name=f"{workshop_topic.replace(' ', '_')}_workshop.zip",
|
411 |
mime="application/zip",
|
412 |
use_container_width=True
|
413 |
)
|
@@ -429,15 +595,35 @@ if hasattr(st.session_state, 'generated'):
|
|
429 |
if st.session_state.design_url:
|
430 |
with st.expander("π¨ Generated Design"):
|
431 |
st.image(st.session_state.design_url, caption="Custom Slide Design")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
432 |
|
433 |
# Sales and booking section
|
434 |
st.divider()
|
435 |
st.subheader("π Ready to Deliver This Workshop?")
|
436 |
-
st.markdown("""
|
437 |
-
### Premium
|
438 |
- **Live Workshop Delivery**: $10,000 per session
|
439 |
- **On-Demand Course**: $5,000 (unlimited access)
|
440 |
- **Pilot Program**: $1,000 refundable deposit
|
|
|
441 |
|
442 |
β¨ **All inclusive**: Customization, materials, and follow-up support
|
443 |
""")
|
@@ -458,12 +644,20 @@ with st.sidebar:
|
|
458 |
else:
|
459 |
st.warning("OpenAI API not set - using enhanced mock data")
|
460 |
|
461 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
462 |
**Premium Features:**
|
463 |
-
- AI-generated
|
|
|
464 |
- Real-world case studies
|
465 |
- Practical code labs
|
466 |
-
- Professional templates
|
467 |
""")
|
468 |
|
469 |
# How it works section
|
@@ -471,10 +665,10 @@ st.divider()
|
|
471 |
st.subheader("π‘ How It Works")
|
472 |
st.markdown("""
|
473 |
1. **Configure** your workshop topic and parameters
|
474 |
-
2. **Generate** premium training materials
|
475 |
3. **Customize** the content to your specific needs
|
476 |
4. **Deliver** high-value corporate training at $10K/session
|
477 |
5. **Reuse** the materials for unlimited revenue
|
478 |
|
479 |
-
*"
|
480 |
""")
|
|
|
14 |
# Load environment variables
|
15 |
load_dotenv()
|
16 |
|
17 |
+
# Initialize API keys
|
18 |
openai.api_key = os.getenv("OPENAI_API_KEY")
|
19 |
+
ELEVENLABS_API_KEY = os.getenv("ELEVENLABS_API_KEY")
|
20 |
|
21 |
# =============================
|
22 |
# ENHANCED AGENT IMPLEMENTATION
|
|
|
293 |
st.error(f"Design generation error: {str(e)}")
|
294 |
return None
|
295 |
|
296 |
+
class VoiceoverAgent:
|
297 |
+
def __init__(self):
|
298 |
+
self.api_key = ELEVENLABS_API_KEY
|
299 |
+
self.voice_id = "21m00Tcm4TlvDq8ikWAM" # Default voice ID (Bella)
|
300 |
+
self.model = "eleven_monolingual_v1"
|
301 |
+
|
302 |
+
def generate_voiceover(self, text, voice_id=None):
|
303 |
+
if not self.api_key:
|
304 |
+
return None
|
305 |
+
|
306 |
+
try:
|
307 |
+
# Use custom voice if provided, otherwise use default
|
308 |
+
voice = voice_id if voice_id else self.voice_id
|
309 |
+
|
310 |
+
url = f"https://api.elevenlabs.io/v1/text-to-speech/{voice}"
|
311 |
+
headers = {
|
312 |
+
"Accept": "audio/mpeg",
|
313 |
+
"Content-Type": "application/json",
|
314 |
+
"xi-api-key": self.api_key
|
315 |
+
}
|
316 |
+
data = {
|
317 |
+
"text": text,
|
318 |
+
"model_id": self.model,
|
319 |
+
"voice_settings": {
|
320 |
+
"stability": 0.7,
|
321 |
+
"similarity_boost": 0.8,
|
322 |
+
"style": 0.5,
|
323 |
+
"use_speaker_boost": True
|
324 |
+
}
|
325 |
+
}
|
326 |
+
response = requests.post(url, json=data, headers=headers)
|
327 |
+
|
328 |
+
if response.status_code == 200:
|
329 |
+
return response.content
|
330 |
+
else:
|
331 |
+
st.error(f"Voiceover API error: {response.status_code} - {response.text}")
|
332 |
+
return None
|
333 |
+
except Exception as e:
|
334 |
+
st.error(f"Voiceover generation error: {str(e)}")
|
335 |
+
return None
|
336 |
+
|
337 |
+
def get_voices(self):
|
338 |
+
if not self.api_key:
|
339 |
+
return []
|
340 |
+
|
341 |
+
try:
|
342 |
+
url = "https://api.elevenlabs.io/v1/voices"
|
343 |
+
headers = {"xi-api-key": self.api_key}
|
344 |
+
response = requests.get(url, headers=headers)
|
345 |
+
|
346 |
+
if response.status_code == 200:
|
347 |
+
return response.json().get("voices", [])
|
348 |
+
return []
|
349 |
+
except:
|
350 |
+
return []
|
351 |
+
|
352 |
# Initialize agents
|
353 |
topic_agent = TopicAgent()
|
354 |
content_agent = ContentAgent()
|
355 |
slide_agent = SlideAgent()
|
356 |
code_agent = CodeAgent()
|
357 |
design_agent = DesignAgent()
|
358 |
+
voiceover_agent = VoiceoverAgent()
|
359 |
|
360 |
# =====================
|
361 |
# STREAMLIT APPLICATION
|
|
|
395 |
border-radius: 10px;
|
396 |
padding: 15px;
|
397 |
}
|
398 |
+
.audio-player {
|
399 |
+
margin: 15px 0;
|
400 |
+
border-radius: 10px;
|
401 |
+
background: rgba(255,255,255,0.1);
|
402 |
+
padding: 15px;
|
403 |
+
}
|
404 |
+
.voice-option {
|
405 |
+
display: flex;
|
406 |
+
align-items: center;
|
407 |
+
margin: 5px 0;
|
408 |
+
padding: 8px;
|
409 |
+
border-radius: 8px;
|
410 |
+
cursor: pointer;
|
411 |
+
transition: background 0.3s;
|
412 |
+
}
|
413 |
+
.voice-option:hover {
|
414 |
+
background: rgba(255,255,255,0.2);
|
415 |
+
}
|
416 |
+
.voice-option.selected {
|
417 |
+
background: rgba(0,180,155,0.3);
|
418 |
+
border: 2px solid #00b09b;
|
419 |
+
}
|
420 |
+
.voice-thumb {
|
421 |
+
width: 40px;
|
422 |
+
height: 40px;
|
423 |
+
border-radius: 50%;
|
424 |
+
margin-right: 10px;
|
425 |
+
object-fit: cover;
|
426 |
+
}
|
427 |
</style>
|
428 |
""", unsafe_allow_html=True)
|
429 |
|
|
|
433 |
st.image("https://cdn-icons-png.flaticon.com/512/1995/1995485.png", width=100)
|
434 |
with col2:
|
435 |
st.title("π€ Workshop in a Box Pro")
|
436 |
+
st.caption("Generate Premium Corporate AI Training Workshops with Voiceovers")
|
437 |
+
|
438 |
+
# Initialize session state
|
439 |
+
if 'workshop_topic' not in st.session_state:
|
440 |
+
st.session_state.workshop_topic = "Advanced Prompt Engineering"
|
441 |
+
if 'generated' not in st.session_state:
|
442 |
+
st.session_state.generated = False
|
443 |
+
if 'generating' not in st.session_state:
|
444 |
+
st.session_state.generating = False
|
445 |
+
if 'voiceovers' not in st.session_state:
|
446 |
+
st.session_state.voiceovers = {}
|
447 |
+
if 'selected_voice' not in st.session_state:
|
448 |
+
st.session_state.selected_voice = "21m00Tcm4TlvDq8ikWAM" # Default voice ID
|
449 |
|
450 |
# Sidebar configuration
|
451 |
with st.sidebar:
|
452 |
st.header("βοΈ Workshop Configuration")
|
453 |
+
|
454 |
+
# Workshop topic input with session state
|
455 |
+
st.session_state.workshop_topic = st.text_input(
|
456 |
+
"Workshop Topic",
|
457 |
+
st.session_state.workshop_topic,
|
458 |
+
key="topic_input",
|
459 |
+
help="Enter the main topic for your workshop"
|
460 |
+
)
|
461 |
+
|
462 |
+
# Validate topic input
|
463 |
+
if st.session_state.workshop_topic.strip() == "":
|
464 |
+
st.warning("Please enter a workshop topic")
|
465 |
+
st.stop()
|
466 |
+
|
467 |
duration = st.slider("Duration (hours)", 1.0, 8.0, 3.0, 0.5)
|
468 |
difficulty = st.selectbox("Difficulty Level",
|
469 |
["Beginner", "Intermediate", "Advanced", "Expert"])
|
470 |
include_code = st.checkbox("Include Code Labs", True)
|
471 |
include_design = st.checkbox("Generate Visual Designs", True)
|
472 |
+
include_voiceover = st.checkbox("Generate Voiceovers", True)
|
473 |
+
|
474 |
+
# Voice selection
|
475 |
+
if include_voiceover:
|
476 |
+
st.subheader("ποΈ Voice Selection")
|
477 |
+
|
478 |
+
# Get available voices
|
479 |
+
voices = voiceover_agent.get_voices()
|
480 |
+
|
481 |
+
# If we have voices, let the user select one
|
482 |
+
if voices:
|
483 |
+
# Create 2 columns for voice selection
|
484 |
+
cols = st.columns(2)
|
485 |
+
for i, voice in enumerate(voices[:4]): # Show first 4 voices
|
486 |
+
with cols[i % 2]:
|
487 |
+
# Create a unique key for each voice button
|
488 |
+
voice_key = f"voice_{voice['voice_id']}"
|
489 |
+
|
490 |
+
# Display voice option
|
491 |
+
if st.button(
|
492 |
+
f"π£οΈ {voice['name']}",
|
493 |
+
key=voice_key,
|
494 |
+
use_container_width=True,
|
495 |
+
help=f"Select {voice['name']} voice"
|
496 |
+
):
|
497 |
+
st.session_state.selected_voice = voice['voice_id']
|
498 |
+
|
499 |
+
# Show which voice is currently selected
|
500 |
+
selected_voice_name = next((v['name'] for v in voices if v['voice_id'] == st.session_state.selected_voice), "Default")
|
501 |
+
st.info(f"Selected Voice: **{selected_voice_name}**")
|
502 |
+
else:
|
503 |
+
st.warning("Couldn't load voices. Using default voice.")
|
504 |
|
505 |
if st.button("β¨ Generate Workshop", type="primary", use_container_width=True):
|
506 |
st.session_state.generating = True
|
507 |
+
st.session_state.voiceovers = {} # Reset previous voiceovers
|
508 |
|
509 |
# Generation pipeline
|
510 |
+
if st.session_state.generating:
|
511 |
+
with st.spinner(f"π Creating your {st.session_state.workshop_topic} workshop..."):
|
512 |
start_time = time.time()
|
513 |
|
514 |
# Agent pipeline
|
515 |
+
outline = topic_agent.generate_outline(st.session_state.workshop_topic, duration, difficulty)
|
516 |
content = content_agent.generate_content(outline)
|
517 |
slides = slide_agent.generate_slides(content)
|
518 |
code_labs = code_agent.generate_code(content) if include_code else None
|
519 |
design_url = design_agent.generate_design(slides) if include_design else None
|
520 |
|
521 |
+
# Generate voiceovers if enabled
|
522 |
+
voiceovers = {}
|
523 |
+
if include_voiceover and ELEVENLABS_API_KEY:
|
524 |
+
for i, module in enumerate(content.get("modules", [])):
|
525 |
+
# Create a short intro for each module (limit to 500 characters)
|
526 |
+
intro_text = f"Welcome to Module {i+1}: {module['title']}. " + \
|
527 |
+
f"In this module, we'll cover: {', '.join(module.get('speaker_notes', []))[:300]}"
|
528 |
+
|
529 |
+
# Generate voiceover
|
530 |
+
audio_data = voiceover_agent.generate_voiceover(
|
531 |
+
intro_text,
|
532 |
+
st.session_state.selected_voice
|
533 |
+
)
|
534 |
+
|
535 |
+
if audio_data:
|
536 |
+
voiceovers[f"module_{i+1}_intro.mp3"] = audio_data
|
537 |
+
|
538 |
# Prepare download package
|
539 |
zip_buffer = io.BytesIO()
|
540 |
with zipfile.ZipFile(zip_buffer, "a") as zip_file:
|
|
|
549 |
zip_file.writestr("slide_design.png", img_data)
|
550 |
except:
|
551 |
pass
|
552 |
+
# Add voiceovers to ZIP
|
553 |
+
for filename, audio_data in voiceovers.items():
|
554 |
+
zip_file.writestr(f"voiceovers/{filename}", audio_data)
|
555 |
|
556 |
# Store results
|
557 |
st.session_state.outline = outline
|
|
|
559 |
st.session_state.slides = slides
|
560 |
st.session_state.code_labs = code_labs
|
561 |
st.session_state.design_url = design_url
|
562 |
+
st.session_state.voiceovers = voiceovers
|
563 |
st.session_state.zip_buffer = zip_buffer
|
564 |
st.session_state.gen_time = round(time.time() - start_time, 2)
|
565 |
st.session_state.generated = True
|
566 |
st.session_state.generating = False
|
567 |
|
568 |
# Results display
|
569 |
+
if st.session_state.generated:
|
570 |
+
st.success(f"β
{st.session_state.workshop_topic} workshop generated in {st.session_state.gen_time} seconds!")
|
571 |
|
572 |
# Download button
|
573 |
st.download_button(
|
574 |
label="π₯ Download Workshop Package",
|
575 |
data=st.session_state.zip_buffer.getvalue(),
|
576 |
+
file_name=f"{st.session_state.workshop_topic.replace(' ', '_')}_workshop.zip",
|
577 |
mime="application/zip",
|
578 |
use_container_width=True
|
579 |
)
|
|
|
595 |
if st.session_state.design_url:
|
596 |
with st.expander("π¨ Generated Design"):
|
597 |
st.image(st.session_state.design_url, caption="Custom Slide Design")
|
598 |
+
|
599 |
+
# Voiceover player
|
600 |
+
if st.session_state.voiceovers:
|
601 |
+
with st.expander("π Voiceover Previews"):
|
602 |
+
for i, (filename, audio_bytes) in enumerate(st.session_state.voiceovers.items()):
|
603 |
+
module_num = filename.split("_")[1]
|
604 |
+
st.subheader(f"Module {module_num} Introduction")
|
605 |
+
|
606 |
+
# Create an audio player for each voiceover
|
607 |
+
st.audio(audio_bytes, format="audio/mp3")
|
608 |
+
|
609 |
+
# Add download button for individual voiceover
|
610 |
+
st.download_button(
|
611 |
+
label=f"Download Module {module_num} Voiceover",
|
612 |
+
data=audio_bytes,
|
613 |
+
file_name=filename,
|
614 |
+
mime="audio/mpeg",
|
615 |
+
key=f"voiceover_dl_{i}"
|
616 |
+
)
|
617 |
|
618 |
# Sales and booking section
|
619 |
st.divider()
|
620 |
st.subheader("π Ready to Deliver This Workshop?")
|
621 |
+
st.markdown(f"""
|
622 |
+
### Premium {st.session_state.workshop_topic} Training Package
|
623 |
- **Live Workshop Delivery**: $10,000 per session
|
624 |
- **On-Demand Course**: $5,000 (unlimited access)
|
625 |
- **Pilot Program**: $1,000 refundable deposit
|
626 |
+
- **Voiceover Add-on**: $500 per module
|
627 |
|
628 |
β¨ **All inclusive**: Customization, materials, and follow-up support
|
629 |
""")
|
|
|
644 |
else:
|
645 |
st.warning("OpenAI API not set - using enhanced mock data")
|
646 |
|
647 |
+
if ELEVENLABS_API_KEY:
|
648 |
+
st.success("ElevenLabs API Connected")
|
649 |
+
elif st.session_state.get('voiceovers'):
|
650 |
+
st.warning("ElevenLabs API not set - using mock voiceovers")
|
651 |
+
|
652 |
+
st.info(f"""
|
653 |
+
**Current Workshop:**
|
654 |
+
{st.session_state.workshop_topic}
|
655 |
+
|
656 |
**Premium Features:**
|
657 |
+
- AI-generated voiceovers
|
658 |
+
- Professional slide designs
|
659 |
- Real-world case studies
|
660 |
- Practical code labs
|
|
|
661 |
""")
|
662 |
|
663 |
# How it works section
|
|
|
665 |
st.subheader("π‘ How It Works")
|
666 |
st.markdown("""
|
667 |
1. **Configure** your workshop topic and parameters
|
668 |
+
2. **Generate** premium training materials with voiceovers
|
669 |
3. **Customize** the content to your specific needs
|
670 |
4. **Deliver** high-value corporate training at $10K/session
|
671 |
5. **Reuse** the materials for unlimited revenue
|
672 |
|
673 |
+
*"The voiceover feature helped me create on-demand courses that sold for $5K each"* - Michael L., AI Consultant
|
674 |
""")
|