From 15feaa8ac49d95e2145f3f1d514d495ea0dedcba Mon Sep 17 00:00:00 2001 From: Rudrasinh Nimeshkumar Ravalji <52209277+rudra717@users.noreply.github.com> Date: Thu, 23 Apr 2026 06:28:12 -0700 Subject: [PATCH] fix(llms): narrow _is_reasoning_model to not match gpt-5.x variants (#4746) Co-authored-by: Claude --- mem0/llms/base.py | 17 ++++++++++------ tests/llms/test_openai.py | 43 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/mem0/llms/base.py b/mem0/llms/base.py index d873cc60e..8812122a5 100644 --- a/mem0/llms/base.py +++ b/mem0/llms/base.py @@ -54,14 +54,19 @@ class LLMBase(ABC): "o1", "o1-preview", "o3-mini", "o3", "gpt-5", "gpt-5o", "gpt-5o-mini", "gpt-5o-micro", } - - if model.lower() in reasoning_models: - return True - + model_lower = model.lower() - if any(reasoning_model in model_lower for reasoning_model in ["gpt-5", "o1", "o3"]): + # Strip provider prefixes (e.g. "openai/o3-mini" -> "o3-mini") + base_model = model_lower.rsplit("/", 1)[-1] + + if base_model in reasoning_models: return True - + + # Match o1/o3 family with prefixes (o1-2024-12-17, o3-2025-04-16) + # but NOT gpt-5.x variants (gpt-5.4-mini supports temperature) + if any(base_model.startswith(prefix) for prefix in ["o1-", "o1.", "o3-", "o3."]): + return True + return False def _get_supported_params(self, **kwargs) -> Dict: diff --git a/tests/llms/test_openai.py b/tests/llms/test_openai.py index 8059a2554..14e91cc75 100644 --- a/tests/llms/test_openai.py +++ b/tests/llms/test_openai.py @@ -285,6 +285,49 @@ def test_store_sent_when_explicitly_false(mock_openai_client): assert call_kwargs["store"] is False +def test_gpt5_mini_not_classified_as_reasoning(mock_openai_client): + """Test that gpt-5.4-mini is NOT treated as a reasoning model. + + gpt-5.4-mini supports temperature and other standard params. + The previous substring match on "gpt-5" incorrectly stripped these. + Regression test for https://github.com/mem0ai/mem0/issues/4738 + """ + config = OpenAIConfig(model="gpt-5.4-mini", temperature=0.1) + llm = OpenAILLM(config) + messages = [{"role": "user", "content": "Hello"}] + + mock_response = Mock() + mock_response.choices = [Mock(message=Mock(content="Response"))] + mock_openai_client.chat.completions.create.return_value = mock_response + + llm.generate_response(messages) + + call_kwargs = mock_openai_client.chat.completions.create.call_args + # gpt-5.4-mini should pass through temperature, not strip it + assert call_kwargs[1].get("temperature") == 0.1 + + +def test_is_reasoning_model_classification(mock_openai_client): + """Test _is_reasoning_model correctly classifies known models.""" + config = OpenAIConfig(model="gpt-4.1") + llm = OpenAILLM(config) + + # Reasoning models — should return True + assert llm._is_reasoning_model("o1") is True + assert llm._is_reasoning_model("o3-mini") is True + assert llm._is_reasoning_model("o3") is True + assert llm._is_reasoning_model("gpt-5") is True + assert llm._is_reasoning_model("o1-preview") is True + assert llm._is_reasoning_model("o1-2024-12-17") is True + assert llm._is_reasoning_model("openai/o3-mini") is True + + # Non-reasoning models — should return False + assert llm._is_reasoning_model("gpt-5.4-mini") is False + assert llm._is_reasoning_model("gpt-5.4") is False + assert llm._is_reasoning_model("gpt-4.1") is False + assert llm._is_reasoning_model("gpt-4.1-nano-2025-04-14") is False + + def test_callback_with_tools(mock_openai_client): mock_callback = Mock() config = OpenAIConfig(model="gpt-4.1-nano-2025-04-14", response_callback=mock_callback)