fix(ollama): carry real Ollama Cloud context windows and capabilities (#126653)

* fix(ollama): carry real Ollama Cloud context windows and capabilities

The ollama-cloud catalog still described three models (minimax-m2.7, glm-5.1,
glm-5.2) plus a retired kimi-k2.5. Every other cloud model — including kimi-k3,
the current flagship — was absent, so core synthesized it at the generic
DEFAULT_CONTEXT_TOKENS of 200k. A kimi-k3 session therefore ran with 200,000 of
its real 1,048,576 token window: 80% of the context silently discarded, with no
warning anywhere in the product.

Describe the full current cloud lineup with context windows, input modalities
and reasoning support verified against live /api/show and the ollama.com model
pages. Only mistral-large-3 lacks thinking (vision + tools + cloud only).

Suffixed refs shared the same defect from the other side: the default lookup is
keyed bare, so `kimi-k3:cloud` missed it and fell to the 128k plugin default.
A hardcoded glm-5.2 literal in buildOllamaModelDefinition had been papering over
that for exactly one model; replace it with a lookup through the canonical
cloud-id normalizer, which model-reasoning.ts already owned, and drop the
duplicate spelling of that helper.

* fix(ollama): cover exact cloud catalog variants

* fix(ollama): remove invalid cloud aliases

* fix(ollama): default Ollama Cloud onboarding to minimax-m3

Cloud onboarding derives `defaultModel` from the first entry of
OLLAMA_CLOUD_DEFAULT_MODELS, so array order silently owned the out-of-box
model choice. Put minimax-m3 (524,288 ctx, thinking + tools + vision) at
index 0, add it to the bundled rows it was missing from, and document the
ordering contract at the declaration.

Pin the resolved default id in the cloud setup tests so a reorder cannot
move it unnoticed, and align the provider doc's onboarding default and
fallback row list.

Claude-Session: https://claude.ai/code/session_01QXUQuDVataA5o16kxNnmoX

* fix(ollama): preserve default and shared model contracts

* test(ollama): consolidate cloud setup capability expectations

---------

Co-authored-by: VACInc <3279061+VACInc@users.noreply.github.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
Vito Cappello
2026-08-20 11:46:39 -04:00
committed by GitHub
parent e50f73d331
commit 4fdfb8b1bf
9 changed files with 548 additions and 85 deletions
+5 -4
View File
@@ -71,10 +71,11 @@ openclaw models set ollama-cloud/kimi-k2.6
```
Hosted ids in the live catalog include `deepseek-v4-flash`, `glm-5.2`,
`gpt-oss:20b`, `kimi-k2.6`, and `minimax-m2.7`. When live discovery returns
nothing, OpenClaw falls back to the bundled rows `minimax-m2.7`, `glm-5.1`,
and `glm-5.2`. The retiring `kimi-k2.5` model is hidden from model pickers but
remains selectable by exact reference until Ollama retires it on July 31, 2026.
`gpt-oss:20b`, `kimi-k3`, and `minimax-m3`. When live discovery returns
nothing, OpenClaw falls back to the bundled rows `minimax-m2.7`, `minimax-m3`,
`kimi-k3`, `glm-5.1`, and `glm-5.2`. Retired `kimi-k2.5` remains marked
deprecated for existing exact references, but is no longer a current hosted
model.
Model ids are cloud catalog ids, not local pull names. If a model name works in
a local Ollama host but is absent from the hosted catalog, use the `ollama`
+16
View File
@@ -2119,6 +2119,8 @@ describe("ollama plugin", () => {
expect(result.provider.baseUrl).toBe("https://ollama.com");
expect(result.provider.models?.map((model: { id: string }) => model.id)).toEqual([
"minimax-m2.7",
"minimax-m3",
"kimi-k3",
"glm-5.1",
"glm-5.2",
]);
@@ -2130,6 +2132,20 @@ describe("ollama plugin", () => {
input: ["text"],
compat: { supportsTools: true, supportsUsageInStreaming: true },
}),
expect.objectContaining({
id: "minimax-m3",
contextWindow: 524_288,
reasoning: true,
input: ["text", "image"],
compat: { supportsTools: true, supportsUsageInStreaming: true },
}),
expect.objectContaining({
id: "kimi-k3",
contextWindow: 1_048_576,
reasoning: true,
input: ["text", "image"],
compat: { supportsTools: true, supportsUsageInStreaming: true },
}),
expect.objectContaining({
id: "glm-5.1",
contextWindow: 202_752,
+377 -41
View File
@@ -8,10 +8,7 @@
"onStartup": true
},
"enabledByDefault": true,
"providers": [
"ollama",
"ollama-cloud"
],
"providers": ["ollama", "ollama-cloud"],
"providerCatalogEntry": "./provider-discovery.ts",
"providerRequest": {
"providers": {
@@ -33,25 +30,17 @@
}
}
},
"syntheticAuthRefs": [
"ollama"
],
"nonSecretAuthMarkers": [
"ollama-local"
],
"syntheticAuthRefs": ["ollama"],
"nonSecretAuthMarkers": ["ollama-local"],
"setup": {
"providers": [
{
"id": "ollama",
"envVars": [
"OLLAMA_API_KEY"
]
"envVars": ["OLLAMA_API_KEY"]
},
{
"id": "ollama-cloud",
"envVars": [
"OLLAMA_API_KEY"
]
"envVars": ["OLLAMA_API_KEY"]
}
]
},
@@ -100,10 +89,7 @@
"name": "kimi-k2.5",
"status": "deprecated",
"reasoning": true,
"input": [
"text",
"image"
],
"input": ["text", "image"],
"cost": {
"input": 0,
"output": 0,
@@ -118,19 +104,199 @@
}
},
{
"id": "minimax-m2.7",
"name": "minimax-m2.7",
"id": "kimi-k2.6",
"name": "kimi-k2.6",
"reasoning": true,
"input": [
"text"
],
"input": ["text", "image"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 196608,
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true
}
},
{
"id": "kimi-k2.7-code",
"name": "kimi-k2.7-code",
"reasoning": true,
"input": ["text", "image"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true
}
},
{
"id": "kimi-k3",
"name": "kimi-k3",
"reasoning": true,
"input": ["text", "image"],
"cost": {
"input": 3,
"output": 15,
"cacheRead": 0.3
},
"contextWindow": 1048576,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true,
"codeMode": "capable"
}
},
{
"id": "deepseek-v4-flash",
"name": "deepseek-v4-flash",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 1048576,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true,
"codeMode": "capable"
}
},
{
"id": "deepseek-v4-flash:0731",
"name": "deepseek-v4-flash:0731",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 1048576,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true
}
},
{
"id": "deepseek-v4-flash:preview",
"name": "deepseek-v4-flash:preview",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 1048576,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true
}
},
{
"id": "deepseek-v4-pro",
"name": "deepseek-v4-pro",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 1048576,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true,
"codeMode": "capable"
}
},
{
"id": "deepseek-v4-pro:0813",
"name": "deepseek-v4-pro:0813",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 1048576,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true
}
},
{
"id": "deepseek-v4-pro:preview",
"name": "deepseek-v4-pro:preview",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 524288,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true
}
},
{
"id": "gemma4",
"name": "gemma4",
"reasoning": true,
"input": ["text", "image"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true
}
},
{
"id": "gemma4:31b",
"name": "gemma4:31b",
"reasoning": true,
"input": ["text", "image"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
@@ -141,9 +307,7 @@
"id": "glm-5.1",
"name": "glm-5.1",
"reasoning": true,
"input": [
"text"
],
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
@@ -162,9 +326,7 @@
"id": "glm-5.2",
"name": "glm-5.2",
"reasoning": true,
"input": [
"text"
],
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
@@ -178,6 +340,186 @@
"supportsUsageInStreaming": true,
"codeMode": "capable"
}
},
{
"id": "gpt-oss:120b",
"name": "gpt-oss:120b",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 131072,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true
}
},
{
"id": "gpt-oss:20b",
"name": "gpt-oss:20b",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 131072,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true
}
},
{
"id": "minimax-m2.7",
"name": "minimax-m2.7",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 196608,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true
}
},
{
"id": "minimax-m3",
"name": "minimax-m3",
"reasoning": true,
"input": ["text", "image"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 524288,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true
}
},
{
"id": "mistral-large-3:675b",
"name": "mistral-large-3:675b",
"reasoning": false,
"input": ["text", "image"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true
}
},
{
"id": "nemotron-3-nano:30b",
"name": "nemotron-3-nano:30b",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true
}
},
{
"id": "nemotron-3-super",
"name": "nemotron-3-super",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true
}
},
{
"id": "nemotron-3-ultra",
"name": "nemotron-3-ultra",
"reasoning": true,
"input": ["text"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true
}
},
{
"id": "qwen3.5",
"name": "qwen3.5",
"reasoning": true,
"input": ["text", "image"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true
}
},
{
"id": "qwen3.5:397b",
"name": "qwen3.5:397b",
"reasoning": true,
"input": ["text", "image"],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 8192,
"compat": {
"supportsTools": true,
"supportsUsageInStreaming": true
}
}
]
}
@@ -188,15 +530,9 @@
}
},
"contracts": {
"embeddingProviders": [
"ollama"
],
"tools": [
"node_inference"
],
"webSearchProviders": [
"ollama"
]
"embeddingProviders": ["ollama"],
"tools": ["node_inference"],
"webSearchProviders": ["ollama"]
},
"configSchema": {
"type": "object",
+23
View File
@@ -5,12 +5,27 @@ const OLLAMA_DOCKER_HOST_BASE_URL = "http://host.docker.internal:11434";
export const OLLAMA_CLOUD_BASE_URL = "https://ollama.com";
export const OLLAMA_CLOUD_PROVIDER_ID = "ollama-cloud";
export const OLLAMA_GLM52_CLOUD_MODEL_ID = "glm-5.2";
/**
* Order is a contract: cloud onboarding merges this list ahead of live discovery and takes
* the first name as `defaultModel` (`setup.runtime.ts`). Reordering this array changes what
* every new setup selects, so keep the intended default at index 0.
*/
export const OLLAMA_CLOUD_DEFAULT_MODELS = [
{
id: "minimax-m2.7",
contextWindow: 196_608,
capabilities: ["completion", "thinking", "tools"],
},
{
id: "minimax-m3",
contextWindow: 524_288,
capabilities: ["completion", "thinking", "tools", "vision"],
},
{
id: "kimi-k3",
contextWindow: 1_048_576,
capabilities: ["completion", "thinking", "tools", "vision"],
},
{
id: "glm-5.1",
contextWindow: 202_752,
@@ -23,6 +38,14 @@ export const OLLAMA_CLOUD_DEFAULT_MODELS = [
},
] as const;
/** Cloud models are referenced bare, `:cloud`-suffixed, and `-cloud`-suffixed. */
export function normalizeOllamaCloudModelId(modelId: string): string {
return modelId
.trim()
.toLowerCase()
.replace(/(?::cloud|-cloud)$/, "");
}
export const OLLAMA_DEFAULT_CONTEXT_WINDOW = 128000;
export const OLLAMA_LOCAL_CONTEXT_TOKENS = 32_768;
export const OLLAMA_DEFAULT_MAX_TOKENS = 8192;
+2 -7
View File
@@ -1,14 +1,9 @@
// Ollama plugin module owns model-specific native thinking contracts.
import { normalizeOllamaCloudModelId } from "./defaults.js";
export function supportsOllamaCloudFullThinkingEffort(modelId: string): boolean {
// These hosted families accept low, medium, high, and max even when
// lightweight catalog projections omit their reasoning metadata.
const normalized = normalizeOllamaCloudModelId(modelId);
return normalized === "glm-5.2" || /^deepseek-v4-(?:flash|pro)$/.test(normalized);
}
function normalizeOllamaCloudModelId(modelId: string): string {
return modelId
.trim()
.toLowerCase()
.replace(/(?::cloud|-cloud)$/, "");
}
@@ -1,10 +1,12 @@
// Ollama tests cover provider models plugin behavior.
import { once } from "node:events";
import { readFileSync } from "node:fs";
import { createServer } from "node:http";
import type { Socket } from "node:net";
import { expectDefined } from "@openclaw/normalization-core";
import { jsonResponse, requestBodyText, requestUrl } from "openclaw/plugin-sdk/test-env";
import { afterEach, describe, expect, it, vi } from "vitest";
import { OLLAMA_DEFAULT_CONTEXT_WINDOW } from "./defaults.js";
import {
buildOllamaProvider,
buildOllamaModelDefinition,
@@ -51,6 +53,68 @@ describe("ollama provider models", () => {
expect(resolveOllamaApiBase("http://127.0.0.1:11434///")).toBe("http://127.0.0.1:11434");
});
it("declares every exact currently served Ollama Cloud model id", () => {
const manifest = JSON.parse(
readFileSync(new URL("../openclaw.plugin.json", import.meta.url), "utf8"),
) as {
modelCatalog: {
providers: Record<
string,
{
models: Array<{
id: string;
status?: string;
contextWindow: number;
input: string[];
reasoning: boolean;
cost?: {
input?: number;
output?: number;
cacheRead?: number;
};
}>;
}
>;
};
};
const models = manifest.modelCatalog.providers["ollama-cloud"]?.models ?? [];
const declared = new Map(models.map((model) => [model.id, model]));
const servedModels = [
["glm-5.1", 202_752, ["text"], true],
["glm-5.2", 1_000_000, ["text"], true],
["minimax-m2.7", 196_608, ["text"], true],
["deepseek-v4-flash", 1_048_576, ["text"], true],
["deepseek-v4-flash:0731", 1_048_576, ["text"], true],
["deepseek-v4-flash:preview", 1_048_576, ["text"], true],
["deepseek-v4-pro", 1_048_576, ["text"], true],
["deepseek-v4-pro:0813", 1_048_576, ["text"], true],
["deepseek-v4-pro:preview", 524_288, ["text"], true],
["gemma4", 262_144, ["text", "image"], true],
["gemma4:31b", 262_144, ["text", "image"], true],
["gpt-oss:120b", 131_072, ["text"], true],
["gpt-oss:20b", 131_072, ["text"], true],
["kimi-k2.6", 262_144, ["text", "image"], true],
["kimi-k2.7-code", 262_144, ["text", "image"], true],
["kimi-k3", 1_048_576, ["text", "image"], true],
["minimax-m3", 524_288, ["text", "image"], true],
["mistral-large-3:675b", 262_144, ["text", "image"], false],
["nemotron-3-nano:30b", 262_144, ["text"], true],
["nemotron-3-super", 262_144, ["text"], true],
["nemotron-3-ultra", 262_144, ["text"], true],
["qwen3.5", 262_144, ["text", "image"], true],
["qwen3.5:397b", 262_144, ["text", "image"], true],
] as const;
servedModels.forEach(([id, contextWindow, input, reasoning]) => {
expect(declared.get(id)).toMatchObject({ contextWindow, input, reasoning });
});
expect([...declared.keys()].toSorted()).toEqual(
[...servedModels.map(([id]) => id), "kimi-k2.5"].toSorted(),
);
expect(declared.get("kimi-k2.5")).toMatchObject({ status: "deprecated" });
expect(declared.get("kimi-k3")?.cost).toEqual({ input: 3, output: 15, cacheRead: 0.3 });
});
it("inspects local models using Ollama's canonical model request field", async () => {
const fetchMock = vi.fn(async (_input: string | URL | Request, _init?: RequestInit) =>
jsonResponse({ model_info: {} }),
@@ -249,6 +313,22 @@ describe("ollama provider models", () => {
);
});
it("resolves known cloud context windows for bare and :cloud model refs", () => {
// A suffixed ref must not silently drop to the generic default when live
// inspection is unavailable; both spellings name the same cloud model.
for (const modelId of ["kimi-k3", "kimi-k3:cloud"]) {
expect(buildOllamaModelDefinition(modelId)).toEqual(
expect.objectContaining({ id: modelId, contextWindow: 1_048_576 }),
);
}
});
it("keeps the generic default for cloud models with no known context window", () => {
expect(buildOllamaModelDefinition("not-a-known-model:cloud")).toEqual(
expect.objectContaining({ contextWindow: OLLAMA_DEFAULT_CONTEXT_WINDOW }),
);
});
it("uses Modelfile num_ctx when it expands the discovered context window", async () => {
const models: OllamaTagModel[] = [{ name: "llama3-32k:latest" }];
const fetchMock = vi.fn(async () =>
+15 -6
View File
@@ -15,6 +15,7 @@ import {
OLLAMA_DEFAULT_COST,
OLLAMA_DEFAULT_MAX_TOKENS,
OLLAMA_LOCAL_CONTEXT_TOKENS,
normalizeOllamaCloudModelId,
} from "./defaults.js";
import { supportsOllamaCloudFullThinkingEffort } from "./model-reasoning.js";
@@ -340,6 +341,18 @@ export function isOllamaCloudModel(modelName: string | undefined): boolean {
return isCloudModelRef(modelName);
}
/**
* Cloud models are referenced both bare (`kimi-k3`) and suffixed (`kimi-k3:cloud`).
* Both spellings must reach the same known context window, or a suffixed ref silently
* falls back to the generic default whenever live inspection is unavailable.
*/
function resolveOllamaCloudDefaultModel(
modelId: string,
): (typeof OLLAMA_CLOUD_DEFAULT_MODELS)[number] | undefined {
const normalized = normalizeOllamaCloudModelId(modelId);
return OLLAMA_CLOUD_DEFAULT_MODELS.find((model) => model.id === normalized);
}
export function isReasoningModelHeuristic(modelId: string): boolean {
return /r1|reasoning|think|reason/i.test(modelId);
}
@@ -371,12 +384,8 @@ export function buildOllamaModelDefinition(
cost: OLLAMA_DEFAULT_COST,
contextWindow:
contextWindow ??
(modelId
.trim()
.toLowerCase()
.replace(/:cloud$/, "") === "glm-5.2"
? 1_000_000
: OLLAMA_DEFAULT_CONTEXT_WINDOW),
resolveOllamaCloudDefaultModel(modelId)?.contextWindow ??
OLLAMA_DEFAULT_CONTEXT_WINDOW,
maxTokens: OLLAMA_DEFAULT_MAX_TOKENS,
compat,
};
@@ -1,6 +1,6 @@
import { formatErrorMessage } from "openclaw/plugin-sdk/error-runtime";
import { selectPreferredLocalModelId } from "openclaw/plugin-sdk/provider-model-shared";
import { OLLAMA_CLOUD_DEFAULT_MODELS } from "./defaults.js";
import { normalizeOllamaCloudModelId, OLLAMA_CLOUD_DEFAULT_MODELS } from "./defaults.js";
import {
buildDefaultOllamaCloudModelDefinition,
buildOllamaModelDefinition,
@@ -126,8 +126,13 @@ export function buildOllamaModelsConfig(
) {
return modelNames.map((name) => {
const discovered = discoveredModelsByName?.get(name);
const defaultModel = defaultModels.find((model) => model.id === name);
if (defaultModel && !discovered) {
// Cloud suggestions arrive suffixed (`kimi-k3:cloud`); the default table is keyed bare.
// Match through the suffix for context/capabilities, but keep the requested id: the
// suffixed spelling is what gets written into config.
const defaultModel = defaultModels.find(
(model) => model.id === normalizeOllamaCloudModelId(name),
);
if (defaultModel && !discovered && defaultModel.id === name) {
return buildDefaultOllamaCloudModelDefinition(defaultModel);
}
const capabilities =
+22 -24
View File
@@ -205,6 +205,7 @@ describe("ollama setup", () => {
const modelIds = result.config.models?.providers?.ollama?.models?.map((m) => m.id);
expect(modelIds?.[0]).toBe("minimax-m2.7");
expect(result.defaultModel).toBe("ollama/minimax-m2.7");
expect(result.config.models?.providers?.ollama?.baseUrl).toBe("https://ollama.com");
expect(result.config.models?.providers?.ollama?.apiKey).toBe("test-ollama-key");
expect(result.credential).toBe("test-ollama-key");
@@ -246,6 +247,8 @@ describe("ollama setup", () => {
expect(modelIds).toEqual([
"gemma4",
"minimax-m2.7:cloud",
"minimax-m3:cloud",
"kimi-k3:cloud",
"glm-5.1:cloud",
"glm-5.2:cloud",
"llama3:8b",
@@ -457,30 +460,23 @@ describe("ollama setup", () => {
const models = result.config.models?.providers?.ollama?.models;
const modelIds = models?.map((m) => m.id);
expect(modelIds).toEqual(["minimax-m2.7", "glm-5.1", "glm-5.2"]);
expect(models).toEqual([
expect.objectContaining({
id: "minimax-m2.7",
contextWindow: 196_608,
reasoning: true,
input: ["text"],
compat: { supportsTools: true, supportsUsageInStreaming: true },
}),
expect.objectContaining({
id: "glm-5.1",
contextWindow: 202_752,
reasoning: true,
input: ["text"],
compat: { supportsTools: true, supportsUsageInStreaming: true },
}),
expect.objectContaining({
id: "glm-5.2",
contextWindow: 1_000_000,
reasoning: true,
input: ["text"],
compat: { supportsTools: true, supportsUsageInStreaming: true },
}),
]);
expect(modelIds).toEqual(["minimax-m2.7", "minimax-m3", "kimi-k3", "glm-5.1", "glm-5.2"]);
expect(models).toEqual(
expect.arrayContaining(
[
{ id: "minimax-m2.7", contextWindow: 196_608 },
{ id: "glm-5.1", contextWindow: 202_752 },
{ id: "glm-5.2", contextWindow: 1_000_000 },
].map((model) =>
expect.objectContaining({
...model,
reasoning: true,
input: ["text"],
compat: { supportsTools: true, supportsUsageInStreaming: true },
}),
),
),
);
});
it("cloud mode populates models from ollama.com /api/tags when reachable", async () => {
@@ -502,6 +498,8 @@ describe("ollama setup", () => {
expect(modelIds).toEqual([
"minimax-m2.7",
"minimax-m3",
"kimi-k3",
"glm-5.1",
"glm-5.2",
"qwen3-coder:480b-cloud",