feat: add music generation API (MiniMax Music 2.5/2.6)
- Add POST /v1/music/generations endpoint (index.dspy) - Add music generation section to API docs - Update load_path.py RBAC permissions for new path - Models: music-2.6, music-2.5 (MiniMax, sync, returns audio URL) - Required params: model, catelogid=music_gen, prompt, lyrics
This commit is contained in:
parent
fb7fa8c082
commit
ae02a7e88c
84
docs/API.md
84
docs/API.md
@ -427,6 +427,90 @@ data: [DONE]
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## POST /v1/music/generations
|
||||||
|
|
||||||
|
音乐生成接口。
|
||||||
|
|
||||||
|
### 必填参数
|
||||||
|
|
||||||
|
| 参数 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `model` | string | 模型名称,如 `"music-2.6"`, `"music-2.5"` |
|
||||||
|
| `catelogid` | string | 目录类型ID,固定为 `"music_gen"` |
|
||||||
|
| `prompt` | string | 音乐风格描述(风格、情绪、场景),如 `"流行音乐, 开心, 适合阳光明媚的下午"` |
|
||||||
|
| `lyrics` | string | 歌词内容,使用 `\n` 分隔每行,可包含结构标签 |
|
||||||
|
|
||||||
|
### 歌词结构标签
|
||||||
|
|
||||||
|
歌词中可包含以下结构标签来优化生成的音乐结构:
|
||||||
|
- `[Intro]` - 前奏
|
||||||
|
- `[Verse]` - 主歌
|
||||||
|
- `[Pre Chorus]` - 预副歌
|
||||||
|
- `[Chorus]` - 副歌
|
||||||
|
- `[Bridge]` - 桥段
|
||||||
|
- `[Outro]` - 尾声
|
||||||
|
- `[Interlude]` - 间奏
|
||||||
|
- `[Hook]` - 记忆点
|
||||||
|
- `[Build Up]` - 情绪铺垫
|
||||||
|
- `[Solo]` - 独奏
|
||||||
|
|
||||||
|
### 请求示例
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"model": "music-2.6",
|
||||||
|
"catelogid": "music_gen",
|
||||||
|
"prompt": "Pop music, happy, suitable for a sunny day",
|
||||||
|
"lyrics": "[Intro]\n\n[Verse]\nWalking down the street\nFeeling the beat\n\n[Chorus]\nDancing in the sun\nHaving so much fun"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 响应格式
|
||||||
|
|
||||||
|
MiniMax 音乐生成为同步接口,直接返回音频URL:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "luid_xxx",
|
||||||
|
"object": "music.generation",
|
||||||
|
"model": "music-2.6",
|
||||||
|
"status": "SUCCEEDED",
|
||||||
|
"audio": "https://...",
|
||||||
|
"created": 1716912000
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 可用模型
|
||||||
|
|
||||||
|
| 模型名称 | model 参数 | 说明 |
|
||||||
|
|---------|-----------|------|
|
||||||
|
| MiniMax Music 2.6 | `music-2.6` | 最新版本,音质最佳 |
|
||||||
|
| MiniMax Music 2.5 | `music-2.5` | 支持14种段落级结构标签,物理级高保真 |
|
||||||
|
|
||||||
|
### MiniMax Music 2.5 特性
|
||||||
|
|
||||||
|
Music 2.5 在「段落级强控制」与「物理级高保真」两大技术难题上实现突破:
|
||||||
|
- 开放全段落标签控制,精准支持14种结构变体
|
||||||
|
- 长度限制:歌词内容 [1, 3500] 个字符
|
||||||
|
- prompt 长度限制:[10, 300] 个字符
|
||||||
|
|
||||||
|
### MiniMax Music 2.0 特性(已过期)
|
||||||
|
|
||||||
|
Music 2.0 能根据文本描述和歌词直接生成包含人声的完整歌曲:
|
||||||
|
- prompt 长度限制:[10, 300] 个字符
|
||||||
|
- lyrics 长度限制:[10, 3000] 个字符
|
||||||
|
- 状态:已过期(expired_date: 2026-01-01)
|
||||||
|
|
||||||
|
### 错误响应
|
||||||
|
|
||||||
|
| 状态码 | 说明 |
|
||||||
|
|--------|------|
|
||||||
|
| 400 | 缺少必填参数或模型不存在 |
|
||||||
|
| 403 | 未登录 |
|
||||||
|
| 429 | 账户余额不足 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## GET /v1/tasks
|
## GET /v1/tasks
|
||||||
|
|
||||||
查询异步任务状态。
|
查询异步任务状态。
|
||||||
|
|||||||
@ -163,6 +163,7 @@ PATHS_LOGINED = [
|
|||||||
f"/{MOD}/v1/models/index.dspy",
|
f"/{MOD}/v1/models/index.dspy",
|
||||||
f"/{MOD}/v1/tasks/index.dspy",
|
f"/{MOD}/v1/tasks/index.dspy",
|
||||||
f"/{MOD}/v1/video/generations/index.dspy",
|
f"/{MOD}/v1/video/generations/index.dspy",
|
||||||
|
f"/{MOD}/v1/music/generations/index.dspy",
|
||||||
|
|
||||||
# 其他子目录
|
# 其他子目录
|
||||||
f"/{MOD}/list_llmcatelogs/index.dspy",
|
f"/{MOD}/list_llmcatelogs/index.dspy",
|
||||||
@ -182,6 +183,7 @@ PATHS_V1_CUSTOMER = [
|
|||||||
f"/{MOD}/v1/chat/completions/index.dspy",
|
f"/{MOD}/v1/chat/completions/index.dspy",
|
||||||
f"/{MOD}/v1/video/generations/index.dspy",
|
f"/{MOD}/v1/video/generations/index.dspy",
|
||||||
f"/{MOD}/v1/image/generations/index.dspy",
|
f"/{MOD}/v1/image/generations/index.dspy",
|
||||||
|
f"/{MOD}/v1/music/generations/index.dspy",
|
||||||
f"/{MOD}/v1/models/index.dspy",
|
f"/{MOD}/v1/models/index.dspy",
|
||||||
f"/{MOD}/v1/tasks/index.dspy",
|
f"/{MOD}/v1/tasks/index.dspy",
|
||||||
]
|
]
|
||||||
|
|||||||
80
wwwroot/v1/music/generations/index.dspy
Normal file
80
wwwroot/v1/music/generations/index.dspy
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
# OpenAI-compatible Music Generation API
|
||||||
|
# POST /v1/music/generations
|
||||||
|
# Required params: model, catelogid, prompt, lyrics
|
||||||
|
# Optional params: output_format, audio_setting
|
||||||
|
#
|
||||||
|
# Example request:
|
||||||
|
# {
|
||||||
|
# "model": "music-2.6",
|
||||||
|
# "catelogid": "music_gen",
|
||||||
|
# "prompt": "Pop music, happy, suitable for a sunny day",
|
||||||
|
# "lyrics": "[Intro]\n\n[Verse]\nWalking down the street\nFeeling the beat\n\n[Chorus]\nDancing in the sun\nHaving so much fun"
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# Response (sync for MiniMax):
|
||||||
|
# {
|
||||||
|
# "id": "luid_xxx",
|
||||||
|
# "object": "music.generation",
|
||||||
|
# "model": "music-2.6",
|
||||||
|
# "status": "SUCCEEDED",
|
||||||
|
# "audio": "https://...",
|
||||||
|
# "created": 1234567890
|
||||||
|
# }
|
||||||
|
|
||||||
|
userid = await get_user()
|
||||||
|
userorgid = await get_userorgid()
|
||||||
|
if userid is None:
|
||||||
|
debug('need login')
|
||||||
|
return openai_403()
|
||||||
|
|
||||||
|
# Validate required parameters
|
||||||
|
if not params_kw.model:
|
||||||
|
d = return_error('Missing required parameter: model')
|
||||||
|
return json_response(d, status=400)
|
||||||
|
|
||||||
|
if not params_kw.catelogid:
|
||||||
|
d = return_error('Missing required parameter: catelogid')
|
||||||
|
return json_response(d, status=400)
|
||||||
|
|
||||||
|
if not params_kw.prompt:
|
||||||
|
d = return_error('Missing required parameter: prompt')
|
||||||
|
return json_response(d, status=400)
|
||||||
|
|
||||||
|
if not params_kw.lyrics:
|
||||||
|
d = return_error('Missing required parameter: lyrics')
|
||||||
|
return json_response(d, status=400)
|
||||||
|
|
||||||
|
lctype = params_kw.catelogid
|
||||||
|
|
||||||
|
env = request._run_ns
|
||||||
|
async with get_sor_context(env, 'llmage') as sor:
|
||||||
|
# Look up llm by model name and catalog type through llm_api_map
|
||||||
|
sql = """select distinct a.* from llm a
|
||||||
|
join llm_api_map m on a.id = m.llmid
|
||||||
|
join llmcatelog b on m.llmcatelogid = b.id
|
||||||
|
where (b.id = ${lctype}$ OR b.name = ${lctype}$)
|
||||||
|
and a.model=${model}$
|
||||||
|
and a.status = 'published'"""
|
||||||
|
recs = await sor.sqlExe(sql, {
|
||||||
|
'lctype': lctype,
|
||||||
|
'model': params_kw.model
|
||||||
|
})
|
||||||
|
if len(recs) == 0:
|
||||||
|
debug(f'{params_kw.model=} not found for catalog {lctype}')
|
||||||
|
return openai_400()
|
||||||
|
params_kw.llmid = recs[0].id
|
||||||
|
|
||||||
|
debug(f'{params_kw.llmid=}')
|
||||||
|
|
||||||
|
# Check balance
|
||||||
|
f = await checkCustomerBalance(params_kw.llmid, userid, userorgid)
|
||||||
|
if not f:
|
||||||
|
debug(f'{userid=} balance not enough')
|
||||||
|
return openai_429()
|
||||||
|
|
||||||
|
# Generate task ID and attach to params
|
||||||
|
if not params_kw.transno:
|
||||||
|
params_kw.transno = getID()
|
||||||
|
|
||||||
|
# Call inference (music generation via MiniMax is synchronous)
|
||||||
|
return await inference(request, env=env)
|
||||||
Loading…
x
Reference in New Issue
Block a user