# ACE-Step OpenRouter API 文档 > 兼容 OpenAI Chat Completions 格式的 AI 音乐生成接口 **Base URL:** `http://{host}:{port}` (默认 `http://127.0.0.1:8002`) --- ## 目录 - [认证](#认证) - [接口列表](#接口列表) - [POST /v1/chat/completions - 生成音乐](#1-生成音乐) - [GET /v1/models - 模型列表](#2-模型列表) - [GET /health - 健康检查](#3-健康检查) - [输入模式](#输入模式) - [音频输入](#音频输入) - [流式响应](#流式响应) - [完整示例](#完整示例) - [错误码](#错误码) --- ## 认证 如果服务端配置了 API Key(环境变量 `OPENROUTER_API_KEY` 或启动参数 `--api-key`),所有请求需在 Header 中携带: ``` Authorization: Bearer ``` 未配置 API Key 时无需认证。 --- ## 接口列表 ### 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 中解析的歌词),此时 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` | cover 强度 (0.0~1.0) | #### audio_config 对象 | 字段 | 类型 | 默认值 | 说明 | |---|---|---|---| | `duration` | float | `null` | 音频时长(秒),不传由 LM 自动决定 | | `bpm` | integer | `null` | 每分钟节拍数,不传由 LM 自动决定 | | `vocal_language` | string | `"en"` | 歌词语言代码(如 `"zh"`, `"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": "", "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 数据部分后解码即可得到 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); // 或直接用于