diff --git a/mindsdb/integrations/utilities/rag/rerankers/base_reranker.py b/mindsdb/integrations/utilities/rag/rerankers/base_reranker.py index 1af4438887..c84b3fa461 100644 --- a/mindsdb/integrations/utilities/rag/rerankers/base_reranker.py +++ b/mindsdb/integrations/utilities/rag/rerankers/base_reranker.py @@ -113,7 +113,14 @@ def _init_client(self): if not openai_api_key: raise ValueError(f"OpenAI API key not found in environment variable {api_key_var}") - base_url = self.base_url or DEFAULT_LLM_ENDPOINT + # For Ollama, the default endpoint is the local Ollama server, not OpenAI. + # Fall back to provider-specific defaults so that users who omit base_url + # get a working configuration out of the box. Fixes gh-11952. + if self.provider == 'ollama': + default_base_url = 'http://localhost:11434/v1' + else: + default_base_url = DEFAULT_LLM_ENDPOINT + base_url = self.base_url or default_base_url self.client = AsyncOpenAI( api_key=openai_api_key, base_url=base_url, timeout=self.request_timeout, max_retries=2 ) diff --git a/tests/unit/interfaces/knowledge_base/test_reranker_base_url.py b/tests/unit/interfaces/knowledge_base/test_reranker_base_url.py new file mode 100644 index 0000000000..a63f22d0ab --- /dev/null +++ b/tests/unit/interfaces/knowledge_base/test_reranker_base_url.py @@ -0,0 +1,97 @@ +"""Tests for BaseLLMReranker provider-specific default base_url (gh-11952). + +When the Ollama provider is configured without an explicit base_url, the +fallback must be http://localhost:11434/v1 — not the OpenAI endpoint. +Before the fix, all Ollama reranker calls silently hit api.openai.com/v1 +and failed with 404. +""" + +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest + +from mindsdb.integrations.utilities.rag.settings import DEFAULT_LLM_ENDPOINT +from mindsdb.integrations.utilities.rag.rerankers.base_reranker import BaseLLMReranker + +OLLAMA_DEFAULT_BASE_URL = "http://localhost:11434/v1" + + +def _make_reranker(**kwargs): + """Construct a BaseLLMReranker with _init_client bypassed.""" + with patch.object(BaseLLMReranker, "_init_client", return_value=None): + obj = BaseLLMReranker(**kwargs) + return obj + + +class TestOllamaDefaultBaseUrl: + """The client must be created with the Ollama-specific URL when base_url is omitted.""" + + def test_ollama_without_base_url_uses_ollama_default(self): + reranker = _make_reranker(provider="ollama", model="llama3.2", api_key="n/a") + reranker.client = None # force re-init + + with patch( + "mindsdb.integrations.utilities.rag.rerankers.base_reranker.AsyncOpenAI" + ) as mock_openai: + reranker._init_client() + + _, kwargs = mock_openai.call_args + assert kwargs.get("base_url") == OLLAMA_DEFAULT_BASE_URL, ( + f"Expected Ollama default {OLLAMA_DEFAULT_BASE_URL!r}, got {kwargs.get('base_url')!r}. " + "Without this fix, Ollama reranker requests hit the OpenAI endpoint and fail with 404." + ) + + def test_ollama_with_explicit_base_url_respects_it(self): + custom_url = "http://my-ollama-server:11434/v1" + reranker = _make_reranker(provider="ollama", model="llama3.2", api_key="n/a", base_url=custom_url) + reranker.client = None + + with patch( + "mindsdb.integrations.utilities.rag.rerankers.base_reranker.AsyncOpenAI" + ) as mock_openai: + reranker._init_client() + + _, kwargs = mock_openai.call_args + assert kwargs.get("base_url") == custom_url + + def test_openai_without_base_url_keeps_openai_default(self): + reranker = _make_reranker(provider="openai", model="gpt-4o", api_key="sk-test") + reranker.client = None + + with patch( + "mindsdb.integrations.utilities.rag.rerankers.base_reranker.AsyncOpenAI" + ) as mock_openai: + reranker._init_client() + + _, kwargs = mock_openai.call_args + assert kwargs.get("base_url") == DEFAULT_LLM_ENDPOINT, ( + "OpenAI provider without explicit base_url must still default to DEFAULT_LLM_ENDPOINT." + ) + + def test_openai_with_explicit_base_url_respects_it(self): + custom_url = "https://my-openai-proxy.example.com/v1" + reranker = _make_reranker(provider="openai", model="gpt-4o", api_key="sk-test", base_url=custom_url) + reranker.client = None + + with patch( + "mindsdb.integrations.utilities.rag.rerankers.base_reranker.AsyncOpenAI" + ) as mock_openai: + reranker._init_client() + + _, kwargs = mock_openai.call_args + assert kwargs.get("base_url") == custom_url + + def test_ollama_default_is_not_openai_endpoint(self): + """Regression: Ollama default must not be the OpenAI endpoint.""" + reranker = _make_reranker(provider="ollama", model="llama3.2", api_key="n/a") + reranker.client = None + + with patch( + "mindsdb.integrations.utilities.rag.rerankers.base_reranker.AsyncOpenAI" + ) as mock_openai: + reranker._init_client() + + _, kwargs = mock_openai.call_args + assert kwargs.get("base_url") != DEFAULT_LLM_ENDPOINT, ( + "Ollama default base_url must not be the OpenAI API endpoint." + )