Skip to content

feat: add MiniMax Music 3.0 provider#22

Open
octo-patch wants to merge 3 commits into
framerslab:masterfrom
octo-patch:octo/20260717-multimodal-music-tool-recvpCPuWU4MSo
Open

feat: add MiniMax Music 3.0 provider#22
octo-patch wants to merge 3 commits into
framerslab:masterfrom
octo-patch:octo/20260717-multimodal-music-tool-recvpCPuWU4MSo

Conversation

@octo-patch

@octo-patch octo-patch commented Jul 17, 2026

Copy link
Copy Markdown

Reason: add MiniMax Music 3.0 support to the existing audio provider registry

Summary

  • add a MiniMax music provider with global and China endpoint selection
  • support text-to-music and cover request options, URL and hex output, MP3/WAV/PCM formats, and response metadata
  • wire MINIMAX_API_KEY into generateMusic() provider detection and document usage

Testing

  • vitest run src/io/media/audio/__tests__/MiniMaxMusicProvider.test.ts src/io/media/audio/__tests__/index.test.ts
  • npm run typecheck
  • npm run build
  • npm run lint (0 errors; existing warnings remain)
  • compiled generateMusic() smoke test with mocked HTTP response

The 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:

  • Introduce a MiniMaxMusicProvider that supports text-to-music and cover generation with region, format, and response options.
  • Register the minimax-music provider in the audio provider registry and support detection via MINIMAX_API_KEY in generateMusic().

Documentation:

  • Document usage of the minimax-music provider and MINIMAX_API_KEY, including region selection and response format options.

Tests:

  • Add unit tests for MiniMaxMusicProvider behavior and extend generateMusic tests to cover MINIMAX_API_KEY provider auto-detection and error handling.

Summary by CodeRabbit

  • New Features

    • Added MiniMax Music as a supported music-generation provider.
    • Supports Music 3.0 via global and China endpoints.
    • Enables URL and hex audio responses, with MP3, WAV, and PCM audio output options.
    • Supports instrumental tracks, cover generation, and reference-based cover inputs.
    • Adds automatic provider detection using MINIMAX_API_KEY.
  • Documentation

    • Updated MiniMax Music API docs with provider configuration, endpoint selection, and PCM-specific audio settings.
  • Tests

    • Expanded coverage for MiniMax Music requests, validation, region switching, and error handling.

Signed-off-by: octo-patch <266937838+octo-patch@users.noreply.github.com>
@sourcery-ai

sourcery-ai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Reviewer's Guide

Adds 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 MiniMaxMusicProvider

sequenceDiagram
  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
Loading

File-Level Changes

Change Details Files
Introduce MiniMaxMusicProvider implementation for MiniMax Music 3.0 with configurable region, response format, audio format, cover support, and rich response metadata.
  • Implemented MiniMaxMusicProvider class that validates and stores API key, region, baseURL, and default model, and uses ApiKeyPool for auth.
  • Added generateMusic implementation that builds MiniMax API JSON payload (including prompt, model, lyrics/lyrics_optimizer, is_instrumental, audio settings, watermark, cover inputs), selects endpoint based on region/baseURL, and issues a POST via fetch.
  • Validated responseFormat (url/hex) and audio formats (mp3/wav/pcm), enforced cover reference input rules, handled MiniMax response status codes, and mapped audio data into AudioResult with URL or base64 output, mimeType, duration, sampleRate, and providerMetadata.
  • Added supports() implementation limited to music and a shutdown() method resetting initialization state.
src/io/media/audio/providers/MiniMaxMusicProvider.ts
Register the MiniMax provider in the audio registry and type system so it can be created by provider id and re-exported from the audio module.
  • Imported MiniMaxMusicProvider into the audio index and registered 'minimax-music' in audioProviderFactories so createAudioProvider can instantiate it.
  • Re-exported MiniMaxMusicProvider from the audio index for external usage.
  • Extended AudioProviderId union type to include 'minimax-music'.
  • Updated audio index tests to assert 'minimax-music' is included in the registered provider IDs.
src/io/media/audio/index.ts
src/io/media/audio/types.ts
src/io/media/audio/__tests__/index.test.ts
Wire MiniMax into generateMusic() provider detection, default model mapping, and error messaging.
  • Added 'minimax-music' case to defaultModelFor in generateMusic runtime tests, defaulting to 'music-3.0'.
  • Extended MUSIC_PROVIDER_ENV_MAP and related JSDoc comments so MINIMAX_API_KEY maps to the 'minimax-music' provider and is considered in provider resolution order.
  • Updated the generateMusic() "no provider configured" error message to include MINIMAX_API_KEY and added a test that MINIMAX_API_KEY auto-selects the MiniMax provider and default model.
  • Ensured generateMusic tests clear MINIMAX_API_KEY when checking the no-provider error path.
