From 4b3a3d79e03c5203f61de2534a7010955c7dd01f Mon Sep 17 00:00:00 2001 From: Anas Khan <83116240+anxkhn@users.noreply.github.com> Date: Sun, 5 Jul 2026 08:10:05 +0530 Subject: [PATCH] client: widen sync generate think type to accept string levels The `think` parameter accepts either a bool or the 'low'/'medium'/'high' string levels, and the request model (GenerateRequest.think) serializes all of them. PR #553 widened this annotation across Client.chat, AsyncClient.generate, and AsyncClient.chat, but missed the sync Client.generate overloads and implementation, which stayed at Optional[bool]. As a result, ollama.generate(..., think='high') (bound to the sync Client.generate) was flagged as a false type error by static type checkers, even though it works at runtime and the equivalent chat() and async generate() calls type-check cleanly. Widen the three sync Client.generate occurrences to Optional[Union[bool, Literal['low', 'medium', 'high']]] to match the other methods, and add a signature-parity test guarding against future drift. Signed-off-by: Anas Khan <83116240+anxkhn@users.noreply.github.com> --- ollama/_client.py | 6 +++--- tests/test_client.py | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/ollama/_client.py b/ollama/_client.py index 18cb0fb4..8dfce824 100644 --- a/ollama/_client.py +++ b/ollama/_client.py @@ -209,7 +209,7 @@ def generate( template: str = '', context: Optional[Sequence[int]] = None, stream: Literal[False] = False, - think: Optional[bool] = None, + think: Optional[Union[bool, Literal['low', 'medium', 'high']]] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, raw: bool = False, @@ -233,7 +233,7 @@ def generate( template: str = '', context: Optional[Sequence[int]] = None, stream: Literal[True] = True, - think: Optional[bool] = None, + think: Optional[Union[bool, Literal['low', 'medium', 'high']]] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, raw: bool = False, @@ -256,7 +256,7 @@ def generate( template: Optional[str] = None, context: Optional[Sequence[int]] = None, stream: bool = False, - think: Optional[bool] = None, + think: Optional[Union[bool, Literal['low', 'medium', 'high']]] = None, logprobs: Optional[bool] = None, top_logprobs: Optional[int] = None, raw: Optional[bool] = None, diff --git a/tests/test_client.py b/tests/test_client.py index 34657513..7b7ab38e 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -1,4 +1,5 @@ import base64 +import inspect import json import os import re @@ -1486,3 +1487,13 @@ async def test_async_client_context_manager(): assert not client._client.is_closed assert client._client.is_closed + + +def test_generate_think_annotation_matches_chat(): + # The `think` parameter accepts bool or the 'low'/'medium'/'high' string levels. + # Client.generate must keep the same annotation as Client.chat and + # AsyncClient.generate so passing a string level does not raise a false type + # error (regression guard for the sync generate overloads/implementation). + expected = inspect.signature(Client.chat).parameters['think'].annotation + assert inspect.signature(Client.generate).parameters['think'].annotation == expected + assert inspect.signature(AsyncClient.generate).parameters['think'].annotation == expected