fix(catalog): omit Ollama Cloud output caps

This commit is contained in:
Wolfgang Schoenberger
2026-06-19 02:51:23 -07:00
parent 492fe5e896
commit 409af331e7
9 changed files with 156 additions and 13 deletions
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased]
### Fixed
- Marked Ollama Cloud catalog models to omit on-the-wire output-token caps, preventing context-window-sized `num_predict` values from causing HTTP 400s for models whose true output cap is not discoverable. ([#2984](https://github.com/can1357/oh-my-pi/issues/2984))
## [16.0.9] - 2026-06-18
### Fixed
@@ -208,6 +208,10 @@ function applyGeneratedModelPolicy(model: ModelSpec<Api>): void {
model.maxTokens = copilotLimits.maxTokens;
}
if (model.provider === "ollama-cloud") {
model.omitMaxOutputTokens = true;
}
// GLM Coding Plan: GLM-5.2 is the selectable 1M served id; pin it so
// endpoint discovery or older bundled fallbacks cannot regress to 200k.
if ((model.provider === "zai" || model.provider === "zhipu-coding-plan") && model.id === "glm-5.2") {
+81 -12
View File
@@ -53268,6 +53268,7 @@
},
"contextWindow": 163840,
"maxTokens": 32000,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53296,6 +53297,7 @@
},
"contextWindow": 163840,
"maxTokens": 163840,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53324,6 +53326,7 @@
},
"contextWindow": 163840,
"maxTokens": 65536,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53352,6 +53355,7 @@
},
"contextWindow": 1048576,
"maxTokens": 1048576,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53381,6 +53385,7 @@
},
"contextWindow": 1048576,
"maxTokens": 1048576,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53409,7 +53414,8 @@
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 262144
"maxTokens": 262144,
"omitMaxOutputTokens": true
},
"devstral-small-2:24b": {
"id": "devstral-small-2:24b",
@@ -53429,7 +53435,8 @@
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 262144
"maxTokens": 262144,
"omitMaxOutputTokens": true
},
"gemini-3-flash-preview": {
"id": "gemini-3-flash-preview",
@@ -53450,6 +53457,7 @@
},
"contextWindow": 1048576,
"maxTokens": 65536,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53480,6 +53488,7 @@
},
"contextWindow": 262144,
"maxTokens": 262144,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53508,6 +53517,7 @@
},
"contextWindow": 202752,
"maxTokens": 131072,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53536,6 +53546,7 @@
},
"contextWindow": 202752,
"maxTokens": 131072,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53564,6 +53575,7 @@
},
"contextWindow": 202752,
"maxTokens": 131072,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53592,6 +53604,7 @@
},
"contextWindow": 202752,
"maxTokens": 131072,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53602,6 +53615,36 @@
]
}
},
"glm-5.2": {
"id": "glm-5.2",
"name": "GLM-5.2",
"api": "ollama-chat",
"provider": "ollama-cloud",
"baseUrl": "https://ollama.com",
"reasoning": true,
"input": [
"text"
],
"cost": {
"input": 0,
"output": 0,
"cacheRead": 0,
"cacheWrite": 0
},
"contextWindow": 976000,
"maxTokens": 131072,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
"high",
"xhigh"
],
"effortMap": {
"xhigh": "max"
}
}
},
"gpt-oss:120b": {
"id": "gpt-oss:120b",
"name": "gpt-oss:120b",
@@ -53620,6 +53663,7 @@
},
"contextWindow": 131072,
"maxTokens": 32768,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53648,6 +53692,7 @@
},
"contextWindow": 131072,
"maxTokens": 32768,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53676,6 +53721,7 @@
},
"contextWindow": 262144,
"maxTokens": 262144,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53704,7 +53750,8 @@
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 262144
"maxTokens": 262144,
"omitMaxOutputTokens": true
},
"kimi-k2.5": {
"id": "kimi-k2.5",
@@ -53725,6 +53772,7 @@
},
"contextWindow": 262144,
"maxTokens": 262144,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53754,6 +53802,7 @@
},
"contextWindow": 262144,
"maxTokens": 262144,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53783,6 +53832,7 @@
},
"contextWindow": 262144,
"maxTokens": 262144,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53811,6 +53861,7 @@
},
"contextWindow": 204800,
"maxTokens": 128000,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53840,6 +53891,7 @@
},
"contextWindow": 204800,
"maxTokens": 131072,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53869,6 +53921,7 @@
},
"contextWindow": 204800,
"maxTokens": 131072,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53898,6 +53951,7 @@
},
"contextWindow": 196608,
"maxTokens": 196608,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53928,6 +53982,7 @@
},
"contextWindow": 512000,
"maxTokens": 131072,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -53956,7 +54011,8 @@
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 128000
"maxTokens": 128000,
"omitMaxOutputTokens": true
},
"ministral-3:3b": {
"id": "ministral-3:3b",
@@ -53976,7 +54032,8 @@
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 128000
"maxTokens": 128000,
"omitMaxOutputTokens": true
},
"ministral-3:8b": {
"id": "ministral-3:8b",
@@ -53996,7 +54053,8 @@
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 128000
"maxTokens": 128000,
"omitMaxOutputTokens": true
},
"mistral-large-3:675b": {
"id": "mistral-large-3:675b",
@@ -54016,7 +54074,8 @@
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 262144
"maxTokens": 262144,
"omitMaxOutputTokens": true
},
"nemotron-3-nano:30b": {
"id": "nemotron-3-nano:30b",
@@ -54036,6 +54095,7 @@
},
"contextWindow": 1048576,
"maxTokens": 131072,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -54064,6 +54124,7 @@
},
"contextWindow": 262144,
"maxTokens": 65536,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -54092,6 +54153,7 @@
},
"contextWindow": 262144,
"maxTokens": 128000,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -54119,7 +54181,8 @@
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 65536
"maxTokens": 65536,
"omitMaxOutputTokens": true
},
"qwen3-coder:480b": {
"id": "qwen3-coder:480b",
@@ -54138,7 +54201,8 @@
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 65536
"maxTokens": 65536,
"omitMaxOutputTokens": true
},
"qwen3-next:80b": {
"id": "qwen3-next:80b",
@@ -54158,6 +54222,7 @@
},
"contextWindow": 262144,
"maxTokens": 32768,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -54187,6 +54252,7 @@
},
"contextWindow": 262144,
"maxTokens": 32768,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -54215,7 +54281,8 @@
"cacheWrite": 0
},
"contextWindow": 262144,
"maxTokens": 131072
"maxTokens": 131072,
"omitMaxOutputTokens": true
},
"qwen3.5:397b": {
"id": "qwen3.5:397b",
@@ -54236,6 +54303,7 @@
},
"contextWindow": 262144,
"maxTokens": 65536,
"omitMaxOutputTokens": true,
"thinking": {
"mode": "effort",
"efforts": [
@@ -54263,7 +54331,8 @@
"cacheWrite": 0
},
"contextWindow": 32768,
"maxTokens": 4096
"maxTokens": 4096,
"omitMaxOutputTokens": true
}
},
"openai": {
@@ -85019,4 +85088,4 @@
}
}
}
}
}
@@ -159,6 +159,7 @@ export function ollamaCloudModelManagerOptions(
discoveredContextWindow !== null && discoveredContextWindow !== undefined
? (providerReference?.maxTokens ?? Math.min(contextWindow, 8192))
: Math.min(contextWindow, 8192),
omitMaxOutputTokens: true,
};
}),
);
@@ -196,6 +196,30 @@ describe("generated model policies", () => {
expect(models[2]?.maxTokens).toBe(64000);
});
it("marks Ollama Cloud generated rows to omit max output tokens", () => {
const models: ModelSpec<Api>[] = [
createSpec({
id: "deepseek-v4-flash",
api: "ollama-chat",
provider: "ollama-cloud",
contextWindow: 1048576,
maxTokens: 1048576,
}),
createSpec({
id: "deepseek-v4-flash",
api: "ollama-chat",
provider: "ollama",
contextWindow: 1048576,
maxTokens: 1048576,
}),
];
applyGeneratedModelPolicies(models);
expect(models[0]?.omitMaxOutputTokens).toBe(true);
expect(models[1]?.omitMaxOutputTokens).toBeUndefined();
});
it("marks OpenCode Go MiMo models as not supporting tool_choice", () => {
const models: ModelSpec<"openai-completions">[] = [
createSpec({
@@ -48,6 +48,40 @@ test("ollama-cloud discovery does not inherit unsafe cross-provider maxTokens",
expect(model?.maxTokens).toBe(8192);
});
test("ollama-cloud discovery always omits max output tokens", async () => {
const fetchMock: FetchImpl = vi.fn(async (input, _init) => {
const url = String(input);
if (url === "https://ollama.com/api/tags") {
return new Response(JSON.stringify({ models: [{ name: "deepseek-v4-flash" }] }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
}
if (url === "https://ollama.com/api/show") {
return new Response(
JSON.stringify({
capabilities: ["completion", "thinking"],
model_info: { "deepseek4.context_length": 1048576 },
}),
{
status: 200,
headers: { "Content-Type": "application/json" },
},
);
}
throw new Error(`Unexpected URL: ${url}`);
});
const options = ollamaCloudModelManagerOptions({ apiKey: "cloud-test-key", fetch: fetchMock });
const models = await options.fetchDynamicModels?.();
const model = models?.find(candidate => candidate.id === "deepseek-v4-flash");
expect(model?.provider).toBe("ollama-cloud");
expect(model?.contextWindow).toBe(1048576);
expect(model?.maxTokens).toBe(1048576);
expect(model?.omitMaxOutputTokens).toBe(true);
});
test("ollama-chat omits num_predict when model opts out of max output tokens", async () => {
let requestBody: Record<string, unknown> | undefined;
const fetchMock: FetchImpl = vi.fn(async (_input, init) => {
+4
View File
@@ -2,6 +2,10 @@
## [Unreleased]
### Fixed
- Preserved bundled `omitMaxOutputTokens` policy when fresh cached provider discovery rows replace Ollama Cloud catalog models, so stale `models.db` entries cannot re-enable context-window-sized `num_predict` values. ([#2984](https://github.com/can1357/oh-my-pi/issues/2984))
## [16.1.1] - 2026-06-19
### Changed
@@ -900,6 +900,7 @@ export class ModelRegistry {
...replacementModel,
contextWindow: replacementModel.contextWindow ?? existing.contextWindow,
maxTokens: replacementModel.maxTokens ?? existing.maxTokens,
omitMaxOutputTokens: replacementModel.omitMaxOutputTokens ?? existing.omitMaxOutputTokens,
...(supportsTools !== undefined ? { supportsTools } : {}),
};
});
@@ -2047,7 +2047,9 @@ describe("ModelRegistry", () => {
});
test("loads cached standard provider discovery models on startup", () => {
expect(standardCache.find("ollama-cloud", "deepseek-v4-pro")?.maxTokens).toBe(384_000);
const model = standardCache.find("ollama-cloud", "deepseek-v4-pro");
expect(model?.maxTokens).toBe(384_000);
expect(model?.omitMaxOutputTokens).toBe(true);
});
test("loads cached special provider discovery models on startup", () => {