@j-o-r/hello-dave 0.0.10 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/README.md.bak.1779452127 +240 -0
- package/TODO.md +30 -8
- package/agents/code_agent.js +6 -6
- package/agents/daisy_agent.js +10 -7
- package/agents/minimax.js +173 -0
- package/agents/stability.js +173 -0
- package/bin/codeDave +1 -1
- package/bin/dave.js +1 -1
- package/docs/music-toolsets.md +137 -0
- package/docs/plans/minimax-music-generation.md +80 -0
- package/docs/plans/unified-agent-architecture.md +146 -0
- package/docs/plans/websocket-streaming-plan.md.bak +317 -0
- package/docs/prompt/task_clarification_and_documentation.md +35 -0
- package/lib/API/minimax/ImageToolset.js +169 -0
- package/lib/API/minimax/MusicToolset.js +290 -0
- package/lib/API/minimax/VideoToolset.js +296 -0
- package/lib/API/minimax/image.generation.md +239 -0
- package/lib/API/minimax/image.js +219 -0
- package/lib/API/minimax/image.to.image.md +257 -0
- package/lib/API/minimax/index.js +16 -0
- package/lib/API/minimax/music.cover.preprocess.md +206 -0
- package/lib/API/minimax/music.generation.md +346 -0
- package/lib/API/minimax/music.js +257 -0
- package/lib/API/minimax/music.lyrics.generation.md +205 -0
- package/lib/API/minimax/video.download.md +133 -0
- package/lib/API/minimax/video.first.last.image.md +186 -0
- package/lib/API/minimax/video.from.image.md +206 -0
- package/lib/API/minimax/video.from.subject.md +164 -0
- package/lib/API/minimax/video.generation.md +192 -0
- package/lib/API/minimax/video.js +339 -0
- package/lib/API/minimax/video.query.md +128 -0
- package/lib/API/stability.ai/ImageToolset.js +357 -0
- package/lib/API/stability.ai/MusicToolset.js +302 -0
- package/lib/API/stability.ai/audio-3.md +205 -0
- package/lib/API/stability.ai/audio.js +679 -0
- package/lib/API/stability.ai/image.js +911 -0
- package/lib/API/stability.ai/image.md +271 -0
- package/lib/API/stability.ai/index.js +11 -0
- package/lib/API/stability.ai/openapi.json +17118 -0
- package/lib/API/x.ai/ImageToolset.js +165 -0
- package/lib/API/x.ai/image.editing.md +86 -0
- package/lib/API/x.ai/image.js +393 -0
- package/lib/API/x.ai/image.md +213 -0
- package/lib/API/x.ai/image.to.generation.md +494 -0
- package/lib/API/x.ai/image.to.video.md +23 -0
- package/lib/API/x.ai/index.js +9 -0
- package/lib/AgentManager.js +1 -1
- package/lib/CdnToolset.js +191 -0
- package/lib/ToolSet.js +19 -1
- package/lib/cdn.js +373 -0
- package/lib/fafs.js +3 -1
- package/lib/genericToolset.js +43 -166
- package/lib/index.js +9 -1
- package/package.json +2 -2
- package/types/API/minimax/ImageToolset.d.ts +3 -0
- package/types/API/minimax/MusicToolset.d.ts +3 -0
- package/types/API/minimax/VideoToolset.d.ts +3 -0
- package/types/API/minimax/image.d.ts +109 -0
- package/types/API/minimax/index.d.ts +15 -0
- package/types/API/minimax/music.d.ts +46 -0
- package/types/API/minimax/video.d.ts +165 -0
- package/types/API/stability.ai/ImageToolset.d.ts +3 -0
- package/types/API/stability.ai/MusicToolset.d.ts +3 -0
- package/types/API/stability.ai/audio.d.ts +193 -0
- package/types/API/stability.ai/image.d.ts +274 -0
- package/types/API/stability.ai/index.d.ts +11 -0
- package/types/API/x.ai/ImageToolset.d.ts +3 -0
- package/types/API/x.ai/image.d.ts +82 -0
- package/types/API/x.ai/index.d.ts +9 -0
- package/types/AgentManager.d.ts +1 -1
- package/types/CdnToolset.d.ts +20 -0
- package/types/ToolSet.d.ts +8 -0
- package/types/cdn.d.ts +141 -0
- package/types/index.d.ts +8 -2
- package/docs/multi-agent-clusters.md.bak +0 -229
|
@@ -0,0 +1,346 @@
|
|
|
1
|
+
> ## Documentation Index
|
|
2
|
+
> Fetch the complete documentation index at: https://platform.minimax.io/docs/llms.txt
|
|
3
|
+
> Use this file to discover all available pages before exploring further.
|
|
4
|
+
|
|
5
|
+
# Music Generation
|
|
6
|
+
|
|
7
|
+
> Use this API to generate a song from lyrics and a prompt.
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
## OpenAPI
|
|
11
|
+
|
|
12
|
+
````yaml POST /v1/music_generation
|
|
13
|
+
openapi: 3.1.0
|
|
14
|
+
info:
|
|
15
|
+
title: MiniMax Music Generation API
|
|
16
|
+
description: >-
|
|
17
|
+
MiniMax music generation API with support for creating music from text
|
|
18
|
+
prompts and lyrics
|
|
19
|
+
license:
|
|
20
|
+
name: MIT
|
|
21
|
+
version: 1.0.0
|
|
22
|
+
servers:
|
|
23
|
+
- url: https://api.minimax.io
|
|
24
|
+
security:
|
|
25
|
+
- bearerAuth: []
|
|
26
|
+
paths:
|
|
27
|
+
/v1/music_generation:
|
|
28
|
+
post:
|
|
29
|
+
tags:
|
|
30
|
+
- Music
|
|
31
|
+
summary: Music Generation
|
|
32
|
+
operationId: generateMusic
|
|
33
|
+
parameters:
|
|
34
|
+
- name: Content-Type
|
|
35
|
+
in: header
|
|
36
|
+
required: true
|
|
37
|
+
description: >-
|
|
38
|
+
The media type of the request body. Must be set to
|
|
39
|
+
`application/json` to ensure the data is sent in JSON format.
|
|
40
|
+
schema:
|
|
41
|
+
type: string
|
|
42
|
+
enum:
|
|
43
|
+
- application/json
|
|
44
|
+
default: application/json
|
|
45
|
+
requestBody:
|
|
46
|
+
content:
|
|
47
|
+
application/json:
|
|
48
|
+
schema:
|
|
49
|
+
$ref: '#/components/schemas/GenerateMusicReq'
|
|
50
|
+
required: true
|
|
51
|
+
responses:
|
|
52
|
+
'200':
|
|
53
|
+
description: ''
|
|
54
|
+
content:
|
|
55
|
+
application/json:
|
|
56
|
+
schema:
|
|
57
|
+
$ref: '#/components/schemas/GenerateMusicResp'
|
|
58
|
+
components:
|
|
59
|
+
schemas:
|
|
60
|
+
GenerateMusicReq:
|
|
61
|
+
type: object
|
|
62
|
+
required:
|
|
63
|
+
- model
|
|
64
|
+
properties:
|
|
65
|
+
model:
|
|
66
|
+
type: string
|
|
67
|
+
description: >-
|
|
68
|
+
The model name. Options:
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
- `music-2.6` (recommended): Text-to-music generation. Available to
|
|
72
|
+
Token Plan and paid users only, with higher RPM.
|
|
73
|
+
|
|
74
|
+
- `music-cover`: Cover generation from a reference audio. Available
|
|
75
|
+
to Token Plan and paid users only, with higher RPM.
|
|
76
|
+
|
|
77
|
+
- `music-2.6-free`: Free-tier version of `music-2.6`. Available to
|
|
78
|
+
all users via API Key, with lower RPM.
|
|
79
|
+
|
|
80
|
+
- `music-cover-free`: Free-tier version of `music-cover`. Available
|
|
81
|
+
to all users via API Key, with lower RPM.
|
|
82
|
+
enum:
|
|
83
|
+
- music-2.6
|
|
84
|
+
- music-cover
|
|
85
|
+
- music-2.6-free
|
|
86
|
+
- music-cover-free
|
|
87
|
+
prompt:
|
|
88
|
+
type: string
|
|
89
|
+
description: >-
|
|
90
|
+
A description of the music, specifying style, mood, and scenario.
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
For example: "`Pop, melancholic, perfect for a rainy night`".
|
|
94
|
+
|
|
95
|
+
<br>
|
|
96
|
+
|
|
97
|
+
Note:
|
|
98
|
+
|
|
99
|
+
- For `music-2.6` / `music-2.6-free` with `is_instrumental: true`:
|
|
100
|
+
Required. Length: 1–2000 characters.
|
|
101
|
+
|
|
102
|
+
- For `music-2.6` / `music-2.6-free` (non-instrumental): Optional.
|
|
103
|
+
Length: 0–2000 characters.
|
|
104
|
+
|
|
105
|
+
- For `music-cover` / `music-cover-free`: Required. Describes the
|
|
106
|
+
target cover style. Length: 10–300 characters.
|
|
107
|
+
maxLength: 2000
|
|
108
|
+
lyrics:
|
|
109
|
+
type: string
|
|
110
|
+
description: >-
|
|
111
|
+
Song lyrics, using `\n` to separate lines. Supports structure tags:
|
|
112
|
+
`[Intro]`, `[Verse]`, `[Pre Chorus]`, `[Chorus]`, `[Interlude]`,
|
|
113
|
+
`[Bridge]`, `[Outro]`, `[Post Chorus]`, `[Transition]`, `[Break]`,
|
|
114
|
+
`[Hook]`, `[Build Up]`, `[Inst]`, `[Solo]`.
|
|
115
|
+
|
|
116
|
+
<br>
|
|
117
|
+
|
|
118
|
+
Note:
|
|
119
|
+
|
|
120
|
+
- For `music-2.6` / `music-2.6-free` with `is_instrumental: true`:
|
|
121
|
+
Not required.
|
|
122
|
+
|
|
123
|
+
- For `music-2.6` / `music-2.6-free` (non-instrumental): Required.
|
|
124
|
+
Length: 1–3500 characters.
|
|
125
|
+
|
|
126
|
+
- For `music-cover` / `music-cover-free`: Optional. If omitted,
|
|
127
|
+
lyrics are automatically extracted from the reference audio via ASR.
|
|
128
|
+
Length: 10–1000 characters.
|
|
129
|
+
|
|
130
|
+
- When `lyrics_optimizer: true` and `lyrics` is empty, the system
|
|
131
|
+
will auto-generate lyrics from `prompt`.
|
|
132
|
+
minLength: 1
|
|
133
|
+
maxLength: 3500
|
|
134
|
+
stream:
|
|
135
|
+
type: boolean
|
|
136
|
+
description: Whether to use streaming output.
|
|
137
|
+
default: false
|
|
138
|
+
output_format:
|
|
139
|
+
type: string
|
|
140
|
+
description: |-
|
|
141
|
+
The output format of the audio. Options: `url` or `hex`.
|
|
142
|
+
|
|
143
|
+
When `stream` is `true`, only `hex` is supported.
|
|
144
|
+
|
|
145
|
+
⚠️ Note: `url` links expire after 24 hours, so download promptly.
|
|
146
|
+
enum:
|
|
147
|
+
- url
|
|
148
|
+
- hex
|
|
149
|
+
default: hex
|
|
150
|
+
audio_setting:
|
|
151
|
+
$ref: '#/components/schemas/AudioSetting'
|
|
152
|
+
lyrics_optimizer:
|
|
153
|
+
type: boolean
|
|
154
|
+
description: >-
|
|
155
|
+
Whether to automatically generate lyrics based on the `prompt`
|
|
156
|
+
description. Only supported on `music-2.6` / `music-2.6-free`.
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
When set to `true` and `lyrics` is empty, the system will
|
|
160
|
+
automatically generate lyrics from the prompt. Default: `false`.
|
|
161
|
+
default: false
|
|
162
|
+
is_instrumental:
|
|
163
|
+
type: boolean
|
|
164
|
+
description: >-
|
|
165
|
+
Whether to generate instrumental music (no vocals). Only supported
|
|
166
|
+
on `music-2.6` / `music-2.6-free`.
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
When set to `true`, the `lyrics` field is not required. Default:
|
|
170
|
+
`false`.
|
|
171
|
+
default: false
|
|
172
|
+
audio_url:
|
|
173
|
+
type: string
|
|
174
|
+
description: >-
|
|
175
|
+
URL of the reference audio. Only used with `music-cover` /
|
|
176
|
+
`music-cover-free` model. Exactly one of `audio_url` or
|
|
177
|
+
`audio_base64` must be provided. Mutually exclusive with
|
|
178
|
+
`cover_feature_id`.
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
Reference audio constraints:
|
|
182
|
+
|
|
183
|
+
- Duration: 6 seconds to 6 minutes
|
|
184
|
+
|
|
185
|
+
- Size: max 50 MB
|
|
186
|
+
|
|
187
|
+
- Format: common audio formats (mp3, wav, flac, etc.)
|
|
188
|
+
audio_base64:
|
|
189
|
+
type: string
|
|
190
|
+
description: >-
|
|
191
|
+
Base64-encoded reference audio. Only used with `music-cover` /
|
|
192
|
+
`music-cover-free` model. Exactly one of `audio_url` or
|
|
193
|
+
`audio_base64` must be provided. Mutually exclusive with
|
|
194
|
+
`cover_feature_id`.
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
Reference audio constraints:
|
|
198
|
+
|
|
199
|
+
- Duration: 6 seconds to 6 minutes
|
|
200
|
+
|
|
201
|
+
- Size: max 50 MB
|
|
202
|
+
|
|
203
|
+
- Format: common audio formats (mp3, wav, flac, etc.)
|
|
204
|
+
cover_feature_id:
|
|
205
|
+
type: string
|
|
206
|
+
description: >-
|
|
207
|
+
Feature ID returned by the [Music Cover
|
|
208
|
+
Preprocess](/api-reference/music-cover-preprocess) API. Used in the
|
|
209
|
+
**two-step cover workflow** to generate a cover with modified
|
|
210
|
+
lyrics.
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
Only used with `music-cover` / `music-cover-free` model. Mutually
|
|
214
|
+
exclusive with `audio_url` and `audio_base64`.
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
- When provided, `lyrics` is required (length: 10–1000 characters)
|
|
218
|
+
|
|
219
|
+
- The `cover_feature_id` is valid for 24 hours
|
|
220
|
+
|
|
221
|
+
- Same audio content returns the same `cover_feature_id`
|
|
222
|
+
example:
|
|
223
|
+
model: music-2.6
|
|
224
|
+
prompt: >-
|
|
225
|
+
Indie folk, melancholic, introspective, longing, solitary walk, coffee
|
|
226
|
+
shop
|
|
227
|
+
lyrics: |-
|
|
228
|
+
[verse]
|
|
229
|
+
Streetlights flicker, the night breeze sighs
|
|
230
|
+
Shadows stretch as I walk alone
|
|
231
|
+
An old coat wraps my silent sorrow
|
|
232
|
+
Wandering, longing, where should I go
|
|
233
|
+
[chorus]
|
|
234
|
+
Pushing the wooden door, the aroma spreads
|
|
235
|
+
In a familiar corner, a stranger gazes
|
|
236
|
+
audio_setting:
|
|
237
|
+
sample_rate: 44100
|
|
238
|
+
bitrate: 256000
|
|
239
|
+
format: mp3
|
|
240
|
+
GenerateMusicResp:
|
|
241
|
+
type: object
|
|
242
|
+
properties:
|
|
243
|
+
data:
|
|
244
|
+
$ref: '#/components/schemas/MusicData'
|
|
245
|
+
base_resp:
|
|
246
|
+
$ref: '#/components/schemas/BaseResp'
|
|
247
|
+
example:
|
|
248
|
+
data:
|
|
249
|
+
audio: hex-encoded audio data
|
|
250
|
+
status: 2
|
|
251
|
+
trace_id: 04ede0ab069fb1ba8be5156a24b1e081
|
|
252
|
+
extra_info:
|
|
253
|
+
music_duration: 25364
|
|
254
|
+
music_sample_rate: 44100
|
|
255
|
+
music_channel: 2
|
|
256
|
+
bitrate: 256000
|
|
257
|
+
music_size: 813651
|
|
258
|
+
analysis_info: null
|
|
259
|
+
base_resp:
|
|
260
|
+
status_code: 0
|
|
261
|
+
status_msg: success
|
|
262
|
+
AudioSetting:
|
|
263
|
+
type: object
|
|
264
|
+
description: Audio output configuration
|
|
265
|
+
properties:
|
|
266
|
+
sample_rate:
|
|
267
|
+
type: integer
|
|
268
|
+
description: 'Sampling rate. Options: `16000`, `24000`, `32000`, `44100`.'
|
|
269
|
+
bitrate:
|
|
270
|
+
type: integer
|
|
271
|
+
description: 'Bitrate. Options: `32000`, `64000`, `128000`, `256000`.'
|
|
272
|
+
format:
|
|
273
|
+
type: string
|
|
274
|
+
description: 'Audio format. Options: `mp3`, `wav`, `pcm`.'
|
|
275
|
+
enum:
|
|
276
|
+
- mp3
|
|
277
|
+
- wav
|
|
278
|
+
- pcm
|
|
279
|
+
MusicData:
|
|
280
|
+
type: object
|
|
281
|
+
properties:
|
|
282
|
+
status:
|
|
283
|
+
type: integer
|
|
284
|
+
description: |-
|
|
285
|
+
Music generation status:
|
|
286
|
+
|
|
287
|
+
1: In progress
|
|
288
|
+
|
|
289
|
+
2: Completed
|
|
290
|
+
audio:
|
|
291
|
+
type: string
|
|
292
|
+
description: |-
|
|
293
|
+
Returned when `output_format` is `hex`.
|
|
294
|
+
|
|
295
|
+
Contains the audio file as a hexadecimal-encoded string.
|
|
296
|
+
BaseResp:
|
|
297
|
+
type: object
|
|
298
|
+
description: Status code and details
|
|
299
|
+
properties:
|
|
300
|
+
status_code:
|
|
301
|
+
type: integer
|
|
302
|
+
description: >-
|
|
303
|
+
Status codes and their meanings:
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
`0`: Success
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
`1002`: Rate limit triggered, retry later
|
|
310
|
+
|
|
311
|
+
|
|
312
|
+
`1004`: Authentication failed, check API key
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
`1008`: Insufficient balance
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
`1026`: Content flagged for sensitive material
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
`2013`: Invalid parameters, check input
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
`2049`: Invalid API key
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
For more information, please refer to the [Error Code
|
|
328
|
+
Reference](/api-reference/errorcode).
|
|
329
|
+
status_msg:
|
|
330
|
+
type: string
|
|
331
|
+
description: Detailed error message
|
|
332
|
+
securitySchemes:
|
|
333
|
+
bearerAuth:
|
|
334
|
+
type: http
|
|
335
|
+
scheme: bearer
|
|
336
|
+
bearerFormat: JWT
|
|
337
|
+
description: >-
|
|
338
|
+
`HTTP: Bearer Auth`
|
|
339
|
+
|
|
340
|
+
- Security Scheme Type: http
|
|
341
|
+
|
|
342
|
+
- HTTP Authorization Scheme: `Bearer API_key`, can be found in [Account
|
|
343
|
+
Management>API
|
|
344
|
+
Keys](https://platform.minimax.io/user-center/basic-information/interface-key).
|
|
345
|
+
|
|
346
|
+
````
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file lib/API/minimax/music.js
|
|
3
|
+
* @module minimax/music
|
|
4
|
+
* @description Pure HTTP wrapper for the Minimax Music Generation API.
|
|
5
|
+
* Fully aligned with the official documentation:
|
|
6
|
+
* - lib/API/minimax/music.generation.md
|
|
7
|
+
* - lib/API/minimax/music.cover.preprocess.md
|
|
8
|
+
* - lib/API/minimax/music.lyrics.generation.md
|
|
9
|
+
*
|
|
10
|
+
* This is a clean library with four distinct workflows:
|
|
11
|
+
* 1. createMusic() → text-to-music (music-2.6 / music-2.6-free)
|
|
12
|
+
* 2. changeMusic() → cover generation (music-cover / music-cover-free)
|
|
13
|
+
* 3. analyzeMusic() → preprocess reference for two-step covers
|
|
14
|
+
* 4. generateLyrics() → create or edit song lyrics
|
|
15
|
+
*
|
|
16
|
+
* Notes on parameters:
|
|
17
|
+
* - Lyrics are optional for both instrumental and non-instrumental generation.
|
|
18
|
+
* - Streaming is not supported; stream is always false.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { request as doRequest } from '@j-o-r/apiserver';
|
|
22
|
+
import fs from 'fs/promises';
|
|
23
|
+
import path from 'path';
|
|
24
|
+
|
|
25
|
+
const BASE_URL = 'https://api.minimax.io/v1';
|
|
26
|
+
|
|
27
|
+
const TMP_DIR = path.join(process.cwd(), '.cache', 'minimax');
|
|
28
|
+
|
|
29
|
+
const getHeaders = () => {
|
|
30
|
+
if (!process.env.MINIMAX_API_KEY) {
|
|
31
|
+
throw new Error('Missing MINIMAX_API_KEY! Please export MINIMAX_API_KEY=your_key');
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
'Content-Type': 'application/json',
|
|
35
|
+
'Authorization': `Bearer ${process.env.MINIMAX_API_KEY}`
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
async function saveAudioToLocal(audioData, filenamePrefix = 'minimax-music') {
|
|
40
|
+
const tmpDir = path.join(process.cwd(), '.cache', 'minimax');
|
|
41
|
+
await fs.mkdir(tmpDir, { recursive: true });
|
|
42
|
+
|
|
43
|
+
const filename = `${filenamePrefix}-${Date.now()}.mp3`;
|
|
44
|
+
const localPath = path.join(tmpDir, filename);
|
|
45
|
+
|
|
46
|
+
if (audioData.startsWith('http')) {
|
|
47
|
+
const response = await fetch(audioData);
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
throw new Error(`Failed to download audio: ${response.status} ${response.statusText}`);
|
|
50
|
+
}
|
|
51
|
+
const buffer = Buffer.from(await response.arrayBuffer());
|
|
52
|
+
await fs.writeFile(localPath, buffer);
|
|
53
|
+
} else {
|
|
54
|
+
const buffer = Buffer.from(audioData, 'hex');
|
|
55
|
+
await fs.writeFile(localPath, buffer);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return localPath;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/* ============================================================
|
|
62
|
+
WORKFLOW 1: CREATE MUSIC (Text-to-Music)
|
|
63
|
+
============================================================ */
|
|
64
|
+
|
|
65
|
+
async function createMusic(prompt, options = {}) {
|
|
66
|
+
const model = options.model || 'music-2.6';
|
|
67
|
+
|
|
68
|
+
if (options.audio_url || options.audio_base64 || options.cover_feature_id) {
|
|
69
|
+
throw new Error(
|
|
70
|
+
'createMusic() does not support reference audio. ' +
|
|
71
|
+
'Use changeMusic() for covers or analyzeMusic() for preprocessing.'
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (!['music-2.6', 'music-2.6-free'].includes(model)) {
|
|
76
|
+
throw new Error(
|
|
77
|
+
`createMusic() expects model 'music-2.6' or 'music-2.6-free'. Got: ${model}. ` +
|
|
78
|
+
'Use changeMusic() for cover models.'
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return requestMusicInternal(prompt, { ...options, model });
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* ============================================================
|
|
86
|
+
WORKFLOW 2: CHANGE MUSIC (Cover / Reference-based)
|
|
87
|
+
============================================================ */
|
|
88
|
+
|
|
89
|
+
async function changeMusic(prompt, options = {}) {
|
|
90
|
+
const model = options.model || 'music-cover';
|
|
91
|
+
|
|
92
|
+
if (!['music-cover', 'music-cover-free'].includes(model)) {
|
|
93
|
+
throw new Error(
|
|
94
|
+
`changeMusic() expects model 'music-cover' or 'music-cover-free'. Got: ${model}. ` +
|
|
95
|
+
'Use createMusic() for text-to-music.'
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const hasAudioRef = !!(options.audio_url || options.audio_base64);
|
|
100
|
+
const hasFeatureId = !!options.cover_feature_id;
|
|
101
|
+
|
|
102
|
+
if (hasAudioRef && hasFeatureId) {
|
|
103
|
+
throw new Error(
|
|
104
|
+
'Invalid parameters for changeMusic(): audio_url/audio_base64 and cover_feature_id are mutually exclusive.\n' +
|
|
105
|
+
'• Use audio_url or audio_base64 for direct one-step cover.\n' +
|
|
106
|
+
'• Use cover_feature_id (from analyzeMusic()) for two-step cover with modified lyrics.'
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (!hasAudioRef && !hasFeatureId) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
'changeMusic() requires a reference: provide either audio_url/audio_base64 or cover_feature_id.'
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return requestMusicInternal(prompt, { ...options, model });
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/* ============================================================
|
|
120
|
+
WORKFLOW 3: ANALYZE MUSIC (Preprocess for Cover)
|
|
121
|
+
============================================================ */
|
|
122
|
+
|
|
123
|
+
async function analyzeMusic(audioUrl, options = {}) {
|
|
124
|
+
return preprocessCoverInternal(audioUrl, options);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/* ============================================================
|
|
128
|
+
WORKFLOW 4: GENERATE LYRICS (New)
|
|
129
|
+
============================================================ */
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Generate complete song lyrics or edit/continue existing lyrics.
|
|
133
|
+
*
|
|
134
|
+
* @param {string} prompt - Theme, style, or editing instruction.
|
|
135
|
+
* @param {Object} [options]
|
|
136
|
+
* @param {'write_full_song'|'edit'} [options.mode='write_full_song']
|
|
137
|
+
* @param {string} [options.lyrics] - Existing lyrics (required for edit mode)
|
|
138
|
+
* @param {string} [options.title]
|
|
139
|
+
*
|
|
140
|
+
* @returns {Promise<{song_title: string, style_tags: string, lyrics: string, raw: object}>}
|
|
141
|
+
*/
|
|
142
|
+
async function generateLyrics(prompt, options = {}) {
|
|
143
|
+
const headers = getHeaders();
|
|
144
|
+
const url = `${BASE_URL}/lyrics_generation`;
|
|
145
|
+
|
|
146
|
+
const body = {
|
|
147
|
+
mode: options.mode || 'write_full_song',
|
|
148
|
+
prompt: prompt || '',
|
|
149
|
+
...(options.lyrics && { lyrics: options.lyrics }),
|
|
150
|
+
...(options.title && { title: options.title })
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
const res = await doRequest(url, 'POST', headers, body);
|
|
154
|
+
|
|
155
|
+
if (res.status !== 200) {
|
|
156
|
+
throw new Error(`Minimax lyrics error ${res.status}: ${JSON.stringify(res.response)}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return {
|
|
160
|
+
song_title: res.response?.song_title,
|
|
161
|
+
style_tags: res.response?.style_tags,
|
|
162
|
+
lyrics: res.response?.lyrics,
|
|
163
|
+
raw: res.response
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/* ============================================================
|
|
168
|
+
INTERNAL IMPLEMENTATION
|
|
169
|
+
============================================================ */
|
|
170
|
+
|
|
171
|
+
async function requestMusicInternal(prompt, options = {}) {
|
|
172
|
+
const headers = getHeaders();
|
|
173
|
+
const url = `${BASE_URL}/music_generation`;
|
|
174
|
+
|
|
175
|
+
const body = {
|
|
176
|
+
model: options.model || 'music-2.6',
|
|
177
|
+
prompt: prompt,
|
|
178
|
+
stream: false, // Streaming is not supported; always false
|
|
179
|
+
output_format: options.output_format || 'url',
|
|
180
|
+
audio_setting: options.audio_setting || {
|
|
181
|
+
sample_rate: 44100,
|
|
182
|
+
bitrate: 256000,
|
|
183
|
+
format: 'mp3'
|
|
184
|
+
},
|
|
185
|
+
lyrics_optimizer: options.lyrics_optimizer ?? false,
|
|
186
|
+
is_instrumental: options.is_instrumental ?? false,
|
|
187
|
+
...options.extra
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
if (options.lyrics && options.lyrics.trim() !== '') {
|
|
191
|
+
body.lyrics = options.lyrics;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (options.audio_url) body.audio_url = options.audio_url;
|
|
195
|
+
if (options.audio_base64) body.audio_base64 = options.audio_base64;
|
|
196
|
+
if (options.cover_feature_id) body.cover_feature_id = options.cover_feature_id;
|
|
197
|
+
|
|
198
|
+
const start = Date.now();
|
|
199
|
+
const res = await doRequest(url, 'POST', headers, body);
|
|
200
|
+
const duration = Date.now() - start;
|
|
201
|
+
|
|
202
|
+
if (res.status !== 200) {
|
|
203
|
+
throw new Error(`Minimax API error ${res.status}: ${JSON.stringify(res.response)}`);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const audioData = res.response?.data?.audio;
|
|
207
|
+
|
|
208
|
+
if (!audioData) {
|
|
209
|
+
throw new Error('No audio data found in data.audio of Minimax response');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const localPath = await saveAudioToLocal(audioData);
|
|
213
|
+
|
|
214
|
+
return {
|
|
215
|
+
audio_url: audioData,
|
|
216
|
+
local_path: localPath,
|
|
217
|
+
duration,
|
|
218
|
+
raw: res.response
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
async function preprocessCoverInternal(audioUrl, options = {}) {
|
|
223
|
+
const headers = getHeaders();
|
|
224
|
+
const url = `${BASE_URL}/music_cover_preprocess`;
|
|
225
|
+
|
|
226
|
+
const body = {
|
|
227
|
+
model: options.model || 'music-cover',
|
|
228
|
+
audio_url: audioUrl,
|
|
229
|
+
...options
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const start = Date.now();
|
|
233
|
+
const res = await doRequest(url, 'POST', headers, body);
|
|
234
|
+
const duration = Date.now() - start;
|
|
235
|
+
|
|
236
|
+
if (res.status !== 200) {
|
|
237
|
+
throw new Error(`Minimax preprocess error ${res.status}: ${JSON.stringify(res.response)}`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
cover_feature_id: res.response?.cover_feature_id,
|
|
242
|
+
formatted_lyrics: res.response?.formatted_lyrics,
|
|
243
|
+
structure_result: res.response?.structure_result,
|
|
244
|
+
audio_duration: res.response?.audio_duration,
|
|
245
|
+
trace_id: res.response?.trace_id,
|
|
246
|
+
raw: res.response
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export {
|
|
251
|
+
getHeaders,
|
|
252
|
+
saveAudioToLocal,
|
|
253
|
+
createMusic,
|
|
254
|
+
changeMusic,
|
|
255
|
+
analyzeMusic,
|
|
256
|
+
generateLyrics
|
|
257
|
+
};
|