This PR adds Ollama support to Stream Daemon, enabling users to run AI-generated messages completely locally with full privacy and zero API costs. Users can now choose between Google Gemini (cloud) or Ollama (local) for AI message generation.
New Features:
- Local LLM Execution - Run AI models on your own hardware
- 100% Privacy - Stream data never leaves your network
- Zero API Costs - Unlimited message generation
- Offline Capable - Works without internet connection
- Multiple Models - Support for gemma2, llama3.2, qwen2.5, mistral, phi3, and more
- Provider Selection - Easy switching between Ollama and Gemini via
LLM_PROVIDERconfig
- Consolidated .env.example - Merged Ollama configuration into main example file
- Updated README - Added Ollama as recommended AI provider option
- AI Messages Guide - Enhanced with detailed Ollama setup instructions
- Migration Guide - Help existing Gemini users understand Ollama option
- Moved all tests to tests/ folder - Better organization
- test_connection.py - Comprehensive integration test using real .env data
- Tests streaming platform authentication (Twitch, YouTube, Kick)
- Tests social platform authentication (Mastodon, Bluesky, Discord, Matrix)
- Tests LLM generation with actual live stream data
- Tests AI message posting to all social platforms
- Production readiness validation
- test_ollama.py - Dedicated Ollama integration testing
- test_local_install.py - Dependency validation
All tests can now run from the tests/ folder with proper import paths.
# Choose your AI provider
LLM_PROVIDER=ollama # or 'gemini'
# Ollama-specific settings
LLM_OLLAMA_HOST=http://192.168.1.100
LLM_OLLAMA_PORT=11434
LLM_MODEL=gemma2:2b # or gemma3:4b, llama3.2:3b, etc.New Files:
tests/test_connection.py- Production-ready integration testingtests/test_ollama.py- Ollama-specific testing (moved from root)tests/test_local_install.py- Dependency validation (moved from root)
Modified Files:
stream_daemon/ai/generator.py- Added Ollama provider support.env.example- Consolidated Ollama and Gemini configurationREADME.md- Updated to highlight Ollama as recommended optiondocs/features/ai-messages.md- Enhanced with Ollama setup guide
Removed Files:
.env.ollama.example- Consolidated into main .env.example
class AIMessageGenerator:
def __init__(self):
self.provider = os.getenv('LLM_PROVIDER', 'gemini').lower()
if self.provider == 'ollama':
self.client = ollama.Client(
host=f"{ollama_host}:{ollama_port}"
)
elif self.provider == 'gemini':
self.client = genai.Client(api_key=api_key)
def authenticate(self) -> bool:
"""Verify connection to selected provider"""
if self.provider == 'ollama':
return self._authenticate_ollama()
elif self.provider == 'gemini':
return self._authenticate_gemini()-
Ollama Provider Tests:
# Test Ollama connection and message generation python3 tests/test_ollama.py # Output: # ✅ SUCCESS: Ollama connection initialized! # ✅ SUCCESS: Generated stream start message! # ✅ SUCCESS: Generated Mastodon message! # ✅ SUCCESS: Generated stream end message!
-
Production Integration Test:
# Test with real .env data and live streams python3 tests/test_connection.py # Results: # ✅ Twitch - LIVE - 110 viewers # ✅ YouTube - LIVE - 245 viewers # ✅ Kick - LIVE - 11,002 viewers # ✅ AI generated message posted to all 4 social platforms # ✅ Stream Daemon is PRODUCTION READY!
-
Dependency Validation:
python3 tests/test_local_install.py # ✅ SUCCESS - All dependencies installed correctly!
- ✅ Ollama server connectivity
- ✅ Model availability verification
- ✅ Message generation for all social platforms (Bluesky, Mastodon, Discord, Matrix)
- ✅ Character limit enforcement (300 for Bluesky, 500 for Mastodon)
- ✅ Stream start and end message generation
- ✅ Error handling and retry logic
- ✅ Fallback to static messages when AI unavailable
- ✅ Real production environment testing with live streams
No changes required! Your current setup continues to work:
# Current setup - still works perfectly
LLM_ENABLE=True
GEMINI_API_KEY=AIzaSyA_your_key_here
LLM_MODEL=gemini-2.0-flash-liteDefault provider is gemini, so existing deployments are unaffected.
# 1. Install Ollama
curl -fsSL https://ollama.com/install.sh | sh
# 2. Pull a model
ollama pull gemma2:2b
# 3. Start server
ollama serve
# 4. Update .env
LLM_ENABLE=True
LLM_PROVIDER=ollama # ← New setting
LLM_OLLAMA_HOST=http://localhost
LLM_OLLAMA_PORT=11434
LLM_MODEL=gemma2:2bSee docs/features/ollama-migration.md for complete guide.
- Latency: 0.5-2 seconds (hardware dependent)
- Cost: $0 per message
- Rate Limit: None (unlimited)
- Privacy: 100% local
- Hardware:
- Minimum: 4GB RAM, CPU-only (slower)
- Recommended: 8GB+ RAM, NVIDIA/AMD/Apple GPU
- Optimal: 16GB+ RAM, dedicated GPU
- Latency: 1-2 seconds
- Cost: ~$0.0001 per message (Gemini 2.0 Flash Lite)
- Rate Limit: 10-30 requests/minute (model dependent)
- Privacy: Data sent to Google Cloud
- Hardware: None required
- AI Messages Guide - Complete setup for both providers
- Ollama Migration Guide - Switching from Gemini
- Ollama Library - Browse available models
- FrankenLLM - Multi-GPU Ollama setup
- Code changes tested locally
- Documentation updated
- Tests pass (
test_ollama.py,test_connection.py) - Backward compatibility maintained
- Example configuration provided
- Migration guide created
- Tests organized in tests/ folder
- .env.example consolidated
This PR enables privacy-conscious users to run AI-generated messages completely locally while maintaining full compatibility with existing Gemini users. No breaking changes, zero configuration changes required for existing deployments.
- ✅ Minimum 2-second delay between requests (stays under 30 RPM limit)
- ✅ Thread-safe coordination across all platforms
- ✅ Maintains existing retry logic with exponential backoff
- ✅ Prevents quota exhaustion from simultaneous streams
Request Pattern Example:
Twitch goes live → 4 social platforms
├─ Request 1: Bluesky (0s)
├─ Request 2: Mastodon (2s delay)
├─ Request 3: Discord (4s delay)
└─ Request 4: Matrix (6s delay)
YouTube goes live → waits for semaphore slots
├─ Request 5: Bluesky (8s)
└─ ...continues with 2s spacing
stream_daemon/platforms/streaming/youtube.py (22 insertions, 4 deletions)
- Added
error_cooldown_timetracking - Implemented 10-minute cooldown check in
is_live() - Enhanced error logging with X/5 counter
- Automatic reset after cooldown expires
stream_daemon/ai/generator.py (35 insertions, 6 deletions)
- Added
threadingimport forSemaphore - Implemented global semaphore (max 4 concurrent)
- Added minimum delay enforcement (2 seconds)
- Thread-safe coordination with locks
- Updated docstrings to reflect actual implementation
✅ Fully backward compatible - no configuration changes required:
- YouTube monitoring works exactly as before, just with automatic recovery
- AI message generation works exactly as before, just with rate limiting
- No breaking changes to APIs or configuration
- Existing behavior preserved, only adds resilience
Test Scenario: Simulate consecutive YouTube API errors
# After 5 errors, enters 10-minute cooldown
# Logs: "YouTube: Maximum consecutive errors (5) reached. Entering 10-minute cooldown."
# During cooldown: "YouTube: In error cooldown. X minutes remaining."
# After cooldown: "YouTube: Error cooldown period ended. Resetting consecutive errors."Manual Testing:
- Tested with invalid API key (triggers errors)
- Verified cooldown countdown in logs
- Confirmed automatic recovery after 10 minutes
- Verified error counter resets correctly
Test Scenario: Multiple streams go live simultaneously
# 3 streaming platforms × 4 social networks = 12 potential requests
# Semaphore limits to 4 concurrent
# 2-second delay between each request
# Total time: ~24 seconds for 12 requests (stays well under 30 RPM)Expected Behavior:
- Requests queue automatically when 4 concurrent limit reached
- Debug logs show: "Rate limiting: waiting Xs before API call"
- No 429 errors from Gemini API
- All announcements eventually post successfully
- Merge this PR to main
- Deploy to production server (192.168.213.210):
ssh user@192.168.213.210 cd /path/to/stream-daemon git pull origin main docker build -f Docker/Dockerfile -t stream-daemon:local . docker stop stream-daemon && docker rm stream-daemon docker-compose up -d # or your container restart method
- Monitor logs for recovery messages:
docker logs -f stream-daemon | grep -E "cooldown|rate limit"
- Verify YouTube resumes after errors
- Watch for Gemini rate limiting working correctly
If issues occur, rollback is simple:
git checkout <previous-commit>
docker build -f Docker/Dockerfile -t stream-daemon:local .
docker-compose restartDocumentation has been updated to reflect new error recovery behavior:
- README.md: Updated YouTube feature description to mention automatic error recovery
- docs/platforms/streaming/youtube.md: Added "Error Recovery" section documenting cooldown behavior
- docs/features/ai-messages.md: Added "Rate Limiting" section documenting semaphore and delay
- Code follows project style guidelines
- Backward compatible (no breaking changes)
- Error handling is comprehensive
- Logging is clear and helpful
- Thread-safe implementation (rate limiting)
- Documentation updated
- Manual testing completed
- Production-ready
After merging:
- YouTube monitoring is resilient: Automatic recovery from temporary API issues without manual intervention
- Gemini API stays under limits: Proactive rate limiting prevents 429 errors even with simultaneous streams
- Better user experience: No lost announcements due to platform errors or rate limits
- Reduced manual intervention: Daemon self-heals from common error conditions
- Production stability: Both issues observed in production logs are resolved
- Fixes production issue: YouTube "5 consecutive errors" permanent disable
- Addresses missing implementation: Gemini rate limiting mentioned in comments but not coded
- Improves resilience: Both YouTube and AI generation become self-healing
Branch: hotfix/youtube-error-recovery
Commits:
e483316- fix: Add automatic recovery for YouTube consecutive errors0e17562- feat: Add Gemini API rate limiting
Ready for: Immediate merge to main and production deployment