From 8010bebb6b4758c470ba815c620bf68f5dbb2b5b Mon Sep 17 00:00:00 2001 From: randoentity <137087500+randoentity@users.noreply.github.com> Date: Thu, 8 May 2025 22:24:46 +0200 Subject: [PATCH 1/4] feat: cmd_enable_thinking Adds a command which can be used to enable or disable reasoning by supported models (Qwen3). This is more robust than using the /no_think prompt and saves a small number of tokens. --- aider/args.py | 8 ++++++++ aider/commands.py | 22 ++++++++++++++++++++++ aider/models.py | 19 +++++++++++++++++++ 3 files changed, 49 insertions(+) diff --git a/aider/args.py b/aider/args.py index 08c9bde76..f0a1fe2f2 100644 --- a/aider/args.py +++ b/aider/args.py @@ -145,6 +145,14 @@ def get_parser(default_config_files, git_root): type=str, help="Set the thinking token budget for models that support it (default: not set)", ) + group.add_argument( + "--enable-thinking", + type=str, + help=( + "Switches between thinking and non-thinking modes for models that support it." + " (default: not set)" + ), + ) group.add_argument( "--verify-ssl", action=argparse.BooleanOptionalAction, diff --git a/aider/commands.py b/aider/commands.py index aaf6d7ddd..19f3b7c74 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -1588,6 +1588,28 @@ class Commands: announcements = "\n".join(self.coder.get_announcements()) self.io.tool_output(announcements) + def cmd_enable_thinking(self, args): + """Enable or disable thinking for models that support it.""" + model = self.coder.main_model + if not args.strip(): + # Display current value if no args are provided + thinking_value = model.get_enable_thinking() + if thinking_value is None: + self.io.tool_output("thinking effort is not currently set.") + else: + self.io.tool_output(f"Current thinking setting: {thinking_value}") + return + + value = args.strip() + model.set_enable_thinking(value) + thinking_value = model.get_enable_thinking() + self.io.tool_output(f"Set enable thinking to {thinking_value}") + self.io.tool_output() + + # Output announcements + announcements = "\n".join(self.coder.get_announcements()) + self.io.tool_output(announcements) + def cmd_copy_context(self, args=None): """Copy the current chat context as markdown, suitable to paste into a web UI""" diff --git a/aider/models.py b/aider/models.py index 67f0458ef..1b95f0283 100644 --- a/aider/models.py +++ b/aider/models.py @@ -755,6 +755,15 @@ class Model(ModelSettings): self.extra_params["extra_body"] = {} self.extra_params["extra_body"]["reasoning_effort"] = effort + def set_enable_thinking(self, setting): + """Set the enable thinking parameter for models that support it""" + if setting is not None: + if not self.extra_params: + self.extra_params = {} + if "extra_body" not in self.extra_params: + self.extra_params["extra_body"] = {} + self.extra_params["extra_body"]["enable_thinking"] = setting + def parse_token_value(self, value): """ Parse a token value string into an integer. @@ -864,6 +873,16 @@ class Model(ModelSettings): return self.extra_params["extra_body"]["reasoning_effort"] return None + def get_enable_thinking(self): + """Get enable thinking value if available""" + if ( + self.extra_params + and "extra_body" in self.extra_params + and "enable_thinking" in self.extra_params["extra_body"] + ): + return self.extra_params["extra_body"]["enable_thinking"] + return None + def is_deepseek_r1(self): name = self.name.lower() if "deepseek" not in name: From 48a8f919b52c9a4c80a92f1e224b75a8bf0fe934 Mon Sep 17 00:00:00 2001 From: randoentity <137087500+randoentity@users.noreply.github.com> Date: Thu, 8 May 2025 22:59:58 +0200 Subject: [PATCH 2/4] fixup: setting validation --- aider/commands.py | 2 +- aider/models.py | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/aider/commands.py b/aider/commands.py index 19f3b7c74..cda8c6f7c 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -1601,7 +1601,7 @@ class Commands: return value = args.strip() - model.set_enable_thinking(value) + model.set_enable_thinking(value, self.io) thinking_value = model.get_enable_thinking() self.io.tool_output(f"Set enable thinking to {thinking_value}") self.io.tool_output() diff --git a/aider/models.py b/aider/models.py index 1b95f0283..e1acc562e 100644 --- a/aider/models.py +++ b/aider/models.py @@ -14,6 +14,7 @@ from typing import Optional, Union import json5 import yaml from PIL import Image +from pydantic import TypeAdapter, ValidationError from aider.dump import dump # noqa: F401 from aider.llm import litellm @@ -755,14 +756,18 @@ class Model(ModelSettings): self.extra_params["extra_body"] = {} self.extra_params["extra_body"]["reasoning_effort"] = effort - def set_enable_thinking(self, setting): + def set_enable_thinking(self, setting, io): """Set the enable thinking parameter for models that support it""" if setting is not None: if not self.extra_params: self.extra_params = {} if "extra_body" not in self.extra_params: self.extra_params["extra_body"] = {} - self.extra_params["extra_body"]["enable_thinking"] = setting + try: + setting = TypeAdapter(bool).validate_python(setting) + self.extra_params["extra_body"]["enable_thinking"] = setting + except ValidationError: + io.tool_warning("Warning: the enable-thinking command expects true or false") def parse_token_value(self, value): """ From 92a982ad9a7bdcc9f7e1d251e3d74d3190a462d4 Mon Sep 17 00:00:00 2001 From: randoentity <137087500+randoentity@users.noreply.github.com> Date: Thu, 8 May 2025 23:38:18 +0200 Subject: [PATCH 3/4] fixup: handle bool input --- aider/commands.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/aider/commands.py b/aider/commands.py index cda8c6f7c..71b53413d 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -1591,7 +1591,7 @@ class Commands: def cmd_enable_thinking(self, args): """Enable or disable thinking for models that support it.""" model = self.coder.main_model - if not args.strip(): + if isinstance(args, str) and not args.strip(): # Display current value if no args are provided thinking_value = model.get_enable_thinking() if thinking_value is None: @@ -1600,8 +1600,7 @@ class Commands: self.io.tool_output(f"Current thinking setting: {thinking_value}") return - value = args.strip() - model.set_enable_thinking(value, self.io) + model.set_enable_thinking(args, self.io) thinking_value = model.get_enable_thinking() self.io.tool_output(f"Set enable thinking to {thinking_value}") self.io.tool_output() From c925cb8ad01e810c9c6efae775bc48e9e33dc719 Mon Sep 17 00:00:00 2001 From: randoentity <137087500+randoentity@users.noreply.github.com> Date: Mon, 12 May 2025 23:06:20 +0200 Subject: [PATCH 4/4] fixup: use template_vars --- aider/commands.py | 2 +- aider/models.py | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/aider/commands.py b/aider/commands.py index 71b53413d..e96f5a81a 100644 --- a/aider/commands.py +++ b/aider/commands.py @@ -1595,7 +1595,7 @@ class Commands: # Display current value if no args are provided thinking_value = model.get_enable_thinking() if thinking_value is None: - self.io.tool_output("thinking effort is not currently set.") + self.io.tool_output("Thinking is not currently set.") else: self.io.tool_output(f"Current thinking setting: {thinking_value}") return diff --git a/aider/models.py b/aider/models.py index e1acc562e..026f36ffc 100644 --- a/aider/models.py +++ b/aider/models.py @@ -765,7 +765,7 @@ class Model(ModelSettings): self.extra_params["extra_body"] = {} try: setting = TypeAdapter(bool).validate_python(setting) - self.extra_params["extra_body"]["enable_thinking"] = setting + self.extra_params["extra_body"].setdefault("template_vars", {}).update({"enable_thinking": setting}) except ValidationError: io.tool_warning("Warning: the enable-thinking command expects true or false") @@ -883,9 +883,10 @@ class Model(ModelSettings): if ( self.extra_params and "extra_body" in self.extra_params - and "enable_thinking" in self.extra_params["extra_body"] + and "template_vars" in self.extra_params["extra_body"] + and "enable_thinking" in self.extra_params["extra_body"]["template_vars"] ): - return self.extra_params["extra_body"]["enable_thinking"] + return self.extra_params["extra_body"]["template_vars"]["enable_thinking"] return None def is_deepseek_r1(self):