Ace-Step-Munk / docs /ko /Openrouter_API_DOC.md
OnyxMunk's picture
Add LoRA training assets: scripts, docs (no binaries), ui, my_dataset
bc9c638
# ACE-Step OpenRouter API λ¬Έμ„œ
> AI μŒμ•… 생성을 μœ„ν•œ OpenAI Chat Completions ν˜Έν™˜ API
**Base URL:** `http://{host}:{port}` (κΈ°λ³Έκ°’ `http://127.0.0.1:8002`)
---
## λͺ©μ°¨
- [인증](#인증)
- [μ—”λ“œν¬μΈνŠΈ](#μ—”λ“œν¬μΈνŠΈ)
- [POST /v1/chat/completions - μŒμ•… 생성](#1-μŒμ•…-생성)
- [GET /v1/models - λͺ¨λΈ λͺ©λ‘](#2-λͺ¨λΈ-λͺ©λ‘)
- [GET /health - ν—¬μŠ€ 체크](#3-ν—¬μŠ€-체크)
- [μž…λ ₯ λͺ¨λ“œ](#μž…λ ₯-λͺ¨λ“œ)
- [μ˜€λ””μ˜€ μž…λ ₯](#μ˜€λ””μ˜€-μž…λ ₯)
- [슀트리밍 응닡](#슀트리밍-응닡)
- [예제](#예제)
- [μ—λŸ¬ μ½”λ“œ](#μ—λŸ¬-μ½”λ“œ)
---
## 인증
μ„œλ²„μ— API ν‚€κ°€ μ„€μ •λœ 경우(ν™˜κ²½ λ³€μˆ˜ `OPENROUTER_API_KEY` λ˜λŠ” `--api-key` ν”Œλž˜κ·Έ μ‚¬μš©), λͺ¨λ“  μš”μ²­μ€ λ‹€μŒ 헀더λ₯Ό 포함해야 ν•©λ‹ˆλ‹€:
```
Authorization: Bearer <your-api-key>
```
API ν‚€κ°€ μ„€μ •λ˜μ§€ μ•Šμ€ 경우 인증이 ν•„μš”ν•˜μ§€ μ•ŠμŠ΅λ‹ˆλ‹€.
---
## μ—”λ“œν¬μΈνŠΈ
### 1. μŒμ•… 생성
**POST** `/v1/chat/completions`
μ±„νŒ… λ©”μ‹œμ§€λ‘œλΆ€ν„° μŒμ•…μ„ μƒμ„±ν•˜κ³  μ˜€λ””μ˜€ 데이터와 LM이 μƒμ„±ν•œ 메타데이터λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
#### μš”μ²­ νŒŒλΌλ―Έν„°
| ν•„λ“œ | νƒ€μž… | ν•„μˆ˜ | κΈ°λ³Έκ°’ | μ„€λͺ… |
|---|---|---|---|---|
| `model` | string | μ•„λ‹ˆμš” | μžλ™ | λͺ¨λΈ ID (`/v1/models`μ—μ„œ 확인) |
| `messages` | array | **예** | - | μ±„νŒ… λ©”μ‹œμ§€ 리슀트. [μž…λ ₯ λͺ¨λ“œ](#μž…λ ₯-λͺ¨λ“œ) μ°Έμ‘° |
| `stream` | boolean | μ•„λ‹ˆμš” | `false` | 슀트리밍 응닡 ν™œμ„±ν™”. [슀트리밍 응닡](#슀트리밍-응닡) μ°Έμ‘° |
| `audio_config` | object | μ•„λ‹ˆμš” | `null` | μ˜€λ””μ˜€ 생성 μ„€μ •. μ•„λž˜ μ°Έμ‘° |
| `temperature` | float | μ•„λ‹ˆμš” | `0.85` | LM μƒ˜ν”Œλ§ μ˜¨λ„ |
| `top_p` | float | μ•„λ‹ˆμš” | `0.9` | LM nucleus sampling νŒŒλΌλ―Έν„° |
| `seed` | int \| string | μ•„λ‹ˆμš” | `null` | 랜덀 μ‹œλ“œ. `batch_size > 1`일 λ•Œ μ‰Όν‘œλ‘œ κ΅¬λΆ„ν•˜μ—¬ μ—¬λŸ¬ 개 μ§€μ • κ°€λŠ₯ (예: `"42,123,456"`) |
| `lyrics` | string | μ•„λ‹ˆμš” | `""` | 직접 μ „λ‹¬λ˜λŠ” 가사 (λ©”μ‹œμ§€μ—μ„œ νŒŒμ‹±λœ 가사보닀 μš°μ„ ). μ„€μ • μ‹œ messages ν…μŠ€νŠΈλŠ” prompt둜 μ‚¬μš© |
| `sample_mode` | boolean | μ•„λ‹ˆμš” | `false` | LLM sample λͺ¨λ“œ ν™œμ„±ν™”. messages ν…μŠ€νŠΈκ°€ sample_query둜 LLM에 μ „λ‹¬λ˜μ–΄ prompt/lyrics μžλ™ 생성 |
| `thinking` | boolean | μ•„λ‹ˆμš” | `false` | 더 κΉŠμ€ 좔둠을 μœ„ν•œ LLM thinking λͺ¨λ“œ ν™œμ„±ν™” |
| `use_format` | boolean | μ•„λ‹ˆμš” | `false` | μ‚¬μš©μžκ°€ prompt/lyricsλ₯Ό μ œκ³΅ν•  λ•Œ LLM ν¬λ§·νŒ…μœΌλ‘œ κ°œμ„  |
| `use_cot_caption` | boolean | μ•„λ‹ˆμš” | `true` | CoTλ₯Ό 톡해 μŒμ•… μ„€λͺ…을 μž¬μž‘μ„±/κ°œμ„  |
| `use_cot_language` | boolean | μ•„λ‹ˆμš” | `true` | CoTλ₯Ό 톡해 보컬 μ–Έμ–΄λ₯Ό μžλ™ 감지 |
| `guidance_scale` | float | μ•„λ‹ˆμš” | `7.0` | Classifier-free guidance scale |
| `batch_size` | int | μ•„λ‹ˆμš” | `1` | 생성할 μ˜€λ””μ˜€ 수 |
| `task_type` | string | μ•„λ‹ˆμš” | `"text2music"` | μž‘μ—… μœ ν˜•. [μ˜€λ””μ˜€ μž…λ ₯](#μ˜€λ””μ˜€-μž…λ ₯) μ°Έμ‘° |
| `repainting_start` | float | μ•„λ‹ˆμš” | `0.0` | repaint μ˜μ—­ μ‹œμž‘ μœ„μΉ˜(초) |
| `repainting_end` | float | μ•„λ‹ˆμš” | `null` | repaint μ˜μ—­ μ’…λ£Œ μœ„μΉ˜(초) |
| `audio_cover_strength` | float | μ•„λ‹ˆμš” | `1.0` | 컀버 강도 (0.0~1.0) |
#### audio_config 객체
| ν•„λ“œ | νƒ€μž… | κΈ°λ³Έκ°’ | μ„€λͺ… |
|---|---|---|---|
| `duration` | float | `null` | μ˜€λ””μ˜€ 길이(초). μƒλž΅ μ‹œ LM이 μžλ™ κ²°μ • |
| `bpm` | integer | `null` | λΆ„λ‹Ή λΉ„νŠΈμˆ˜(BPM). μƒλž΅ μ‹œ LM이 μžλ™ κ²°μ • |
| `vocal_language` | string | `"en"` | 보컬 μ–Έμ–΄ μ½”λ“œ (예: `"ko"`, `"en"`, `"ja"`) |
| `instrumental` | boolean | `null` | 보컬 μ—†λŠ” 연주곑 μ—¬λΆ€. μƒλž΅ μ‹œ 가사에 따라 μžλ™ νŒλ‹¨ |
| `format` | string | `"mp3"` | 좜λ ₯ μ˜€λ””μ˜€ 포맷 |
| `key_scale` | string | `null` | μ‘°μ„± (예: `"C major"`) |
| `time_signature` | string | `null` | λ°•μž (예: `"4/4"`) |
> **messages ν…μŠ€νŠΈμ˜ μ˜λ―ΈλŠ” λͺ¨λ“œμ— 따라 λ‹€λ¦…λ‹ˆλ‹€:**
> - `lyrics` μ„€μ • μ‹œ β†’ messages ν…μŠ€νŠΈ = prompt (μŒμ•… μ„€λͺ…)
> - `sample_mode: true` μ„€μ • μ‹œ β†’ messages ν…μŠ€νŠΈ = sample_query (LLMμ—κ²Œ λͺ¨λ“  것을 μƒμ„±ν•˜λ„λ‘ 함)
> - λ‘˜ λ‹€ λ―Έμ„€μ • β†’ μžλ™ 감지: νƒœκ·Έκ°€ 있으면 νƒœκ·Έ λͺ¨λ“œ, κ°€μ‚¬μ²˜λŸΌ 보이면 가사 λͺ¨λ“œ, κ·Έ μ™Έ sample λͺ¨λ“œ
#### messages ν˜•μ‹
일반 ν…μŠ€νŠΈμ™€ λ©€ν‹°λͺ¨λ‹¬(ν…μŠ€νŠΈ + μ˜€λ””μ˜€) 두 κ°€μ§€ ν˜•μ‹μ„ μ§€μ›ν•©λ‹ˆλ‹€:
**일반 ν…μŠ€νŠΈ:**
```json
{
"messages": [
{"role": "user", "content": "μž…λ ₯ λ‚΄μš©"}
]
}
```
**λ©€ν‹°λͺ¨λ‹¬ (μ˜€λ””μ˜€ μž…λ ₯ 포함):**
```json
{
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "이 λ…Έλž˜λ₯Ό μ»€λ²„ν•΄μ€˜"},
{
"type": "input_audio",
"input_audio": {
"data": "<base64 μ˜€λ””μ˜€ 데이터>",
"format": "mp3"
}
}
]
}
]
}
```
---
#### λΉ„μŠ€νŠΈλ¦¬λ° 응닡 (`stream: false`)
```json
{
"id": "chatcmpl-a1b2c3d4e5f6g7h8",
"object": "chat.completion",
"created": 1706688000,
"model": "acemusic/acestep-v15-turbo",
"choices": [
{
"index": 0,
"message": {
"role": "assistant",
"content": "## Metadata\n**Caption:** Upbeat pop song...\n**BPM:** 120\n**Duration:** 30s\n**Key:** C major\n\n## Lyrics\n[Verse 1]\nHello world...",
"audio": [
{
"type": "audio_url",
"audio_url": {
"url": "data:audio/mpeg;base64,SUQzBAAAAAAAI1RTU0UAAAA..."
}
}
]
},
"finish_reason": "stop"
}
],
"usage": {
"prompt_tokens": 10,
"completion_tokens": 100,
"total_tokens": 110
}
}
```
**응닡 ν•„λ“œ μ„€λͺ…:**
| ν•„λ“œ | μ„€λͺ… |
|---|---|
| `choices[0].message.content` | LM이 μƒμ„±ν•œ ν…μŠ€νŠΈ 정보. Metadata(Caption/BPM/Duration/Key/Time Signature/Language)와 Lyricsλ₯Ό 포함. LM이 κ΄€μ—¬ν•˜μ§€ μ•Šμ€ 경우 `"Music generated successfully."` λ°˜ν™˜ |
| `choices[0].message.audio` | μ˜€λ””μ˜€ 데이터 λ°°μ—΄. 각 ν•­λͺ©μ— `type` (`"audio_url"`)κ³Ό `audio_url.url` (Base64 Data URL, ν˜•μ‹: `data:audio/mpeg;base64,...`)을 포함 |
| `choices[0].finish_reason` | `"stop"`은 정상 μ™„λ£Œλ₯Ό λ‚˜νƒ€λƒ„ |
**μ˜€λ””μ˜€ λ””μ½”λ”© ν˜•μ‹:**
`audio_url.url` 값은 Data URL ν˜•μ‹: `data:audio/mpeg;base64,<base64_data>`
μ‰Όν‘œ μ΄ν›„μ˜ base64 데이터 뢀뢄을 μΆ”μΆœν•˜μ—¬ λ””μ½”λ”©ν•˜λ©΄ MP3 νŒŒμΌμ„ 얻을 수 μžˆμŠ΅λ‹ˆλ‹€:
```python
import base64
url = response["choices"][0]["message"]["audio"][0]["audio_url"]["url"]
# "data:audio/mpeg;base64," 접두사 제거
b64_data = url.split(",", 1)[1]
audio_bytes = base64.b64decode(b64_data)
with open("output.mp3", "wb") as f:
f.write(audio_bytes)
```
```javascript
const url = response.choices[0].message.audio[0].audio_url.url;
const b64Data = url.split(",")[1];
const audioBytes = atob(b64Data);
// Data URL을 <audio> νƒœκ·Έμ— 직접 μ‚¬μš© κ°€λŠ₯
const audio = new Audio(url);
audio.play();
```
---
### 2. λͺ¨λΈ λͺ©λ‘
**GET** `/v1/models`
μ‚¬μš© κ°€λŠ₯ν•œ λͺ¨λΈ 정보λ₯Ό λ°˜ν™˜ν•©λ‹ˆλ‹€.
#### 응닡
```json
{
"data": [
{
"id": "acemusic/acestep-v15-turbo",
"name": "ACE-Step",
"created": 1706688000,
"description": "High-performance text-to-music generation model. Supports multiple styles, lyrics input, and various audio durations.",
"input_modalities": ["text", "audio"],
"output_modalities": ["audio", "text"],
"context_length": 4096,
"pricing": {"prompt": "0", "completion": "0", "request": "0"},
"supported_sampling_parameters": ["temperature", "top_p"]
}
]
}
```
---
### 3. ν—¬μŠ€ 체크
**GET** `/health`
#### 응닡
```json
{
"status": "ok",
"service": "ACE-Step OpenRouter API",
"version": "1.0"
}
```
---
## μž…λ ₯ λͺ¨λ“œ
μ‹œμŠ€ν…œμ€ λ§ˆμ§€λ§‰ `user` λ©”μ‹œμ§€μ˜ λ‚΄μš©μ— 따라 μž…λ ₯ λͺ¨λ“œλ₯Ό μžλ™μœΌλ‘œ μ„ νƒν•©λ‹ˆλ‹€. `lyrics` λ˜λŠ” `sample_mode` ν•„λ“œλ‘œ λͺ…μ‹œμ μœΌλ‘œ μ§€μ •ν•  μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€.
### λͺ¨λ“œ 1: νƒœκ·Έ λͺ¨λ“œ (ꢌμž₯)
`<prompt>`와 `<lyrics>` νƒœκ·Έλ₯Ό μ‚¬μš©ν•˜μ—¬ μŒμ•… μ„€λͺ…κ³Ό 가사λ₯Ό λͺ…μ‹œμ μœΌλ‘œ μ§€μ •ν•©λ‹ˆλ‹€:
```json
{
"messages": [
{
"role": "user",
"content": "<prompt>A gentle acoustic ballad in C major, female vocal</prompt>\n<lyrics>[Verse 1]\nSunlight through the window\nA brand new day begins\n\n[Chorus]\nWe are the dreamers\nWe are the light</lyrics>"
}
],
"audio_config": {
"duration": 30,
"vocal_language": "en"
}
}
```
- `<prompt>...</prompt>` β€” μŒμ•… μŠ€νƒ€μΌ/μž₯λ©΄ μ„€λͺ… (caption)
- `<lyrics>...</lyrics>` β€” 가사 λ‚΄μš©
- ν•˜λ‚˜μ˜ νƒœκ·Έλ§Œ μ‚¬μš©ν•  μˆ˜λ„ 있음
- `use_format: true`일 λ•Œ LLM이 prompt와 lyricsλ₯Ό μžλ™μœΌλ‘œ κ°œμ„ 
### λͺ¨λ“œ 2: μžμ—°μ–΄ λͺ¨λ“œ (μƒ˜ν”Œ λͺ¨λ“œ)
μ›ν•˜λŠ” μŒμ•…μ„ μžμ—°μ–΄λ‘œ μ„€λͺ…ν•©λ‹ˆλ‹€. μ‹œμŠ€ν…œμ΄ LLM을 μ‚¬μš©ν•˜μ—¬ prompt와 lyricsλ₯Ό μžλ™μœΌλ‘œ μƒμ„±ν•©λ‹ˆλ‹€:
```json
{
"messages": [
{"role": "user", "content": "여름과 여행에 κ΄€ν•œ μ‹ λ‚˜λŠ” νŒμ†‘μ„ λ§Œλ“€μ–΄μ€˜"}
],
"sample_mode": true,
"audio_config": {
"vocal_language": "ko"
}
}
```
트리거 쑰건: `sample_mode: true`, λ˜λŠ” λ©”μ‹œμ§€μ— νƒœκ·Έκ°€ μ—†κ³  κ°€μ‚¬μ²˜λŸΌ 보이지 μ•Šμ„ λ•Œ μžλ™ 트리거.
### λͺ¨λ“œ 3: 가사 μ „μš© λͺ¨λ“œ
ꡬ쑰 λ§ˆμ»€κ°€ μžˆλŠ” 가사λ₯Ό 직접 μ „λ‹¬ν•˜λ©΄ μ‹œμŠ€ν…œμ΄ μžλ™μœΌλ‘œ μΈμ‹ν•©λ‹ˆλ‹€:
```json
{
"messages": [
{
"role": "user",
"content": "[Verse 1]\nWalking down the street\nFeeling the beat\n\n[Chorus]\nDance with me tonight\nUnder the moonlight"
}
],
"audio_config": {"duration": 30}
}
```
트리거 쑰건: λ©”μ‹œμ§€μ— `[Verse]`, `[Chorus]` λ“±μ˜ λ§ˆμ»€κ°€ ν¬ν•¨λ˜κ±°λ‚˜ μ—¬λŸ¬ μ€„μ˜ 짧은 ν…μŠ€νŠΈ ꡬ쑰λ₯Ό κ°€μ§„ 경우.
### λͺ¨λ“œ 4: 가사 + Prompt 뢄리
`lyrics` ν•„λ“œλ‘œ 가사λ₯Ό 직접 μ „λ‹¬ν•˜κ³ , messages ν…μŠ€νŠΈλŠ” μžλ™μœΌλ‘œ prompt둜 μ‚¬μš©λ©λ‹ˆλ‹€:
```json
{
"messages": [
{"role": "user", "content": "Energetic EDM with heavy bass drops"}
],
"lyrics": "[Verse 1]\nFeel the rhythm in your soul\nLet the music take control\n\n[Drop]\n(instrumental break)",
"audio_config": {
"bpm": 128,
"duration": 60
}
}
```
### 연주곑 λͺ¨λ“œ
`audio_config.instrumental: true` μ„€μ •:
```json
{
"messages": [
{"role": "user", "content": "<prompt>Epic orchestral cinematic score, dramatic and powerful</prompt>"}
],
"audio_config": {
"instrumental": true,
"duration": 30
}
}
```
---
## μ˜€λ””μ˜€ μž…λ ₯
λ©€ν‹°λͺ¨λ‹¬ messagesλ₯Ό 톡해 μ˜€λ””μ˜€ 파일(base64 인코딩)을 μ „λ‹¬ν•˜μ—¬ cover, repaint λ“±μ˜ μž‘μ—…μ— μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
### task_type μœ ν˜•
| task_type | μ„€λͺ… | μ˜€λ””μ˜€ μž…λ ₯ ν•„μš” |
|---|---|---|
| `text2music` | ν…μŠ€νŠΈμ—μ„œ μŒμ•… 생성 (κΈ°λ³Έκ°’) | 선택 (reference둜) |
| `cover` | 컀버/μŠ€νƒ€μΌ μ „ν™˜ | src_audio ν•„μš” |
| `repaint` | λΆ€λΆ„ λ¦¬νŽ˜μΈνŒ… | src_audio ν•„μš” |
| `lego` | μ˜€λ””μ˜€ μ ‘ν•© | src_audio ν•„μš” |
| `extract` | μ˜€λ””μ˜€ μΆ”μΆœ | src_audio ν•„μš” |
| `complete` | μ˜€λ””μ˜€ 이어쓰기 | src_audio ν•„μš” |
### μ˜€λ””μ˜€ λΌμš°νŒ… κ·œμΉ™
μ—¬λŸ¬ `input_audio` 블둝은 μˆœμ„œλŒ€λ‘œ λ‹€λ₯Έ νŒŒλΌλ―Έν„°μ— λΌμš°νŒ…λ©λ‹ˆλ‹€ (닀쀑 이미지 μ—…λ‘œλ“œμ™€ μœ μ‚¬):
| task_type | audio[0] | audio[1] |
|---|---|---|
| `text2music` | reference_audio (μŠ€νƒ€μΌ μ°Έμ‘°) | - |
| `cover/repaint/lego/extract/complete` | src_audio (νŽΈμ§‘ λŒ€μƒ μ˜€λ””μ˜€) | reference_audio (선택적 μŠ€νƒ€μΌ μ°Έμ‘°) |
### μ˜€λ””μ˜€ μž…λ ₯ 예제
**Cover μž‘μ—… (컀버):**
```json
{
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "<prompt>Jazz style cover with saxophone</prompt>"},
{
"type": "input_audio",
"input_audio": {"data": "<base64 원본 μ˜€λ””μ˜€>", "format": "mp3"}
}
]
}
],
"task_type": "cover",
"audio_cover_strength": 0.8,
"audio_config": {"duration": 30}
}
```
**Repaint μž‘μ—… (λΆ€λΆ„ λ¦¬νŽ˜μΈνŒ…):**
```json
{
"messages": [
{
"role": "user",
"content": [
{"type": "text", "text": "<prompt>Replace with guitar solo</prompt>"},
{
"type": "input_audio",
"input_audio": {"data": "<base64 원본 μ˜€λ””μ˜€>", "format": "mp3"}
}
]
}
],
"task_type": "repaint",
"repainting_start": 10.0,
"repainting_end": 20.0,
"audio_config": {"duration": 30}
}
```
---
## 슀트리밍 응닡
`"stream": true`둜 μ„€μ •ν•˜λ©΄ SSE(Server-Sent Events) 슀트리밍이 ν™œμ„±ν™”λ©λ‹ˆλ‹€.
### 이벀트 ν˜•μ‹
각 μ΄λ²€νŠΈλŠ” `data: `둜 μ‹œμž‘ν•˜κ³  JSON이 λ’€λ”°λ₯΄λ©° 이쀑 μ€„λ°”κΏˆ `\n\n`으둜 λλ‚©λ‹ˆλ‹€:
```
data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1706688000,"model":"acemusic/acestep-v15-turbo","choices":[{"index":0,"delta":{...},"finish_reason":null}]}
```
### 슀트리밍 이벀트 μˆœμ„œ
| 단계 | delta λ‚΄μš© | μ„€λͺ… |
|---|---|---|
| 1. μ΄ˆκΈ°ν™” | `{"role":"assistant","content":""}` | μ—°κ²° 수립 |
| 2. LM μ½˜ν…μΈ  | `{"content":"\n\n## Metadata\n..."}` | LM μ‚¬μš© μ‹œ metadata와 lyrics 전솑 |
| 3. ν•˜νŠΈλΉ„νŠΈ | `{"content":"."}` | μ˜€λ””μ˜€ 생성 쀑 2μ΄ˆλ§ˆλ‹€ 전솑, μ—°κ²° μœ μ§€ |
| 4. μ˜€λ””μ˜€ 데이터 | `{"audio":[{"type":"audio_url","audio_url":{"url":"data:..."}}]}` | μ˜€λ””μ˜€ base64 데이터 |
| 5. μ™„λ£Œ | `finish_reason: "stop"` | 생성 μ™„λ£Œ |
| 6. μ’…λ£Œ | `data: [DONE]` | 슀트림 μ’…λ£Œ 마컀 |
### 슀트리밍 응닡 μ˜ˆμ‹œ
```
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1706688000,"model":"acemusic/acestep-v15-turbo","choices":[{"index":0,"delta":{"role":"assistant","content":""},"finish_reason":null}]}
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1706688000,"model":"acemusic/acestep-v15-turbo","choices":[{"index":0,"delta":{"content":"\n\n## Metadata\n**Caption:** Upbeat pop\n**BPM:** 120"},"finish_reason":null}]}
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1706688000,"model":"acemusic/acestep-v15-turbo","choices":[{"index":0,"delta":{"content":"."},"finish_reason":null}]}
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1706688000,"model":"acemusic/acestep-v15-turbo","choices":[{"index":0,"delta":{"audio":[{"type":"audio_url","audio_url":{"url":"data:audio/mpeg;base64,..."}}]},"finish_reason":null}]}
data: {"id":"chatcmpl-abc123","object":"chat.completion.chunk","created":1706688000,"model":"acemusic/acestep-v15-turbo","choices":[{"index":0,"delta":{},"finish_reason":"stop"}]}
data: [DONE]
```
### ν΄λΌμ΄μ–ΈνŠΈ μΈ‘ 슀트리밍 처리
```python
import json
import httpx
with httpx.stream("POST", "http://127.0.0.1:8002/v1/chat/completions", json={
"messages": [{"role": "user", "content": "κ²½μΎŒν•œ 기타 곑을 μƒμ„±ν•΄μ€˜"}],
"sample_mode": True,
"stream": True,
"audio_config": {"instrumental": True}
}) as response:
content_parts = []
audio_url = None
for line in response.iter_lines():
if not line or not line.startswith("data: "):
continue
if line == "data: [DONE]":
break
chunk = json.loads(line[6:])
delta = chunk["choices"][0]["delta"]
if "content" in delta and delta["content"]:
content_parts.append(delta["content"])
if "audio" in delta and delta["audio"]:
audio_url = delta["audio"][0]["audio_url"]["url"]
if chunk["choices"][0].get("finish_reason") == "stop":
print("생성 μ™„λ£Œ!")
print("Content:", "".join(content_parts))
if audio_url:
import base64
b64_data = audio_url.split(",", 1)[1]
with open("output.mp3", "wb") as f:
f.write(base64.b64decode(b64_data))
```
```javascript
const response = await fetch("http://127.0.0.1:8002/v1/chat/completions", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
messages: [{ role: "user", content: "κ²½μΎŒν•œ 기타 곑을 μƒμ„±ν•΄μ€˜" }],
sample_mode: true,
stream: true,
audio_config: { instrumental: true }
})
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
let audioUrl = null;
let content = "";
while (true) {
const { done, value } = await reader.read();
if (done) break;
const text = decoder.decode(value);
for (const line of text.split("\n")) {
if (!line.startsWith("data: ") || line === "data: [DONE]") continue;
const chunk = JSON.parse(line.slice(6));
const delta = chunk.choices[0].delta;
if (delta.content) content += delta.content;
if (delta.audio) audioUrl = delta.audio[0].audio_url.url;
}
}
// audioUrl은 <audio src="...">에 직접 μ‚¬μš© κ°€λŠ₯
```
---
## 예제
### 예제 1: μžμ—°μ–΄ 생성 (κ°€μž₯ κ°„λ‹¨ν•œ μ‚¬μš©λ²•)
```bash
curl -X POST http://127.0.0.1:8002/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"messages": [
{"role": "user", "content": "κ³ ν–₯κ³Ό 좔얡에 κ΄€ν•œ λΆ€λ“œλŸ¬μš΄ 포크 솑"}
],
"sample_mode": true,
"audio_config": {"vocal_language": "ko"}
}'
```
### 예제 2: νƒœκ·Έ λͺ¨λ“œ + νŒŒλΌλ―Έν„° μ§€μ •
```bash
curl -X POST http://127.0.0.1:8002/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "<prompt>Energetic EDM track with heavy bass drops and synth leads</prompt><lyrics>[Verse 1]\nFeel the rhythm in your soul\nLet the music take control\n\n[Drop]\n(instrumental break)</lyrics>"
}
],
"audio_config": {
"bpm": 128,
"duration": 60,
"vocal_language": "en"
}
}'
```
### 예제 3: 연주곑 + LM κ°œμ„  λΉ„ν™œμ„±ν™”
```bash
curl -X POST http://127.0.0.1:8002/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"messages": [
{
"role": "user",
"content": "<prompt>Peaceful piano solo, slow tempo, jazz harmony</prompt>"
}
],
"use_cot_caption": false,
"audio_config": {
"instrumental": true,
"duration": 45
}
}'
```
### 예제 4: 슀트리밍 μš”μ²­
```bash
curl -X POST http://127.0.0.1:8002/v1/chat/completions \
-H "Content-Type: application/json" \
-N \
-d '{
"messages": [
{"role": "user", "content": "생일 μΆ•ν•˜ λ…Έλž˜λ₯Ό λ§Œλ“€μ–΄μ€˜"}
],
"sample_mode": true,
"stream": true
}'
```
### 예제 5: λ©€ν‹° μ‹œλ“œ 배치 생성
```bash
curl -X POST http://127.0.0.1:8002/v1/chat/completions \
-H "Content-Type: application/json" \
-d '{
"messages": [
{"role": "user", "content": "<prompt>Lo-fi hip hop beat</prompt>"}
],
"batch_size": 3,
"seed": "42,123,456",
"audio_config": {
"instrumental": true,
"duration": 30
}
}'
```
---
## μ—λŸ¬ μ½”λ“œ
| HTTP μƒνƒœ μ½”λ“œ | μ„€λͺ… |
|---|---|
| 400 | 잘λͺ»λœ μš”μ²­ ν˜•μ‹ λ˜λŠ” μœ νš¨ν•œ μž…λ ₯ λˆ„λ½ |
| 401 | API ν‚€ λˆ„λ½ λ˜λŠ” μœ νš¨ν•˜μ§€ μ•ŠμŒ |
| 429 | μ„œλΉ„μŠ€ κ³ΌλΆ€ν•˜, 큐 가득 μ°Έ |
| 500 | μŒμ•… 생성 쀑 λ‚΄λΆ€ 였λ₯˜ λ°œμƒ |
| 503 | λͺ¨λΈμ΄ 아직 μ΄ˆκΈ°ν™”λ˜μ§€ μ•ŠμŒ |
| 504 | 생성 νƒ€μž„μ•„μ›ƒ |
μ—λŸ¬ 응닡 ν˜•μ‹:
```json
{
"detail": "μ—λŸ¬ μ„€λͺ… λ©”μ‹œμ§€"
}
```
---
## μ„œλ²„ μ„€μ • (ν™˜κ²½ λ³€μˆ˜)
λ‹€μŒ ν™˜κ²½ λ³€μˆ˜λ‘œ μ„œλ²„λ₯Ό μ„€μ •ν•  수 μžˆμŠ΅λ‹ˆλ‹€ (운영 참고용):
| λ³€μˆ˜λͺ… | κΈ°λ³Έκ°’ | μ„€λͺ… |
|---|---|---|
| `OPENROUTER_API_KEY` | μ—†μŒ | API 인증 ν‚€ |
| `OPENROUTER_HOST` | `127.0.0.1` | 리슨 μ£Όμ†Œ |
| `OPENROUTER_PORT` | `8002` | 리슨 포트 |
| `ACESTEP_CONFIG_PATH` | `acestep-v15-turbo` | DiT λͺ¨λΈ μ„€μ • 경둜 |
| `ACESTEP_DEVICE` | `auto` | μΆ”λ‘  λ””λ°”μ΄μŠ€ |
| `ACESTEP_LM_MODEL_PATH` | `acestep-5Hz-lm-0.6B` | LLM λͺ¨λΈ 경둜 |
| `ACESTEP_LM_BACKEND` | `vllm` | LLM μΆ”λ‘  λ°±μ—”λ“œ |
| `ACESTEP_QUEUE_MAXSIZE` | `200` | μž‘μ—… 큐 μ΅œλŒ€ μš©λŸ‰ |
| `ACESTEP_GENERATION_TIMEOUT` | `600` | λΉ„μŠ€νŠΈλ¦¬λ° μš”μ²­ νƒ€μž„μ•„μ›ƒ(초) |