From d9a42d82c37768de21bee52803e3bf4f2053e5a0 Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Tue, 4 Aug 2026 16:32:22 -0700 Subject: [PATCH 1/6] Add `thinking_token_budget` handling in `LLMRequest` model with tests --- neon_data_models/models/api/llm.py | 30 +++++++++++++ tests/models/api/test_llm.py | 68 ++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) diff --git a/neon_data_models/models/api/llm.py b/neon_data_models/models/api/llm.py index 2889047..f9714f9 100644 --- a/neon_data_models/models/api/llm.py +++ b/neon_data_models/models/api/llm.py @@ -117,6 +117,23 @@ def beam_search(self) -> bool: def beam_search(self, value: bool): self.extra_body["use_beam_search"] = value + @property + def thinking_token_budget(self) -> Optional[int]: + return self.extra_body.get('thinking_token_budget') + + @thinking_token_budget.setter + def thinking_token_budget(self, value: Optional[int]): + if value is None: + self.extra_body.pop("thinking_token_budget", None) + if "chat_template_kwargs" in self.extra_body: + self.extra_body["chat_template_kwargs"].pop( + "add_thinking_start", None) + elif isinstance(value, int): + assert value >= 0, "thinking_token_budget must be positive" + self.extra_body["thinking_token_budget"] = value + self.extra_body.setdefault("chat_template_kwargs", + {})["add_thinking_start"] = True + @property def best_of(self) -> int: return self.extra_body['best_of'] @@ -187,6 +204,19 @@ def validate_request(self): # If beam search is enabled, temperature must be set to 0.0 if self.beam_search: assert self.temperature == 0.0, "Beam search requires temperature 0" + + if self.thinking_token_budget is not None: + if self.thinking_token_budget < 0: + raise ValueError("thinking_token_budget must be positive") + if self.extra_body.get("chat_template_kwargs", {}).get( + "add_thinking_start") is not True: + raise ValueError("add_thinking_start must be True if " + "thinking_token_budget is set") + if self.extra_body.get("chat_template_kwargs", {}).get( + "add_thinking_start") is True: + if self.thinking_token_budget is None: + raise ValueError("thinking_token_budget must be set if " + "add_thinking_start is True") return self @property diff --git a/tests/models/api/test_llm.py b/tests/models/api/test_llm.py index 8e64314..811c19b 100644 --- a/tests/models/api/test_llm.py +++ b/tests/models/api/test_llm.py @@ -89,6 +89,7 @@ def test_llm_request(self): self.assertIsInstance(valid_request.persona, LLMPersona) self.assertTrue(valid_request.stream) self.assertFalse(valid_request.beam_search) + self.assertIsNone(valid_request.thinking_token_budget) self.assertEqual(len(valid_request.history), len(test_history)) self.assertEqual(len(valid_request.to_completion_kwargs()['messages']), 2 * valid_request.max_history + 2) @@ -122,6 +123,42 @@ def test_llm_request(self): self.assertFalse(valid_no_stream.beam_search) self.assertFalse(valid_no_stream.stream) + # Valid thinking_token_budget via extra_body + thinking_request = LLMRequest( + query=test_query, history=test_history, persona=test_persona, + model=test_model, + extra_body={"thinking_token_budget": 128, + "chat_template_kwargs": {"add_thinking_start": True}}) + self.assertEqual(thinking_request.thinking_token_budget, 128) + self.assertTrue(thinking_request.extra_body["chat_template_kwargs"][ + "add_thinking_start"]) + self.assertEqual( + thinking_request.to_completion_kwargs()["extra_body"][ + "thinking_token_budget"], 128) + + # Valid zero thinking_token_budget + zero_thinking = LLMRequest( + query=test_query, history=test_history, persona=test_persona, + model=test_model, + extra_body={"thinking_token_budget": 0, + "chat_template_kwargs": {"add_thinking_start": True}}) + self.assertEqual(zero_thinking.thinking_token_budget, 0) + + # thinking_token_budget property setter enables chat_template_kwargs + setter_request = LLMRequest(query=test_query, history=test_history, + persona=test_persona, model=test_model) + setter_request.thinking_token_budget = 64 + self.assertEqual(setter_request.thinking_token_budget, 64) + self.assertTrue(setter_request.extra_body["chat_template_kwargs"][ + "add_thinking_start"]) + # Clearing thinking_token_budget also clears add_thinking_start + setter_request.thinking_token_budget = None + self.assertIsNone(setter_request.thinking_token_budget) + self.assertNotIn("thinking_token_budget", setter_request.extra_body) + self.assertNotIn("add_thinking_start", + setter_request.extra_body.get( + "chat_template_kwargs", {})) + # Validate `llm` history input old_history = [("user", "hello"), ("llm", "Hi, how can I help you today?"), @@ -151,6 +188,37 @@ def test_llm_request(self): LLMRequest(query=test_query, history=test_history, persona=test_persona, model=test_model, stream=False, beam_search=True, best_of=1) + # Invalid thinking_token_budget without add_thinking_start + with self.assertRaises(ValidationError): + LLMRequest(query=test_query, history=test_history, + persona=test_persona, model=test_model, + extra_body={"thinking_token_budget": 128}) + # Invalid add_thinking_start without thinking_token_budget + with self.assertRaises(ValidationError): + LLMRequest( + query=test_query, history=test_history, persona=test_persona, + model=test_model, + extra_body={"chat_template_kwargs": { + "add_thinking_start": True}}) + # Invalid negative thinking_token_budget + with self.assertRaises(ValidationError): + LLMRequest( + query=test_query, history=test_history, persona=test_persona, + model=test_model, + extra_body={"thinking_token_budget": -1, + "chat_template_kwargs": { + "add_thinking_start": True}}) + # Invalid add_thinking_start=False with thinking_token_budget + with self.assertRaises(ValidationError): + LLMRequest( + query=test_query, history=test_history, persona=test_persona, + model=test_model, + extra_body={"thinking_token_budget": 128, + "chat_template_kwargs": { + "add_thinking_start": False}}) + # Invalid negative thinking_token_budget via property setter + with self.assertRaises(AssertionError): + setter_request.thinking_token_budget = -1 # Invalid history test_history.append(("invalid_key", "okay")) with self.assertRaises(ValidationError): From 9250cbd0530fcb6ccada95c3b54b03942f782a4c Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Tue, 4 Aug 2026 17:15:03 -0700 Subject: [PATCH 2/6] Add support for `reasoning_content` in LLMResponse objects --- neon_data_models/models/api/llm.py | 2 ++ tests/models/api/test_llm.py | 7 +++++++ 2 files changed, 9 insertions(+) diff --git a/neon_data_models/models/api/llm.py b/neon_data_models/models/api/llm.py index f9714f9..2a9d7b4 100644 --- a/neon_data_models/models/api/llm.py +++ b/neon_data_models/models/api/llm.py @@ -250,6 +250,8 @@ def to_completion_kwargs(self, mq2role: dict = None) -> dict: class LLMResponse(BaseModel): response: str = Field(description="LLM Response to the input query") + reasoning_content: Optional[str] = Field( + None, description="Thinking/reasoning trace returned by the LLM") history: List[Tuple[LlmMessageRole, str]] = Field( description="List of (role, content) tuples in chronological order " "(`response` is in the last list element)") diff --git a/tests/models/api/test_llm.py b/tests/models/api/test_llm.py index 811c19b..66b0035 100644 --- a/tests/models/api/test_llm.py +++ b/tests/models/api/test_llm.py @@ -235,8 +235,15 @@ def test_llm_response(self): # Valid response with valid history response = LLMResponse(response=valid_response, history=valid_history) self.assertEqual(response.response, valid_response) + self.assertIsNone(response.reasoning_content) self.assertEqual(response.history, valid_history) + # Valid response with a reasoning trace + reasoning_content = "We need answer the user's greeting." + response = LLMResponse(response=valid_response, history=valid_history, + reasoning_content=reasoning_content) + self.assertEqual(response.reasoning_content, reasoning_content) + # Valid response with legacy history response = LLMResponse(response=valid_response, history=legacy_history) self.assertEqual(response.response, valid_response) From 45c50943106f9fa6fdc50215b529ea3a81dc4388 Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Tue, 4 Aug 2026 18:00:34 -0700 Subject: [PATCH 3/6] Enforce that `thinking_token_budget` must be smaller than `max_tokens` --- neon_data_models/models/api/llm.py | 6 ++++++ tests/models/api/test_llm.py | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/neon_data_models/models/api/llm.py b/neon_data_models/models/api/llm.py index 2a9d7b4..07a4419 100644 --- a/neon_data_models/models/api/llm.py +++ b/neon_data_models/models/api/llm.py @@ -130,6 +130,9 @@ def thinking_token_budget(self, value: Optional[int]): "add_thinking_start", None) elif isinstance(value, int): assert value >= 0, "thinking_token_budget must be positive" + if value >= self.max_tokens: + raise ValueError( + "thinking_token_budget must be smaller than max_tokens") self.extra_body["thinking_token_budget"] = value self.extra_body.setdefault("chat_template_kwargs", {})["add_thinking_start"] = True @@ -208,6 +211,9 @@ def validate_request(self): if self.thinking_token_budget is not None: if self.thinking_token_budget < 0: raise ValueError("thinking_token_budget must be positive") + if self.thinking_token_budget >= self.max_tokens: + raise ValueError( + "thinking_token_budget must be smaller than max_tokens") if self.extra_body.get("chat_template_kwargs", {}).get( "add_thinking_start") is not True: raise ValueError("add_thinking_start must be True if " diff --git a/tests/models/api/test_llm.py b/tests/models/api/test_llm.py index 66b0035..a3009c5 100644 --- a/tests/models/api/test_llm.py +++ b/tests/models/api/test_llm.py @@ -216,9 +216,28 @@ def test_llm_request(self): extra_body={"thinking_token_budget": 128, "chat_template_kwargs": { "add_thinking_start": False}}) + # Invalid thinking_token_budget equal to max_tokens + with self.assertRaises(ValidationError): + LLMRequest( + query=test_query, history=test_history, persona=test_persona, + model=test_model, max_tokens=128, + extra_body={"thinking_token_budget": 128, + "chat_template_kwargs": { + "add_thinking_start": True}}) + # Invalid thinking_token_budget greater than max_tokens + with self.assertRaises(ValidationError): + LLMRequest( + query=test_query, history=test_history, persona=test_persona, + model=test_model, max_tokens=128, + extra_body={"thinking_token_budget": 129, + "chat_template_kwargs": { + "add_thinking_start": True}}) # Invalid negative thinking_token_budget via property setter with self.assertRaises(AssertionError): setter_request.thinking_token_budget = -1 + # Invalid thinking_token_budget via property setter + with self.assertRaises(ValueError): + setter_request.thinking_token_budget = setter_request.max_tokens # Invalid history test_history.append(("invalid_key", "okay")) with self.assertRaises(ValidationError): From c2c3b306b9b391459cbbb23189a70e324f3e3b21 Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Wed, 5 Aug 2026 10:05:14 -0700 Subject: [PATCH 4/6] Add handling for `skip_special_tokens` --- neon_data_models/models/api/llm.py | 7 +++++++ tests/models/api/test_llm.py | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/neon_data_models/models/api/llm.py b/neon_data_models/models/api/llm.py index 07a4419..5554047 100644 --- a/neon_data_models/models/api/llm.py +++ b/neon_data_models/models/api/llm.py @@ -125,6 +125,7 @@ def thinking_token_budget(self) -> Optional[int]: def thinking_token_budget(self, value: Optional[int]): if value is None: self.extra_body.pop("thinking_token_budget", None) + self.extra_body.pop("skip_special_tokens", None) if "chat_template_kwargs" in self.extra_body: self.extra_body["chat_template_kwargs"].pop( "add_thinking_start", None) @@ -134,6 +135,7 @@ def thinking_token_budget(self, value: Optional[int]): raise ValueError( "thinking_token_budget must be smaller than max_tokens") self.extra_body["thinking_token_budget"] = value + self.extra_body["skip_special_tokens"] = False self.extra_body.setdefault("chat_template_kwargs", {})["add_thinking_start"] = True @@ -223,6 +225,11 @@ def validate_request(self): if self.thinking_token_budget is None: raise ValueError("thinking_token_budget must be set if " "add_thinking_start is True") + # Reasoning parsers need the literal think tags in decoded text + if self.extra_body.get("skip_special_tokens") is True: + raise ValueError("skip_special_tokens must be False if " + "add_thinking_start is True") + self.extra_body["skip_special_tokens"] = False return self @property diff --git a/tests/models/api/test_llm.py b/tests/models/api/test_llm.py index a3009c5..4604d06 100644 --- a/tests/models/api/test_llm.py +++ b/tests/models/api/test_llm.py @@ -132,10 +132,20 @@ def test_llm_request(self): self.assertEqual(thinking_request.thinking_token_budget, 128) self.assertTrue(thinking_request.extra_body["chat_template_kwargs"][ "add_thinking_start"]) + self.assertFalse(thinking_request.extra_body["skip_special_tokens"]) self.assertEqual( thinking_request.to_completion_kwargs()["extra_body"][ "thinking_token_budget"], 128) + # Valid thinking with explicit skip_special_tokens=False + thinking_no_skip = LLMRequest( + query=test_query, history=test_history, persona=test_persona, + model=test_model, + extra_body={"thinking_token_budget": 128, + "skip_special_tokens": False, + "chat_template_kwargs": {"add_thinking_start": True}}) + self.assertFalse(thinking_no_skip.extra_body["skip_special_tokens"]) + # Valid zero thinking_token_budget zero_thinking = LLMRequest( query=test_query, history=test_history, persona=test_persona, @@ -143,6 +153,7 @@ def test_llm_request(self): extra_body={"thinking_token_budget": 0, "chat_template_kwargs": {"add_thinking_start": True}}) self.assertEqual(zero_thinking.thinking_token_budget, 0) + self.assertFalse(zero_thinking.extra_body["skip_special_tokens"]) # thinking_token_budget property setter enables chat_template_kwargs setter_request = LLMRequest(query=test_query, history=test_history, @@ -151,6 +162,7 @@ def test_llm_request(self): self.assertEqual(setter_request.thinking_token_budget, 64) self.assertTrue(setter_request.extra_body["chat_template_kwargs"][ "add_thinking_start"]) + self.assertFalse(setter_request.extra_body["skip_special_tokens"]) # Clearing thinking_token_budget also clears add_thinking_start setter_request.thinking_token_budget = None self.assertIsNone(setter_request.thinking_token_budget) @@ -216,6 +228,15 @@ def test_llm_request(self): extra_body={"thinking_token_budget": 128, "chat_template_kwargs": { "add_thinking_start": False}}) + # Invalid skip_special_tokens=True with thinking enabled + with self.assertRaises(ValidationError): + LLMRequest( + query=test_query, history=test_history, persona=test_persona, + model=test_model, + extra_body={"thinking_token_budget": 128, + "skip_special_tokens": True, + "chat_template_kwargs": { + "add_thinking_start": True}}) # Invalid thinking_token_budget equal to max_tokens with self.assertRaises(ValidationError): LLMRequest( From 28fff6a49f834cd3775d45a13773fd0080b18c37 Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Wed, 5 Aug 2026 14:05:41 -0700 Subject: [PATCH 5/6] Fix Cursor-suggested `reasoning` field name --- neon_data_models/models/api/llm.py | 2 +- tests/models/api/test_llm.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/neon_data_models/models/api/llm.py b/neon_data_models/models/api/llm.py index 5554047..ba569c3 100644 --- a/neon_data_models/models/api/llm.py +++ b/neon_data_models/models/api/llm.py @@ -263,7 +263,7 @@ def to_completion_kwargs(self, mq2role: dict = None) -> dict: class LLMResponse(BaseModel): response: str = Field(description="LLM Response to the input query") - reasoning_content: Optional[str] = Field( + reasoning: Optional[str] = Field( None, description="Thinking/reasoning trace returned by the LLM") history: List[Tuple[LlmMessageRole, str]] = Field( description="List of (role, content) tuples in chronological order " diff --git a/tests/models/api/test_llm.py b/tests/models/api/test_llm.py index 4604d06..5e158b8 100644 --- a/tests/models/api/test_llm.py +++ b/tests/models/api/test_llm.py @@ -275,14 +275,14 @@ def test_llm_response(self): # Valid response with valid history response = LLMResponse(response=valid_response, history=valid_history) self.assertEqual(response.response, valid_response) - self.assertIsNone(response.reasoning_content) + self.assertIsNone(response.reasoning) self.assertEqual(response.history, valid_history) # Valid response with a reasoning trace - reasoning_content = "We need answer the user's greeting." + reasoning = "We need answer the user's greeting." response = LLMResponse(response=valid_response, history=valid_history, - reasoning_content=reasoning_content) - self.assertEqual(response.reasoning_content, reasoning_content) + reasoning=reasoning) + self.assertEqual(response.reasoning, reasoning) # Valid response with legacy history response = LLMResponse(response=valid_response, history=legacy_history) From eff085657d8033db1a1036665061cca37f9e5131 Mon Sep 17 00:00:00 2001 From: Daniel McKnight Date: Fri, 7 Aug 2026 13:19:24 -0700 Subject: [PATCH 6/6] Troubleshoot non-thinking case with Cursor --- neon_data_models/models/api/llm.py | 54 +++++++++++++++++++++--------- tests/models/api/test_llm.py | 35 ++++++++++++++++++- 2 files changed, 72 insertions(+), 17 deletions(-) diff --git a/neon_data_models/models/api/llm.py b/neon_data_models/models/api/llm.py index ba569c3..6bb54e3 100644 --- a/neon_data_models/models/api/llm.py +++ b/neon_data_models/models/api/llm.py @@ -129,15 +129,25 @@ def thinking_token_budget(self, value: Optional[int]): if "chat_template_kwargs" in self.extra_body: self.extra_body["chat_template_kwargs"].pop( "add_thinking_start", None) + self.extra_body["chat_template_kwargs"].pop( + "enable_thinking", None) elif isinstance(value, int): assert value >= 0, "thinking_token_budget must be positive" if value >= self.max_tokens: raise ValueError( "thinking_token_budget must be smaller than max_tokens") - self.extra_body["thinking_token_budget"] = value + # Reasoning parsers need the literal think tags in decoded text self.extra_body["skip_special_tokens"] = False - self.extra_body.setdefault("chat_template_kwargs", - {})["add_thinking_start"] = True + template_kwargs = self.extra_body.setdefault( + "chat_template_kwargs", {}) + self.extra_body["thinking_token_budget"] = value + if value == 0: + # Treat `0` as a request for no thinking at all + template_kwargs["add_thinking_start"] = False + template_kwargs["enable_thinking"] = False + else: + template_kwargs["add_thinking_start"] = True + template_kwargs.pop("enable_thinking", None) @property def best_of(self) -> int: @@ -210,26 +220,31 @@ def validate_request(self): if self.beam_search: assert self.temperature == 0.0, "Beam search requires temperature 0" - if self.thinking_token_budget is not None: - if self.thinking_token_budget < 0: - raise ValueError("thinking_token_budget must be positive") - if self.thinking_token_budget >= self.max_tokens: + requested_budget = self.extra_body.get("thinking_token_budget") + add_thinking_start = self.extra_body.get( + "chat_template_kwargs", {}).get("add_thinking_start") + if requested_budget is not None and requested_budget < 0: + raise ValueError("thinking_token_budget must be positive") + if requested_budget: + if requested_budget >= self.max_tokens: raise ValueError( "thinking_token_budget must be smaller than max_tokens") - if self.extra_body.get("chat_template_kwargs", {}).get( - "add_thinking_start") is not True: + if add_thinking_start is not True: raise ValueError("add_thinking_start must be True if " "thinking_token_budget is set") - if self.extra_body.get("chat_template_kwargs", {}).get( - "add_thinking_start") is True: - if self.thinking_token_budget is None: - raise ValueError("thinking_token_budget must be set if " - "add_thinking_start is True") # Reasoning parsers need the literal think tags in decoded text if self.extra_body.get("skip_special_tokens") is True: raise ValueError("skip_special_tokens must be False if " "add_thinking_start is True") - self.extra_body["skip_special_tokens"] = False + self.thinking_token_budget = requested_budget + elif requested_budget == 0 or add_thinking_start is False: + # Either parameter alone means no thinking is requested; set both + # so the request cannot ask the model to open a think block that + # it has no budget to complete + self.thinking_token_budget = 0 + elif add_thinking_start is True: + raise ValueError("thinking_token_budget must be set if " + "add_thinking_start is True") return self @property @@ -253,12 +268,19 @@ def to_completion_kwargs(self, mq2role: dict = None) -> dict: history.insert(0, {"role": "system", "content": self.persona.system_prompt}) history.append({"role": "user", "content": self.query}) + extra_body = dict(self.extra_body) + if extra_body.get("thinking_token_budget") == 0: + # A zero budget requests no thinking, but an inference engine reads + # it as a budget to spend; it opens a think block and closes it + # after one generated token, leaving the model to complete its + # interrupted reasoning as response content + extra_body.pop("thinking_token_budget") return {"model": self.model, "messages": history, "max_tokens": self.max_tokens, "temperature": self.temperature, "stream": self.stream, - "extra_body": self.extra_body} + "extra_body": extra_body} class LLMResponse(BaseModel): diff --git a/tests/models/api/test_llm.py b/tests/models/api/test_llm.py index 5e158b8..07fac23 100644 --- a/tests/models/api/test_llm.py +++ b/tests/models/api/test_llm.py @@ -146,7 +146,7 @@ def test_llm_request(self): "chat_template_kwargs": {"add_thinking_start": True}}) self.assertFalse(thinking_no_skip.extra_body["skip_special_tokens"]) - # Valid zero thinking_token_budget + # A zero thinking_token_budget requests no thinking at all zero_thinking = LLMRequest( query=test_query, history=test_history, persona=test_persona, model=test_model, @@ -154,6 +154,25 @@ def test_llm_request(self): "chat_template_kwargs": {"add_thinking_start": True}}) self.assertEqual(zero_thinking.thinking_token_budget, 0) self.assertFalse(zero_thinking.extra_body["skip_special_tokens"]) + self.assertFalse(zero_thinking.extra_body["chat_template_kwargs"][ + "add_thinking_start"]) + self.assertFalse(zero_thinking.extra_body["chat_template_kwargs"][ + "enable_thinking"]) + self.assertNotIn("thinking_token_budget", + zero_thinking.to_completion_kwargs()["extra_body"]) + + # Thinking may be disabled with `add_thinking_start` alone + disabled_thinking = LLMRequest( + query=test_query, history=test_history, persona=test_persona, + model=test_model, + extra_body={"chat_template_kwargs": {"add_thinking_start": False}}) + self.assertEqual(disabled_thinking.thinking_token_budget, 0) + self.assertFalse(disabled_thinking.extra_body["skip_special_tokens"]) + self.assertFalse(disabled_thinking.extra_body["chat_template_kwargs"][ + "enable_thinking"]) + self.assertNotIn( + "thinking_token_budget", + disabled_thinking.to_completion_kwargs()["extra_body"]) # thinking_token_budget property setter enables chat_template_kwargs setter_request = LLMRequest(query=test_query, history=test_history, @@ -163,6 +182,16 @@ def test_llm_request(self): self.assertTrue(setter_request.extra_body["chat_template_kwargs"][ "add_thinking_start"]) self.assertFalse(setter_request.extra_body["skip_special_tokens"]) + # A zero budget via the property setter disables thinking + setter_request.thinking_token_budget = 0 + self.assertEqual(setter_request.thinking_token_budget, 0) + self.assertNotIn("thinking_token_budget", + setter_request.to_completion_kwargs()["extra_body"]) + self.assertFalse(setter_request.extra_body["chat_template_kwargs"][ + "add_thinking_start"]) + self.assertFalse(setter_request.extra_body["chat_template_kwargs"][ + "enable_thinking"]) + self.assertFalse(setter_request.extra_body["skip_special_tokens"]) # Clearing thinking_token_budget also clears add_thinking_start setter_request.thinking_token_budget = None self.assertIsNone(setter_request.thinking_token_budget) @@ -170,6 +199,10 @@ def test_llm_request(self): self.assertNotIn("add_thinking_start", setter_request.extra_body.get( "chat_template_kwargs", {})) + self.assertNotIn("enable_thinking", + setter_request.extra_body.get( + "chat_template_kwargs", {})) + self.assertNotIn("skip_special_tokens", setter_request.extra_body) # Validate `llm` history input old_history = [("user", "hello"),