Files

551 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# K.G.One API Reference
K.G.One exposes a REST API on port **8000**. These endpoints are consumed by K.G.Studio's AI Musician Assistant and are also directly callable for programmatic or headless use.
> **Interactive docs (Swagger UI):** `http://localhost:8000/docs`
---
## System
| Method | URL | Description |
|--------|-----|-------------|
| `GET` | `/health` | Server health check |
| `POST` | `/v1/models/load` | Load a model onto the GPU (unloads the active one first) |
| `GET` | `/v1/models/status` | Return the currently active model |
---
### `GET /health`
**Response**
```json
{
"status": "ok",
"active_model": "clip"
}
```
`active_model` is `null` when no model is loaded.
---
### `POST /v1/models/load`
Loads a model onto the GPU. If a different model is currently active, it is shut down first.
For `"fullsong"` and `"clip"` this call **blocks** until the sub-service reports healthy (model weights loaded). Expect 30120 seconds on first run.
For `"separator"` it only terminates the currently running model to free VRAM — no persistent process is started. Returns immediately.
**Request**
| Field | Type | Required | Values |
|-------|------|----------|--------|
| `model` | string | yes | `"clip"`, `"fullsong"`, or `"separator"` |
```json
{ "model": "separator" }
```
**Response**
```json
{
"active_model": "separator",
"status": "ready"
}
```
**Error — unknown model (400)**
```json
{ "detail": "Unknown model 'foo'. Must be 'fullsong', 'clip', or 'separator'." }
```
---
### `GET /v1/models/status`
**Response**
```json
{
"active_model": "fullsong"
}
```
---
## Full-song generation (ACE-Step 1.5)
> All `/v1/fullsong/*` endpoints return HTTP 503 if `fullsong` is not the active model.
| Method | URL | Description |
|--------|-----|-------------|
| `POST` | `/v1/fullsong/generate` | Submit a full-song generation task |
| `GET` | `/v1/fullsong/result/{task_id}` | Poll task status and retrieve result |
| `GET` | `/v1/fullsong/audio/{task_id}` | Download a generated audio file |
---
### `POST /v1/fullsong/generate`
Proxied to ACE-Step's `/release_task`. All listed fields are forwarded as-is; any additional ACE-Step fields are also accepted and passed through.
See [ACE-Step API docs](https://github.com/ace-step/ACE-Step-1.5/blob/main/docs/en/API.md) for the complete parameter spec.
**Request**
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `caption` | string | required | Musical style/description |
| `lyrics` | string | `null` | Song lyrics (`[verse]`, `[chorus]` tags supported) |
| `instrumental` | boolean | `null` | Generate without vocals |
| `inference_steps` | integer | `null` | Diffusion steps (8 = turbo, 50 = full quality) |
| `guidance_scale` | number | `null` | Classifier-free guidance scale |
| `use_random_seed` | boolean | `null` | Use a random seed |
| `seed` | integer | `null` | Fixed seed value (used when `use_random_seed` is false) |
| `thinking` | boolean | `null` | Enable ACE-Step thinking mode |
| `batch_size` | integer | `null` | Number of outputs to generate |
| `audio_format` | string | `null` | `"mp3"`, `"wav"`, `"flac"`, `"opus"` |
**Example request**
```bash
curl -X POST 'http://127.0.0.1:8000/v1/fullsong/generate' \
-H 'Content-Type: application/json' \
-d '{
"caption": "Genre: Eurodance, 90s dance-pop, upbeat electronic. Catchy, energetic, nostalgic 90s Eurodance with a strong four-on-the-floor beat. Bright synth leads, punchy bassline. Tempo: ~130 BPM.",
"lyrics": "[verse]\nStep in the scene, yeah the bassline drops\n[chorus]\nWe are dancing in the neon in the night",
"instrumental": false,
"inference_steps": 8,
"guidance_scale": 7.0,
"use_random_seed": true,
"thinking": true,
"batch_size": 1,
"audio_format": "mp3"
}'
```
**Response**
```json
{
"data": {
"task_id": "58e15e57-3022-4f43-8ada-64ea52a9064a",
"status": "queued",
"queue_position": 1
},
"code": 200,
"error": null,
"timestamp": 1776227065582,
"extra": null
}
```
---
### `GET /v1/fullsong/result/{task_id}`
Poll until `status` is `1` (succeeded). Recommended interval: 25 seconds.
**Response — pending / running**
```json
{
"data": [
{
"task_id": "58e15e57-3022-4f43-8ada-64ea52a9064a",
"status": 0,
"progress": 0.1,
"stage": "Phase 1: Generating CoT metadata..."
}
],
"code": 200
}
```
**Response — succeeded**
```json
{
"data": [
{
"task_id": "58e15e57-3022-4f43-8ada-64ea52a9064a",
"status": 1,
"progress": 1.0,
"stage": "succeeded"
}
],
"code": 200
}
```
Once `status` is `1`, download the audio with `GET /v1/fullsong/audio/{task_id}`.
---
### `GET /v1/fullsong/audio/{task_id}`
Download the generated audio file. Only works once the task has succeeded (`status == 1`).
**Query parameter:** `index` (optional, default `0`) — selects a specific file when `batch_size > 1`.
**Example**
```bash
curl -X GET \
'http://127.0.0.1:8000/v1/fullsong/audio/58e15e57-3022-4f43-8ada-64ea52a9064a?index=0' \
--output song.mp3
```
**Response headers**
```
content-disposition: attachment; filename="58e15e57-3022-4f43-8ada-64ea52a9064a.mp3"
content-type: audio/mpeg
```
---
## Clip generation (Foundation-1)
> All `/v1/clip/*` endpoints return HTTP 503 if `clip` is not the active model.
Foundation-1 generates short instrument clips (4 or 8 bars) from a structured text prompt, producing both a WAV audio file and a MIDI transcription simultaneously.
| Method | URL | Description |
|--------|-----|-------------|
| `POST` | `/v1/clip/generate` | Submit a clip generation task |
| `GET` | `/v1/clip/result/{task_id}` | Poll task status |
| `GET` | `/v1/clip/audio/{task_id}` | Download the generated WAV file |
| `GET` | `/v1/clip/midi/{task_id}` | Download the generated MIDI file |
---
### `POST /v1/clip/generate`
**Request**
| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `prompt` | string | required | Comma-separated descriptor tags (see prompt guide below) |
| `negative_prompt` | string | `""` | Tags to avoid |
| `bars` | integer | `4` | Clip length: `4` or `8` |
| `bpm` | integer | `140` | Tempo in BPM (e.g. 100, 110, 120, 128, 130, 140, 150) |
| `note` | string | `"C"` | Root note: `A` through `G#` |
| `scale` | string | `"minor"` | `"major"` or `"minor"` |
| `steps` | integer | `75` | Diffusion steps (1500; 75 is a good balance) |
| `cfg_scale` | number | `7.0` | Classifier-free guidance (025) |
| `seed` | integer | `-1` | `-1` for random |
| `sampler_type` | string | `"dpmpp-2m-sde"` | Sampler algorithm |
| `sigma_min` | number | `0.03` | Minimum noise sigma |
| `sigma_max` | number | `500.0` | Maximum noise sigma |
| `cfg_rescale` | number | `0.0` | CFG rescale factor (01) |
**Prompt format**
Foundation-1 prompts are structured tag lists:
```
[Instrument family], [Sub-type], [Timbre descriptors], [FX], [Bars], [BPM], [Key]
```
Examples:
- `"Piano, Rhodes Piano, Warm, Bright, Lush, 8 Bars, 120 BPM, C major"`
- `"Bass, FM Bass, Acid, Gritty, Thick, 8 Bars, 140 BPM, E minor"`
- `"Synth, Wavetable Synth, Pad, Wide, Silky, 4 Bars, 128 BPM, A minor"`
**Example request**
```bash
curl -X POST 'http://127.0.0.1:8000/v1/clip/generate' \
-H 'Content-Type: application/json' \
-d '{
"prompt": "Gritty, Acid, Bassline, 303, Synth Lead, FM, Sub, Upper Mids, High Phaser, High Reverb, Pitch Bend, 8 Bars, 140 BPM, E minor",
"bars": 8,
"bpm": 140,
"note": "E",
"scale": "minor",
"steps": 75,
"cfg_scale": 7,
"seed": -1,
"sampler_type": "dpmpp-2m-sde",
"sigma_min": 0.03,
"sigma_max": 500,
"cfg_rescale": 0
}'
```
**Response**
```json
{
"task_id": "7ab55a6a-c478-45a0-bab7-1fd9cbd8597d"
}
```
---
### `GET /v1/clip/result/{task_id}`
Poll until `status` is `"complete"`. Recommended interval: 25 seconds.
**Response — pending / running**
```json
{
"task_id": "7ab55a6a-c478-45a0-bab7-1fd9cbd8597d",
"status": "running",
"error": null
}
```
**Response — complete**
```json
{
"task_id": "7ab55a6a-c478-45a0-bab7-1fd9cbd8597d",
"status": "complete"
}
```
Once complete, download the files using the same `task_id`:
- `GET /v1/clip/audio/{task_id}` → WAV
- `GET /v1/clip/midi/{task_id}` → MIDI
**Response — error**
```json
{
"task_id": "7ab55a6a-c478-45a0-bab7-1fd9cbd8597d",
"status": "error",
"error": "Generation failed — check server logs."
}
```
---
### `GET /v1/clip/audio/{task_id}`
Download the generated WAV file (32 kHz stereo).
```bash
curl -X GET \
'http://127.0.0.1:8000/v1/clip/audio/7ab55a6a-c478-45a0-bab7-1fd9cbd8597d' \
--output clip.wav
```
**Response headers**
```
content-type: audio/wav
content-length: 2419244
```
---
### `GET /v1/clip/midi/{task_id}`
Download the MIDI transcription (via [basic-pitch](https://github.com/spotify/basic-pitch)).
```bash
curl -X GET \
'http://127.0.0.1:8000/v1/clip/midi/7ab55a6a-c478-45a0-bab7-1fd9cbd8597d' \
--output clip.mid
```
**Response headers**
```
content-type: audio/midi
content-length: 1458
```
---
## Stem separation (python-audio-separator)
> All `/v1/separator/*` endpoints return HTTP 503 if `separator` is not the active model.
Separates an uploaded audio file into individual stems using UVR models. Outputs are always MP3.
| Method | URL | Description |
|--------|-----|-------------|
| `POST` | `/v1/separator/separate` | Upload audio + select model → task ID |
| `GET` | `/v1/separator/result/{task_id}` | Poll task status and retrieve output filenames |
| `GET` | `/v1/separator/download/{filename}` | Download a separated stem file |
---
### `POST /v1/separator/separate`
Accepts `multipart/form-data`.
**Form fields**
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `file` | file | yes | Audio file to separate (MP3, WAV, FLAC, …) |
| `model_filename` | string | yes | One of the supported models below |
**Supported models**
| `model_filename` | Stems produced |
|-----------------|----------------|
| `UVR-MDX-NET-Inst_HQ_3.onnx` | 2 — Vocals, Instrumental |
| `MDX23C-8KFFT-InstVoc_HQ.ckpt` | 2 — Vocals, Instrumental |
| `htdemucs_6s.yaml` | 6 — bass, drums, guitar, other, piano, vocals |
Models are downloaded automatically by `audio-separator` on first use.
**Example request**
```bash
curl -X POST 'http://127.0.0.1:8000/v1/separator/separate' \
-H 'accept: application/json' \
-F 'file=@song.mp3;type=audio/mpeg' \
-F 'model_filename=MDX23C-8KFFT-InstVoc_HQ.ckpt'
```
**Response**
```json
{ "task_id": "0b93cb76-616d-45f0-9580-61d615cd6a76" }
```
---
### `GET /v1/separator/result/{task_id}`
Poll until `status` is `"complete"`. Separation typically takes 1060 seconds.
**Response — running**
```json
{
"task_id": "0b93cb76-616d-45f0-9580-61d615cd6a76",
"status": "running"
}
```
**Response — complete**
```json
{
"task_id": "0b93cb76-616d-45f0-9580-61d615cd6a76",
"status": "complete",
"files": [
"0b93cb76-616d-45f0-9580-61d615cd6a76_(Instrumental)_MDX23C-8KFFT-InstVoc_HQ.mp3",
"0b93cb76-616d-45f0-9580-61d615cd6a76_(Vocals)_MDX23C-8KFFT-InstVoc_HQ.mp3"
]
}
```
**Response — error**
```json
{
"task_id": "0b93cb76-616d-45f0-9580-61d615cd6a76",
"status": "error",
"error": "Separation failed — check server logs."
}
```
---
### `GET /v1/separator/download/{filename}`
Download a stem MP3 file. `filename` is one of the entries from the `files` list above.
```bash
curl -X GET \
'http://127.0.0.1:8000/v1/separator/download/0b93cb76-616d-45f0-9580-61d615cd6a76_%28Vocals%29_MDX23C-8KFFT-InstVoc_HQ.mp3' \
--output vocals.mp3
```
**Response headers**
```
content-disposition: attachment; filename="0b93cb76-...(Vocals)_MDX23C-8KFFT-InstVoc_HQ.mp3"
content-type: audio/mpeg
```
---
## Typical Workflows
### Generate a full song
```bash
# 1. Load ACE-Step
curl -X POST http://localhost:8000/v1/models/load \
-H "Content-Type: application/json" \
-d '{"model": "fullsong"}'
# 2. Submit generation
TASK=$(curl -s -X POST http://localhost:8000/v1/fullsong/generate \
-H "Content-Type: application/json" \
-d '{
"caption": "lo-fi hip hop, mellow piano, soft drums, vinyl crackle",
"instrumental": true,
"inference_steps": 8,
"use_random_seed": true,
"audio_format": "mp3"
}' | python -c "import sys,json; print(json.load(sys.stdin)['data']['task_id'])")
# 3. Poll until status == 1
curl http://localhost:8000/v1/fullsong/result/$TASK
# 4. Download
curl "http://localhost:8000/v1/fullsong/audio/$TASK" --output song.mp3
```
### Generate a MIDI + WAV clip
```bash
# 1. Load Foundation-1
curl -X POST http://localhost:8000/v1/models/load \
-H "Content-Type: application/json" \
-d '{"model": "clip"}'
# 2. Submit generation
TASK=$(curl -s -X POST http://localhost:8000/v1/clip/generate \
-H "Content-Type: application/json" \
-d '{
"prompt": "Keys, Rhodes Piano, Warm, Lush, 8 Bars, 90 BPM, D major",
"bars": 8, "bpm": 90, "note": "D", "scale": "major", "steps": 75
}' | python -c "import sys,json; print(json.load(sys.stdin)['task_id'])")
# 3. Poll until status == "complete"
curl http://localhost:8000/v1/clip/result/$TASK
# 4. Download both files
curl "http://localhost:8000/v1/clip/audio/$TASK" --output clip.wav
curl "http://localhost:8000/v1/clip/midi/$TASK" --output clip.mid
```
### Separate stems from an audio file
```bash
# 1. Load separator (frees VRAM from any active model)
curl -X POST http://localhost:8000/v1/models/load \
-H "Content-Type: application/json" \
-d '{"model": "separator"}'
# 2. Submit separation
TASK=$(curl -s -X POST http://localhost:8000/v1/separator/separate \
-F "file=@song.mp3" \
-F "model_filename=UVR-MDX-NET-Inst_HQ_3.onnx" | python -c "import sys,json; print(json.load(sys.stdin)['task_id'])")
# 3. Poll until complete
curl http://localhost:8000/v1/separator/result/$TASK
# 4. Download stems (use filenames from the result response)
curl "http://localhost:8000/v1/separator/download/...(Vocals)....mp3" --output vocals.mp3
curl "http://localhost:8000/v1/separator/download/...(Instrumental)....mp3" --output instrumental.mp3
```
---
## Error Reference
| HTTP Status | Meaning |
|-------------|---------|
| `400` | Bad request (e.g. unknown model name) |
| `404` | Task ID or file not found |
| `503` | Requested model is not currently loaded, or sub-service is unreachable |
**503 body when wrong model is active:**
```json
{
"detail": {
"error": "Model 'clip' is not loaded. POST /v1/models/load first.",
"active_model": "fullsong"
}
}
```