Skip to content
Draft
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
58 changes: 58 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
.PHONY: help venv install test api cli-help clean

# 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 virtual environment and cache files"
@echo ""

# Create virtual environment
venv:
@echo "Creating virtual environment..."
python -m venv venv
@echo "Virtual environment created. Activate 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

# 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
clean:
@echo "Cleaning up..."
rm -rf venv
find . -type d -name "__pycache__" -exec rm -rf {} + 2>/dev/null || true
find . -type d -name "*.egg-info" -exec rm -rf {} + 2>/dev/null || true
find . -type d -name ".pytest_cache" -exec rm -rf {} + 2>/dev/null || true
@echo "Clean complete!"
111 changes: 109 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

# Expected output:
# ┏━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
# ┃ 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,27 @@ 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

# 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 +293,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
80 changes: 77 additions & 3 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,58 @@
from .generator import MusicGenerator
from .presets import PresetManager

# Configure logging
logging.basicConfig(level=logging.INFO)
# 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 (Python 3.7+ compatibility)
# reconfigure() is available in Python 3.7+ for text streams
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", "us-central1")
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 +107,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 @@ -115,6 +169,26 @@ def root():
}


@app.get("/config", response_model=ConfigResponse)
def get_config():
"""Get API configuration information (safe, no secrets)."""
mode = os.getenv("MUSIC_GEN_MODE", "simulate")
region = os.getenv("GOOGLE_CLOUD_REGION", "us-central1") 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