feat: add MiniMax Music 3.0 provider#22
Conversation
Signed-off-by: octo-patch <266937838+octo-patch@users.noreply.github.com>
Reviewer's GuideAdds a new MiniMax Music 3.0 audio provider, wires it into the audio provider registry and generateMusic() env-var detection, and documents how to use it, including region selection, response formats, and tests for behavior and error handling. Sequence diagram for generateMusic using MiniMaxMusicProvidersequenceDiagram
actor Client
participant generateMusic
participant MiniMaxMusicProvider
participant MiniMaxMusicAPI
Client->>generateMusic: generateMusic(opts)
generateMusic->>generateMusic: MUSIC_PROVIDER_ENV_MAP
alt [MINIMAX_API_KEY set]
generateMusic->>MiniMaxMusicProvider: initialize(config)
generateMusic->>MiniMaxMusicProvider: generateMusic(request)
MiniMaxMusicProvider->>MiniMaxMusicAPI: fetch(endpoint)
MiniMaxMusicAPI-->>MiniMaxMusicProvider: json()
MiniMaxMusicProvider-->>generateMusic: AudioResult
else [MINIMAX_API_KEY not set]
generateMusic->>generateMusic: fallback to other providers
end
generateMusic-->>Client: AudioResult
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughAdds a MiniMax Music provider with global and China endpoints, URL or hex audio responses, provider registration, ChangesMiniMax Music
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant Application
participant generateMusic
participant MiniMaxMusicProvider
participant MiniMaxMusicAPI
Application->>generateMusic: generate music request
generateMusic->>MiniMaxMusicProvider: select provider and generateMusic
MiniMaxMusicProvider->>MiniMaxMusicAPI: POST request with bearer auth
MiniMaxMusicAPI-->>MiniMaxMusicProvider: audio URL or hex response
MiniMaxMusicProvider-->>generateMusic: AudioResult
generateMusic-->>Application: generated music
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
ESLint install timed out. The project may have too many dependencies for the sandbox. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
PR Summary by QodoAdd MiniMax Music 3.0 provider (global/China endpoints)
AI Description
Diagram
High-Level Assessment
Files changed (8)
|
There was a problem hiding this comment.
Hey - I've found 3 issues, and left some high level feedback:
- The MiniMax response error handling treats any missing
base_respas a failure (payload.base_resp?.status_code !== 0), which will throw even onresponse.okwithoutbase_resp; consider defaulting to success whenbase_respis absent or explicitly checking for non-zero codes only when present. - The hex-to-base64 conversion relies on
Buffer, which may not be available or desirable in browser/edge runtimes; consider using a runtime-agnostic approach (e.g.,Uint8Array+btoaor a small utility) to keep this provider compatible across environments.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- The MiniMax response error handling treats any missing `base_resp` as a failure (`payload.base_resp?.status_code !== 0`), which will throw even on `response.ok` without `base_resp`; consider defaulting to success when `base_resp` is absent or explicitly checking for non-zero codes only when present.
- The hex-to-base64 conversion relies on `Buffer`, which may not be available or desirable in browser/edge runtimes; consider using a runtime-agnostic approach (e.g., `Uint8Array` + `btoa` or a small utility) to keep this provider compatible across environments.
## Individual Comments
### Comment 1
<location path="src/api/generateMusic.ts" line_range="363-367" />
<code_context>
if (providerChain.length === 0) {
throw new Error(
- 'No music provider configured. Set SUNO_API_KEY, STABILITY_API_KEY, REPLICATE_API_TOKEN, or FAL_API_KEY.',
+ 'No music provider configured. Set MINIMAX_API_KEY, SUNO_API_KEY, STABILITY_API_KEY, REPLICATE_API_TOKEN, or FAL_API_KEY.',
);
}
</code_context>
<issue_to_address>
**suggestion:** Align the error message provider order with the actual resolution priority.
The message now lists `MINIMAX_API_KEY` first, but `MUSIC_PROVIDER_ENV_MAP` probes in the order `SUNO_API_KEY`, `STABILITY_API_KEY`, `REPLICATE_API_TOKEN`, `FAL_API_KEY`, then `MINIMAX_API_KEY`. To avoid confusing users when debugging config, please either match the message order to the actual probe order or reword it so it doesn’t suggest a priority that differs from the implementation.
```suggestion
if (providerChain.length === 0) {
throw new Error(
'No music provider configured. Set SUNO_API_KEY, STABILITY_API_KEY, REPLICATE_API_TOKEN, FAL_API_KEY, or MINIMAX_API_KEY.',
);
}
```
</issue_to_address>
### Comment 2
<location path="src/io/media/audio/__tests__/MiniMaxMusicProvider.test.ts" line_range="147-153" />
<code_context>
+ .rejects.toThrow('MiniMax music generation failed (200): invalid request');
+ });
+
+ it('rejects unsupported audio formats before sending a request', async () => {
+ await provider.initialize({ apiKey: 'minimax-test-key' });
+
+ await expect(provider.generateMusic({ prompt: 'test', outputFormat: 'flac' }))
+ .rejects.toThrow('MiniMax music audio format must be "mp3", "wav", or "pcm".');
+ expect(fetchSpy).not.toHaveBeenCalled();
+ });
+});
</code_context>
<issue_to_address>
**suggestion (testing):** Mirror this audio format validation test with one for invalid responseFormat values
There’s equivalent validation for responseFormat (only 'url' and 'hex' are allowed) that isn’t covered. Please add a similar test that calls generateMusic with an invalid responseFormat (e.g. 'foo'), asserts it rejects with "responseFormat must be \"url\" or \"hex\"", and confirms fetch is not called.
```suggestion
it('rejects unsupported audio formats before sending a request', async () => {
await provider.initialize({ apiKey: 'minimax-test-key' });
await expect(provider.generateMusic({ prompt: 'test', outputFormat: 'flac' }))
.rejects.toThrow('MiniMax music audio format must be "mp3", "wav", or "pcm".');
expect(fetchSpy).not.toHaveBeenCalled();
});
it('rejects unsupported response formats before sending a request', async () => {
await provider.initialize({ apiKey: 'minimax-test-key' });
await expect(provider.generateMusic({ prompt: 'test', responseFormat: 'foo' }))
.rejects.toThrow('responseFormat must be "url" or "hex"');
expect(fetchSpy).not.toHaveBeenCalled();
});
```
</issue_to_address>
### Comment 3
<location path="src/io/media/audio/providers/MiniMaxMusicProvider.ts" line_range="110" />
<code_context>
+ this.isInitialized = true;
+ }
+
+ async generateMusic(request: MusicGenerateRequest): Promise<AudioResult> {
+ if (!this.isInitialized) {
+ throw new Error('MiniMax Music provider is not initialized. Call initialize() first.');
</code_context>
<issue_to_address>
**issue (complexity):** Consider refactoring `generateMusic` into smaller helper methods for resolution, validation, body construction, and result assembly to keep the core flow simple and orchestration-focused.
The core logic in `generateMusic` is solid but packed into a single method; you can keep behavior identical while reducing complexity by extracting a few focused helpers.
You already have clear boundaries you can leverage:
1. **Endpoint / region resolution**
Move the region/endpoint branching into a helper so `generateMusic` reads more linearly:
```ts
private resolveEndpoint(options: MiniMaxMusicProviderOptions): {
region: MiniMaxMusicRegion;
endpoint: string;
} {
const region =
options.region === 'china' || options.region === 'global'
? options.region
: this._config.region;
const endpoint =
options.baseURL?.trim() ||
(options.region ? MUSIC_ENDPOINTS[region] : this._config.baseURL);
return { region, endpoint };
}
```
Usage:
```ts
const { region, endpoint } = this.resolveEndpoint(options);
```
2. **Response / audio format validation**
Centralize union validation and error messages to avoid repeated inline checks:
```ts
private ensureResponseFormat(options: MiniMaxMusicProviderOptions): MiniMaxMusicResponseFormat {
const responseFormat = options.responseFormat ?? 'url';
if (responseFormat !== 'url' && responseFormat !== 'hex') {
throw new Error('MiniMax music responseFormat must be "url" or "hex".');
}
return responseFormat;
}
private ensureAudioFormat(
options: MiniMaxMusicProviderOptions,
request: MusicGenerateRequest,
): MiniMaxMusicAudioFormat {
const requestedAudioFormat = options.audioSetting?.format ?? request.outputFormat ?? 'mp3';
if (requestedAudioFormat !== 'mp3' && requestedAudioFormat !== 'wav' && requestedAudioFormat !== 'pcm') {
throw new Error('MiniMax music audio format must be "mp3", "wav", or "pcm".');
}
return requestedAudioFormat;
}
```
Usage:
```ts
const responseFormat = this.ensureResponseFormat(options);
const audioFormat = this.ensureAudioFormat(options, request);
```
3. **Cover input validation**
Make the cover-specific business rule explicit in its own helper:
```ts
private validateCoverInputs(
isCover: boolean,
options: MiniMaxMusicProviderOptions,
): void {
const referenceInputs = [options.audioUrl, options.audioBase64, options.coverFeatureId]
.filter((value) => typeof value === 'string' && value.length > 0);
if (isCover && referenceInputs.length !== 1) {
throw new Error(
'MiniMax music cover requires exactly one of audioUrl, audioBase64, or coverFeatureId.',
);
}
}
```
Usage:
```ts
const isCover = model === 'music-cover' || model === 'music-cover-free';
this.validateCoverInputs(isCover, options);
```
4. **Request body construction**
Move the object building + options logic into a helper, keeping `generateMusic` focused on orchestration:
```ts
private buildRequestBody(
request: MusicGenerateRequest,
options: MiniMaxMusicProviderOptions,
model: string,
region: MiniMaxMusicRegion,
responseFormat: MiniMaxMusicResponseFormat,
audioFormat: MiniMaxMusicAudioFormat,
isCover: boolean,
): Record<string, unknown> {
const body: Record<string, unknown> = {
model,
prompt: request.prompt,
stream: false,
output_format: responseFormat,
audio_setting: {
...options.audioSetting,
format: audioFormat,
},
};
if (options.lyrics !== undefined) body.lyrics = options.lyrics;
if (!isCover) {
body.lyrics_optimizer =
options.lyricsOptimizer ?? (!options.lyrics && !options.isInstrumental);
body.is_instrumental = options.isInstrumental ?? false;
}
if (region === 'china' && options.aigcWatermark !== undefined) {
body.aigc_watermark = options.aigcWatermark;
}
if (options.audioUrl) body.audio_url = options.audioUrl;
if (options.audioBase64) body.audio_base64 = options.audioBase64;
if (options.coverFeatureId) body.cover_feature_id = options.coverFeatureId;
return body;
}
```
Usage:
```ts
const body = this.buildRequestBody(
request,
options,
model,
region,
responseFormat,
audioFormat,
isCover,
);
```
5. **Generated audio assembly**
Replace inline conditional spreads with a small helper that is easier to read and extend:
```ts
private buildGeneratedAudio(
audio: string,
responseFormat: MiniMaxMusicResponseFormat,
audioFormat: MiniMaxMusicAudioFormat,
payload: MiniMaxMusicResponse,
region: MiniMaxMusicRegion,
): AudioResult['audio'][number] {
const extra = payload.extra_info ?? {};
const durationMs = extra.music_duration;
const result: any = {
mimeType: AUDIO_MIME_TYPES[audioFormat],
providerMetadata: {
traceId: payload.trace_id,
status: payload.data?.status,
responseFormat,
region,
channelCount: extra.music_channel,
bitrate: extra.bitrate,
sizeBytes: extra.music_size,
},
};
if (responseFormat === 'url') {
result.url = audio;
} else {
result.base64 = Buffer.from(audio, 'hex').toString('base64');
}
if (durationMs !== undefined) {
result.durationSec = durationMs / 1000;
}
if (extra.music_sample_rate !== undefined) {
result.sampleRate = extra.music_sample_rate;
}
return result;
}
```
Usage:
```ts
const generatedAudio = this.buildGeneratedAudio(
audio,
responseFormat,
audioFormat,
payload,
region,
);
```
With these helpers, `generateMusic` becomes a short orchestration method (resolve options → validate → build body → call API → parse → build result), which should address the complexity concern without changing behavior.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| it('rejects unsupported audio formats before sending a request', async () => { | ||
| await provider.initialize({ apiKey: 'minimax-test-key' }); | ||
|
|
||
| await expect(provider.generateMusic({ prompt: 'test', outputFormat: 'flac' })) | ||
| .rejects.toThrow('MiniMax music audio format must be "mp3", "wav", or "pcm".'); | ||
| expect(fetchSpy).not.toHaveBeenCalled(); | ||
| }); |
There was a problem hiding this comment.
suggestion (testing): Mirror this audio format validation test with one for invalid responseFormat values
There’s equivalent validation for responseFormat (only 'url' and 'hex' are allowed) that isn’t covered. Please add a similar test that calls generateMusic with an invalid responseFormat (e.g. 'foo'), asserts it rejects with "responseFormat must be "url" or "hex"", and confirms fetch is not called.
| it('rejects unsupported audio formats before sending a request', async () => { | |
| await provider.initialize({ apiKey: 'minimax-test-key' }); | |
| await expect(provider.generateMusic({ prompt: 'test', outputFormat: 'flac' })) | |
| .rejects.toThrow('MiniMax music audio format must be "mp3", "wav", or "pcm".'); | |
| expect(fetchSpy).not.toHaveBeenCalled(); | |
| }); | |
| it('rejects unsupported audio formats before sending a request', async () => { | |
| await provider.initialize({ apiKey: 'minimax-test-key' }); | |
| await expect(provider.generateMusic({ prompt: 'test', outputFormat: 'flac' })) | |
| .rejects.toThrow('MiniMax music audio format must be "mp3", "wav", or "pcm".'); | |
| expect(fetchSpy).not.toHaveBeenCalled(); | |
| }); | |
| it('rejects unsupported response formats before sending a request', async () => { | |
| await provider.initialize({ apiKey: 'minimax-test-key' }); | |
| await expect(provider.generateMusic({ prompt: 'test', responseFormat: 'foo' })) | |
| .rejects.toThrow('responseFormat must be "url" or "hex"'); | |
| expect(fetchSpy).not.toHaveBeenCalled(); | |
| }); |
| this.isInitialized = true; | ||
| } | ||
|
|
||
| async generateMusic(request: MusicGenerateRequest): Promise<AudioResult> { |
There was a problem hiding this comment.
issue (complexity): Consider refactoring generateMusic into smaller helper methods for resolution, validation, body construction, and result assembly to keep the core flow simple and orchestration-focused.
The core logic in generateMusic is solid but packed into a single method; you can keep behavior identical while reducing complexity by extracting a few focused helpers.
You already have clear boundaries you can leverage:
- Endpoint / region resolution
Move the region/endpoint branching into a helper so generateMusic reads more linearly:
private resolveEndpoint(options: MiniMaxMusicProviderOptions): {
region: MiniMaxMusicRegion;
endpoint: string;
} {
const region =
options.region === 'china' || options.region === 'global'
? options.region
: this._config.region;
const endpoint =
options.baseURL?.trim() ||
(options.region ? MUSIC_ENDPOINTS[region] : this._config.baseURL);
return { region, endpoint };
}Usage:
const { region, endpoint } = this.resolveEndpoint(options);- Response / audio format validation
Centralize union validation and error messages to avoid repeated inline checks:
private ensureResponseFormat(options: MiniMaxMusicProviderOptions): MiniMaxMusicResponseFormat {
const responseFormat = options.responseFormat ?? 'url';
if (responseFormat !== 'url' && responseFormat !== 'hex') {
throw new Error('MiniMax music responseFormat must be "url" or "hex".');
}
return responseFormat;
}
private ensureAudioFormat(
options: MiniMaxMusicProviderOptions,
request: MusicGenerateRequest,
): MiniMaxMusicAudioFormat {
const requestedAudioFormat = options.audioSetting?.format ?? request.outputFormat ?? 'mp3';
if (requestedAudioFormat !== 'mp3' && requestedAudioFormat !== 'wav' && requestedAudioFormat !== 'pcm') {
throw new Error('MiniMax music audio format must be "mp3", "wav", or "pcm".');
}
return requestedAudioFormat;
}Usage:
const responseFormat = this.ensureResponseFormat(options);
const audioFormat = this.ensureAudioFormat(options, request);- Cover input validation
Make the cover-specific business rule explicit in its own helper:
private validateCoverInputs(
isCover: boolean,
options: MiniMaxMusicProviderOptions,
): void {
const referenceInputs = [options.audioUrl, options.audioBase64, options.coverFeatureId]
.filter((value) => typeof value === 'string' && value.length > 0);
if (isCover && referenceInputs.length !== 1) {
throw new Error(
'MiniMax music cover requires exactly one of audioUrl, audioBase64, or coverFeatureId.',
);
}
}Usage:
const isCover = model === 'music-cover' || model === 'music-cover-free';
this.validateCoverInputs(isCover, options);- Request body construction
Move the object building + options logic into a helper, keeping generateMusic focused on orchestration:
private buildRequestBody(
request: MusicGenerateRequest,
options: MiniMaxMusicProviderOptions,
model: string,
region: MiniMaxMusicRegion,
responseFormat: MiniMaxMusicResponseFormat,
audioFormat: MiniMaxMusicAudioFormat,
isCover: boolean,
): Record<string, unknown> {
const body: Record<string, unknown> = {
model,
prompt: request.prompt,
stream: false,
output_format: responseFormat,
audio_setting: {
...options.audioSetting,
format: audioFormat,
},
};
if (options.lyrics !== undefined) body.lyrics = options.lyrics;
if (!isCover) {
body.lyrics_optimizer =
options.lyricsOptimizer ?? (!options.lyrics && !options.isInstrumental);
body.is_instrumental = options.isInstrumental ?? false;
}
if (region === 'china' && options.aigcWatermark !== undefined) {
body.aigc_watermark = options.aigcWatermark;
}
if (options.audioUrl) body.audio_url = options.audioUrl;
if (options.audioBase64) body.audio_base64 = options.audioBase64;
if (options.coverFeatureId) body.cover_feature_id = options.coverFeatureId;
return body;
}Usage:
const body = this.buildRequestBody(
request,
options,
model,
region,
responseFormat,
audioFormat,
isCover,
);- Generated audio assembly
Replace inline conditional spreads with a small helper that is easier to read and extend:
private buildGeneratedAudio(
audio: string,
responseFormat: MiniMaxMusicResponseFormat,
audioFormat: MiniMaxMusicAudioFormat,
payload: MiniMaxMusicResponse,
region: MiniMaxMusicRegion,
): AudioResult['audio'][number] {
const extra = payload.extra_info ?? {};
const durationMs = extra.music_duration;
const result: any = {
mimeType: AUDIO_MIME_TYPES[audioFormat],
providerMetadata: {
traceId: payload.trace_id,
status: payload.data?.status,
responseFormat,
region,
channelCount: extra.music_channel,
bitrate: extra.bitrate,
sizeBytes: extra.music_size,
},
};
if (responseFormat === 'url') {
result.url = audio;
} else {
result.base64 = Buffer.from(audio, 'hex').toString('base64');
}
if (durationMs !== undefined) {
result.durationSec = durationMs / 1000;
}
if (extra.music_sample_rate !== undefined) {
result.sampleRate = extra.music_sample_rate;
}
return result;
}Usage:
const generatedAudio = this.buildGeneratedAudio(
audio,
responseFormat,
audioFormat,
payload,
region,
);With these helpers, generateMusic becomes a short orchestration method (resolve options → validate → build body → call API → parse → build result), which should address the complexity concern without changing behavior.
Code Review by Qodo
1.
|
| const requestedAudioFormat = options.audioSetting?.format ?? request.outputFormat ?? 'mp3'; | ||
| if (requestedAudioFormat !== 'mp3' && requestedAudioFormat !== 'wav' && requestedAudioFormat !== 'pcm') { | ||
| throw new Error('MiniMax music audio format must be "mp3", "wav", or "pcm".'); | ||
| } | ||
| const audioFormat: MiniMaxMusicAudioFormat = requestedAudioFormat; |
There was a problem hiding this comment.
3. Pcm output not typable 🐞 Bug ⚙ Maintainability
MiniMaxMusicProvider supports pcm, but the shared AudioOutputFormat type used by GenerateMusicOptions.outputFormat does not include pcm. TypeScript callers therefore cannot request PCM via outputFormat without unsafe casts, contradicting the provider’s capabilities.
Agent Prompt
### Issue description
MiniMax provider accepts `'pcm'`, but the public `AudioOutputFormat` union does not include it, preventing type-safe usage through the high-level API (`GenerateMusicOptions.outputFormat`).
### Issue Context
- MiniMax supports `mp3|wav|pcm`.
- `AudioOutputFormat` is a shared type across providers; extending it may require ensuring other providers either support or cleanly reject `'pcm'`.
### Fix Focus Areas
- src/io/media/audio/types.ts[38-40]
- src/io/media/audio/providers/MiniMaxMusicProvider.ts[12-15]
- src/io/media/audio/providers/MiniMaxMusicProvider.ts[126-130]
### Suggested fix
Choose one:
1) Add `'pcm'` to `AudioOutputFormat` and ensure other providers handle it sensibly (ignore or throw with clear error), OR
2) Keep `AudioOutputFormat` as-is and document that MiniMax PCM must be requested via `providerOptions.audioSetting.format`, not `outputFormat`.
ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/io/media/audio/providers/MiniMaxMusicProvider.ts`:
- Around line 174-178: Update the response handling in the MiniMax music
generation method so HTTP failures are checked before relying on
response.json(), allowing empty or HTML error bodies to retain response.status
and response.statusText diagnostics. Parse JSON defensively for successful or
structured responses, then preserve the existing base_resp validation and
MiniMax error message behavior.
- Around line 131-139: The MiniMax cover validation in the provider must require
valid lyrics when the sole reference input is coverFeatureId. Update the guard
near isCover and referenceInputs to validate lyrics length is between 10 and
1000 characters for that path, while preserving existing reference-count
validation, and add a test covering an invalid or missing-lyrics coverFeatureId
request.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: f7b6e8b6-3f8a-4553-bfe4-5225a61a0adb
📒 Files selected for processing (8)
docs/getting-started/HIGH_LEVEL_API.mdsrc/api/generateMusic.tssrc/api/runtime/__tests__/generateMusic.test.tssrc/io/media/audio/__tests__/MiniMaxMusicProvider.test.tssrc/io/media/audio/__tests__/index.test.tssrc/io/media/audio/index.tssrc/io/media/audio/providers/MiniMaxMusicProvider.tssrc/io/media/audio/types.ts
Signed-off-by: octo-patch <266937838+octo-patch@users.noreply.github.com>
Signed-off-by: octo-patch <266937838+octo-patch@users.noreply.github.com>
Reason: add MiniMax Music 3.0 support to the existing audio provider registry
Summary
MINIMAX_API_KEYintogenerateMusic()provider detection and document usageTesting
vitest run src/io/media/audio/__tests__/MiniMaxMusicProvider.test.ts src/io/media/audio/__tests__/index.test.tsnpm run typechecknpm run buildnpm run lint(0 errors; existing warnings remain)generateMusic()smoke test with mocked HTTP responseThe full test suite was also attempted. Its unrelated native SQLite and external-auth tests do not pass in this local environment; the changed audio path and all required build checks pass.
Summary by Sourcery
Add MiniMax Music 3.0 as a first-class music generation provider with env-based auto-detection and documentation updates.
New Features:
Documentation:
Tests:
Summary by CodeRabbit
New Features
MINIMAX_API_KEY.Documentation
Tests