From 465d124183e5a57cbd9a301b91c2bb633d353935 Mon Sep 17 00:00:00 2001 From: Parth Sareen Date: Sat, 24 Jan 2026 21:42:56 -0500 Subject: [PATCH] cmd: fix opencode config (#13894) --- cmd/config/opencode.go | 31 +++++++++++++--- cmd/config/opencode_test.go | 70 +++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 5 deletions(-) diff --git a/cmd/config/opencode.go b/cmd/config/opencode.go index 6ccf45145..ace67f2e4 100644 --- a/cmd/config/opencode.go +++ b/cmd/config/opencode.go @@ -105,17 +105,26 @@ func (o *OpenCode) Edit(modelList []string) error { for name, cfg := range models { if cfgMap, ok := cfg.(map[string]any); ok { - if displayName, ok := cfgMap["name"].(string); ok { - if strings.HasSuffix(displayName, "[Ollama]") && !selectedSet[name] { - delete(models, name) - } + if isOllamaModel(cfgMap) && !selectedSet[name] { + delete(models, name) } } } for _, model := range modelList { + if existing, ok := models[model].(map[string]any); ok { + // migrate existing models without _launch marker + if isOllamaModel(existing) { + existing["_launch"] = true + if name, ok := existing["name"].(string); ok { + existing["name"] = strings.TrimSuffix(name, " [Ollama]") + } + } + continue + } models[model] = map[string]any{ - "name": fmt.Sprintf("%s [Ollama]", model), + "name": model, + "_launch": true, } } @@ -201,3 +210,15 @@ func (o *OpenCode) Models() []string { slices.Sort(keys) return keys } + +// isOllamaModel reports whether a model config entry is managed by us +func isOllamaModel(cfg map[string]any) bool { + if v, ok := cfg["_launch"].(bool); ok && v { + return true + } + // previously used [Ollama] as a suffix for the model managed by ollama launch + if name, ok := cfg["name"].(string); ok { + return strings.HasSuffix(name, "[Ollama]") + } + return false +} diff --git a/cmd/config/opencode_test.go b/cmd/config/opencode_test.go index 524dfc59e..de8a9efd2 100644 --- a/cmd/config/opencode_test.go +++ b/cmd/config/opencode_test.go @@ -161,6 +161,76 @@ func TestOpenCodeEdit(t *testing.T) { assertOpenCodeModelNotExists(t, configPath, "mistral") }) + t.Run("preserve user customizations on managed models", func(t *testing.T) { + cleanup() + if err := o.Edit([]string{"llama3.2"}); err != nil { + t.Fatal(err) + } + + // Add custom fields to the model entry (simulating user edits) + data, _ := os.ReadFile(configPath) + var cfg map[string]any + json.Unmarshal(data, &cfg) + provider := cfg["provider"].(map[string]any) + ollama := provider["ollama"].(map[string]any) + models := ollama["models"].(map[string]any) + entry := models["llama3.2"].(map[string]any) + entry["_myPref"] = "custom-value" + entry["_myNum"] = 42 + configData, _ := json.MarshalIndent(cfg, "", " ") + os.WriteFile(configPath, configData, 0o644) + + // Re-run Edit — should preserve custom fields + if err := o.Edit([]string{"llama3.2"}); err != nil { + t.Fatal(err) + } + + data, _ = os.ReadFile(configPath) + json.Unmarshal(data, &cfg) + provider = cfg["provider"].(map[string]any) + ollama = provider["ollama"].(map[string]any) + models = ollama["models"].(map[string]any) + entry = models["llama3.2"].(map[string]any) + + if entry["_myPref"] != "custom-value" { + t.Errorf("_myPref was lost: got %v", entry["_myPref"]) + } + if entry["_myNum"] != float64(42) { + t.Errorf("_myNum was lost: got %v", entry["_myNum"]) + } + if v, ok := entry["_launch"].(bool); !ok || !v { + t.Errorf("_launch marker missing or false: got %v", entry["_launch"]) + } + }) + + t.Run("migrate legacy [Ollama] suffix entries", func(t *testing.T) { + cleanup() + // Write a config with a legacy entry (has [Ollama] suffix but no _launch marker) + os.MkdirAll(configDir, 0o755) + os.WriteFile(configPath, []byte(`{"provider":{"ollama":{"models":{"llama3.2":{"name":"llama3.2 [Ollama]"}}}}}`), 0o644) + + if err := o.Edit([]string{"llama3.2"}); err != nil { + t.Fatal(err) + } + + data, _ := os.ReadFile(configPath) + var cfg map[string]any + json.Unmarshal(data, &cfg) + provider := cfg["provider"].(map[string]any) + ollama := provider["ollama"].(map[string]any) + models := ollama["models"].(map[string]any) + entry := models["llama3.2"].(map[string]any) + + // _launch marker should be added + if v, ok := entry["_launch"].(bool); !ok || !v { + t.Errorf("_launch marker not added during migration: got %v", entry["_launch"]) + } + // [Ollama] suffix should be stripped + if name, ok := entry["name"].(string); !ok || name != "llama3.2" { + t.Errorf("name suffix not stripped: got %q", entry["name"]) + } + }) + t.Run("remove model preserves non-ollama models", func(t *testing.T) { cleanup() os.MkdirAll(configDir, 0o755)