Your Personalized NewsBriefing - Built for ElevenLabs x a16z AI Agents Hackathon 2025
- π Morning Calm:
sonicpress_news_elevenlabs.mp4
- π Evening Energetic:
sonicpress_news_mistralai.mp4
- β‘ Breaking Urgent:
sonicpress_news_andrej_karpathy.mp4
- Personalized News Aggregation: Multi-category news curation with custom topic support
- AI-Powered Summarization: Intelligent content distillation using Mistral AI
- Smart Relevance Filtering: Ensures only topic-relevant articles are included in your briefing
- Dynamic Voice Synthesis: ElevenLabs-powered narration with 3 distinct modes:
- π Morning Calm: Warm, relaxed delivery
- π Evening Energetic: Dynamic, engaging style
- β‘ Breaking Urgent: Authoritative, urgent tone
- Real-time Video Generation: Automated video creation with dynamic image fetching
- Cloud Integration: Google Cloud Storage for asset management
- Modern Web Interface: Streamlit-based UI with NYTimes-inspired styling
- Cloud-Ready: Optimized for Google Cloud Run deployment
The SonicPress system uses an agentic approach with asynchronous tool calls to generate personalized news content:
flowchart TD
%% User interaction and Streamlit
User([User]) --> StreamlitUI[Streamlit Web UI]
StreamlitUI --> UserQuery[User Query Input]
%% Main agent flow
UserQuery --> InitSystem[Initialize Chat History with System Prompt]
InitSystem --> StartLoop[Start Loop]
StartLoop --> CallLLM[Call LiteLLM via Proxy API]
CallLLM --> HasToolCalls{Has Tool Calls?}
HasToolCalls -->|Yes| ExtractToolDetails[Extract Tool Call Details]
HasToolCalls -->|No| ExtractFinalContents[Extract Final Contents]
ExtractToolDetails --> ToolType{Tool Type}
ToolType -->|get_preferences| GetPreferences[Call get_preferences]
ToolType -->|fetch_and_summarize| FetchNews[Call fetch_and_summarize via Exa API]
ToolType -->|generate_news_script| GenScript[Call generate_news_script]
ToolType -->|text_to_speech| TextToSpeech[Call text_to_speech via ElevenLabs]
ToolType -->|upload_audio| UploadAudio[Call upload_audio to GCS]
GetPreferences --> StorePreferences[Store Preferences in State]
FetchNews --> StoreSummaries[Store Summaries in State]
GenScript --> StoreScript[Store Script in State]
TextToSpeech --> StoreAudio[Store Audio Path in State]
UploadAudio --> StoreAudioURL[Store Audio URL in State]
StorePreferences --> UpdateHistory[Update Chat History with Function Result]
StoreSummaries --> UpdateHistory
StoreScript --> UpdateHistory
StoreAudio --> UpdateHistory
StoreAudioURL --> UpdateHistory
UpdateHistory --> StartLoop
ExtractFinalContents --> CheckForVideo[Check if Audio Exists for Video]
CheckForVideo --> VideoNeeded{Need Video?}
VideoNeeded -->|Yes| GenerateVideo[Generate Video with MoviePy & FFmpeg]
VideoNeeded -->|No| DisplayResults[Display Results]
GenerateVideo --> StoreVideo[Store Video Path in State]
StoreVideo --> DisplayResults
%% Results back to UI
DisplayResults --> StreamlitUI
%% Cloud Run deployment
subgraph CloudRun[Google Cloud Run]
direction TB
StreamlitUI
UserQuery
InitSystem
StartLoop
CallLLM
HasToolCalls
ExtractToolDetails
ToolType
GetPreferences
FetchNews
GenScript
TextToSpeech
UploadAudio
StorePreferences
StoreSummaries
StoreScript
StoreAudio
StoreAudioURL
UpdateHistory
ExtractFinalContents
CheckForVideo
VideoNeeded
GenerateVideo
StoreVideo
DisplayResults
end
%% External storage
UploadAudio --> GCS[(Google Cloud Storage)]
StoreVideo --> GCS
GCS --> StreamlitUI
%% Styling
style CallLLM fill:#f9c6e5,stroke:#d44a7a,stroke-width:2px
style HasToolCalls fill:#ffe066,stroke:#d4a44a,stroke-width:2px
style ToolType fill:#ffe066,stroke:#d4a44a,stroke-width:2px
style VideoNeeded fill:#ffe066,stroke:#d4a44a,stroke-width:2px
style GetPreferences fill:#c6d9f7,stroke:#4a6da7,stroke-width:2px
style FetchNews fill:#c6d9f7,stroke:#4a6da7,stroke-width:2px
style GenScript fill:#c6d9f7,stroke:#4a6da7,stroke-width:2px
style TextToSpeech fill:#c6d9f7,stroke:#4a6da7,stroke-width:2px
style UploadAudio fill:#c6d9f7,stroke:#4a6da7,stroke-width:2px
style GenerateVideo fill:#c6d9f7,stroke:#4a6da7,stroke-width:2px
style ExtractToolDetails fill:#f0f0f0,stroke:#333,stroke-width:2px
style ExtractFinalContents fill:#a8e6cf,stroke:#3a8f5d,stroke-width:2px
style DisplayResults fill:#a8e6cf,stroke:#3a8f5d,stroke-width:2px
style StreamlitUI fill:#61dafb,stroke:#2d8bba,stroke-width:2px
style CloudRun fill:#f1f8ff,stroke:#4285f4,stroke-width:2px
style GCS fill:#c6f7d6,stroke:#4aa76d,stroke-width:2px
The flowchart illustrates the complete system architecture:
- User Interaction: Users interact with the Streamlit web interface to input preferences
- Agent Loop: The NewsAgent runs a loop that checks for tool calls and executes them
- Decision Points: The system makes decisions based on tool call availability and type
- Tool Execution: Different tools handle specific tasks (fetching news, generating scripts, etc.)
- State Management: Results from each tool call are stored in the agent's state
- External APIs: The system integrates with ElevenLabs, Mistral AI, and Exa for various services
- Final Processing: After completing all tool calls, the system generates video if audio exists
- Cloud Infrastructure: Everything runs on Google Cloud Run with media stored in Google Cloud Storage
- Core: Python 3.11+, Poetry
- AI/ML:
- ElevenLabs API (Voice Synthesis)
- Mistral AI (Text Processing)
- LiteLLM for model management
- Frontend: Streamlit
- Storage: Google Cloud Storage
- News Source: Exa News API
- Video Processing: MoviePy, FFmpeg
- Cloud Platform: Google Cloud Run
- Python 3.11+
- Poetry for dependency management
- FFmpeg installed on system
- API Keys for:
- ElevenLabs
- Mistral AI
- Exa News
- Google Cloud credentials (optional for cloud storage)
- Clone the Repository:
git clone https://github.com/yourusername/agentic-news.git
cd agentic-news
- Install Dependencies:
poetry install
- Environment Setup:
cp .env.example .env
Edit .env
with your credentials:
EXA_API_KEY=your_exa_key
ELEVENLABS_API_KEY=your_elevenlabs_key
MISTRAL_API_KEY=your_mistral_key
GCS_BUCKET_NAME=your_bucket_name (optional)
GOOGLE_CREDS_JSON=path_to_your_creds.json (optional)
- Run the Application:
poetry run streamlit run streamlit_app.py
The easiest way to deploy SonicPress to Google Cloud Run is using the provided deployment script:
# Make the script executable
chmod +x deploy_to_cloud_run.sh
# Run the deployment script
./deploy_to_cloud_run.sh
The script will:
- Check for required dependencies (gcloud CLI)
- Verify you're logged into Google Cloud
- Create necessary API keys in Secret Manager
- Create a GCS bucket for media storage
- Build and push the Docker image
- Deploy the application to Cloud Run
If you prefer to deploy manually, follow these steps:
- Set up API Keys in Secret Manager:
# IMPORTANT: Use echo -n to avoid newline characters in your secrets
echo -n "your_exa_key" | gcloud secrets create sonicpress-exa-key --data-file=-
echo -n "your_elevenlabs_key" | gcloud secrets create sonicpress-elevenlabs-key --data-file=-
echo -n "your_mistral_key" | gcloud secrets create sonicpress-mistral-key --data-file=-
echo -n "your_bucket_name" | gcloud secrets create sonicpress-gcs-bucket --data-file=-
echo -n "your_litellm_proxy_url" | gcloud secrets create sonicpress-litellm-proxy-url --data-file=-
- Create a GCS Bucket for Media Storage:
gsutil mb -l us-central1 gs://your_bucket_name
gsutil iam ch allUsers:objectViewer gs://your_bucket_name
- Build and Push the Docker Image:
# Build the image
docker build -t gcr.io/your-project-id/sonicpress:latest .
# Push to Google Container Registry
docker push gcr.io/your-project-id/sonicpress:latest
- Deploy to Cloud Run:
gcloud run deploy sonicpress \
--image=gcr.io/your-project-id/sonicpress:latest \
--platform=managed \
--region=us-central1 \
--memory=2Gi \
--cpu=2 \
--min-instances=0 \
--max-instances=10 \
--timeout=3600 \
--set-secrets="MISTRAL_API_KEY=sonicpress-mistral-key:latest,EXA_API_KEY=sonicpress-exa-key:latest,ELEVENLABS_API_KEY=sonicpress-elevenlabs-key:latest" \
--set-env-vars="LITELLM_PROXY_URL=your_litellm_proxy_url,GCS_BUCKET_NAME=your_bucket_name" \
--allow-unauthenticated
If you encounter issues with your Cloud Run deployment, check the following:
-
API Key Format: Ensure your API keys don't contain newline characters. Use
echo -n
when creating secrets.echo -n "your_api_key" | gcloud secrets versions add sonicpress-exa-key --data-file=-
-
Check Logs: View the service logs to identify specific errors.
gcloud logging read "resource.type=cloud_run_revision AND resource.labels.service_name=sonicpress" --limit=20
-
Update Environment Variables: If you need to update environment variables or secrets:
gcloud run services update sonicpress --region=us-central1 --set-secrets=SECRET_NAME=secret-name:latest
-
Verify Configuration: Check the current configuration of your service:
gcloud run services describe sonicpress --region=us-central1 --format="yaml(spec.template.spec.containers[0].env)"
-
LiteLLM Proxy: If using the LiteLLM Proxy, ensure it's properly deployed and accessible.
Build and run with Docker:
# Build the image
docker build -t sonicpress .
# Run the container
docker run -d -p 8080:8080 \
-e EXA_API_KEY=your_key \
-e ELEVENLABS_API_KEY=your_key \
-e MISTRAL_API_KEY=your_key \
-e GCS_BUCKET_NAME=your_bucket \
-e GOOGLE_CREDS_JSON=your_creds \
sonicpress
- Select news categories or enter custom topics
- Choose your preferred voice mode
- Adjust content settings (articles per topic, news age)
- Click "Compile My Headlines"
- Download the generated video or audio
Each voice mode has optimized parameters:
VOICE_SETTINGS = {
"Morning Calm": {
"stability": 0.71,
"similarity_boost": 0.85,
"style": 0.35,
"speed": 1.0
},
"Evening Energetic": {
"stability": 0.71,
"similarity_boost": 0.85,
"style": 0.35,
"speed": 1.0
},
"Breaking Urgent": {
"stability": 0.71,
"similarity_boost": 0.85,
"style": 0.35,
"speed": 1.0
}
}
The system automatically creates engaging news videos:
- Dynamic Image Fetching: Retrieves relevant images for each article
- Structured Layout: Organizes content with headlines, images, and summaries
- Scrolling Ticker: Displays headlines in a news-style ticker
- Adaptive Formatting: Adjusts layout based on available content
- Resolution: 1280x720 with H.264 encoding
- Audio: AAC codec, 44.1kHz
SonicPress uses a LiteLLM Proxy for improved reliability and performance of LLM API calls. The proxy provides:
- Fallbacks: If one model fails, it automatically retries with another model
- Load balancing: Distributes requests across multiple models or providers
- Unified API: Uses the same API for all LLM providers
For optimal performance, we recommend deploying the LiteLLM Proxy to Cloud Run:
# Navigate to the litellm_proxy directory
cd litellm_proxy
# Make the deployment script executable
chmod +x deploy_gcloud.sh
# Set your Mistral API key
export MISTRAL_API_KEY=your_mistral_api_key
# Run the deployment script
./deploy_gcloud.sh
The script will:
- Build a Docker image for the LiteLLM Proxy
- Push it to Google Container Registry
- Deploy it to Cloud Run with the appropriate configuration
- Return the service URL to use in your application
If you prefer to run the LiteLLM Proxy locally for development:
# Set your Mistral API key
export MISTRAL_API_KEY=your_api_key_here
# Navigate to the litellm_proxy directory
cd litellm_proxy
# Build and run the Docker container
docker build -t litellm-proxy .
docker run -d -p 8080:8080 -e MISTRAL_API_KEY=$MISTRAL_API_KEY litellm-proxy
After deploying the LiteLLM Proxy, update your application to use it:
# Set the LITELLM_PROXY_URL environment variable
export LITELLM_PROXY_URL=https://your-litellm-proxy-url
# Update the application configuration
python litellm_proxy/update_app.py
If you encounter issues with the LiteLLM Proxy:
-
Check Proxy Logs: View the proxy service logs to identify errors.
gcloud logging read "resource.type=cloud_run_revision AND resource.labels.service_name=litellm-proxy" --limit=20
-
Verify API Keys: Ensure your Mistral API key is correctly set in the proxy service.
echo -n "your_mistral_api_key" | gcloud secrets versions add litellm-mistral-key --data-file=- gcloud run services update litellm-proxy --region=us-central1 --set-env-vars=MISTRAL_API_KEY=your_mistral_api_key
-
Test the Proxy: Send a test request to verify it's working.
curl -X POST "https://your-litellm-proxy-url/v1/chat/completions" \ -H "Content-Type: application/json" \ -d '{ "model": "mistral-small", "messages": [{"role": "user", "content": "Hello, world!"}] }'
If you encounter "No articles found" errors, try the following solutions:
-
Check API Keys: Ensure your API keys are correctly formatted and don't contain newline characters.
# Update the EXA_API_KEY in Secret Manager (remove any newline characters) echo -n "your_exa_api_key" | gcloud secrets versions add sonicpress-exa-key --data-file=- # Update the service to use the latest version gcloud run services update sonicpress --region=us-central1 --set-secrets=EXA_API_KEY=sonicpress-exa-key:latest
-
Verify Environment Variables: Make sure all required environment variables are set in your service.
# Check the current configuration gcloud run services describe sonicpress --region=us-central1 --format="yaml(spec.template.spec.containers[0].env)"
-
Broaden Search Parameters: Try using more general topics or a broader date range.
- In the UI, select broader categories like "Tech and Innovation" instead of specific technologies
- Increase the "News age (days)" slider to search for older articles
-
Check Logs for Specific Errors: Look for error messages in the logs.
# Check for errors related to the Exa API gcloud logging read "resource.type=cloud_run_revision AND resource.labels.service_name=sonicpress AND textPayload:Exa" --limit=20 # Check for errors related to the Mistral API gcloud logging read "resource.type=cloud_run_revision AND resource.labels.service_name=sonicpress AND textPayload:Mistral" --limit=20
-
Verify LiteLLM Proxy Connection: Ensure the LiteLLM Proxy is working correctly.
# Check if the LiteLLM Proxy URL is correctly set gcloud run services describe sonicpress --region=us-central1 --format="value(spec.template.spec.containers[0].env[?(@.name=='LITELLM_PROXY_URL')].value)" # Check the LiteLLM Proxy logs gcloud logging read "resource.type=cloud_run_revision AND resource.labels.service_name=litellm-proxy" --limit=20
If you encounter errors like:
MoviePy Error: creation of None failed because of the following error:
convert-im6.q16: no images defined `PNG32:/tmp/tmpXXXXXXX.png' @ error/convert.c/ConvertImageCommand/3229.
This is related to ImageMagick configuration. Here's how to fix it:
-
For Local Development:
- Ensure ImageMagick is installed:
apt-get install imagemagick
- Modify the ImageMagick policy file:
sudo nano /etc/ImageMagick-6/policy.xml
- Update resource limits and permissions:
<!-- Increase memory limits --> <policy domain="resource" name="memory" value="8GiB"/> <policy domain="resource" name="map" value="4GiB"/> <policy domain="resource" name="disk" value="8GiB"/> <!-- Allow PDF operations if needed --> <policy domain="coder" rights="read|write" pattern="PDF" />
- Ensure ImageMagick is installed:
-
For Docker/Cloud Run Deployment:
- The Dockerfile already includes the necessary configuration
- If you're still encountering issues, check that the ImageMagick policy is being properly modified during the build process
- You can verify the policy file inside the container:
docker exec -it your_container_id cat /etc/ImageMagick-6/policy.xml
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Open a Pull Request
Built for the ElevenLabs x a16z AI Agents Hackathon 2025, focusing on:
- Voice AI Innovation
- Autonomous Agents
- Real-world Applications
- Exa for news search API
- Mistral AI for language processing
- Google Cloud Platform for infrastructure
- Raghav Gupta
- Hrishabh Bhargava