src/api/runtime/__tests__/generateMusic.test.ts
src/api/generateMusic.ts
Document MiniMax usage with generateMusic(), including provider id, env var, region override, and response format.
  • Updated HIGH_LEVEL_API docs to describe MiniMax Music using MINIMAX_API_KEY and provider 'minimax-music', defaulting to Music 3.0 and global endpoint.
  • Added a TypeScript example showing generateMusic() with providerOptions.region='china' and responseFormat='url' for MiniMax Music.
docs/getting-started/HIGH_LEVEL_API.md
Add unit tests for MiniMaxMusicProvider behavior and error handling.
  • Added tests to validate default Music 3.0 generation using the global endpoint, Authorization header, URL output, and request body structure (including lyrics_optimizer and is_instrumental defaults).
  • Added tests to verify China region behavior, hex response conversion to base64, audio_setting propagation, and aigcWatermark handling.
  • Added tests for cover-generation behavior ensuring exactly one reference field is sent and that lyrics-related fields are omitted for cover models.
  • Added tests for instrumental behavior (lyrics_optimizer=false), API error surfacing from base_resp, and pre-request validation of unsupported audio formats (e.g., flac).
src/io/media/audio/__tests__/MiniMaxMusicProvider.test.ts

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: f033b6d3-971d-4383-bfe8-7343bc1a3e9e

📥 Commits

Reviewing files that changed from the base of the PR and between fb19d53 and fd05c0f.

📒 Files selected for processing (4)
  • docs/getting-started/HIGH_LEVEL_API.md
  • src/api/generateMusic.ts
  • src/io/media/audio/__tests__/MiniMaxMusicProvider.test.ts
  • src/io/media/audio/providers/MiniMaxMusicProvider.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • docs/getting-started/HIGH_LEVEL_API.md
  • src/api/generateMusic.ts
  • src/io/media/audio/providers/MiniMaxMusicProvider.ts

📝 Walkthrough

Walkthrough

Adds a MiniMax Music provider with global and China endpoints, URL or hex audio responses, provider registration, MINIMAX_API_KEY auto-detection, documentation, and comprehensive request/response tests.

Changes

MiniMax Music

Layer / File(s) Summary
Provider contract and lifecycle
src/io/media/audio/types.ts, src/io/media/audio/providers/MiniMaxMusicProvider.ts
Defines MiniMax Music configuration and format types, initializes API credentials and region settings, and implements capability and shutdown methods.
Music generation request and response
src/io/media/audio/providers/MiniMaxMusicProvider.ts, src/io/media/audio/__tests__/MiniMaxMusicProvider.test.ts
Constructs music and cover requests, validates API responses, converts hex audio to base64, and tests output formats, endpoints, options, and errors.
Provider wiring and automatic detection
src/io/media/audio/index.ts, src/io/media/audio/__tests__/index.test.ts, src/api/generateMusic.ts, src/api/runtime/__tests__/generateMusic.test.ts, docs/getting-started/HIGH_LEVEL_API.md
Registers and exports minimax-music, enables MINIMAX_API_KEY detection, tests automatic selection, and documents configuration and endpoint selection.

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
Loading

Suggested reviewers: jddunn, victor-evogor

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding the MiniMax Music 3.0 provider.
Description check ✅ Passed The description explains the purpose, summarizes the changes, and includes testing notes that cover the main template requirements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Add MiniMax Music 3.0 provider (global/China endpoints)

✨ Enhancement 🧪 Tests 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Add a new minimax-music audio provider backed by MiniMax Music 3.0.
• Enable provider auto-detection via MINIMAX_API_KEY and register it in the audio factory.
• Document usage and add focused unit tests for provider behavior and env-var detection.
Diagram

graph TD
  caller(["Caller"]) --> genMusic["generateMusic()"] --> detect["Resolve provider (env/opts)"] --> registry["Audio provider registry"] --> provider["MiniMaxMusicProvider"]
  provider --> apiGlobal{{"MiniMax API (global)"}}
  provider --> apiChina{{"MiniMax API (china)"}}
Loading
High-Level Assessment

The chosen approach (first-class provider implementation + registry wiring + env-var detection + tests/docs) fits the existing audio subsystem architecture and matches how other providers are integrated. Alternatives like relying solely on a configurable baseURL without a region option were implicitly considered, but the current design improves usability while still allowing overrides via baseURL.

Files changed (8) +414 / -3

Enhancement (4) +233 / -3
generateMusic.tsAdd 'MINIMAX_API_KEY' to music provider auto-detection chain +4/-3

