Ollama Ignores think: false on the OpenAI Endpoint

If you call a thinking model such as Gemma 4 through Ollama’s OpenAI-compatible endpoint, /v1/chat/completions, and send think: false, the model thinks anyway. That endpoint has no think parameter. Send reasoning_effort: "none" instead, and Ollama turns thinking off.

Why think: false does nothing there

Ollama has two chat APIs, and they control thinking differently.

The native /api/chat takes a think field: true, false, or a level such as "low" or "high". If you leave it out and the model is able to think, Ollama turns thinking on for you. That default is why Gemma 4, Qwen 3 and the other thinking models reason before answering unless you say otherwise.

The OpenAI-compatible /v1/chat/completions reads your request into a struct that mirrors OpenAI’s API. It has reasoning_effort and reasoning.effort, but no think. Fields it doesn’t know are dropped without an error, so think: false never gets past the front door. As far as Ollama can tell, the request said nothing about thinking, and the default applies: thinking on.

It isn’t that Ollama reads false as true. On this endpoint the field simply doesn’t exist.

reasoning_effort is the one that gets translated. In Ollama 0.31 (openai/openai.go), the accepted values are "high", "medium", "low", "max" and "none". "none" becomes think: false; the others become a thinking level. Any other value returns a 400.

Endpoint To turn thinking off
/api/chat (native) "think": false
/v1/chat/completions (OpenAI-compatible) "reasoning_effort": "none", or "reasoning": {"effort": "none"}

One exception on the native side: Ollama’s docs say GPT-OSS takes only "low", "medium" or "high" and ignores true and false. Its reasoning can’t be switched off completely.

The fix

With curl:

curl http://localhost:11434/v1/chat/completions -d '{
  "model": "gemma4:31b",
  "messages": [{"role": "user", "content": "Name three primary colors."}],
  "reasoning_effort": "none"
}'

With the OpenAI Python SDK, pass it in extra_body, which goes into the JSON body as is:

from openai import OpenAI

# The SDK insists on a key; Ollama ignores it.
client = OpenAI(base_url="http://localhost:11434/v1", api_key="ollama")

response = client.chat.completions.create(
    model="gemma4:31b",
    messages=[{"role": "user", "content": "Name three primary colors."}],
    temperature=0,
    max_tokens=2000,
    extra_body={"reasoning_effort": "none"},
)
print(response.choices[0].message.content)

Send it only to servers that accept it. My extraction code can also fall back to OpenAI’s gpt-4o-mini, which rejects reasoning_effort with a 400. So I add the field only when a custom base URL is configured, and keep an environment variable to override it:

import os

def extraction_extra_body() -> dict:
    effort = os.getenv("MEMORY_EXTRACTION_REASONING_EFFORT")
    if effort is None:
        # "none" for a local server (Ollama); nothing for the OpenAI default.
        effort = "none" if os.getenv("MEMORY_EXTRACTION_BASE_URL") else ""
    return {"reasoning_effort": effort} if effort else {}

Setting the variable to "low" lets some thinking back in, and an empty string turns the field off.

If you can use the native API instead, think: false works as documented:

curl http://localhost:11434/api/chat -d '{
  "model": "gemma4:31b",
  "messages": [{"role": "user", "content": "Name three primary colors."}],
  "think": false,
  "stream": false
}'

How to tell whether the model thought

On the OpenAI-compatible endpoint, Ollama returns the thinking in a separate reasoning field on the message (delta.reasoning when streaming), next to content. If reasoning has text in it, the model thought. On the native API the same text is in message.thinking.

The second sign is the token count. Thinking tokens are generated tokens, so they count against max_tokens, and usage.completion_tokens includes them. With a small budget, a thinking model can spend all of it before it starts the answer. The response then comes back with finish_reason: "length", an empty content, and all the text in reasoning. If you’re parsing JSON out of content, that looks like the model returned nothing.

Turning thinking off is a trade. For extraction and short structured output, the reasoning mostly costs time and tokens. For arithmetic or multi-step questions, expect worse answers without it.

How I ran into it

The first time was in April, while I was adding a thinking control to Catalyst’s chat. Gemma 4 on Ollama kept thinking with think: false in the request, so the chat moved to reasoning_effort, the same field OpenAI uses.

It came back on July 1 in a place I wasn’t watching. Catalyst’s memory extraction pulls facts, memorable moments and a summary out of a conversation, as JSON, and that day I moved it to gemma4:31b on Ollama. Gemma reasons by default, and the extraction calls had small budgets: 1,000 tokens for facts and moments, 500 for the summary. Thinking could use up the budget before the JSON ended, a JSON array that is cut off doesn’t parse, and the step quietly produces no memories.

The fix was reasoning_effort: "none" through extra_body, plus larger budgets: 2,000 and 1,000 tokens. On the same input, an extraction call went from 42 seconds and 561 tokens to 3.7 seconds and 101 tokens, with the same output.

By: Gavi Narra on: