mirror of
https://github.com/Shubhamsaboo/awesome-llm-apps.git
synced 2026-04-30 15:20:47 -05:00
feat: Revamp Blog to Podcast Agent with improved API key handling and audio generation
- Updated Streamlit UI for better user experience with API key inputs and blog URL entry. - Enhanced podcast generation process by integrating ElevenLabs for audio output. - Improved error handling and summary display for generated podcasts. - Updated requirements to ensure compatibility with newer library versions.
This commit is contained in:
@@ -1,100 +1,86 @@
|
|||||||
import os
|
import os
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
from agno.agent import Agent
|
from agno.agent import Agent
|
||||||
|
from agno.run.agent import RunOutput
|
||||||
from agno.models.openai import OpenAIChat
|
from agno.models.openai import OpenAIChat
|
||||||
from agno.tools.eleven_labs import ElevenLabsTools
|
|
||||||
from agno.tools.firecrawl import FirecrawlTools
|
from agno.tools.firecrawl import FirecrawlTools
|
||||||
from agno.agent import Agent, RunResponse
|
from elevenlabs import ElevenLabs
|
||||||
from agno.utils.audio import write_audio_to_file
|
|
||||||
from agno.utils.log import logger
|
|
||||||
import streamlit as st
|
import streamlit as st
|
||||||
|
|
||||||
# Streamlit Page Setup
|
# Streamlit Setup
|
||||||
st.set_page_config(page_title="📰 ➡️ 🎙️ Blog to Podcast Agent", page_icon="🎙️")
|
st.set_page_config(page_title="📰 ➡️ 🎙️ Blog to Podcast", page_icon="🎙️")
|
||||||
st.title("📰 ➡️ 🎙️ Blog to Podcast Agent")
|
st.title("📰 ➡️ 🎙️ Blog to Podcast Agent")
|
||||||
|
|
||||||
# Sidebar: API Keys
|
# API Keys (Runtime Input)
|
||||||
st.sidebar.header("🔑 API Keys")
|
st.sidebar.header("🔑 API Keys")
|
||||||
|
openai_key = st.sidebar.text_input("OpenAI API Key", type="password")
|
||||||
|
elevenlabs_key = st.sidebar.text_input("ElevenLabs API Key", type="password")
|
||||||
|
firecrawl_key = st.sidebar.text_input("Firecrawl API Key", type="password")
|
||||||
|
|
||||||
openai_api_key = st.sidebar.text_input("OpenAI API Key", type="password")
|
# Blog URL Input
|
||||||
elevenlabs_api_key = st.sidebar.text_input("ElevenLabs API Key", type="password")
|
url = st.text_input("Enter Blog URL:", "")
|
||||||
firecrawl_api_key = st.sidebar.text_input("Firecrawl API Key", type="password")
|
|
||||||
|
|
||||||
# Check if all keys are provided
|
# Generate Button
|
||||||
keys_provided = all([openai_api_key, elevenlabs_api_key, firecrawl_api_key])
|
if st.button("🎙️ Generate Podcast", disabled=not all([openai_key, elevenlabs_key, firecrawl_key])):
|
||||||
|
if not url.strip():
|
||||||
# Input: Blog URL
|
st.warning("Please enter a blog URL")
|
||||||
url = st.text_input("Enter the Blog URL:", "")
|
|
||||||
|
|
||||||
# Button: Generate Podcast
|
|
||||||
generate_button = st.button("🎙️ Generate Podcast", disabled=not keys_provided)
|
|
||||||
|
|
||||||
if not keys_provided:
|
|
||||||
st.warning("Please enter all required API keys to enable podcast generation.")
|
|
||||||
|
|
||||||
if generate_button:
|
|
||||||
if url.strip() == "":
|
|
||||||
st.warning("Please enter a blog URL first.")
|
|
||||||
else:
|
else:
|
||||||
# Set API keys as environment variables for Agno and Tools
|
with st.spinner("Scraping blog and generating podcast..."):
|
||||||
os.environ["OPENAI_API_KEY"] = openai_api_key
|
|
||||||
os.environ["ELEVEN_LABS_API_KEY"] = elevenlabs_api_key
|
|
||||||
os.environ["FIRECRAWL_API_KEY"] = firecrawl_api_key
|
|
||||||
|
|
||||||
with st.spinner("Processing... Scraping blog, summarizing and generating podcast 🎶"):
|
|
||||||
try:
|
try:
|
||||||
blog_to_podcast_agent = Agent(
|
# Set API keys
|
||||||
name="Blog to Podcast Agent",
|
os.environ["OPENAI_API_KEY"] = openai_key
|
||||||
agent_id="blog_to_podcast_agent",
|
os.environ["FIRECRAWL_API_KEY"] = firecrawl_key
|
||||||
|
|
||||||
|
# Create agent for scraping and summarization
|
||||||
|
agent = Agent(
|
||||||
|
name="Blog Summarizer",
|
||||||
model=OpenAIChat(id="gpt-4o"),
|
model=OpenAIChat(id="gpt-4o"),
|
||||||
tools=[
|
tools=[FirecrawlTools()],
|
||||||
ElevenLabsTools(
|
|
||||||
voice_id="JBFqnCBsd6RMkjVDRZzb",
|
|
||||||
model_id="eleven_multilingual_v2",
|
|
||||||
target_directory="audio_generations",
|
|
||||||
),
|
|
||||||
FirecrawlTools(),
|
|
||||||
],
|
|
||||||
description="You are an AI agent that can generate audio using the ElevenLabs API.",
|
|
||||||
instructions=[
|
instructions=[
|
||||||
"When the user provides a blog URL:",
|
"Scrape the blog URL and create a concise, engaging summary (max 2000 characters) suitable for a podcast.",
|
||||||
"1. Use FirecrawlTools to scrape the blog content",
|
"The summary should be conversational and capture the main points."
|
||||||
"2. Create a concise summary of the blog content that is NO MORE than 2000 characters long",
|
|
||||||
"3. The summary should capture the main points while being engaging and conversational",
|
|
||||||
"4. Use the ElevenLabsTools to convert the summary to audio",
|
|
||||||
"Ensure the summary is within the 2000 character limit to avoid ElevenLabs API limits",
|
|
||||||
],
|
],
|
||||||
markdown=True,
|
|
||||||
debug_mode=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
podcast: RunResponse = blog_to_podcast_agent.run(
|
# Get summary
|
||||||
f"Convert the blog content to a podcast: {url}"
|
response: RunOutput = agent.run(f"Scrape and summarize this blog for a podcast: {url}")
|
||||||
)
|
summary = response.content if hasattr(response, 'content') else str(response)
|
||||||
|
|
||||||
save_dir = "audio_generations"
|
if summary:
|
||||||
os.makedirs(save_dir, exist_ok=True)
|
# Initialize ElevenLabs client and generate audio
|
||||||
|
client = ElevenLabs(api_key=elevenlabs_key)
|
||||||
if podcast.audio and len(podcast.audio) > 0:
|
|
||||||
filename = f"{save_dir}/podcast_{uuid4()}.wav"
|
# Generate audio using text_to_speech.convert
|
||||||
write_audio_to_file(
|
audio_generator = client.text_to_speech.convert(
|
||||||
audio=podcast.audio[0].base64_audio,
|
text=summary,
|
||||||
filename=filename
|
voice_id="JBFqnCBsd6RMkjVDRZzb",
|
||||||
|
model_id="eleven_multilingual_v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
st.success("Podcast generated successfully! 🎧")
|
# Collect audio chunks if it's a generator
|
||||||
audio_bytes = open(filename, "rb").read()
|
audio_chunks = []
|
||||||
st.audio(audio_bytes, format="audio/wav")
|
for chunk in audio_generator:
|
||||||
|
if chunk:
|
||||||
|
audio_chunks.append(chunk)
|
||||||
|
audio_bytes = b"".join(audio_chunks)
|
||||||
|
|
||||||
|
# Display audio
|
||||||
|
st.success("Podcast generated! 🎧")
|
||||||
|
st.audio(audio_bytes, format="audio/mp3")
|
||||||
|
|
||||||
|
# Download button
|
||||||
st.download_button(
|
st.download_button(
|
||||||
label="Download Podcast",
|
"Download Podcast",
|
||||||
data=audio_bytes,
|
audio_bytes,
|
||||||
file_name="generated_podcast.wav",
|
"podcast.mp3",
|
||||||
mime="audio/wav"
|
"audio/mp3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Show summary
|
||||||
|
with st.expander("📄 Podcast Summary"):
|
||||||
|
st.write(summary)
|
||||||
else:
|
else:
|
||||||
st.error("No audio was generated. Please try again.")
|
st.error("Failed to generate summary")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
st.error(f"An error occurred: {e}")
|
st.error(f"Error: {e}")
|
||||||
logger.error(f"Streamlit app error: {e}")
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
agno==1.2.8
|
agno>=2.2.10
|
||||||
streamlit==1.44.1
|
streamlit>=1.40.2
|
||||||
openai
|
openai>=1.102.0
|
||||||
Requests
|
requests
|
||||||
firecrawl-py
|
firecrawl-py>=4.6.0
|
||||||
elevenlabs
|
elevenlabs>=1.0.0
|
||||||
Reference in New Issue
Block a user