Add 'MINIMAX_API_KEY' to music provider auto-detection chain

• Extends the env-var-to-provider mapping so 'MINIMAX_API_KEY' resolves to 'minimax-music'. Updates inline docs and the "no provider configured" error message to include MiniMax as a supported configuration path.

src/api/generateMusic.ts

index.tsRegister and export MiniMaxMusicProvider in the audio subsystem +3/-0

Register and export MiniMaxMusicProvider in the audio subsystem

• Adds the MiniMax provider import/export and registers the 'minimax-music' factory in the built-in 'audioProviderFactories' map so it can be instantiated by ID.

src/io/media/audio/index.ts

MiniMaxMusicProvider.tsImplement MiniMax Music API provider (Music 3.0 + cover options) +225/-0

Implement MiniMax Music API provider (Music 3.0 + cover options)

• Adds a new 'IAudioGenerator' implementation that calls MiniMax Music generation, supporting global/China endpoints, URL/hex response formats, and MP3/WAV/PCM output. Includes cover-generation reference input validation, request option mapping (lyrics, instrumental flags, watermark), and surfaces response metadata (trace/status/duration/sample rate) in 'providerMetadata'.

src/io/media/audio/providers/MiniMaxMusicProvider.ts

types.tsAdd 'minimax-music' to well-known AudioProviderId union +1/-0

Add 'minimax-music' to well-known AudioProviderId union

• Extends the 'AudioProviderId' type union to include 'minimax-music' so the provider can be referenced in a type-safe way.

src/io/media/audio/types.ts

Tests (3) +169 / -0
generateMusic.test.tsTest 'MINIMAX_API_KEY' provider auto-detection in generateMusic() +14/-0

Test 'MINIMAX_API_KEY' provider auto-detection in generateMusic()

• Updates the test harness mock to define a default model for 'minimax-music' ('music-3.0'). Adds a new test asserting 'generateMusic()' selects 'minimax-music' when only 'MINIMAX_API_KEY' is set, and ensures cleanup in the no-provider test.

src/api/runtime/tests/generateMusic.test.ts

MiniMaxMusicProvider.test.tsAdd unit tests for MiniMaxMusicProvider request/response mapping +154/-0

Add unit tests for MiniMaxMusicProvider request/response mapping

• Introduces a comprehensive test suite covering global vs China endpoints, URL vs hex responses (including hex-to-base64 conversion), cover generation reference input validation, instrumental/lyrics optimizer behavior, API error propagation, and preflight rejection of unsupported output formats.

src/io/media/audio/tests/MiniMaxMusicProvider.test.ts

index.test.tsAssert minimax-music is registered in the audio provider registry +1/-0

Assert minimax-music is registered in the audio provider registry

• Updates the provider registry test to include 'minimax-music' in the expected built-in factory list.

src/io/media/audio/tests/index.test.ts

Documentation (1) +12 / -0
HIGH_LEVEL_API.mdDocument MiniMax Music provider usage and China endpoint selection +12/-0

Document MiniMax Music provider usage and China endpoint selection

• Adds a 'generateMusic()' example for the 'minimax-music' provider using 'MINIMAX_API_KEY'. Documents default behavior (Music 3.0 + global endpoint) and selecting the China endpoint via 'providerOptions.region'.

docs/getting-started/HIGH_LEVEL_API.md

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 3 issues, and left some high level feedback:

  • 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.
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>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/api/generateMusic.ts
Comment on lines +147 to +153
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();
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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> {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

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);
  1. 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);
  1. 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);
  1. 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,
);
  1. 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.

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Jul 17, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Region bypasses baseURL ✓ Resolved 🐞 Bug ≡ Correctness
Description
In MiniMaxMusicProvider.generateMusic(), endpoint selection uses options.region truthiness rather
than the validated region, so any truthy but invalid providerOptions.region can force a built-in
endpoint and ignore the configured baseURL (e.g., proxy/staging). This can silently route requests
to an unintended endpoint even though region itself falls back to config.
Code

src/io/media/audio/providers/MiniMaxMusicProvider.ts[R117-121]

+    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);
Evidence
providerOptions is an unvalidated Record, and asOptions() is a plain cast; endpoint uses
options.region ? ... so any truthy invalid value triggers the built-in endpoint selection and can
ignore the configured baseURL.

