Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ wheels/

# Virtual environments
venv/
*venv/
env/
ENV/

Expand Down
71 changes: 71 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
.PHONY: help venv install test api cli-help clean clean-all

# Default target
help:
@echo "Music Track Generator - Available targets:"
@echo ""
@echo " make venv - Create a virtual environment"
@echo " make install - Install dependencies and package"
@echo " make test - Run tests with pytest"
@echo " make api - Start the API server"
@echo " make cli-help - Show CLI help"
@echo " make clean - Remove cache and build artifacts (preserves venv)"
@echo " make clean-all - Remove everything including virtual environment"
@echo ""

# Create virtual environment
venv:
@if [ -d venv ]; then \
echo "Virtual environment 'venv' already exists. Skipping creation."; \
else \
echo "Creating virtual environment..."; \
python -m venv venv; \
echo "Virtual environment created."; \
fi
@echo "Activate the virtual environment with:"
@echo " source venv/bin/activate (Linux/Mac)"
@echo " venv\\Scripts\\activate (Windows)"

# Install dependencies
install:
@echo "Installing dependencies..."
pip install -r requirements.txt
pip install -e .
@echo "Installation complete!"

# Run tests
test:
@echo "Running tests..."
pytest tests/ -v

# Start API server
api:
@echo "Starting API server on http://0.0.0.0:8080"
@echo "Press Ctrl+C to stop"
uvicorn music_generator.api:app --host 0.0.0.0 --port 8080

# Define common clean commands
CLEAN_FIND_COMMANDS = find . -type d \( -name "__pycache__" -o -name "*.egg-info" -o -name ".pytest_cache" \) -prune -exec rm -rf {} + 2>/dev/null || true

# Show CLI help
cli-help:
@echo "Music Track Generator CLI Commands:"
@echo ""
music-gen --help
@echo ""
@echo "Generate command options:"
@echo ""
music-gen generate --help

# Clean up cache and artifacts (preserves venv)
clean:
@echo "Cleaning cache and build artifacts..."
@$(CLEAN_FIND_COMMANDS)
@echo "Clean complete!"

# Clean up everything including virtual environment
clean-all:
@echo "Cleaning everything including venv..."
rm -rf venv
@$(CLEAN_FIND_COMMANDS)
@echo "Clean complete!"
117 changes: 115 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,22 @@ Music track generation system with CLI and REST API. Generate music tracks with
- Python 3.9 or higher
- (Optional) Google Cloud Platform account for GCP mode

### Quick Install
### Quick Install with Makefile

```bash
# Clone the repository
git clone https://github.com/kngms/github-dev-sandbox.git
cd github-dev-sandbox

# Create virtual environment and install
make venv
source venv/bin/activate # On Windows: venv\Scripts\activate
make install
```

### Manual Install

```bash
# Install dependencies
pip install -r requirements.txt

Expand All @@ -63,25 +72,91 @@ pip install -e .

## Quick Start

### CLI Usage (No Credentials Needed)
### Makefile Commands

```bash
# Show all available commands
make help

# Install dependencies
make install

# Run tests
make test

# Start API server
make api

# Show CLI help
make cli-help
```

### CLI Usage (No Credentials Needed - Simulate Mode)

```bash
# List available presets
music-gen list-presets

# Example output (order may vary):
# ┏━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
# ┃ Name ┃ Genre ┃ Description ┃
# ┣━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
# ┃ classical_orchestral ┃ classical ┃ Classical orchestral composition ┃
# ┃ electronic_dance ┃ electronic ┃ Upbeat electronic dance music ┃
# ┃ jazz_smooth ┃ jazz ┃ Smooth jazz with relaxed tempo ┃
# ┃ pop_catchy ┃ pop ┃ Catchy pop song with radio-friendly... ┃
# ┃ rock_anthem ┃ rock ┃ High-energy rock anthem with powerful...┃
# ┗━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

# Generate a track (simulate mode by default)
music-gen generate \
--text "Walking down the street, feeling the beat..." \
--genre rock \
--preset rock_anthem

# Expected output:
# Using preset: rock_anthem
#
# Track Configuration:
# Mode: simulate
# Genre: rock
# Duration: 180s (3m 0s)
# Temperature: 0.8
#
# Text Input:
# ╭────────────────────────────────────────────────────────────╮
# │ Walking down the street, feeling the beat... │
# ╰────────────────────────────────────────────────────────────╯
#
# Estimated cost: $0.0280 USD
# Proceed with generation? [Y/n]: y
#
# ✓ Track generation completed!
# Status: simulated
#
# Track generation simulated (no GCP credentials required).
```

### API Server

```bash
# Start the API server (binds to 0.0.0.0:8080 by default)
make api
# or
uvicorn music_generator.api:app --host 0.0.0.0 --port 8080

# Expected startup logs:
# INFO: Started server process
# INFO: Waiting for application startup.
# ============================================================
# Music Track Generator API - Startup Configuration
# ============================================================
# MUSIC_GEN_MODE: simulate
# MUSIC_GEN_API_KEY set: no
# ============================================================
# INFO: Application startup complete.
# INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)

# Or set custom port
PORT=3000 uvicorn music_generator.api:app --host 0.0.0.0 --port 3000
```
Expand Down Expand Up @@ -185,6 +260,33 @@ uvicorn music_generator.api:app --host 0.0.0.0 --port 8080

### API Endpoints

#### Get Configuration
```bash
# GET /config - Returns safe configuration info (no secrets)
curl http://localhost:8080/config

