add ability to disable cloud (#14221)
* add ability to disable cloud Users can now easily opt-out of cloud inference and web search by setting ``` "disable_ollama_cloud": true ``` in their `~/.ollama/server.json` settings file. After a setting update, the server must be restarted. Alternatively, setting the environment variable `OLLAMA_NO_CLOUD=1` will also disable cloud features. While users previously were able to avoid cloud models by not pulling or `ollama run`ing them, this gives them an easy way to enforce that decision. Any attempt to run a cloud model when cloud is disabled will fail. The app's old "airplane mode" setting, which did a similar thing for hiding cloud models within the app is now unified with this new cloud disabled mode. That setting has been replaced with a "Cloud" toggle, which behind the scenes edits `server.json` and then restarts the server. * gate cloud models across TUI and launch flows when cloud is disabled Block cloud models from being selected, launched, or written to integration configs when cloud mode is turned off: - TUI main menu: open model picker instead of launching with a disabled cloud model - cmd.go: add IsCloudModelDisabled checks for all Selection* paths - LaunchCmd: filter cloud models from saved Editor configs before launch, fall through to picker if none remain - Editor Run() methods (droid, opencode, openclaw): filter cloud models before calling Edit() and persist the cleaned list - Export SaveIntegration, remove SaveIntegrationModel wrapper that was accumulating models instead of replacing them * rename saveIntegration to SaveIntegration in config.go and tests * cmd/config: add --model guarding and empty model list fixes * Update docs/faq.mdx Co-authored-by: Jeffrey Morgan <jmorganca@gmail.com> * Update internal/cloud/policy.go Co-authored-by: Jeffrey Morgan <jmorganca@gmail.com> * Update internal/cloud/policy.go Co-authored-by: Jeffrey Morgan <jmorganca@gmail.com> * Update server/routes.go Co-authored-by: Jeffrey Morgan <jmorganca@gmail.com> * Revert "Update internal/cloud/policy.go" This reverts commit 8bff8615f9b5751fc5c0d1273b07c9de651e07f9. Since this error shows up in other integrations, we want it to be prefixed with Ollama * rename cloud status * more status renaming * fix tests that weren't updated after rename --------- Co-authored-by: ParthSareen <parth.sareen@ollama.com> Co-authored-by: Jeffrey Morgan <jmorganca@gmail.com>
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
package cloud
|
||||
|
||||
import (
|
||||
"github.com/ollama/ollama/envconfig"
|
||||
)
|
||||
|
||||
const DisabledMessagePrefix = "ollama cloud is disabled"
|
||||
|
||||
// Status returns whether cloud is disabled and the source of the decision.
|
||||
// Source is one of: "none", "env", "config", "both".
|
||||
func Status() (disabled bool, source string) {
|
||||
return envconfig.NoCloud(), envconfig.NoCloudSource()
|
||||
}
|
||||
|
||||
func Disabled() bool {
|
||||
return envconfig.NoCloud()
|
||||
}
|
||||
|
||||
func DisabledError(operation string) string {
|
||||
if operation == "" {
|
||||
return DisabledMessagePrefix
|
||||
}
|
||||
|
||||
return DisabledMessagePrefix + ": " + operation
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package cloud
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStatus(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
envValue string
|
||||
configContent string
|
||||
disabled bool
|
||||
source string
|
||||
}{
|
||||
{
|
||||
name: "none",
|
||||
disabled: false,
|
||||
source: "none",
|
||||
},
|
||||
{
|
||||
name: "env only",
|
||||
envValue: "1",
|
||||
disabled: true,
|
||||
source: "env",
|
||||
},
|
||||
{
|
||||
name: "config only",
|
||||
configContent: `{"disable_ollama_cloud": true}`,
|
||||
disabled: true,
|
||||
source: "config",
|
||||
},
|
||||
{
|
||||
name: "both",
|
||||
envValue: "1",
|
||||
configContent: `{"disable_ollama_cloud": true}`,
|
||||
disabled: true,
|
||||
source: "both",
|
||||
},
|
||||
{
|
||||
name: "invalid config ignored",
|
||||
configContent: `{invalid json`,
|
||||
disabled: false,
|
||||
source: "none",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
home := t.TempDir()
|
||||
if tt.configContent != "" {
|
||||
configPath := filepath.Join(home, ".ollama", "server.json")
|
||||
if err := os.MkdirAll(filepath.Dir(configPath), 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile(configPath, []byte(tt.configContent), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
setTestHome(t, home)
|
||||
t.Setenv("OLLAMA_NO_CLOUD", tt.envValue)
|
||||
|
||||
disabled, source := Status()
|
||||
if disabled != tt.disabled {
|
||||
t.Fatalf("disabled: expected %v, got %v", tt.disabled, disabled)
|
||||
}
|
||||
if source != tt.source {
|
||||
t.Fatalf("source: expected %q, got %q", tt.source, source)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDisabledError(t *testing.T) {
|
||||
if got := DisabledError(""); got != DisabledMessagePrefix {
|
||||
t.Fatalf("expected %q, got %q", DisabledMessagePrefix, got)
|
||||
}
|
||||
|
||||
want := DisabledMessagePrefix + ": remote inference is unavailable"
|
||||
if got := DisabledError("remote inference is unavailable"); got != want {
|
||||
t.Fatalf("expected %q, got %q", want, got)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package cloud
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ollama/ollama/envconfig"
|
||||
)
|
||||
|
||||
func setTestHome(t *testing.T, home string) {
|
||||
t.Helper()
|
||||
t.Setenv("HOME", home)
|
||||
t.Setenv("USERPROFILE", home)
|
||||
envconfig.ReloadServerConfig()
|
||||
}
|
||||
Reference in New Issue
Block a user