ChatGPT
fix: make upload and fallback robust
5a90820
|
Raw
History Blame Contribute Delete
18.9 kB
# API documentation
Last updated: 2026-05-12
The active app is `app.py`, a FastAPI application.
## Start server
```bash
uvicorn app:app --host 0.0.0.0 --port 7860
```
## `GET /api/health`
Returns backend health.
```json
{"status":"ok"}
```
## `GET /api/config`
Returns supported models, stems, default pipeline params, stage definitions, and clustering mode labels.
```bash
curl http://127.0.0.1:7860/api/config
```
Important response keys:
| Key | Meaning |
|---|---|
| `separation_backends` | Supported separation engines: `spleeter`, `demucs`, and `none`. |
| `spleeter_models` | Supported Spleeter model profiles. |
| `spleeter_stems` | Valid stems per Spleeter model, plus `all`. |
| `demucs_models` | Supported Demucs model names. |
| `demucs_stems` | Valid stems per Demucs model, plus `all`. |
| `defaults` | Default `PipelineParams`. |
| `stages` | Pipeline stage definitions. |
| `clustering_modes` | Human-readable labels for batch and online clustering modes. |
## `GET /api/jobs`
Lists active in-memory jobs and completed run manifests found under `.runs/`.
```bash
curl http://127.0.0.1:7860/api/jobs?limit=50
```
Response:
```json
{
"active": [],
"history": [
{
"id": "58ca0db4ac74",
"status": "complete",
"filename": "song.wav",
"created_at": 1778540000.0,
"duration_sec": 2.4,
"audio_duration_sec": 8.0,
"realtime_factor": 0.3,
"bpm": 120.0,
"hit_count": 32,
"cluster_count": 8,
"clustering_mode": "online_preview",
"stem": "all",
"error": null
}
]
}
```
`created_at` is the manifest file modification time as a Unix timestamp.
## `POST /api/jobs`
Creates an extraction job.
The endpoint accepts browser-form-style JSON values for numeric and boolean params. For example, `{ "subdivision": "16" }` is coerced to integer `16` before validation. Invalid values return `400` with an actionable `detail` message suitable for direct UI display.
Content type: `multipart/form-data`
Fields:
| Field | Type | Required | Description |
|---|---|---:|---|
| `file` | file | yes | Audio source. |
| `params` | JSON string | no | Partial or full pipeline params. |
Example:
```bash
curl -F 'file=@song.wav' \
-F 'params={"separation_backend":"spleeter","spleeter_model":"spleeter:4stems","stem":"drums","clustering_mode":"online_preview","target_min":4,"target_max":12,"synthesize":true}' \
http://127.0.0.1:7860/api/jobs
```
Response status: `202 Accepted`
Invalid params response example:
```json
{"detail":"Invalid extraction parameter: subdivision must be one of 4, 8, 16, 32, 64"}
```
```json
{
"id": "58ca0db4ac74",
"status": "pending",
"filename": "song.wav",
"params": {"stem": "all", "clustering_mode": "online_preview"},
"stages": [],
"logs": [],
"result": null,
"error": null
}
```
## `GET /api/jobs/{job_id}`
Poll job status and retrieve results. This works for active in-memory jobs and completed historical jobs whose manifest is still present in `.runs/`.
Statuses:
| Status | Meaning |
|---|---|
| `pending` | Job is queued. |
| `running` | Job is executing. |
| `complete` | Result and artifacts are ready. |
| `error` | Pipeline failed; `error` and `traceback` are populated. |
Completed jobs contain:
| Key | Meaning |
|---|---|
| `duration_sec` | Total wall time. |
| `audio_duration_sec` | Duration of processed stem/source. |
| `realtime_factor` | `duration_sec / audio_duration_sec`. |
| `bpm` | Detected tempo. |
| `hit_count` | Number of accepted onsets/hits. |
| `cluster_count` | Number of sample clusters. |
| `stages` | Per-stage timing/status/detail list. |
| `samples` | Representative sample rows with score, duration, first onset, and playback/download URL. |
| `hits` | Per-detected-hit review rows with onset, duration, label, cluster, representative flag, and playback/download URL. |
| `overview` | Decimated envelope and clickable onset markers for waveform display. |
| `files` | Relative artifact paths. Includes `source`, `stem`, `context_bed`, `target_reconstruction`, `reconstruction`, `midi`, and `archive` when available. |
| `file_urls` | Direct API URLs for top-level artifacts. |
## `GET /api/jobs/{job_id}/events`
Streams job snapshots as server-sent events. This is the preferred progress channel for the frontend; polling remains supported via `GET /api/jobs/{job_id}`.
```bash
curl -N http://127.0.0.1:7860/api/jobs/58ca0db4ac74/events
```
Event shape:
```text
event: job
data: {"id":"58ca0db4ac74","status":"running","stages":[...]}
```
The stream closes after `complete` or `error`. Completed historical jobs emit one final `job` event and close.
## Top-level artifact meanings
| Key | Path | Meaning |
|---|---|---|
| `source` | `source.wav` | Normalized source mix used for source preview. |
| `stem` | `stem.wav` | Target stem being sampled. |
| `context_bed` | `context_bed.wav` | Non-target stems/context bed; silent for `stem=all`. |
| `target_reconstruction` | `target_reconstruction.wav` | Sample-triggered reconstruction of only the target stem. |
| `reconstruction` | `reconstruction.wav` | Full-context reproduced mix: context bed plus target reconstruction. |
| `midi` | `reconstruction.mid` | MIDI trigger reconstruction. |
| `archive` | `sample-pack.zip` | Complete sample pack and reproduction artifacts. |
## `GET /api/jobs/{job_id}/files/{relative_path}`
Downloads an artifact from a completed job.
Examples:
```bash
curl -O http://127.0.0.1:7860/api/jobs/58ca0db4ac74/files/sample-pack.zip
curl -O http://127.0.0.1:7860/api/jobs/58ca0db4ac74/files/reconstruction.mid
curl -O http://127.0.0.1:7860/api/jobs/58ca0db4ac74/files/reconstruction.wav
curl -O http://127.0.0.1:7860/api/jobs/58ca0db4ac74/files/target_reconstruction.wav
curl -O http://127.0.0.1:7860/api/jobs/58ca0db4ac74/files/samples/hihat_open_0.wav
curl -O http://127.0.0.1:7860/api/jobs/58ca0db4ac74/files/review/hits/hit_00000_kick.wav
```
The endpoint prevents path traversal by resolving downloads under `.runs/<job-id>/output/` and requiring the final path to remain relative to that output root.
## `POST /api/cache/clear`
Clears the in-memory DSP cache and disk stem/source cache.
```bash
curl -X POST http://127.0.0.1:7860/api/cache/clear
```
Response:
```json
{"status":"cleared","scope":"memory+disk"}
```
## Pipeline parameters
Defined in `pipeline_runner.PipelineParams`.
| Parameter | Default | Meaning |
|---|---:|---|
| `stem` | `drums` | Source/stem to extract, or `all` to bypass source separation. Valid values depend on the selected backend/model. |
| `separation_backend` | `spleeter` | Source-separation engine: `spleeter`, `demucs`, or `none`. |
| `spleeter_model` | `spleeter:4stems` | Spleeter model profile used by the default backend. |
| `demucs_model` | `htdemucs_ft` | Demucs model used when `separation_backend=demucs` or fallback is needed. |
| `demucs_shifts` | `1` | Test-time shifts for Demucs quality/speed tradeoff. |
| `demucs_overlap` | `0.25` | Demucs chunk overlap. |
| `onset_mode` | `auto` | `auto`, `percussive`, `harmonic`, or `broadband`. |
| `onset_delta` | `0.12` | Peak-pick threshold. |
| `energy_threshold_db` | `-35` | RMS gate for accepting hits. |
| `pre_pad` | `0.003` | Seconds of audio before onset. |
| `min_dur` | `0.02` | Minimum hit duration. |
| `max_dur` | `1.5` | Maximum hit duration. |
| `min_gap` | `0.03` | Minimum time between onsets. |
| `ncc_threshold` | `0.80` | Similarity threshold. Also used by online clustering assignment. |
| `attack_ms` | `25` | Transient window used for NCC/prototypes. |
| `mel_threshold` | `0.75` | Candidate prefilter threshold. For online mode, lower values such as `0.62` are useful. |
| `linkage` | `average` | Agglomerative linkage for `batch_quality`. |
| `clustering_mode` | `batch_quality` | `batch_quality` or `online_preview`. |
| `target_min` | `5` | Lower cluster target; `0` disables target mode in batch mode. |
| `target_max` | `20` | Upper cluster target; `0` disables target/cap mode. |
| `synthesize` | `true` | Write synthesized alternates for clusters with multiple hits. |
| `quantize_midi` | `true` | Snap MIDI notes to grid. |
| `subdivision` | `16` | MIDI grid subdivision. |
| `device` | `cpu` | Torch device for Demucs. |
| `use_disk_cache` | `true` | Cache decoded full mix/stems by source digest and extraction settings. |
| `allow_backend_fallback` | `true` | If Spleeter is selected but unavailable/fails, fall back to Demucs instead of failing the job. |
## Sample-card action API
These endpoints back the simplified card workflow in the reference-style UI. They mutate `supervision_state.json` and preserve the original batch manifest.
### `POST /api/jobs/{job_id}/export-selected`
Exports only the currently selected representative sample labels into `selected/` artifacts.
Body:
```json
{"labels":["kick_0","snare_0"],"synthesize":true}
```
Response shape:
```json
{
"export": {
"kind": "selected-sample-export",
"files": {"archive": "selected/sample-pack.zip", "midi": "selected/reconstruction.mid"},
"file_urls": {}
},
"state": {}
}
```
Rules:
- `labels` must contain at least one visible sample label.
- Only selected semantic clusters are rendered.
- Suppressed hits remain excluded.
- Pinned/drawn representatives are honored.
- The export is written under `.runs/<job-id>/output/selected/` and does not mutate the original pack.
### `POST /api/jobs/{job_id}/samples/{sample_label}/draw`
Cycles a card to the next active representative hit in that semantic cluster. The chosen hit is persisted as a representative override, so later selected/all edited exports use the same choice.
Response:
```json
{"sample": {"label": "kick_0", "url": "..."}, "state": {}}
```
### `POST /api/jobs/{job_id}/samples/{sample_label}/edit`
Applies a timing edit to the current representative and rewrites its preview WAV immediately.
Body:
```json
{"start_offset_ms":-8,"tail_offset_ms":24}
```
The backend slices from `stem.wav`, writes `overrides/hits/*_edited.wav`, updates the representative hit in semantic state, and returns a refreshed card row.
## Interactive supervision API
The interactive supervision API is backed by `supervised_state.py` and persists state as:
```text
.runs/<job_id>/output/supervision_state.json
```
The batch `manifest.json` remains immutable. Supervised edits update semantic state and can be rendered into a separate edited export under `supervised/` without mutating original artifacts.
### `GET /api/jobs/{job_id}/state`
Returns the supervised state for a completed job. If the state file does not exist yet, it is created from the batch manifest.
Response keys:
| Key | Meaning |
|---|---|
| `summary` | Counts for hits, clusters, constraints, events, suggestions, suppressed/forced hits, locked clusters, latest export, undo availability. |
| `hits` | Semantic hit rows with confidence, suppression/favorite/review flags, file URLs, and current cluster assignment. |
| `clusters` | Semantic clusters with hit IDs, representative hit, confidence, locked state, and suppressed count. |
| `review_queue` | Low-confidence/high-priority hits sorted for review. |
| `constraints` | Recent replayable constraints. |
| `events` | Recent state mutation events. |
| `suggestions` | Open move/split/suppress suggestions, including exact `diff` previews. |
```bash
curl http://127.0.0.1:7860/api/jobs/<job-id>/state
```
### `POST /api/jobs/{job_id}/hits/force-onset`
Creates a user-forced hit slice from `stem.wav` and adds it to semantic state.
Body:
```json
{
"onset_sec": 0.123,
"duration_ms": 160,
"target_cluster_id": "cluster:0",
"label": "snare"
}
```
Required fields:
| Field | Required | Meaning |
|---|---:|---|
| `onset_sec` | yes | Onset location in seconds. |
| `duration_ms` | no | Slice length. If omitted, the system slices until the next active onset or a bounded default. |
| `target_cluster_id` | no | Existing cluster to place the hit into. If omitted, a new user cluster is created. |
| `label` | no | Override label. If omitted, the rule-based classifier labels the forced slice. |
Effects:
- writes `review/hits/hit_NNNNN_<label>_forced.wav`,
- creates a semantic hit with `source=forced`,
- creates `force-onset` and `force-cluster` constraints,
- appends `hit.force_onset`,
- recomputes confidence and review queue.
### `POST /api/jobs/{job_id}/hits/{hit_id}/restore`
Restores a suppressed hit.
Effects:
- sets `suppressed=false`,
- clears `review_status=suppressed` back to `unreviewed`,
- creates a `restore-hit` constraint,
- appends `hit.restored`,
- recomputes confidence and review queue.
### `POST /api/jobs/{job_id}/export`
Renders the current semantic state into edited artifacts under `supervised/`. This does not modify the original `manifest.json`, original samples, or original ZIP.
Body:
```json
{
"synthesize": true,
"quantize": true,
"subdivision": 16
}
```
Response shape:
```json
{
"export": {
"kind": "supervised-export",
"hit_count": 17,
"cluster_count": 10,
"files": {
"archive": "supervised/sample-pack.zip",
"midi": "supervised/reconstruction.mid",
"target_reconstruction": "supervised/target_reconstruction.wav",
"reconstruction": "supervised/reconstruction.wav"
},
"file_urls": {}
},
"state": {}
}
```
Export rules:
- suppressed hits are excluded,
- forced hits are included,
- moved/pulled hits use current semantic cluster membership,
- favorite/pinned representatives are honored before quality scoring,
- cluster labels are sanitized for filenames,
- `supervision_state.json` receives `latest_export` and a `supervised.exported` event.
### `POST /api/jobs/{job_id}/hits/{hit_id}/move`
Moves a hit into an existing target cluster.
Body:
```json
{"target_cluster_id":"cluster:0"}
```
Effects:
- updates hit membership in `supervision_state.json`,
- creates `force-cluster`,
- creates `must-link` to the target representative when possible,
- appends events,
- recomputes confidence/review queue,
- may create similar-hit move suggestions,
- pushes an undo snapshot.
Example:
```bash
curl -X POST http://127.0.0.1:7860/api/jobs/<job-id>/hits/hit%3A00003/move \
-H 'Content-Type: application/json' \
-d '{"target_cluster_id":"cluster:0"}'
```
### `POST /api/jobs/{job_id}/hits/{hit_id}/pull-out`
Pulls a hit into a new user cluster.
Optional body:
```json
{"label":"snare_user_1"}
```
Effects:
- creates a new `cluster:user:*` cluster,
- creates `cannot-link` from the source representative when possible,
- creates `force-cluster`,
- may create split suggestions,
- pushes an undo snapshot.
### `POST /api/jobs/{job_id}/hits/{hit_id}/suppress`
Marks a hit as bleed/noise/non-sample material.
Body:
```json
{"reason":"bleed"}
```
Effects:
- marks the hit `suppressed`,
- creates `suppress-pattern`,
- may create similar suppression suggestions,
- recomputes confidence and review priority.
### `POST /api/jobs/{job_id}/hits/{hit_id}/review`
Stores a review decision for a hit.
Body:
```json
{"status":"accepted"}
```
Supported statuses:
| Status | Meaning |
|---|---|
| `unreviewed` | Clear explicit review status. |
| `accepted` | Mark the hit as reviewed/accepted. |
| `favorite` | Mark as favorite and pin as semantic representative for its cluster. |
### `POST /api/jobs/{job_id}/clusters/{cluster_id}/lock`
Locks or unlocks a cluster.
Body:
```json
{"locked":true}
```
Lock state is persisted and shown in the cluster board. It does not yet alter future full pipeline reruns.
### `GET /api/jobs/{job_id}/suggestions`
Returns open suggestions and the state summary.
```bash
curl http://127.0.0.1:7860/api/jobs/<job-id>/suggestions
```
### `POST /api/jobs/{job_id}/suggestions/{suggestion_id}/accept`
Applies a suggestion and records accepted constraints/examples.
Supported suggestion types:
- `move-hits`,
- `split-hits`,
- `suppress-hits`.
### `POST /api/jobs/{job_id}/suggestions/{suggestion_id}/reject`
Marks a suggestion rejected and records an event.
### `GET /api/jobs/{job_id}/explain/cluster/{cluster_id}`
Returns explanation data for one cluster:
- label,
- locked state,
- confidence and reasons,
- representative hit,
- hit counts,
- label distribution,
- lowest-confidence outliers,
- relevant constraints,
- summary string.
### `POST /api/jobs/{job_id}/undo`
Restores the previous semantic state snapshot if available.
```bash
curl -X POST http://127.0.0.1:7860/api/jobs/<job-id>/undo
```
## Job progress contract
Every serialized job now includes a top-level `progress` object. The same object is sent through `GET /api/jobs/{job_id}`, `GET /api/jobs`, and `GET /api/jobs/{job_id}/events` job events.
Example:
```json
{
"fraction": 0.1875,
"completed_steps": 1,
"total_steps": 8,
"completed_units": 12.0,
"total_units": 64.0,
"stage_key": "stem",
"stage_label": "Stem separation / source load",
"stage_fraction": 0.5,
"stage_work_done": 4,
"stage_work_total": 8,
"basis": "exact completed work units: Demucs chunks when available; Spleeter and non-instrumented stages advance only at real stage boundaries; no time-based estimates"
}
```
Semantics:
- `fraction` is the completed work-unit fraction used by the UI waveform progress tint.
- `stage_fraction` is the current stage-local progress when known.
- `stage_work_done` and `stage_work_total` are exact work-unit counters when a stage exposes work units.
- Demucs separated-stem extraction exposes exact completed split chunks.
- Spleeter reports coarse start/complete boundaries because the backend does not expose reliable chunk callbacks here.
- Non-instrumented stages update at exact stage boundaries only.
- The API does not provide guessed ETA or interpolated time progress.
## Progressive `partial_samples`
Active/running jobs may include `partial_samples` before `result` is available. These rows are emitted only after the corresponding sample WAV has been written, and they include the same `url` decoration used by final samples.
Final results remain authoritative under `result.samples`.
## `PipelineParams.auto_tune`
`auto_tune` defaults to `true`. When enabled, the pipeline tunes onset sensitivity and group bounds from the loaded target stem before final onset detection. The effective tuned values are returned in the job params/result manifest.
## Upload/runtime fallback update (2026-05-12)
- Added a visible top-bar `Choose audio` affordance in addition to whole-app drag/drop.
- Fixed the default hidden state of the error banner so placeholder errors are not shown on page load.
- API errors now surface request path/status/detail in the visible banner and pipeline logs.
- `/api/config` now includes runtime diagnostics for optional separation backends.
- If Spleeter is unavailable, the simple UI keeps the app usable by switching to full-mix mode; backend fallback also uses full-mix rather than silently launching Demucs.