Skip to content

feat(minimax): add native text-to-speech support - #621

Merged
SantiagoDePolonia merged 1 commit into
ENTERPILOT:mainfrom
octo-patch:octo/20260731-tts-tool-recvqKm1NbrLT9
Aug 3, 2026
Merged

feat(minimax): add native text-to-speech support#621
SantiagoDePolonia merged 1 commit into
ENTERPILOT:mainfrom
octo-patch:octo/20260731-tts-tool-recvqKm1NbrLT9

Conversation

@octo-patch

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

Copy link
Copy Markdown
Contributor

Reason: Add native MiniMax text-to-speech support through the existing audio route.

Summary

  • Implement core.AudioProvider for the synchronous /t2a_v2 endpoint.
  • Translate model, text, voice, speed, and supported audio formats, then decode hexadecimal audio responses.
  • Surface native response errors and explicitly reject unsupported transcription requests.
  • Add focused tests for request translation, defaults, validation, response errors, and decoded audio.

Checks

  • go test ./internal/providers/minimax
  • go vet ./internal/providers/minimax
  • golangci-lint run ./internal/providers/minimax/...
  • go test ./internal/...
  • git diff --check origin/main...HEAD
  • Secret scan over origin/main...HEAD

Summary by CodeRabbit

  • New Features

    • Added MiniMax text-to-speech support, including MP3 and other supported audio formats.
    • Added audio format and playback speed validation, with sensible defaults.
    • Added decoding and validation of generated audio responses.
  • Limitations

    • Speech-to-text transcription is not supported by the MiniMax audio provider.
  • Tests

    • Added coverage for successful speech generation, validation, authorization, provider errors, and malformed audio responses.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

MiniMax now implements core.AudioProvider speech creation. It validates requests, calls /t2a_v2, decodes hexadecimal audio, returns content types, handles errors, and rejects unsupported transcription requests.

Changes

MiniMax audio support

Layer / File(s) Summary
Audio contracts and operation support
internal/providers/minimax/audio.go
MiniMax defines native speech request and response structures, normalizes formats and speed, and rejects transcription requests.
Speech request and response flow
internal/providers/minimax/audio.go, internal/providers/minimax/audio_test.go
CreateSpeech validates input, sends authenticated requests to /t2a_v2, handles provider errors, decodes hexadecimal audio, and returns the correct content type. Tests cover successful requests, defaults, validation, provider errors, malformed audio, and interface support.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Poem

I’m a rabbit with audio in tow,
Through MiniMax pathways, the bytes now flow.
MP3, WAV, FLAC, PCM in the pack,
Hex turns to sound and errors turn back.
Transcription waits outside the door—
Synthesis hops onward, ready for more.

🚥 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 clearly and concisely identifies the main change: native MiniMax text-to-speech support.
Description check ✅ Passed The description explains the change, implementation details, tests, and validation steps; the optional AI section is not required.
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

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@internal/providers/minimax/audio_test.go`:
- Around line 141-157: Add a test near TestCreateSpeech_ReturnsNativeStatusError
named TestCreateSpeech_ReturnsUpstreamHTTPStatusError that makes the mock server
respond with a non-2xx status such as http.StatusUnauthorized and a JSON error
payload, then call provider.CreateSpeech and assert it returns a non-nil error
containing the upstream status message. Ensure the test exercises the
core.ParseProviderError path for HTTP-level failures.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 78ced0da-7807-43f0-9467-7e8151d3624b

📥 Commits

Reviewing files that changed from the base of the PR and between 61493f4 and e3895b6.

📒 Files selected for processing (2)
  • internal/providers/minimax/audio.go
  • internal/providers/minimax/audio_test.go

Comment on lines +141 to +157
func TestCreateSpeech_ReturnsNativeStatusError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"data":null,"base_resp":{"status_code":1004,"status_msg":"invalid voice"}}`))
}))
defer server.Close()

