From 4373b8285a3ef1928a4365fadbfd4f4b388cc6a2 Mon Sep 17 00:00:00 2001 From: Dallin Romney Date: Fri, 20 Mar 2026 17:50:30 -0700 Subject: [PATCH] fix(ollama): check model template for tool support before sending tools Ollama models only support tool calling if their template includes .Tools. The heuristic in toolSupport.ts was too broad, causing 400 errors for models whose templates lack tool support. Two changes: - Query /api/show template for .Tools (same pattern as existing .Suffix FIM check) and skip sending tools when the template says no - Exclude Ollama cloud models (:cloud tag) in the heuristic, since cloud tool calling is unreliable Fixes #9639 #9646 #9875 #10304 #10461 #10539 #10561 #10593 #10640 Fixes #10672 #10833 #10876 #11308 #11382 #11402 --- core/llm/llms/Ollama.ts | 14 ++++++++++++-- core/llm/toolSupport.ts | 21 +++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/core/llm/llms/Ollama.ts b/core/llm/llms/Ollama.ts index d2d8660b644..5f064677724 100644 --- a/core/llm/llms/Ollama.ts +++ b/core/llm/llms/Ollama.ts @@ -161,6 +161,7 @@ class Ollama extends BaseLLM implements ModelInstaller { private static modelsBeingInstalledMutex = new Mutex(); private fimSupported: boolean = false; + private templateSupportsTools: boolean | undefined = undefined; constructor(options: LLMOptions) { super(options); @@ -227,6 +228,11 @@ class Ollama extends BaseLLM implements ModelInstaller { * it's a good indication the model supports FIM. */ this.fimSupported = !!body?.template?.includes(".Suffix"); + + // Check if model template supports tool calling (same pattern as .Suffix above) + if (body?.template) { + this.templateSupportsTools = body.template.includes(".Tools"); + } }) .catch((e) => { // console.warn("Error calling the Ollama /api/show endpoint: ", e); @@ -424,8 +430,12 @@ class Ollama extends BaseLLM implements ModelInstaller { stream: options.stream, // format: options.format, // Not currently in base completion options }; - // This logic is because tools can ONLY be included with user message for ollama - if (options.tools?.length && ollamaMessages.at(-1)?.role === "user") { + // Only include tools with user messages, and only if the template supports them + if ( + options.tools?.length && + ollamaMessages.at(-1)?.role === "user" && + this.templateSupportsTools !== false + ) { chatOptions.tools = options.tools.map((tool) => ({ type: "function", function: { diff --git a/core/llm/toolSupport.ts b/core/llm/toolSupport.ts index 099424c61ab..a80fea628c0 100644 --- a/core/llm/toolSupport.ts +++ b/core/llm/toolSupport.ts @@ -155,6 +155,27 @@ export const PROVIDER_TOOL_SUPPORT: Record boolean> = modelName = model; } + // Some Ollama cloud models don't support tools despite matching the + // family-name heuristic below (https://ollama.com/search?c=cloud) + if (modelName.toLowerCase().includes(":cloud")) { + if ( + [ + "cogito-2.1", + "deepseek-v3.2", + "gemini-3-flash-preview", + "glm-4.6", + "glm-4.7", + "glm-5", + "kimi-k2.5", + "minimax-m2", + "minimax-m2.5", + "minimax-m2.7", + ].some((part) => modelName.toLowerCase().startsWith(part)) + ) { + return false; + } + } + if ( ["vision", "math", "guard", "mistrallite", "mistral-openorca"].some( (part) => modelName.toLowerCase().includes(part),