Move minimax reasoning_split injection to provider factory

Inject reasoning_split at provider creation time to allow user ExtraBody
settings to be preserved
This commit is contained in:
uiyzzi
2026-03-22 20:37:06 +08:00
parent a005e5bb70
commit de0364c8ec
4 changed files with 122 additions and 23 deletions
-21
View File
@@ -1100,27 +1100,6 @@ func TestConfigLogLevelEmpty(t *testing.T) {
}
}
func TestDefaultConfig_MinimaxExtraBody(t *testing.T) {
cfg := DefaultConfig()
var minimaxCfg *ModelConfig
for i := range cfg.ModelList {
if cfg.ModelList[i].Model == "minimax/MiniMax-M2.5" {
minimaxCfg = &cfg.ModelList[i]
break
}
}
if minimaxCfg == nil {
t.Fatal("Minimax model not found in ModelList")
}
if minimaxCfg.ExtraBody == nil {
t.Fatal("Minimax ExtraBody should not be nil")
}
if got, ok := minimaxCfg.ExtraBody["reasoning_split"]; !ok || got != true {
t.Fatalf("Minimax ExtraBody[reasoning_split] = %v, want true", got)
}
}
func TestModelConfig_ExtraBodyRoundTrip(t *testing.T) {
dir := t.TempDir()
cfgPath := filepath.Join(dir, "config.json")
-1
View File
@@ -376,7 +376,6 @@ func DefaultConfig() *Config {
Model: "minimax/MiniMax-M2.5",
APIBase: "https://api.minimaxi.com/v1",
APIKey: "",
ExtraBody: map[string]any{"reasoning_split": true},
},
// LongCat - https://longcat.chat/platform
+26 -1
View File
@@ -117,7 +117,7 @@ func CreateProviderFromConfig(cfg *config.ModelConfig) (LLMProvider, string, err
case "litellm", "openrouter", "groq", "zhipu", "gemini", "nvidia",
"ollama", "moonshot", "shengsuanyun", "deepseek", "cerebras",
"vivgrid", "volcengine", "vllm", "qwen", "qwen-intl", "qwen-international", "dashscope-intl",
"qwen-us", "dashscope-us", "mistral", "avian", "minimax", "longcat", "modelscope", "novita",
"qwen-us", "dashscope-us", "mistral", "avian", "longcat", "modelscope", "novita",
"coding-plan", "alibaba-coding", "qwen-coding":
// All other OpenAI-compatible HTTP providers
if cfg.APIKey == "" && cfg.APIBase == "" {
@@ -136,6 +136,31 @@ func CreateProviderFromConfig(cfg *config.ModelConfig) (LLMProvider, string, err
cfg.ExtraBody,
), modelID, nil
case "minimax":
// Minimax requires reasoning_split: true in the request body
if cfg.APIKey == "" && cfg.APIBase == "" {
return nil, "", fmt.Errorf("api_key or api_base is required for HTTP-based protocol %q", protocol)
}
apiBase := cfg.APIBase
if apiBase == "" {
apiBase = getDefaultAPIBase(protocol)
}
extraBody := cfg.ExtraBody
if extraBody == nil {
extraBody = make(map[string]any)
}
if _, ok := extraBody["reasoning_split"]; !ok {
extraBody["reasoning_split"] = true
}
return NewHTTPProviderWithMaxTokensFieldAndRequestTimeout(
cfg.APIKey,
apiBase,
cfg.Proxy,
cfg.MaxTokensField,
cfg.RequestTimeout,
extraBody,
), modelID, nil
case "anthropic":
if cfg.AuthMethod == "oauth" || cfg.AuthMethod == "token" {
// Use OAuth credentials from auth store
+96
View File
@@ -6,6 +6,7 @@
package providers
import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
@@ -603,3 +604,98 @@ func TestGetDefaultAPIBase_QwenUSAliases(t *testing.T) {
}
}
}
func TestCreateProviderFromConfig_MinimaxInjectsReasoningSplit(t *testing.T) {
var requestBody map[string]any
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"choices":[{"message":{"content":"ok"},"finish_reason":"stop"}]}`))
}))
defer server.Close()
cfg := &config.ModelConfig{
ModelName: "test-minimax",
Model: "minimax/MiniMax-M2.5",
APIKey: "test-key",
APIBase: server.URL,
}
provider, modelID, err := CreateProviderFromConfig(cfg)
if err != nil {
t.Fatalf("CreateProviderFromConfig() error = %v", err)
}
if provider == nil {
t.Fatal("CreateProviderFromConfig() returned nil provider")
}
if modelID != "MiniMax-M2.5" {
t.Errorf("modelID = %q, want %q", modelID, "MiniMax-M2.5")
}
_, err = provider.Chat(
t.Context(),
[]Message{{Role: "user", Content: "hi"}},
nil,
modelID,
nil,
)
if err != nil {
t.Fatalf("Chat() error = %v", err)
}
// Verify reasoning_split is automatically injected
if got, ok := requestBody["reasoning_split"]; !ok || got != true {
t.Fatalf("reasoning_split = %v, want true", got)
}
}
func TestCreateProviderFromConfig_MinimaxPreservesUserExtraBody(t *testing.T) {
var requestBody map[string]any
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{"choices":[{"message":{"content":"ok"},"finish_reason":"stop"}]}`))
}))
defer server.Close()
cfg := &config.ModelConfig{
ModelName: "test-minimax-custom",
Model: "minimax/MiniMax-M2.5",
APIKey: "test-key",
APIBase: server.URL,
ExtraBody: map[string]any{"custom_field": "test"},
}
provider, modelID, err := CreateProviderFromConfig(cfg)
if err != nil {
t.Fatalf("CreateProviderFromConfig() error = %v", err)
}
_, err = provider.Chat(
t.Context(),
[]Message{{Role: "user", Content: "hi"}},
nil,
modelID,
nil,
)
if err != nil {
t.Fatalf("Chat() error = %v", err)
}
// Verify reasoning_split is automatically injected
if got, ok := requestBody["reasoning_split"]; !ok || got != true {
t.Fatalf("reasoning_split = %v, want true", got)
}
// Verify user's custom field is preserved
if got, ok := requestBody["custom_field"]; !ok || got != "test" {
t.Fatalf("custom_field = %v, want test", got)
}
}