# Note: Requires authentication via X-API-Key header or Authorization Bearer token
# when MUSIC_GEN_API_KEY is set.

# With authentication (when API key is configured):
curl -H "X-API-Key: your-secret-key" http://localhost:8080/config

# Response example:
# {
# "mode": "simulate",
# "region": null,
# "project": null,
# "presets_available": [
# "classical_orchestral",
# "electronic_dance",
# "jazz_smooth",
# "pop_catchy",
# "rock_anthem"
# ],
# "auth_enabled": false
# }
```

#### Generate Track
```bash
# POST /tracks/generate
Expand All @@ -197,6 +299,17 @@ curl -X POST http://localhost:8080/tracks/generate \
"preset_name": "rock_anthem"
}'

# Response example:
# {
# "status": "simulated",
# "mode": "simulate",
# "genre": "rock",
# "duration_seconds": 180,
# "prompt": "Generate a rock music track with the following specifications...",
# "metadata": {...},
# "message": "Track generation simulated (no GCP credentials required)."
# }

# With custom structure (no preset)
curl -X POST http://localhost:8080/tracks/generate \
-H "Content-Type: application/json" \
Expand Down
85 changes: 81 additions & 4 deletions src/music_generator/api.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
"""FastAPI server for music track generation."""

import os
import sys
from typing import Optional, List, Dict, Any
from contextlib import asynccontextmanager
from fastapi import FastAPI, HTTPException, Header, Depends, Query
from pydantic import BaseModel, Field
import logging
Expand All @@ -10,15 +12,61 @@
from .generator import MusicGenerator
from .presets import PresetManager

# Configure logging
logging.basicConfig(level=logging.INFO)
# Constants
DEFAULT_GCP_REGION = "us-central1"

# Configure logging with UTF-8 encoding
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(sys.stdout)
]
)
# Ensure UTF-8 encoding for stdout in supported Python 3.9+ runtimes
# reconfigure() is available on text streams in these Python versions
if hasattr(sys.stdout, 'reconfigure'):
sys.stdout.reconfigure(encoding='utf-8')

logger = logging.getLogger(__name__)


def print_startup_banner():
"""Print startup configuration banner."""
mode = os.getenv("MUSIC_GEN_MODE", "simulate")
api_key = os.getenv("MUSIC_GEN_API_KEY")
api_key_set = "yes" if api_key else "no"

logger.info("=" * 60)
logger.info("Music Track Generator API - Startup Configuration")
logger.info("=" * 60)
logger.info(f"MUSIC_GEN_MODE: {mode}")
logger.info(f"MUSIC_GEN_API_KEY set: {api_key_set}")

if mode == "gcp":
project = os.getenv("GOOGLE_CLOUD_PROJECT", "(not set)")
region = os.getenv("GOOGLE_CLOUD_REGION", DEFAULT_GCP_REGION)
logger.info(f"GOOGLE_CLOUD_PROJECT: {project}")
logger.info(f"GOOGLE_CLOUD_REGION: {region}")

logger.info("=" * 60)


@asynccontextmanager
async def lifespan(app: FastAPI):
"""Lifespan context manager for startup and shutdown events."""
# Startup
print_startup_banner()
yield
# Shutdown (nothing to do for now)


# Initialize FastAPI app
app = FastAPI(
title="Music Track Generator API",
description="Generate music tracks with configurable genres, structures, and styles",
version="0.1.0"
version="0.1.0",
lifespan=lifespan
)

# Get API key from environment (optional)
Expand Down Expand Up @@ -62,6 +110,15 @@ class PromptTip(BaseModel):
tips: Optional[str]


class ConfigResponse(BaseModel):
"""API configuration response."""
mode: str
region: Optional[str]
project: Optional[str]
presets_available: List[str]
auth_enabled: bool


# API Key Authentication
def verify_api_key(
x_api_key: Optional[str] = Header(None, alias="X-API-Key"),
Expand Down Expand Up @@ -93,7 +150,7 @@ def get_generator() -> MusicGenerator:
"""Get music generator instance based on mode."""
mode = os.getenv("MUSIC_GEN_MODE", "simulate")
project_id = os.getenv("GOOGLE_CLOUD_PROJECT")
location = os.getenv("GOOGLE_CLOUD_REGION", "us-central1")
location = os.getenv("GOOGLE_CLOUD_REGION", DEFAULT_GCP_REGION)

return MusicGenerator(
mode=mode,
Expand All @@ -115,6 +172,26 @@ def root():
}


@app.get("/config", response_model=ConfigResponse, dependencies=[Depends(verify_api_key)])
def get_config():
"""Get API configuration information (safe, no secrets)."""
mode = os.getenv("MUSIC_GEN_MODE", "simulate")
region = os.getenv("GOOGLE_CLOUD_REGION", DEFAULT_GCP_REGION) if mode == "gcp" else None
project = os.getenv("GOOGLE_CLOUD_PROJECT") if mode == "gcp" else None
auth_enabled = bool(API_KEY)

# Get list of available presets
preset_names = preset_manager.list_presets()

return ConfigResponse(
mode=mode,
region=region,
project=project,
presets_available=preset_names,
auth_enabled=auth_enabled
)


@app.post("/tracks/generate", response_model=GenerateTrackResponse, dependencies=[Depends(verify_api_key)])
def generate_track(request: GenerateTrackRequest):
"""Generate a music track.
Expand Down
Loading
Loading