src/io/media/audio/providers/MiniMaxMusicProvider.ts[71-73]
src/io/media/audio/providers/MiniMaxMusicProvider.ts[115-121]
src/io/media/audio/types.ts[81-139]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`endpoint` selection currently checks `options.region` for truthiness, not whether it is a supported value. Because `providerOptions` is untyped at runtime, a value like `{ region: 'cn' }` will bypass `this._config.baseURL` and use `MUSIC_ENDPOINTS[region]`, unexpectedly ignoring configured proxies/custom endpoints.
### Issue Context
- `providerOptions` arrives as `Record<string, unknown>` and is cast without validation.
- `region` is validated, but the endpoint override condition is not.
### Fix Focus Areas
- src/io/media/audio/providers/MiniMaxMusicProvider.ts[115-122]
### Suggested fix
- Compute a boolean like `const hasRegionOverride = options.region === 'china' || options.region === 'global';`
- Use it consistently:
- `const region = hasRegionOverride ? options.region : this._config.region;`
- `const endpoint = options.baseURL?.trim() || (hasRegionOverride ? MUSIC_ENDPOINTS[region] : this._config.baseURL);`
- (Optionally) throw a clear error if `options.region` is provided but not one of the supported values, mirroring the existing validation for `responseFormat` and audio format.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Whitespace cover refs allowed ✓ Resolved 🐞 Bug ☼ Reliability
Description
Cover reference validation counts any non-empty string (including whitespace-only) and later
forwards any truthy value into audio_url/audio_base64/cover_feature_id. This can let invalid
whitespace references pass the “exactly one” cover check or be sent to the API, causing avoidable
request failures.
Code

src/io/media/audio/providers/MiniMaxMusicProvider.ts[R132-163]

+    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.',
+      );
+    }
+
+    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;
Evidence
Reference inputs are counted using value.length > 0 without trimming, and then forwarded to the
request body based on truthiness, so whitespace-only strings are treated as valid inputs in both
places.

src/io/media/audio/providers/MiniMaxMusicProvider.ts[132-139]
src/io/media/audio/providers/MiniMaxMusicProvider.ts[161-163]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Cover inputs (`audioUrl`, `audioBase64`, `coverFeatureId`) are validated with `value.length > 0` and then serialized using truthiness checks. Whitespace-only strings (e.g. `'   '`) will be treated as present and can either:
- satisfy the `referenceInputs.length === 1` requirement while being invalid, or
- incorrectly increase the count and fail the exactly-one check.
### Issue Context
This provider already validates `responseFormat` and audio formats strictly; cover inputs should be normalized similarly.
### Fix Focus Areas
- src/io/media/audio/providers/MiniMaxMusicProvider.ts[132-139]
- src/io/media/audio/providers/MiniMaxMusicProvider.ts[161-163]
### Suggested fix
- Normalize inputs once (and reuse both for counting and body assignment), e.g.:
- `const audioUrl = typeof options.audioUrl === 'string' ? options.audioUrl.trim() : undefined;`
- `const coverFeatureId = typeof options.coverFeatureId === 'string' ? options.coverFeatureId.trim() : undefined;`
- Consider whether trimming `audioBase64` is acceptable; at minimum, validate non-whitespace.
- Build `referenceInputs` from normalized values.
- Only set `body.audio_url/audio_base64/cover_feature_id` from normalized values.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

3. PCM output not typable 🐞 Bug ⚙ Maintainability
Description
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.
Code

src/io/media/audio/providers/MiniMaxMusicProvider.ts[R126-130]

+    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;
Evidence
The MiniMax provider explicitly permits pcm, but the core audio output-format union omits it, and
the high-level API’s outputFormat is typed using that union.

src/io/media/audio/providers/MiniMaxMusicProvider.ts[12-15]
src/io/media/audio/providers/MiniMaxMusicProvider.ts[126-130]
src/io/media/audio/types.ts[38-40]
src/api/generateMusic.ts[233-235]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

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


Grey Divider

Qodo Logo

Comment thread src/io/media/audio/providers/MiniMaxMusicProvider.ts Outdated
Comment thread src/io/media/audio/providers/MiniMaxMusicProvider.ts Outdated
Comment on lines +126 to +130
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;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Informational

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 66b987c and fb19d53.

📒 Files selected for processing (8)
  • docs/getting-started/HIGH_LEVEL_API.md
  • src/api/generateMusic.ts
  • src/api/runtime/__tests__/generateMusic.test.ts
  • src/io/media/audio/__tests__/MiniMaxMusicProvider.test.ts
  • src/io/media/audio/__tests__/index.test.ts
  • src/io/media/audio/index.ts
  • src/io/media/audio/providers/MiniMaxMusicProvider.ts
  • src/io/media/audio/types.ts

Comment thread src/io/media/audio/providers/MiniMaxMusicProvider.ts
Comment thread src/io/media/audio/providers/MiniMaxMusicProvider.ts Outdated
Signed-off-by: octo-patch <266937838+octo-patch@users.noreply.github.com>
Signed-off-by: octo-patch <266937838+octo-patch@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant