feat(openai): support adjusting thinking level for MiniMax-M3#3594
Merged
Conversation
MiniMax-M3 exposes a single binary thinking knob (adaptive|disabled)
on its OpenAI-compatible endpoint at api.minimaxi.com, but our
provider unconditionally emits reasoning_effort — which M3 rejects.
Detect api.minimaxi.com as a separate wire shape alongside
api.deepseek.com and:
- emit thinking.type=adaptive|disabled in buildRequest
- omit reasoning_effort entirely (M3 has no level scale)
- translate /effort auto to 'adaptive' (the M3 default, since M3
ships with thinking on out of the box and 'auto' semantically
means 'don't override the model default')
- map legacy level names from other vendors so a stale /effort
value still resolves to a valid M3 level:
off → disabled (retired DeepSeek 'no thinking' — M3
actually supports 'thinking off')
low/medium/high → adaptive
xhigh/max → disabled
- in NormalizeEffort, surface a MiniMax-specific usage hint
(auto|adaptive|disabled) when an unknown level is supplied
internal/config/effort.go mirrors the change: M3 entries now expose
/levels=[auto,adaptive,disabled] with default=adaptive. Tests cover
the wire shape (TestBuildRequestMiniMaxThinking) and the
config-layer remap (TestNormalizeEffortMiniMax,
TestNormalizeEffortMiniMaxRejectsGarbage,
TestEffectiveEffortMiniMax).
Refactor: the host-matching logic was duplicated across
internal/provider/openai/openai.go (private *BaseURL helpers) and
internal/config/effort.go (private *Entry wrappers). Extract the
shared primitive into internal/provider/openai/host.go as
matchesVendorHost, plus thin exported wrappers IsDeepSeek and
IsMiniMax. The *Entry wrappers in effort.go now just gate on the
openai kind and delegate. Adding a new gateway is now a one-line
change in host.go rather than a four-line change across two files.
Add internal/provider/openai/host_test.go pinning the matching
rule (canonical hostname exact match, plus any subdomain of the
apex) directly at the source.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rebased @lightfront's #3432 onto current main-v2 and reconciled it with the reasoning-protocol refactor + the effort=auto handling that landed since it was opened (#3571).
What it does
Teaches the openai provider to speak MiniMax-M3's binary thinking knob (
adaptive|disabled) forapi.minimaxi.com(and*.minimaxi.com), mirroring the existing DeepSeek wire shape:thinking.type = adaptive|disabledand omitsreasoning_effort(M3 rejects it)/effortexposesauto|adaptive|disabled(defaultadaptive); stale level names from other vendors map to the nearest valid M3 valuehost.go(IsDeepSeek/IsMiniMax) centralizes vendor-host detectionReconciliation notes (vs the original)
reasoning_protocolmachinery:minimaxis gated byprotocol == ""(likedeepseek), so an explicit protocol override still wins.IsDeepSeekfrom the newhost.goand dropped the now-redundant localisDeepSeekBaseURL(identical semantics).isMiniMaxEntryfallback inEffortCapability/NormalizeEffort(it isn't aReasoningProtocolvalue), leaving the deepseek/openai protocol paths intact.Testing
go build ./...,go vet,go test ./internal/provider/openai ./internal/configall green (MiniMax + the existing DeepSeek/OpenAI/effort-auto tests).Closes #3432
Original work by @lightfront.