mirror of
https://github.com/ollama/ollama.git
synced 2026-04-22 08:45:53 +02:00
* 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 8bff8615f9.
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>
129 lines
4.6 KiB
TypeScript
129 lines
4.6 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { Model } from "@/gotypes";
|
|
import { mergeModels, FEATURED_MODELS } from "@/utils/mergeModels";
|
|
import "@/api";
|
|
|
|
describe("Model merging logic", () => {
|
|
it("should handle cloud models with -cloud suffix", () => {
|
|
const localModels: Model[] = [
|
|
new Model({ model: "gpt-oss:120b-cloud" }),
|
|
new Model({ model: "llama3:latest" }),
|
|
new Model({ model: "mistral:latest" }),
|
|
];
|
|
|
|
const merged = mergeModels(localModels);
|
|
|
|
// First verify cloud models are first and in FEATURED_MODELS order
|
|
const cloudModels = FEATURED_MODELS.filter((m: string) =>
|
|
m.endsWith("cloud"),
|
|
);
|
|
for (let i = 0; i < cloudModels.length; i++) {
|
|
expect(merged[i].model).toBe(cloudModels[i]);
|
|
expect(merged[i].isCloud()).toBe(true);
|
|
}
|
|
|
|
// Then verify non-cloud featured models are next and in FEATURED_MODELS order
|
|
const nonCloudFeatured = FEATURED_MODELS.filter(
|
|
(m: string) => !m.endsWith("cloud"),
|
|
);
|
|
for (let i = 0; i < nonCloudFeatured.length; i++) {
|
|
const model = merged[i + cloudModels.length];
|
|
expect(model.model).toBe(nonCloudFeatured[i]);
|
|
expect(model.isCloud()).toBe(false);
|
|
}
|
|
|
|
// Verify local models are preserved and come after featured models
|
|
const featuredCount = FEATURED_MODELS.length;
|
|
expect(merged[featuredCount].model).toBe("llama3:latest");
|
|
expect(merged[featuredCount + 1].model).toBe("mistral:latest");
|
|
|
|
// Length should be exactly featured models plus our local models
|
|
expect(merged.length).toBe(FEATURED_MODELS.length + 2);
|
|
});
|
|
|
|
it("should hide cloud models when cloud is disabled", () => {
|
|
const localModels: Model[] = [
|
|
new Model({ model: "gpt-oss:120b-cloud" }),
|
|
new Model({ model: "llama3:latest" }),
|
|
new Model({ model: "mistral:latest" }),
|
|
];
|
|
|
|
const merged = mergeModels(localModels, true); // cloud disabled = true
|
|
|
|
// No cloud models should be present
|
|
const cloudModels = merged.filter((m) => m.isCloud());
|
|
expect(cloudModels.length).toBe(0);
|
|
|
|
// Should have non-cloud featured models
|
|
const nonCloudFeatured = FEATURED_MODELS.filter(
|
|
(m) => !m.endsWith("cloud"),
|
|
);
|
|
for (let i = 0; i < nonCloudFeatured.length; i++) {
|
|
const model = merged[i];
|
|
expect(model.model).toBe(nonCloudFeatured[i]);
|
|
expect(model.isCloud()).toBe(false);
|
|
}
|
|
|
|
// Local models should be preserved
|
|
const featuredCount = nonCloudFeatured.length;
|
|
expect(merged[featuredCount].model).toBe("llama3:latest");
|
|
expect(merged[featuredCount + 1].model).toBe("mistral:latest");
|
|
});
|
|
|
|
it("should handle empty input", () => {
|
|
const merged = mergeModels([]);
|
|
|
|
// First verify cloud models are first and in FEATURED_MODELS order
|
|
const cloudModels = FEATURED_MODELS.filter((m) => m.endsWith("cloud"));
|
|
for (let i = 0; i < cloudModels.length; i++) {
|
|
expect(merged[i].model).toBe(cloudModels[i]);
|
|
expect(merged[i].isCloud()).toBe(true);
|
|
}
|
|
|
|
// Then verify non-cloud featured models are next and in FEATURED_MODELS order
|
|
const nonCloudFeatured = FEATURED_MODELS.filter(
|
|
(m) => !m.endsWith("cloud"),
|
|
);
|
|
for (let i = 0; i < nonCloudFeatured.length; i++) {
|
|
const model = merged[i + cloudModels.length];
|
|
expect(model.model).toBe(nonCloudFeatured[i]);
|
|
expect(model.isCloud()).toBe(false);
|
|
}
|
|
|
|
// Length should be exactly FEATURED_MODELS length
|
|
expect(merged.length).toBe(FEATURED_MODELS.length);
|
|
});
|
|
|
|
it("should sort models correctly", () => {
|
|
const localModels: Model[] = [
|
|
new Model({ model: "zephyr:latest" }),
|
|
new Model({ model: "alpha:latest" }),
|
|
new Model({ model: "gpt-oss:120b-cloud" }),
|
|
];
|
|
|
|
const merged = mergeModels(localModels);
|
|
|
|
// First verify cloud models are first and in FEATURED_MODELS order
|
|
const cloudModels = FEATURED_MODELS.filter((m) => m.endsWith("cloud"));
|
|
for (let i = 0; i < cloudModels.length; i++) {
|
|
expect(merged[i].model).toBe(cloudModels[i]);
|
|
expect(merged[i].isCloud()).toBe(true);
|
|
}
|
|
|
|
// Then verify non-cloud featured models are next and in FEATURED_MODELS order
|
|
const nonCloudFeatured = FEATURED_MODELS.filter(
|
|
(m) => !m.endsWith("cloud"),
|
|
);
|
|
for (let i = 0; i < nonCloudFeatured.length; i++) {
|
|
const model = merged[i + cloudModels.length];
|
|
expect(model.model).toBe(nonCloudFeatured[i]);
|
|
expect(model.isCloud()).toBe(false);
|
|
}
|
|
|
|
// Non-featured local models should be at the end in alphabetical order
|
|
const featuredCount = FEATURED_MODELS.length;
|
|
expect(merged[featuredCount].model).toBe("alpha:latest");
|
|
expect(merged[featuredCount + 1].model).toBe("zephyr:latest");
|
|
});
|
|
});
|