|
| 1 | +# ACE-Step CPP UI — API Reference |
| 2 | + |
| 3 | +This document maps and describes the API surface used by the **acestep-cpp-ui** Electron app: how the UI talks to the backend, and how the backend talks to the acestep.cpp engine. It is intended to support migrations and future development. |
| 4 | + |
| 5 | +**Scope:** API and IPC only. Build, packaging, and tooling are out of scope. |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## 1. Architecture Overview |
| 10 | + |
| 11 | +- **UI (React)** runs in the Electron renderer and loads `http://127.0.0.1:PORT` (or Vite proxy in dev). |
| 12 | +- **Backend (Express)** runs on `PORT` (default `3001`). All UI calls use **same-origin relative URLs** (`API_BASE = ''` in `services/api.ts`). |
| 13 | +- **acestep.cpp** is used in one of two ways: |
| 14 | + - **Spawn mode:** Backend runs `ace-lm`, `ace-synth`, and optionally `ace-understand` as child processes (no Node addons). |
| 15 | + - **HTTP mode:** Backend calls an external acestep-cpp server at `ACESTEP_API_URL` (e.g. `http://localhost:7860`). |
| 16 | + |
| 17 | +There are **no WebSockets** for app logic; only one **SSE** stream (model download progress). |
| 18 | + |
| 19 | +--- |
| 20 | + |
| 21 | +## 2. Electron IPC |
| 22 | + |
| 23 | +Used only by the **loading/setup window** (`electron/loading.html`), not by the main React app. |
| 24 | + |
| 25 | +| Channel | Direction | Description | |
| 26 | +|---------------|-----------------|-------------| |
| 27 | +| `setup:status` | Main → Renderer | Progress during startup and first-run model downloads. | |
| 28 | + |
| 29 | +**Preload API** (`electron/preload.js`): |
| 30 | + |
| 31 | +- **`setupAPI.onStatus(callback)`** — Registers a listener for `setup:status`. Payload: `{ msg, pct, label }`. |
| 32 | +- **`electronAPI.platform`** — `process.platform` (e.g. `'darwin'`, `'linux'`, `'win32'`). |
| 33 | +- **`electronAPI.versions`** — `{ electron, node }` (version strings). |
| 34 | + |
| 35 | +The main React app does **not** use `invoke` or `send`; it talks to the backend only over HTTP. |
| 36 | + |
| 37 | +--- |
| 38 | + |
| 39 | +## 3. HTTP API (UI → Express) |
| 40 | + |
| 41 | +Base URL is the same origin (relative). All JSON request/response unless noted. Auth: `Authorization: Bearer <token>` where required. |
| 42 | + |
| 43 | +### 3.1 Health (no auth) |
| 44 | + |
| 45 | +| Method | Path | Description | |
| 46 | +|--------|----------------|-------------| |
| 47 | +| GET | `/health` | App health. Response: `{ status, service }`. | |
| 48 | +| GET | `/api/generate/health` | Backend + acestep health. Response: `{ healthy, mode, lmBin?, ditVaeBin?, aceStepUrl }`. | |
| 49 | + |
| 50 | +### 3.2 Auth (`/api/auth`) |
| 51 | + |
| 52 | +| Method | Path | Auth | Request | Response | |
| 53 | +|--------|----------------|------|---------|----------| |
| 54 | +| GET | `/api/auth/auto` | No | — | `AuthResponse`: `{ user: User, token }`. | |
| 55 | +| POST | `/api/auth/setup` | No | `{ username }` | `AuthResponse`. | |
| 56 | +| GET | `/api/auth/me` | Yes | — | `{ user: User }`. | |
| 57 | +| POST | `/api/auth/logout` | No | — | `{ success: boolean }`. | |
| 58 | +| POST | `/api/auth/refresh` | Yes | — | `AuthResponse`. | |
| 59 | +| PATCH | `/api/auth/username` | Yes | `{ username }` | `AuthResponse`. | |
| 60 | + |
| 61 | +**Types:** `User`: `{ id, username, isAdmin?, bio?, avatar_url?, banner_url?, createdAt? }`. `AuthResponse`: `{ user: User, token: string }`. |
| 62 | + |
| 63 | +### 3.3 Songs (`/api/songs`) |
| 64 | + |
| 65 | +| Method | Path | Auth | Request | Response | |
| 66 | +|--------|------|------|---------|----------| |
| 67 | +| GET | `/api/songs` | Yes | — | `{ songs: Song[] }`. | |
| 68 | +| GET | `/api/songs/public` | No | Query: `limit`, `offset` | `{ songs: Song[] }`. | |
| 69 | +| GET | `/api/songs/public/featured` | No | — | `{ songs: Song[] }`. | |
| 70 | +| GET | `/api/songs/:id` | Optional | — | `{ song: Song }`. | |
| 71 | +| GET | `/api/songs/:id/full` | Optional | — | `{ song: Song, comments: Comment[] }`. | |
| 72 | +| POST | `/api/songs` | Yes | `Partial<Song>` | `{ song: Song }`. | |
| 73 | +| PATCH | `/api/songs/:id` | Yes | `Partial<Song>` | `{ song: Song }`. | |
| 74 | +| DELETE | `/api/songs/:id` | Yes | — | `{ success: boolean }`. | |
| 75 | +| POST | `/api/songs/:id/like` | Yes | — | `{ liked: boolean }`. | |
| 76 | +| GET | `/api/songs/liked/list` | Yes | — | `{ songs: Song[] }`. | |
| 77 | +| PATCH | `/api/songs/:id/privacy` | Yes | — | `{ isPublic: boolean }`. | |
| 78 | +| POST | `/api/songs/:id/play` | Optional | — | `{ viewCount: number }`. | |
| 79 | +| GET | `/api/songs/:id/comments` | Optional | — | `{ comments: Comment[] }`. | |
| 80 | +| POST | `/api/songs/:id/comments` | Yes | `{ content }` | `{ comment: Comment }`. | |
| 81 | +| DELETE | `/api/songs/comments/:commentId` | Yes | — | `{ success: boolean }`. | |
| 82 | + |
| 83 | +**Song** (main fields): `id`, `title`, `lyrics`, `style`, `caption?`, `cover_url?`, `audio_url?` / `audioUrl?`, `duration?`, `bpm?`, `key_scale?`, `time_signature?`, `tags`, `is_public`, `like_count?`, `view_count?`, `user_id?`, `created_at`, `creator?`, `ditModel?`, `generation_params?`. |
| 84 | + |
| 85 | +### 3.4 Generation (`/api/generate`) |
| 86 | + |
| 87 | +| Method | Path | Auth | Request | Response | |
| 88 | +|--------|------|------|---------|----------| |
| 89 | +| POST | `/api/generate` | Yes | `GenerationParams` (see below) | `GenerationJob` (jobId, status, queuePosition). | |
| 90 | +| GET | `/api/generate/status/:jobId` | Yes | — | `GenerationJob` (full status, result when done). | |
| 91 | +| GET | `/api/generate/history` | Yes | — | `{ jobs: GenerationJob[] }`. | |
| 92 | +| POST | `/api/generate/upload-audio` | Yes | `FormData` field `audio` (file) | `{ url, key }`. | |
| 93 | +| POST | `/api/generate/format` | Yes | Format body (see below) | Format response (see below). | |
| 94 | +| GET | `/api/generate/random-description` | Yes | — | `{ description, instrumental, vocalLanguage }`. | |
| 95 | +| GET | `/api/generate/models` | No | — | `{ models: Array<{ name, is_active?, is_preloaded? }> }`. | |
| 96 | +| GET | `/api/generate/limits` | No | — | Limits object (e.g. `tier`, `gpu_memory_gb`, `max_duration_*`, `max_batch_size_*`). | |
| 97 | +| GET | `/api/generate/health` | No | — | See §3.1. | |
| 98 | +| GET | `/api/generate/endpoints` | Yes | — | `{ endpoints: { provider, mode, lmBin?, ditVaeBin?, apiUrl? } }`. | |
| 99 | +| GET | `/api/generate/audio` | No | Query: `path` | Binary audio stream (proxy to acestep or local file). | |
| 100 | +| GET | `/api/generate/logs` | Yes | — | `{ jobs: Array<{ jobId, status, startTime, stage?, logCount }> }`. | |
| 101 | +| GET | `/api/generate/logs/:jobId` | Yes | Query: `after` (index) | `{ lines: string[], total, status }`. | |
| 102 | +| GET | `/api/generate/debug/:taskId` | Yes | — | `{ rawResponse }`. | |
| 103 | + |
| 104 | +**GenerationParams** (key fields): `customMode?`, `songDescription?`, `lyrics`, `style`, `title`, `instrumental`, `vocalLanguage?`, `duration?`, `bpm?`, `keyScale?`, `timeSignature?`, `inferenceSteps?`, `guidanceScale?`, `batchSize?`, `randomSeed?`, `seed?`, `thinking?`, `audioFormat?` ('wav' \| 'mp3'), `inferMethod?`, `shift?`, LM params (`lmTemperature?`, `lmCfgScale?`, `lmTopK?`, `lmTopP?`, `lmNegativePrompt?`, `lmBackend?`, `lmModel?`), `referenceAudioUrl?`, `sourceAudioUrl?`, `referenceAudioTitle?`, `sourceAudioTitle?`, `audioCodes?`, `repaintingStart?`, `repaintingEnd?`, `instruction?`, `audioCoverStrength?`, `taskType?` (e.g. `text2music`, `cover`, `audio2audio`, `repaint`, `lego`, `passthrough`), `useAdg?`, `cfgIntervalStart?`, `cfgIntervalEnd?`, `customTimesteps?`, `useCotMetas?`, `useCotCaption?`, `useCotLanguage?`, `autogen?`, `trackName?`, `completeTrackClasses?`, `isFormatCaption?`, `ditModel?`, and other expert options. |
| 105 | + |
| 106 | +**GenerationJob:** `jobId`, `id?`, `status`: 'pending' \| 'queued' \| 'running' \| 'succeeded' \| 'failed', `queuePosition?`, `etaSeconds?`, `progress?`, `stage?`, `params?`, `created_at?`, `result?`: `{ audioUrls, bpm?, duration?, keyScale?, timeSignature? }`, `error?`. |
| 107 | + |
| 108 | +**Format request:** `caption`, `lyrics?`, `bpm?`, `duration?`, `keyScale?`, `timeSignature?`, `temperature?`, `topK?`, `topP?`, `lmModel?`, `lmBackend?`. **Format response:** `caption?`, `lyrics?`, `bpm?`, `duration?`, `key_scale?`, `vocal_language?`, `time_signature?`, `status_message?`, `error?`. |
| 109 | + |
| 110 | +### 3.5 LoRA (`/api/lora`) |
| 111 | + |
| 112 | +| Method | Path | Auth | Request | Response | |
| 113 | +|--------|------|------|---------|----------| |
| 114 | +| POST | `/api/lora/load` | Yes | `{ lora_path }` | `{ message, lora_path?, loaded? }`. | |
| 115 | +| POST | `/api/lora/unload` | Yes | — | `{ message }`. | |
| 116 | +| POST | `/api/lora/scale` | Yes | `{ scale }` (0–1) | `{ message, scale? }`. | |
| 117 | +| POST | `/api/lora/toggle` | Yes | `{ enabled }` | `{ message, active }`. | |
| 118 | +| GET | `/api/lora/status` | Yes | — | `{ loaded, active, scale, path }`. | |
| 119 | + |
| 120 | +### 3.6 Reference tracks (`/api/reference-tracks`) |
| 121 | + |
| 122 | +| Method | Path | Auth | Request | Response | |
| 123 | +|--------|------|------|---------|----------| |
| 124 | +| GET | `/api/reference-tracks` | Yes | — | List of reference tracks. | |
| 125 | +| POST | `/api/reference-tracks` | Yes | `FormData` field `audio` (file) | Created track. | |
| 126 | +| PATCH | `/api/reference-tracks/:id` | Yes | Partial track (e.g. title) | Updated track. | |
| 127 | +| POST | `/api/reference-tracks/:id/transcribe` | Yes | — | Transcription result. | |
| 128 | +| POST | `/api/reference-tracks/:id/understand` | Yes | — | Understand result (metadata/lyrics from ace-understand). | |
| 129 | +| POST | `/api/reference-tracks/understand-url` | Yes | `{ audioUrl }` | Same shape as understand. | |
| 130 | +| DELETE | `/api/reference-tracks/:id` | Yes | — | Success. | |
| 131 | + |
| 132 | +### 3.7 Models — GGUF catalog & downloads (`/api/models`) |
| 133 | + |
| 134 | +| Method | Path | Auth | Request | Response | |
| 135 | +|--------|------|------|---------|----------| |
| 136 | +| GET | `/api/models/catalog` | No | — | `{ catalog: CatalogEntry[], repo }`. | |
| 137 | +| GET | `/api/models/status` | No | — | `ModelStatus`: `{ modelsDir, activeModel, onDisk, catalog, queue }`. | |
| 138 | +| POST | `/api/models/download` | Yes | `{ files: string[] }` (filenames) | `{ enqueued, queueLength }`. | |
| 139 | +| GET | `/api/models/download/stream` | No | — | **SSE** stream: events e.g. `progress` with job data. | |
| 140 | +| POST | `/api/models/active` | Yes | `{ filename }` | `{ message, filename, path }`. | |
| 141 | + |
| 142 | +**CatalogEntry:** `filename`, `label`, `group` ('vae' \| 'encoder' \| 'lm' \| 'dit'), `quant`, `variant?`, `essential`, `approxSizeMB`, plus from status: `downloaded?`, `queued?`, `active?`. **DownloadJob:** `id`, `filename`, `status`, `downloadedBytes`, `totalBytes`, `error?`. |
| 143 | + |
| 144 | +### 3.8 Users (`/api/users`) |
| 145 | + |
| 146 | +| Method | Path | Auth | Request | Response | |
| 147 | +|--------|------|------|---------|----------| |
| 148 | +| GET | `/api/users/public/featured` | No | — | `{ creators: (UserProfile & { follower_count? })[] }`. | |
| 149 | +| GET | `/api/users/:username` | Optional | — | `{ user: UserProfile }`. | |
| 150 | +| GET | `/api/users/:username/songs` | No | — | `{ songs: Song[] }`. | |
| 151 | +| GET | `/api/users/:username/playlists` | No | — | `{ playlists }`. | |
| 152 | +| PATCH | `/api/users/me` | Yes | `Partial<User>` | `{ user: User }`. | |
| 153 | +| POST | `/api/users/me/avatar` | Yes | `FormData` field `avatar` | `{ user, url }`. | |
| 154 | +| POST | `/api/users/me/banner` | Yes | `FormData` field `banner` | `{ user, url }`. | |
| 155 | +| POST | `/api/users/:username/follow` | Yes | — | `{ following, followerCount }`. | |
| 156 | +| GET | `/api/users/:username/followers` | No | — | `{ followers: User[] }`. | |
| 157 | +| GET | `/api/users/:username/following` | No | — | `{ following: User[] }`. | |
| 158 | +| GET | `/api/users/:username/stats` | Optional | — | `{ followerCount, followingCount, isFollowing }`. | |
| 159 | + |
| 160 | +### 3.9 Playlists (`/api/playlists`) |
| 161 | + |
| 162 | +| Method | Path | Auth | Request | Response | |
| 163 | +|--------|------|------|---------|----------| |
| 164 | +| POST | `/api/playlists` | Yes | `{ name, description, isPublic }` | `{ playlist: Playlist }`. | |
| 165 | +| GET | `/api/playlists` | Yes | — | `{ playlists: Playlist[] }`. | |
| 166 | +| GET | `/api/playlists/public/featured` | No | — | `{ playlists }`. | |
| 167 | +| GET | `/api/playlists/:id` | Optional | — | `{ playlist, songs }`. | |
| 168 | +| POST | `/api/playlists/:id/songs` | Yes | `{ songId }` | `{ success }`. | |
| 169 | +| DELETE | `/api/playlists/:id/songs/:songId` | Yes | — | `{ success }`. | |
| 170 | +| PATCH | `/api/playlists/:id` | Yes | `Partial<Playlist>` | `{ playlist }`. | |
| 171 | +| DELETE | `/api/playlists/:id` | Yes | — | Success. | |
| 172 | + |
| 173 | +### 3.10 Search, contact, proxies |
| 174 | + |
| 175 | +| Method | Path | Auth | Request | Response | |
| 176 | +|--------|------|------|---------|----------| |
| 177 | +| GET | `/api/search` | No | Query: `q`, optional `type` ('songs' \| 'creators' \| 'playlists' \| 'all') | `{ songs, creators, playlists }`. | |
| 178 | +| POST | `/api/contact` | No | `ContactFormData`: `name`, `email`, `subject`, `message`, `category` | `{ success, message?, id? }`. | |
| 179 | +| GET | `/api/oembed` | No | Query: `url` (song page URL) | oEmbed JSON. | |
| 180 | +| GET | `/api/proxy/image` | No | Query: `url` | Proxied image binary. | |
| 181 | +| GET | `/api/pexels/photos` | No | Query: `query`. Optional header `x-pexels-api-key` | Pexels API response. | |
| 182 | +| GET | `/api/pexels/videos` | No | Same | Pexels API response. | |
| 183 | + |
| 184 | +--- |
| 185 | + |
| 186 | +## 4. Backend → acestep.cpp (HTTP fallback only) |
| 187 | + |
| 188 | +When **spawn mode** is not used (no `ace-lm` / `ace-synth` binaries), the server uses `config.acestep.apiUrl` (e.g. `http://localhost:7860`). |
| 189 | + |
| 190 | +| Method | URL / path | Request | Response / notes | |
| 191 | +|--------|------------|---------|-------------------| |
| 192 | +| GET | `{apiUrl}/health` | — | Health check. | |
| 193 | +| POST | `{apiUrl}/v1/generate` | JSON body from `buildHttpRequest(params)` (see `server/src/services/acestep.ts`) | `{ audio_paths?, bpm?, key_scale?, time_signature?, duration?, error? }`. | |
| 194 | +| GET | `{apiUrl}/v1/audio?path=...` | — | Binary audio. | |
| 195 | +| GET | `{apiUrl}/v1/models` | — | `{ models: [...] }` (or legacy `data.models`). | |
| 196 | +| GET | `{apiUrl}/v1/limits` | — | Limits object. | |
| 197 | +| POST | `{apiUrl}/format_input` | `{ prompt, lyrics, temperature, param_obj }` | Format/enhance response. | |
| 198 | +| POST | `{apiUrl}/v1/lora/load` | `{ lora_path }` | — | |
| 199 | +| POST | `{apiUrl}/v1/lora/unload` | — | — | |
| 200 | +| POST | `{apiUrl}/v1/lora/scale` | `{ scale }` | — | |
| 201 | +| POST | `{apiUrl}/v1/lora/toggle` | `{ enabled }` | — | |
| 202 | + |
| 203 | +Spawn mode does not use these HTTP calls; it runs `ace-lm` and `ace-synth` with a JSON request file and reads output files from a temp directory. |
| 204 | + |
| 205 | +--- |
| 206 | + |
| 207 | +## 5. Configuration and environment |
| 208 | + |
| 209 | +Values that affect the API or backend behaviour (from `server/src/config/index.ts` and root/server `.env`): |
| 210 | + |
| 211 | +| Variable | Default / note | |
| 212 | +|----------|-----------------| |
| 213 | +| `PORT` | `3001` — Express and UI server. | |
| 214 | +| `ACESTEP_API_URL` | `http://localhost:7860` — Used only in HTTP fallback mode. | |
| 215 | +| `MODELS_DIR` | `<APP_ROOT>/models` — GGUF models. | |
| 216 | +| `AUDIO_DIR` | `<server_root>/public/audio` — Served at `/audio`. | |
| 217 | +| `DATABASE_PATH` | `<APP_ROOT>/data/acestep.db`. | |
| 218 | +| `JWT_SECRET` | Local default; set in production. | |
| 219 | +| `ACE_LM_BIN`, `ACE_SYNTH_BIN`, `ACE_UNDERSTAND_BIN` | Optional; otherwise resolved from `bin/`. | |
| 220 | +| `ACESTEP_MODEL`, `ACESTEP_BASE_MODEL`, `LM_MODEL`, `TEXT_ENCODER_MODEL`, `VAE_MODEL` | Optional overrides for model paths. | |
| 221 | +| `PEXELS_API_KEY` | Optional; can be overridden per-request via `x-pexels-api-key`. | |
| 222 | +| `FRONTEND_URL` | `http://localhost:PORT`. | |
| 223 | + |
| 224 | +Electron sets `process.env.PORT`, `MODELS_DIR`, `AUDIO_DIR`, `DATABASE_PATH`, `JWT_SECRET`, and binary paths when starting the server; the UI still uses same-origin relative URLs. |
| 225 | + |
| 226 | +--- |
| 227 | + |
| 228 | +## 6. Types and clients |
| 229 | + |
| 230 | +- **UI API client:** `services/api.ts` — defines `authApi`, `songsApi`, `generateApi`, `usersApi`, `playlistsApi`, `searchApi`, `contactApi`, `modelsApi`, and the types referenced in this document (`User`, `AuthResponse`, `Song`, `GenerationParams`, `GenerationJob`, `CatalogEntry`, `DownloadJob`, `ModelStatus`, `UserProfile`, `Playlist`, `SearchResult`, `ContactFormData`). |
| 231 | +- **Server route types:** `GenerateBody` in `server/src/routes/generate.ts` aligns with `GenerationParams`; `server/src/services/acestep.ts` defines `GenerationParams`, `GenerationResult`, `JobStatus`, `LoraState`, `UnderstandResult`, and the HTTP response type for `v1/generate`. |
| 232 | + |
| 233 | +--- |
| 234 | + |
| 235 | +## 7. Static and special routes |
| 236 | + |
| 237 | +- **Audio files:** `GET /audio/*` — Express static from `config.storage.audioDir`. |
| 238 | +- **Editors:** `/editor` (AudioMass), `/demucs-web` (Demucs) — static with relaxed CSP where needed. |
| 239 | +- **Song share / SEO:** `GET /song/:id` — Serves HTML meta for bots; redirects others to the app with `?song=:id`. |
| 240 | +- **Contact admin** (not used by main UI): `GET/PATCH/DELETE /api/contact/admin/*`, `GET /api/contact/admin/unread-count` — admin-only. |
| 241 | + |
| 242 | +This file is the single source of truth for the UI-facing and backend→acestep API surface as of the last update. |
0 commit comments