provider := NewWithHTTPClient("key", server.URL, server.Client(), llmclient.Hooks{})
_, err := provider.CreateSpeech(context.Background(), &core.AudioSpeechRequest{
Model: "speech-2.8-hd",
Input: "hello",
Voice: "voice-id",
})
if err == nil || !strings.Contains(err.Error(), "invalid voice") {
t.Fatalf("CreateSpeech() error = %v, want native status message", err)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a test for the upstream non-2xx HTTP status path.

The existing tests cover a native business error returned with HTTP 200 (TestCreateSpeech_ReturnsNativeStatusError) and malformed hex audio. No test exercises the branch in audio.go that calls core.ParseProviderError when the upstream HTTP status itself is outside 200-299 (audio.go lines 111-113). Add a test where the mock server returns a non-2xx status code (for example 401 or 500) to confirm the error is parsed and surfaced correctly.

As per path instructions, **/*_test.go: "Add or update table-driven tests for behavior changes, covering request translation, response normalization, error handling, default configuration, and provider-specific parameter mapping."

✅ Proposed additional test
func TestCreateSpeech_ReturnsUpstreamHTTPStatusError(t *testing.T) {
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(http.StatusUnauthorized)
		_, _ = w.Write([]byte(`{"base_resp":{"status_code":1004,"status_msg":"invalid api key"}}`))
	}))
	defer server.Close()

	provider := NewWithHTTPClient("key", server.URL, server.Client(), llmclient.Hooks{})
	_, err := provider.CreateSpeech(context.Background(), &core.AudioSpeechRequest{
		Model: "speech-2.8-hd",
		Input: "hello",
		Voice: "voice-id",
	})
	if err == nil {
		t.Fatal("CreateSpeech() error = nil, want non-nil for non-2xx upstream status")
	}
}
🤖 Prompt for 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.

In `@internal/providers/minimax/audio_test.go` around lines 141 - 157, Add a test
near TestCreateSpeech_ReturnsNativeStatusError named
TestCreateSpeech_ReturnsUpstreamHTTPStatusError that makes the mock server
respond with a non-2xx status such as http.StatusUnauthorized and a JSON error
payload, then call provider.CreateSpeech and assert it returns a non-nil error
containing the upstream status message. Ensure the test exercises the
core.ParseProviderError path for HTTP-level failures.

Source: Path instructions

@codecov-commenter

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 80.95238% with 16 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/providers/minimax/audio.go 80.95% 8 Missing and 8 partials ⚠️

📢 Thoughts on this report? Let us know!

@greptile-apps

greptile-apps Bot commented Jul 31, 2026

Copy link
Copy Markdown

Confidence Score: 3/5

Not safe to merge until T-Rex findings are addressed.

Production-path checks reproduced a local rejection for compatible requests containing Instructions and confirmed that large chunked provider responses are accepted without an application-level limit.

T-Rex reproduced 2 failing behaviors at runtime in internal/providers/minimax/audio.go; the change needs fixes before it is safe to merge.

Files Needing Attention: internal/providers/minimax/audio.go

Security Review

The MiniMax speech response path accepts and buffers arbitrarily large chunked upstream responses before JSON parsing and hexadecimal decoding. A compromised, misconfigured, or unexpectedly large provider response can therefore drive multiple large allocations and cause process memory exhaustion. Bound the encoded response size before reading it, accounting for hexadecimal expansion and JSON overhead.

T-Rex T-Rex Logs

What T-Rex did

  • T-Rex produced a proof for the posted P1 finding and attached reproduction and validation artifacts.
  • T-Rex produced a proof for the posted P2 finding, with 1 MiB and 16 MiB decoded-audio chunked response logs and the existing native MiniMax speech path execution log.
  • A second proof was produced for the posted P1 finding, with no artifacts attached.
  • A general contract validation confirmed that instructions are rejected early in the MiniMax path, with upstream_calls=0, at internal/providers/minimax/audio.go:65-67.

View all artifacts

T-Rex Ran code and verified through T-Rex

Comments Outside Diff (1)

  1. General comment

    P1 MiniMax speech rejects compatible requests that include Instructions

    • Bug
      • An otherwise-valid OpenAI-compatible speech request with non-empty Instructions is rejected before the MiniMax endpoint is called, although the native MiniMax payload does not use that field.
    • Cause
      • CreateSpeech explicitly rejects non-blank req.Instructions at internal/providers/minimax/audio.go:65-67.
    • Fix
      • Remove the Instructions validation branch and leave Instructions unmapped/ignored when translating to MiniMax's native speechRequest.

    T-Rex Ran code and verified through T-Rex

Reviews (1): Last reviewed commit: "feat(minimax): add native text-to-speech..." | Re-trigger Greptile

Comment on lines +65 to +67
if strings.TrimSpace(req.Instructions) != "" {
return nil, core.NewInvalidRequestError("minimax speech does not support instructions", nil)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Instructions reject compatible speech requests

An otherwise-valid OpenAI-compatible speech request with a non-empty Instructions value is rejected locally and never reaches MiniMax. MiniMax does not consume this field, so it should be ignored during native request translation rather than causing callers that share speech requests across providers to fail.

Context Used: CLAUDE.md (source)

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Artifacts

Focused MiniMax instructions reproduction source

  • A Go harness starts a real local HTTP server, invokes the production MiniMax provider, and records whether the upstream endpoint was called, providing the focused execution probe.

MiniMax speech control request without instructions

  • The control run invokes the production provider without Instructions and records a successful simulated MiniMax call, establishing the otherwise-valid baseline.

MiniMax speech request with instructions rejected locally

  • The non-empty Instructions run records the local unsupported-instructions error and zero upstream calls, proving MiniMax was not called.

Existing MiniMax instructions validation test

  • The repository's focused existing test passes while asserting that Instructions produces the unsupported-instructions validation error, corroborating the observed behavior.

View artifacts

T-Rex Ran code and verified through T-Rex

}
defer func() { _ = upstream.Body.Close() }()

responseBody, err := io.ReadAll(upstream.Body)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 security Provider response body has no size limit

io.ReadAll(upstream.Body) buffers the complete MiniMax response before JSON parsing and hexadecimal audio decoding. A chunked response has no application-level cap, so a large or malicious upstream payload causes several large allocations and can exhaust process memory. Limit the encoded response size before reading it and return an error once the limit is exceeded.

Artifacts

Authored chunked oversized-response harness source

  • This harness sends a MiniMax-shaped chunked response without Content-Length through production CreateSpeech and asserts that its entire hex audio payload is accepted and decoded, proving the test exercises the unbounded read path.

1 MiB decoded-audio chunked response execution log

  • The production-path harness accepted a 2,097,214-byte chunked JSON response and returned 1,048,576 decoded audio bytes with exit code 0, proving the response is buffered rather than rejected by an explicit cap.

16 MiB decoded-audio chunked response execution log

  • The production-path harness accepted a 33,554,494-byte chunked JSON response and returned 16,777,216 decoded audio bytes with exit code 0, proving substantially oversized provider output is accepted without a bound.

Existing native MiniMax speech path execution log

  • The existing native endpoint and hex-decode test passed against the same provider package with exit code 0, confirming the focused validation ran in the real MiniMax speech flow.

View artifacts

T-Rex Ran code and verified through T-Rex

@SantiagoDePolonia

Copy link
Copy Markdown
Contributor

@octo-patch Looks promising! Check out the comments made by AI reviewers.

@SantiagoDePolonia

Copy link
Copy Markdown
Contributor

Merging this one because it's definitely an improvement. Also I'll add a few missing pieces here in the follow-up PR.

Thank you for your contribution! Feel free to be a GoModel's sponsor if you like the software!

@SantiagoDePolonia
SantiagoDePolonia merged commit b10ee6b into ENTERPILOT:main Aug 3, 2026
14 checks passed
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.

3 participants