Music-project / ACE-Step-1.5 /docs /ko /Openrouter_API_DOC.md
Shubham Sattigeri
Initial commit
116a8d1
|
Raw
History Blame Contribute Delete
20 kB

A newer version of the Gradio SDK is available: 6.20.0

Upgrade

ACE-Step OpenRouter API ๋ฌธ์„œ

AI ์Œ์•… ์ƒ์„ฑ์„ ์œ„ํ•œ OpenAI Chat Completions ํ˜ธํ™˜ API

Base URL: http://{host}:{port} (๊ธฐ๋ณธ๊ฐ’ http://127.0.0.1:8002)


๋ชฉ์ฐจ


์ธ์ฆ

์„œ๋ฒ„์— 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 ํ…์ŠคํŠธ = caption
  • sample_mode: true ์„ค์ • ์‹œ โ†’ messages ํ…์ŠคํŠธ = sample_query (LLM์—๊ฒŒ ๋ชจ๋“  ๊ฒƒ์„ ์ƒ์„ฑํ•˜๋„๋ก ํ•จ)
  • ๋‘˜ ๋‹ค ๋ฏธ์„ค์ • โ†’ ์ž๋™ ๊ฐ์ง€: ํƒœ๊ทธ๊ฐ€ ์žˆ์œผ๋ฉด ํƒœ๊ทธ ๋ชจ๋“œ, ๊ฐ€์‚ฌ์ฒ˜๋Ÿผ ๋ณด์ด๋ฉด ๊ฐ€์‚ฌ ๋ชจ๋“œ, ๊ทธ ์™ธ caption ๋กœ ์ง์ ‘ ์ƒ์„ฑ์— ์‚ฌ์šฉ

messages ํ˜•์‹

์ผ๋ฐ˜ ํ…์ŠคํŠธ์™€ ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ(ํ…์ŠคํŠธ + ์˜ค๋””์˜ค) ๋‘ ๊ฐ€์ง€ ํ˜•์‹์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค:

์ผ๋ฐ˜ ํ…์ŠคํŠธ:

{
  "messages": [
    {"role": "user", "content": "์ž…๋ ฅ ๋‚ด์šฉ"}
  ]
}

๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ (์˜ค๋””์˜ค ์ž…๋ ฅ ํฌํ•จ):

{
  "messages": [
    {
      "role": "user",
      "content": [
        {"type": "text", "text": "์ด ๋…ธ๋ž˜๋ฅผ ์ปค๋ฒ„ํ•ด์ค˜"},
        {
          "type": "input_audio",
          "input_audio": {
            "data": "<base64 ์˜ค๋””์˜ค ๋ฐ์ดํ„ฐ>",
            "format": "mp3"
          }
        }
      ]
    }
  ]
}

๋น„์ŠคํŠธ๋ฆฌ๋ฐ ์‘๋‹ต (stream: false)

{
  "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 ํŒŒ์ผ์„ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

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)
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

์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ๋ชจ๋ธ ์ •๋ณด๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.

์‘๋‹ต

{
  "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

์‘๋‹ต

{
  "status": "ok",
  "service": "ACE-Step OpenRouter API",
  "version": "1.0"
}

์ž…๋ ฅ ๋ชจ๋“œ

์‹œ์Šคํ…œ์€ ๋งˆ์ง€๋ง‰ user ๋ฉ”์‹œ์ง€์˜ ๋‚ด์šฉ์— ๋”ฐ๋ผ ์ž…๋ ฅ ๋ชจ๋“œ๋ฅผ ์ž๋™์œผ๋กœ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. lyrics ๋˜๋Š” sample_mode ํ•„๋“œ๋กœ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•  ์ˆ˜๋„ ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“œ 1: ํƒœ๊ทธ ๋ชจ๋“œ (๊ถŒ์žฅ)

<prompt>์™€ <lyrics> ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์Œ์•… ์„ค๋ช…๊ณผ ๊ฐ€์‚ฌ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค:

{
  "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๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค:

{
  "messages": [
    {"role": "user", "content": "์—ฌ๋ฆ„๊ณผ ์—ฌํ–‰์— ๊ด€ํ•œ ์‹ ๋‚˜๋Š” ํŒ์†ก์„ ๋งŒ๋“ค์–ด์ค˜"}
  ],
  "sample_mode": true,
  "audio_config": {
    "vocal_language": "ko"
  }
}

ํŠธ๋ฆฌ๊ฑฐ ์กฐ๊ฑด: sample_mode: true, ๋˜๋Š” ๋ฉ”์‹œ์ง€์— ํƒœ๊ทธ๊ฐ€ ์—†๊ณ  ๊ฐ€์‚ฌ์ฒ˜๋Ÿผ ๋ณด์ด์ง€ ์•Š์„ ๋•Œ ์ž๋™ ํŠธ๋ฆฌ๊ฑฐ.

๋ชจ๋“œ 3: ๊ฐ€์‚ฌ ์ „์šฉ ๋ชจ๋“œ

๊ตฌ์กฐ ๋งˆ์ปค๊ฐ€ ์žˆ๋Š” ๊ฐ€์‚ฌ๋ฅผ ์ง์ ‘ ์ „๋‹ฌํ•˜๋ฉด ์‹œ์Šคํ…œ์ด ์ž๋™์œผ๋กœ ์ธ์‹ํ•ฉ๋‹ˆ๋‹ค:

{
  "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๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค:

{
  "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 ์„ค์ •:

{
  "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 ์ž‘์—… (์ปค๋ฒ„):

{
  "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 ์ž‘์—… (๋ถ€๋ถ„ ๋ฆฌํŽ˜์ธํŒ…):

{
  "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]

ํด๋ผ์ด์–ธํŠธ ์ธก ์ŠคํŠธ๋ฆฌ๋ฐ ์ฒ˜๋ฆฌ

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))
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: ์ž์—ฐ์–ด ์ƒ์„ฑ (๊ฐ€์žฅ ๊ฐ„๋‹จํ•œ ์‚ฌ์šฉ๋ฒ•)

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: ํƒœ๊ทธ ๋ชจ๋“œ + ํŒŒ๋ผ๋ฏธํ„ฐ ์ง€์ •

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 ๊ฐœ์„  ๋น„ํ™œ์„ฑํ™”

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: ์ŠคํŠธ๋ฆฌ๋ฐ ์š”์ฒญ

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: ๋ฉ€ํ‹ฐ ์‹œ๋“œ ๋ฐฐ์น˜ ์ƒ์„ฑ

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 ์ƒ์„ฑ ํƒ€์ž„์•„์›ƒ

์—๋Ÿฌ ์‘๋‹ต ํ˜•์‹:

{
  "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 ๋น„์ŠคํŠธ๋ฆฌ๋ฐ ์š”์ฒญ ํƒ€์ž„์•„์›ƒ(์ดˆ)