diff --git a/core/llm/llms/Ollama.ts b/core/llm/llms/Ollama.ts index d2d8660b64..5f06467772 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 099424c61a..a80fea628c 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),