Spaces:
Sleeping
Sleeping
Nikita Makarov commited on
Commit Β·
3a37184
1
Parent(s): 369b798
updated structure
Browse files- .gitignore +21 -30
- README.md +26 -1
- ARCHITECTURE.md β docs/ARCHITECTURE.md +0 -0
- DEPLOYMENT.md β docs/DEPLOYMENT.md +0 -0
- GET_GEMINI_KEY.md β docs/GET_GEMINI_KEY.md +0 -0
- INSTALL_FFMPEG.md β docs/INSTALL_FFMPEG.md +0 -0
- INSTALL_VOICE_INPUT.md β docs/INSTALL_VOICE_INPUT.md +0 -0
- PROJECT_OVERVIEW.txt β docs/PROJECT_OVERVIEW.txt +0 -0
- PROJECT_SUMMARY.md β docs/PROJECT_SUMMARY.md +0 -0
- QUICKSTART.md β docs/QUICKSTART.md +0 -0
- SETUP_GUIDE.md β docs/SETUP_GUIDE.md +0 -0
- START_HERE.md β docs/START_HERE.md +0 -0
- run.py +18 -0
- src/__init__.py +2 -0
- app.py β src/app.py +13 -6
- config.py β src/config.py +0 -0
- demo_assets.py β src/demo_assets.py +0 -0
- {mcp_servers β src/mcp_servers}/__init__.py +0 -0
- {mcp_servers β src/mcp_servers}/music_server.py +0 -0
- {mcp_servers β src/mcp_servers}/news_server.py +0 -0
- {mcp_servers β src/mcp_servers}/podcast_server.py +0 -0
- radio_agent.py β src/radio_agent.py +3 -1
- rag_system.py β src/rag_system.py +4 -1
- test_app.py β src/test_app.py +0 -0
- tts_service.py β src/tts_service.py +0 -0
- voice_input.py β src/voice_input.py +0 -0
.gitignore
CHANGED
|
@@ -4,50 +4,41 @@ __pycache__/
|
|
| 4 |
*$py.class
|
| 5 |
*.so
|
| 6 |
.Python
|
| 7 |
-
build/
|
| 8 |
-
develop-eggs/
|
| 9 |
-
dist/
|
| 10 |
-
downloads/
|
| 11 |
-
eggs/
|
| 12 |
-
.eggs/
|
| 13 |
-
lib/
|
| 14 |
-
lib64/
|
| 15 |
-
parts/
|
| 16 |
-
sdist/
|
| 17 |
-
var/
|
| 18 |
-
wheels/
|
| 19 |
*.egg-info/
|
| 20 |
-
|
| 21 |
-
|
| 22 |
|
| 23 |
-
# Virtual
|
|
|
|
| 24 |
venv/
|
| 25 |
-
env/
|
| 26 |
ENV/
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
|
| 29 |
# IDE
|
| 30 |
.vscode/
|
| 31 |
.idea/
|
| 32 |
*.swp
|
| 33 |
*.swo
|
| 34 |
-
*~
|
| 35 |
|
| 36 |
# OS
|
| 37 |
.DS_Store
|
| 38 |
Thumbs.db
|
| 39 |
|
| 40 |
-
#
|
| 41 |
-
.
|
| 42 |
-
|
| 43 |
-
# User data
|
| 44 |
-
user_data.json
|
| 45 |
-
*.mp3
|
| 46 |
-
*.wav
|
| 47 |
-
|
| 48 |
-
# Logs
|
| 49 |
-
*.log
|
| 50 |
|
| 51 |
-
#
|
| 52 |
-
|
| 53 |
|
|
|
|
|
|
|
|
|
| 4 |
*$py.class
|
| 5 |
*.so
|
| 6 |
.Python
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
*.egg-info/
|
| 8 |
+
dist/
|
| 9 |
+
build/
|
| 10 |
|
| 11 |
+
# Virtual environments
|
| 12 |
+
.venv/
|
| 13 |
venv/
|
|
|
|
| 14 |
ENV/
|
| 15 |
+
|
| 16 |
+
# Audio files (generated)
|
| 17 |
+
audio/*.mp3
|
| 18 |
+
audio/*.wav
|
| 19 |
+
|
| 20 |
+
# Logs
|
| 21 |
+
logs/*.log
|
| 22 |
+
|
| 23 |
+
# User data
|
| 24 |
+
user_data.json
|
| 25 |
|
| 26 |
# IDE
|
| 27 |
.vscode/
|
| 28 |
.idea/
|
| 29 |
*.swp
|
| 30 |
*.swo
|
|
|
|
| 31 |
|
| 32 |
# OS
|
| 33 |
.DS_Store
|
| 34 |
Thumbs.db
|
| 35 |
|
| 36 |
+
# Temporary files
|
| 37 |
+
*.part
|
| 38 |
+
*.tmp
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 39 |
|
| 40 |
+
# ChromaDB
|
| 41 |
+
chroma_db/
|
| 42 |
|
| 43 |
+
# Music cache
|
| 44 |
+
music_cache/
|
README.md
CHANGED
|
@@ -30,6 +30,25 @@ tags:
|
|
| 30 |
|
| 31 |
**AI Radio** is an intelligent, personalized radio station powered by cutting-edge AI technology. It creates a unique listening experience tailored to your preferences, mood, and interests. Built for the **MCP 1st Birthday Competition**, this app demonstrates autonomous agent behavior, MCP integration, and advanced RAG capabilities.
|
| 32 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
## β¨ Features
|
| 34 |
|
| 35 |
### π΅ Personalized Music
|
|
@@ -118,10 +137,16 @@ google_api_key: str = "your-gemini-api-key-here"
|
|
| 118 |
|
| 119 |
4. Run the app:
|
| 120 |
```bash
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
python app.py
|
| 122 |
```
|
| 123 |
|
| 124 |
-
5. Open your browser to `http://localhost:
|
| 125 |
|
| 126 |
## π How to Use
|
| 127 |
|
|
|
|
| 30 |
|
| 31 |
**AI Radio** is an intelligent, personalized radio station powered by cutting-edge AI technology. It creates a unique listening experience tailored to your preferences, mood, and interests. Built for the **MCP 1st Birthday Competition**, this app demonstrates autonomous agent behavior, MCP integration, and advanced RAG capabilities.
|
| 32 |
|
| 33 |
+
## π Project Structure
|
| 34 |
+
|
| 35 |
+
```
|
| 36 |
+
ai_radio/
|
| 37 |
+
βββ src/ # Source code
|
| 38 |
+
β βββ app.py # Main Gradio application
|
| 39 |
+
β βββ config.py # Configuration
|
| 40 |
+
β βββ radio_agent.py # AI agent logic
|
| 41 |
+
β βββ tts_service.py # Text-to-speech service
|
| 42 |
+
β βββ rag_system.py # RAG system
|
| 43 |
+
β βββ voice_input.py # Voice input handling
|
| 44 |
+
β βββ mcp_servers/ # MCP server implementations
|
| 45 |
+
βββ docs/ # Documentation files
|
| 46 |
+
βββ audio/ # Generated audio files
|
| 47 |
+
βββ logs/ # Log files
|
| 48 |
+
βββ requirements.txt # Python dependencies
|
| 49 |
+
βββ run.py # Application entry point
|
| 50 |
+
```
|
| 51 |
+
|
| 52 |
## β¨ Features
|
| 53 |
|
| 54 |
### π΅ Personalized Music
|
|
|
|
| 137 |
|
| 138 |
4. Run the app:
|
| 139 |
```bash
|
| 140 |
+
python run.py
|
| 141 |
+
```
|
| 142 |
+
|
| 143 |
+
Or from the src directory:
|
| 144 |
+
```bash
|
| 145 |
+
cd src
|
| 146 |
python app.py
|
| 147 |
```
|
| 148 |
|
| 149 |
+
5. Open your browser to `http://localhost:7863`
|
| 150 |
|
| 151 |
## π How to Use
|
| 152 |
|
ARCHITECTURE.md β docs/ARCHITECTURE.md
RENAMED
|
File without changes
|
DEPLOYMENT.md β docs/DEPLOYMENT.md
RENAMED
|
File without changes
|
GET_GEMINI_KEY.md β docs/GET_GEMINI_KEY.md
RENAMED
|
File without changes
|
INSTALL_FFMPEG.md β docs/INSTALL_FFMPEG.md
RENAMED
|
File without changes
|
INSTALL_VOICE_INPUT.md β docs/INSTALL_VOICE_INPUT.md
RENAMED
|
File without changes
|
PROJECT_OVERVIEW.txt β docs/PROJECT_OVERVIEW.txt
RENAMED
|
File without changes
|
PROJECT_SUMMARY.md β docs/PROJECT_SUMMARY.md
RENAMED
|
File without changes
|
QUICKSTART.md β docs/QUICKSTART.md
RENAMED
|
File without changes
|
SETUP_GUIDE.md β docs/SETUP_GUIDE.md
RENAMED
|
File without changes
|
START_HERE.md β docs/START_HERE.md
RENAMED
|
File without changes
|
run.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/usr/bin/env python3
|
| 2 |
+
"""Entry point for AI Radio application"""
|
| 3 |
+
import sys
|
| 4 |
+
import os
|
| 5 |
+
|
| 6 |
+
# Add src directory to Python path
|
| 7 |
+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "src"))
|
| 8 |
+
|
| 9 |
+
# Import and run the app
|
| 10 |
+
from app import demo
|
| 11 |
+
|
| 12 |
+
if __name__ == "__main__":
|
| 13 |
+
demo.launch(
|
| 14 |
+
server_name="0.0.0.0",
|
| 15 |
+
server_port=7863,
|
| 16 |
+
share=False
|
| 17 |
+
)
|
| 18 |
+
|
src/__init__.py
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""AI Radio - Personalized Radio Station with AI Host"""
|
| 2 |
+
|
app.py β src/app.py
RENAMED
|
@@ -7,6 +7,13 @@ import re
|
|
| 7 |
from typing import Dict, Any, List, Optional
|
| 8 |
import threading
|
| 9 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
from config import get_config
|
| 11 |
from radio_agent import RadioAgent
|
| 12 |
from tts_service import TTSService
|
|
@@ -180,7 +187,7 @@ def play_next_segment():
|
|
| 180 |
if getattr(tts_service, "available", True):
|
| 181 |
audio_bytes = tts_service.text_to_speech(next_batch)
|
| 182 |
if audio_bytes:
|
| 183 |
-
host_audio_file = f"segment_{radio_state['current_segment_index']}_batch_{batch_index}.mp3"
|
| 184 |
tts_service.save_audio(audio_bytes, host_audio_file)
|
| 185 |
|
| 186 |
# Build display info (same news segment)
|
|
@@ -233,7 +240,7 @@ def play_next_segment():
|
|
| 233 |
if text and getattr(tts_service, "available", True):
|
| 234 |
audio_bytes = tts_service.text_to_speech(text)
|
| 235 |
if audio_bytes:
|
| 236 |
-
host_audio_file = f"segment_{radio_state['current_segment_index']}.mp3"
|
| 237 |
tts_service.save_audio(audio_bytes, host_audio_file)
|
| 238 |
|
| 239 |
elif segment["type"] == "news":
|
|
@@ -245,7 +252,7 @@ def play_next_segment():
|
|
| 245 |
if getattr(tts_service, "available", True):
|
| 246 |
audio_bytes = tts_service.text_to_speech(first_batch)
|
| 247 |
if audio_bytes:
|
| 248 |
-
host_audio_file = f"segment_{radio_state['current_segment_index']}_batch_1.mp3"
|
| 249 |
tts_service.save_audio(audio_bytes, host_audio_file)
|
| 250 |
# Store remaining batches for sequential playback
|
| 251 |
segment["remaining_batches"] = script_batches[1:] if len(script_batches) > 1 else []
|
|
@@ -266,7 +273,7 @@ def play_next_segment():
|
|
| 266 |
if commentary and getattr(tts_service, "available", True):
|
| 267 |
audio_bytes = tts_service.text_to_speech(commentary)
|
| 268 |
if audio_bytes:
|
| 269 |
-
host_audio_file = f"segment_{radio_state['current_segment_index']}_host.mp3"
|
| 270 |
tts_service.save_audio(audio_bytes, host_audio_file)
|
| 271 |
|
| 272 |
# Get music track and create streaming player
|
|
@@ -363,7 +370,7 @@ def play_next_segment():
|
|
| 363 |
if intro and getattr(tts_service, "available", True):
|
| 364 |
audio_bytes = tts_service.text_to_speech(intro)
|
| 365 |
if audio_bytes:
|
| 366 |
-
host_audio_file = f"segment_{radio_state['current_segment_index']}_host.mp3"
|
| 367 |
tts_service.save_audio(audio_bytes, host_audio_file)
|
| 368 |
|
| 369 |
# Progress info
|
|
@@ -561,7 +568,7 @@ def handle_voice_request():
|
|
| 561 |
if getattr(tts_service, "available", True):
|
| 562 |
audio_bytes = tts_service.text_to_speech(host_response)
|
| 563 |
if audio_bytes:
|
| 564 |
-
audio_file = f"voice_request_{int(time.time())}.mp3"
|
| 565 |
tts_service.save_audio(audio_bytes, audio_file)
|
| 566 |
|
| 567 |
# Create music player HTML
|
|
|
|
| 7 |
from typing import Dict, Any, List, Optional
|
| 8 |
import threading
|
| 9 |
|
| 10 |
+
# Get project root directory (parent of src/)
|
| 11 |
+
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
| 12 |
+
AUDIO_DIR = os.path.join(PROJECT_ROOT, "audio")
|
| 13 |
+
LOG_DIR = os.path.join(PROJECT_ROOT, "logs")
|
| 14 |
+
os.makedirs(AUDIO_DIR, exist_ok=True)
|
| 15 |
+
os.makedirs(LOG_DIR, exist_ok=True)
|
| 16 |
+
|
| 17 |
from config import get_config
|
| 18 |
from radio_agent import RadioAgent
|
| 19 |
from tts_service import TTSService
|
|
|
|
| 187 |
if getattr(tts_service, "available", True):
|
| 188 |
audio_bytes = tts_service.text_to_speech(next_batch)
|
| 189 |
if audio_bytes:
|
| 190 |
+
host_audio_file = os.path.join(AUDIO_DIR, f"segment_{radio_state['current_segment_index']}_batch_{batch_index}.mp3")
|
| 191 |
tts_service.save_audio(audio_bytes, host_audio_file)
|
| 192 |
|
| 193 |
# Build display info (same news segment)
|
|
|
|
| 240 |
if text and getattr(tts_service, "available", True):
|
| 241 |
audio_bytes = tts_service.text_to_speech(text)
|
| 242 |
if audio_bytes:
|
| 243 |
+
host_audio_file = os.path.join(AUDIO_DIR, f"segment_{radio_state['current_segment_index']}.mp3")
|
| 244 |
tts_service.save_audio(audio_bytes, host_audio_file)
|
| 245 |
|
| 246 |
elif segment["type"] == "news":
|
|
|
|
| 252 |
if getattr(tts_service, "available", True):
|
| 253 |
audio_bytes = tts_service.text_to_speech(first_batch)
|
| 254 |
if audio_bytes:
|
| 255 |
+
host_audio_file = os.path.join(AUDIO_DIR, f"segment_{radio_state['current_segment_index']}_batch_1.mp3")
|
| 256 |
tts_service.save_audio(audio_bytes, host_audio_file)
|
| 257 |
# Store remaining batches for sequential playback
|
| 258 |
segment["remaining_batches"] = script_batches[1:] if len(script_batches) > 1 else []
|
|
|
|
| 273 |
if commentary and getattr(tts_service, "available", True):
|
| 274 |
audio_bytes = tts_service.text_to_speech(commentary)
|
| 275 |
if audio_bytes:
|
| 276 |
+
host_audio_file = os.path.join(AUDIO_DIR, f"segment_{radio_state['current_segment_index']}_host.mp3")
|
| 277 |
tts_service.save_audio(audio_bytes, host_audio_file)
|
| 278 |
|
| 279 |
# Get music track and create streaming player
|
|
|
|
| 370 |
if intro and getattr(tts_service, "available", True):
|
| 371 |
audio_bytes = tts_service.text_to_speech(intro)
|
| 372 |
if audio_bytes:
|
| 373 |
+
host_audio_file = os.path.join(AUDIO_DIR, f"segment_{radio_state['current_segment_index']}_host.mp3")
|
| 374 |
tts_service.save_audio(audio_bytes, host_audio_file)
|
| 375 |
|
| 376 |
# Progress info
|
|
|
|
| 568 |
if getattr(tts_service, "available", True):
|
| 569 |
audio_bytes = tts_service.text_to_speech(host_response)
|
| 570 |
if audio_bytes:
|
| 571 |
+
audio_file = os.path.join(AUDIO_DIR, f"voice_request_{int(time.time())}.mp3")
|
| 572 |
tts_service.save_audio(audio_bytes, audio_file)
|
| 573 |
|
| 574 |
# Create music player HTML
|
config.py β src/config.py
RENAMED
|
File without changes
|
demo_assets.py β src/demo_assets.py
RENAMED
|
File without changes
|
{mcp_servers β src/mcp_servers}/__init__.py
RENAMED
|
File without changes
|
{mcp_servers β src/mcp_servers}/music_server.py
RENAMED
|
File without changes
|
{mcp_servers β src/mcp_servers}/news_server.py
RENAMED
|
File without changes
|
{mcp_servers β src/mcp_servers}/podcast_server.py
RENAMED
|
File without changes
|
radio_agent.py β src/radio_agent.py
RENAMED
|
@@ -16,7 +16,9 @@ from rag_system import RadioRAGSystem
|
|
| 16 |
# -----------------------------------------------------------------------------
|
| 17 |
# LLM Logging Setup
|
| 18 |
# -----------------------------------------------------------------------------
|
| 19 |
-
|
|
|
|
|
|
|
| 20 |
os.makedirs(LOG_DIR, exist_ok=True)
|
| 21 |
|
| 22 |
llm_logger = logging.getLogger("ai_radio.llm")
|
|
|
|
| 16 |
# -----------------------------------------------------------------------------
|
| 17 |
# LLM Logging Setup
|
| 18 |
# -----------------------------------------------------------------------------
|
| 19 |
+
# Get project root directory (parent of src/)
|
| 20 |
+
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
| 21 |
+
LOG_DIR = os.path.join(PROJECT_ROOT, "logs")
|
| 22 |
os.makedirs(LOG_DIR, exist_ok=True)
|
| 23 |
|
| 24 |
llm_logger = logging.getLogger("ai_radio.llm")
|
rag_system.py β src/rag_system.py
RENAMED
|
@@ -7,6 +7,9 @@ from llama_index.core import VectorStoreIndex, Document, Settings
|
|
| 7 |
from llama_index.core.storage.storage_context import StorageContext
|
| 8 |
from llama_index.core.vector_stores import SimpleVectorStore
|
| 9 |
from llama_index.llms.openai import OpenAI as LlamaOpenAI
|
|
|
|
|
|
|
|
|
|
| 10 |
# from llama_index.embeddings.gemini import GeminiEmbedding # Commented out - using Nebius instead
|
| 11 |
# from llama_index.llms.gemini import Gemini # Commented out - using Nebius instead
|
| 12 |
|
|
@@ -65,7 +68,7 @@ class RadioRAGSystem:
|
|
| 65 |
# Load existing index or create new one
|
| 66 |
self.index = None
|
| 67 |
self.documents = []
|
| 68 |
-
self.user_data_file = "user_data.json"
|
| 69 |
|
| 70 |
self._load_user_data()
|
| 71 |
|
|
|
|
| 7 |
from llama_index.core.storage.storage_context import StorageContext
|
| 8 |
from llama_index.core.vector_stores import SimpleVectorStore
|
| 9 |
from llama_index.llms.openai import OpenAI as LlamaOpenAI
|
| 10 |
+
|
| 11 |
+
# Get project root directory (parent of src/)
|
| 12 |
+
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
| 13 |
# from llama_index.embeddings.gemini import GeminiEmbedding # Commented out - using Nebius instead
|
| 14 |
# from llama_index.llms.gemini import Gemini # Commented out - using Nebius instead
|
| 15 |
|
|
|
|
| 68 |
# Load existing index or create new one
|
| 69 |
self.index = None
|
| 70 |
self.documents = []
|
| 71 |
+
self.user_data_file = os.path.join(PROJECT_ROOT, "user_data.json")
|
| 72 |
|
| 73 |
self._load_user_data()
|
| 74 |
|
test_app.py β src/test_app.py
RENAMED
|
File without changes
|
tts_service.py β src/tts_service.py
RENAMED
|
File without changes
|
voice_input.py β src/voice_input.py
RENAMED
|
File without changes
|