From ecabb1bd991bdb2ee6393a10869d358c7b995b9e Mon Sep 17 00:00:00 2001 From: Andrian Balanescu Date: Sun, 12 Jul 2026 07:41:18 +0000 Subject: [PATCH] fix: extract hardcoded tools list into overridable _tools() method LitellmModel._query passed tools=[BASH_TOOL] as a literal, making it impossible to add custom tools without copying the entire method body into a subclass. Extract the tool list into a _tools() method that defaults to [BASH_TOOL], preserving existing behavior while allowing subclasses to override just the tool list. Closes #889 --- src/minisweagent/models/litellm_model.py | 6 ++++- tests/models/test_litellm_model.py | 32 ++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/minisweagent/models/litellm_model.py b/src/minisweagent/models/litellm_model.py index 32bdbc791..49f56b883 100644 --- a/src/minisweagent/models/litellm_model.py +++ b/src/minisweagent/models/litellm_model.py @@ -61,12 +61,16 @@ def __init__(self, *, config_class: Callable = LitellmModelConfig, **kwargs): if self.config.litellm_model_registry and Path(self.config.litellm_model_registry).is_file(): litellm.utils.register_model(json.loads(Path(self.config.litellm_model_registry).read_text())) + def _tools(self) -> list[dict]: + """Tool schemas offered to the model. Override to expose more than bash.""" + return [BASH_TOOL] + def _query(self, messages: list[dict[str, str]], **kwargs): try: return litellm.completion( model=self.config.model_name, messages=messages, - tools=[BASH_TOOL], + tools=self._tools(), **(self.config.model_kwargs | kwargs), ) except litellm.exceptions.AuthenticationError as e: diff --git a/tests/models/test_litellm_model.py b/tests/models/test_litellm_model.py index d4426a780..21e2ace0f 100644 --- a/tests/models/test_litellm_model.py +++ b/tests/models/test_litellm_model.py @@ -38,6 +38,38 @@ def test_query_includes_bash_tool(self, mock_cost, mock_completion): mock_completion.assert_called_once() assert mock_completion.call_args.kwargs["tools"] == [BASH_TOOL] + def test_tools_defaults_to_bash_tool(self): + model = LitellmModel(model_name="gpt-4") + assert model._tools() == [BASH_TOOL] + + @patch("minisweagent.models.litellm_model.litellm.completion") + @patch("minisweagent.models.litellm_model.litellm.cost_calculator.completion_cost") + def test_tools_override_changes_tools_passed_to_completion(self, mock_cost, mock_completion): + custom_tool = { + "type": "function", + "function": { + "name": "custom", + "parameters": {"type": "object", "properties": {}, "required": []}, + }, + } + + class CustomModel(LitellmModel): + def _tools(self): + return [BASH_TOOL, custom_tool] + + tool_call = MagicMock() + tool_call.function.name = "bash" + tool_call.function.arguments = '{"command": "echo test"}' + tool_call.id = "call_1" + mock_completion.return_value = _mock_litellm_response([tool_call]) + mock_cost.return_value = 0.001 + + model = CustomModel(model_name="gpt-4") + model.query([{"role": "user", "content": "test"}]) + + mock_completion.assert_called_once() + assert mock_completion.call_args.kwargs["tools"] == [BASH_TOOL, custom_tool] + @patch("minisweagent.models.litellm_model.litellm.completion") @patch("minisweagent.models.litellm_model.litellm.cost_calculator.completion_cost") def test_parse_actions_valid_tool_call(self, mock_cost, mock_completion):