fix(catalog): omit Ollama Cloud output caps
This commit is contained in:
@@ -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") {
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
Reference in New Issue
Block a user