From 598b74d42c8cced0aad5fea90592fc23fe108c2c Mon Sep 17 00:00:00 2001 From: Parth Sareen Date: Thu, 12 Feb 2026 14:29:50 -0800 Subject: [PATCH] cmd/config: add minimax-m2.5 (#14223) --- cmd/config/integrations.go | 23 ++++++++++++++++++++++- cmd/config/integrations_test.go | 30 +++++++++++++++--------------- cmd/config/opencode.go | 19 ------------------- 3 files changed, 37 insertions(+), 35 deletions(-) diff --git a/cmd/config/integrations.go b/cmd/config/integrations.go index e9554a8ab..f06659672 100644 --- a/cmd/config/integrations.go +++ b/cmd/config/integrations.go @@ -63,12 +63,33 @@ var integrations = map[string]Runner{ // recommendedModels are shown when the user has no models or as suggestions. // Order matters: local models first, then cloud models. var recommendedModels = []ModelItem{ + {Name: "minimax-m2.5:cloud", Description: "Fast, efficient coding and real-world productivity", Recommended: true}, {Name: "glm-5:cloud", Description: "Reasoning and code generation", Recommended: true}, {Name: "kimi-k2.5:cloud", Description: "Multimodal reasoning with subagents", Recommended: true}, {Name: "glm-4.7-flash", Description: "Reasoning and code generation locally", Recommended: true}, {Name: "qwen3:8b", Description: "Efficient all-purpose assistant", Recommended: true}, } +// cloudModelLimits maps cloud model base names to their token limits. +// TODO(parthsareen): grab context/output limits from model info instead of hardcoding +var cloudModelLimits = map[string]cloudModelLimit{ + "minimax-m2.5": {Context: 204_800, Output: 128_000}, + "cogito-2.1:671b": {Context: 163_840, Output: 65_536}, + "deepseek-v3.1:671b": {Context: 163_840, Output: 163_840}, + "deepseek-v3.2": {Context: 163_840, Output: 65_536}, + "glm-4.6": {Context: 202_752, Output: 131_072}, + "glm-4.7": {Context: 202_752, Output: 131_072}, + "gpt-oss:120b": {Context: 131_072, Output: 131_072}, + "gpt-oss:20b": {Context: 131_072, Output: 131_072}, + "kimi-k2:1t": {Context: 262_144, Output: 262_144}, + "kimi-k2.5": {Context: 262_144, Output: 262_144}, + "kimi-k2-thinking": {Context: 262_144, Output: 262_144}, + "nemotron-3-nano:30b": {Context: 1_048_576, Output: 131_072}, + "qwen3-coder:480b": {Context: 262_144, Output: 65_536}, + "qwen3-coder-next": {Context: 262_144, Output: 32_768}, + "qwen3-next:80b": {Context: 262_144, Output: 32_768}, +} + // recommendedVRAM maps local recommended models to their approximate VRAM requirement. var recommendedVRAM = map[string]string{ "glm-4.7-flash": "~25GB", @@ -1062,7 +1083,7 @@ func buildModelList(existing []modelInfo, preChecked []string, current string) ( if vram := recommendedVRAM[items[i].Name]; vram != "" { parts = append(parts, vram) } - parts = append(parts, "install?") + parts = append(parts, "(not downloaded)") items[i].Description = strings.Join(parts, ", ") } } diff --git a/cmd/config/integrations_test.go b/cmd/config/integrations_test.go index f08a73f94..4ff07681a 100644 --- a/cmd/config/integrations_test.go +++ b/cmd/config/integrations_test.go @@ -394,14 +394,14 @@ func names(items []ModelItem) []string { func TestBuildModelList_NoExistingModels(t *testing.T) { items, _, _, _ := buildModelList(nil, nil, "") - want := []string{"glm-5:cloud", "kimi-k2.5:cloud", "glm-4.7-flash", "qwen3:8b"} + want := []string{"minimax-m2.5:cloud", "glm-5:cloud", "kimi-k2.5:cloud", "glm-4.7-flash", "qwen3:8b"} if diff := cmp.Diff(want, names(items)); diff != "" { t.Errorf("with no existing models, items should be recommended in order (-want +got):\n%s", diff) } for _, item := range items { - if !strings.HasSuffix(item.Description, "install?") { - t.Errorf("item %q should have description ending with 'install?', got %q", item.Name, item.Description) + if !strings.HasSuffix(item.Description, "(not downloaded)") { + t.Errorf("item %q should have description ending with '(not downloaded)', got %q", item.Name, item.Description) } } } @@ -416,7 +416,7 @@ func TestBuildModelList_OnlyLocalModels_CloudRecsAtBottom(t *testing.T) { got := names(items) // Recommended pinned at top (local recs first, then cloud recs when only-local), then installed non-recs - want := []string{"glm-4.7-flash", "qwen3:8b", "glm-5:cloud", "kimi-k2.5:cloud", "llama3.2", "qwen2.5"} + want := []string{"glm-4.7-flash", "qwen3:8b", "minimax-m2.5:cloud", "glm-5:cloud", "kimi-k2.5:cloud", "llama3.2", "qwen2.5"} if diff := cmp.Diff(want, got); diff != "" { t.Errorf("recs pinned at top, local recs before cloud recs (-want +got):\n%s", diff) } @@ -432,7 +432,7 @@ func TestBuildModelList_BothCloudAndLocal_RegularSort(t *testing.T) { got := names(items) // All recs pinned at top (cloud before local in mixed case), then non-recs - want := []string{"glm-5:cloud", "kimi-k2.5:cloud", "glm-4.7-flash", "qwen3:8b", "llama3.2"} + want := []string{"minimax-m2.5:cloud", "glm-5:cloud", "kimi-k2.5:cloud", "glm-4.7-flash", "qwen3:8b", "llama3.2"} if diff := cmp.Diff(want, got); diff != "" { t.Errorf("recs pinned at top, cloud recs first in mixed case (-want +got):\n%s", diff) } @@ -463,12 +463,12 @@ func TestBuildModelList_ExistingRecommendedMarked(t *testing.T) { for _, item := range items { switch item.Name { case "glm-4.7-flash", "glm-5:cloud": - if strings.HasSuffix(item.Description, "install?") { - t.Errorf("installed recommended %q should not have 'install?' suffix, got %q", item.Name, item.Description) + if strings.HasSuffix(item.Description, "(not downloaded)") { + t.Errorf("installed recommended %q should not have '(not downloaded)' suffix, got %q", item.Name, item.Description) } - case "kimi-k2.5:cloud", "qwen3:8b": - if !strings.HasSuffix(item.Description, "install?") { - t.Errorf("non-installed recommended %q should have 'install?' suffix, got %q", item.Name, item.Description) + case "minimax-m2.5:cloud", "kimi-k2.5:cloud", "qwen3:8b": + if !strings.HasSuffix(item.Description, "(not downloaded)") { + t.Errorf("non-installed recommended %q should have '(not downloaded)' suffix, got %q", item.Name, item.Description) } } } @@ -486,7 +486,7 @@ func TestBuildModelList_ExistingCloudModelsNotPushedToBottom(t *testing.T) { // glm-4.7-flash and glm-5:cloud are installed so they sort normally; // kimi-k2.5:cloud and qwen3:8b are not installed so they go to the bottom // All recs: cloud first in mixed case, then local, in rec order within each - want := []string{"glm-5:cloud", "kimi-k2.5:cloud", "glm-4.7-flash", "qwen3:8b"} + want := []string{"minimax-m2.5:cloud", "glm-5:cloud", "kimi-k2.5:cloud", "glm-4.7-flash", "qwen3:8b"} if diff := cmp.Diff(want, got); diff != "" { t.Errorf("all recs, cloud first in mixed case (-want +got):\n%s", diff) } @@ -504,15 +504,15 @@ func TestBuildModelList_HasRecommendedCloudModel_OnlyNonInstalledAtBottom(t *tes // kimi-k2.5:cloud is installed so it sorts normally; // the rest of the recommendations are not installed so they go to the bottom // All recs pinned at top (cloud first in mixed case), then non-recs - want := []string{"glm-5:cloud", "kimi-k2.5:cloud", "glm-4.7-flash", "qwen3:8b", "llama3.2"} + want := []string{"minimax-m2.5:cloud", "glm-5:cloud", "kimi-k2.5:cloud", "glm-4.7-flash", "qwen3:8b", "llama3.2"} if diff := cmp.Diff(want, got); diff != "" { t.Errorf("recs pinned at top, cloud first in mixed case (-want +got):\n%s", diff) } for _, item := range items { if !slices.Contains([]string{"kimi-k2.5:cloud", "llama3.2"}, item.Name) { - if !strings.HasSuffix(item.Description, "install?") { - t.Errorf("non-installed %q should have 'install?' suffix, got %q", item.Name, item.Description) + if !strings.HasSuffix(item.Description, "(not downloaded)") { + t.Errorf("non-installed %q should have '(not downloaded)' suffix, got %q", item.Name, item.Description) } } } @@ -648,7 +648,7 @@ func TestBuildModelList_RecsAboveNonRecs(t *testing.T) { lastRecIdx := -1 firstNonRecIdx := len(got) for i, name := range got { - isRec := name == "glm-4.7-flash" || name == "qwen3:8b" || name == "glm-5:cloud" || name == "kimi-k2.5:cloud" + isRec := name == "glm-4.7-flash" || name == "qwen3:8b" || name == "minimax-m2.5:cloud" || name == "glm-5:cloud" || name == "kimi-k2.5:cloud" if isRec && i > lastRecIdx { lastRecIdx = i } diff --git a/cmd/config/opencode.go b/cmd/config/opencode.go index 0fe9f8a8f..1ca4aa2cf 100644 --- a/cmd/config/opencode.go +++ b/cmd/config/opencode.go @@ -24,25 +24,6 @@ type cloudModelLimit struct { Output int } -// cloudModelLimits maps cloud model base names to their token limits. -// TODO(parthsareen): grab context/output limits from model info instead of hardcoding -var cloudModelLimits = map[string]cloudModelLimit{ - "cogito-2.1:671b": {Context: 163_840, Output: 65_536}, - "deepseek-v3.1:671b": {Context: 163_840, Output: 163_840}, - "deepseek-v3.2": {Context: 163_840, Output: 65_536}, - "glm-4.6": {Context: 202_752, Output: 131_072}, - "glm-4.7": {Context: 202_752, Output: 131_072}, - "gpt-oss:120b": {Context: 131_072, Output: 131_072}, - "gpt-oss:20b": {Context: 131_072, Output: 131_072}, - "kimi-k2:1t": {Context: 262_144, Output: 262_144}, - "kimi-k2.5": {Context: 262_144, Output: 262_144}, - "kimi-k2-thinking": {Context: 262_144, Output: 262_144}, - "nemotron-3-nano:30b": {Context: 1_048_576, Output: 131_072}, - "qwen3-coder:480b": {Context: 262_144, Output: 65_536}, - "qwen3-coder-next": {Context: 262_144, Output: 32_768}, - "qwen3-next:80b": {Context: 262_144, Output: 32_768}, -} - // lookupCloudModelLimit returns the token limits for a cloud model. // It tries the exact name first, then strips the ":cloud" suffix. func lookupCloudModelLimit(name string) (cloudModelLimit, bool) {