fix(ollama): exclude None parameters from options dictionary (#33208)

This commit is contained in:
Mason Daugherty
2025-10-02 11:25:15 -04:00
committed by GitHub
parent eaa6dcce9e
commit 63097db4fc
2 changed files with 125 additions and 21 deletions

View File

@@ -741,26 +741,30 @@ class ChatOllama(BaseChatModel):
if self.stop is not None: if self.stop is not None:
stop = self.stop stop = self.stop
options_dict = kwargs.pop( options_dict = kwargs.pop("options", None)
"options", if options_dict is None:
{ # Only include parameters that are explicitly set (not None)
"mirostat": self.mirostat, options_dict = {
"mirostat_eta": self.mirostat_eta, k: v
"mirostat_tau": self.mirostat_tau, for k, v in {
"num_ctx": self.num_ctx, "mirostat": self.mirostat,
"num_gpu": self.num_gpu, "mirostat_eta": self.mirostat_eta,
"num_thread": self.num_thread, "mirostat_tau": self.mirostat_tau,
"num_predict": self.num_predict, "num_ctx": self.num_ctx,
"repeat_last_n": self.repeat_last_n, "num_gpu": self.num_gpu,
"repeat_penalty": self.repeat_penalty, "num_thread": self.num_thread,
"temperature": self.temperature, "num_predict": self.num_predict,
"seed": self.seed, "repeat_last_n": self.repeat_last_n,
"stop": self.stop if stop is None else stop, "repeat_penalty": self.repeat_penalty,
"tfs_z": self.tfs_z, "temperature": self.temperature,
"top_k": self.top_k, "seed": self.seed,
"top_p": self.top_p, "stop": self.stop if stop is None else stop,
}, "tfs_z": self.tfs_z,
) "top_k": self.top_k,
"top_p": self.top_p,
}.items()
if v is not None
}
params = { params = {
"messages": ollama_messages, "messages": ollama_messages,

View File

@@ -24,7 +24,7 @@ MODEL_NAME = "llama3.1"
@contextmanager @contextmanager
def _mock_httpx_client_stream( def _mock_httpx_client_stream(
*args: Any, **kwargs: Any *_args: Any, **_kwargs: Any
) -> Generator[Response, Any, Any]: ) -> Generator[Response, Any, Any]:
yield Response( yield Response(
status_code=200, status_code=200,
@@ -310,3 +310,103 @@ def test_load_response_with_actual_content_is_not_skipped(
assert result.content == "This is actual content" assert result.content == "This is actual content"
assert result.response_metadata.get("done_reason") == "load" assert result.response_metadata.get("done_reason") == "load"
assert not caplog.text assert not caplog.text
def test_none_parameters_excluded_from_options() -> None:
"""Test that None parameters are excluded from the options dict sent to Ollama."""
response = [
{
"model": "test-model",
"created_at": "2025-01-01T00:00:00.000000000Z",
"done": True,
"done_reason": "stop",
"message": {"role": "assistant", "content": "Hello!"},
}
]
with patch("langchain_ollama.chat_models.Client") as mock_client_class:
mock_client = MagicMock()
mock_client_class.return_value = mock_client
mock_client.chat.return_value = response
# Create ChatOllama with only num_ctx set
llm = ChatOllama(model="test-model", num_ctx=4096)
llm.invoke([HumanMessage("Hello")])
# Verify that chat was called
assert mock_client.chat.called
# Get the options dict that was passed to chat
call_kwargs = mock_client.chat.call_args[1]
options = call_kwargs.get("options", {})
# Only num_ctx should be in options, not None parameters
assert "num_ctx" in options
assert options["num_ctx"] == 4096
# These parameters should NOT be in options since they were None
assert "mirostat" not in options
assert "mirostat_eta" not in options
assert "mirostat_tau" not in options
assert "tfs_z" not in options
def test_all_none_parameters_results_in_empty_options() -> None:
"""Test that when all parameters are None, options dict is empty."""
response = [
{
"model": "test-model",
"created_at": "2025-01-01T00:00:00.000000000Z",
"done": True,
"done_reason": "stop",
"message": {"role": "assistant", "content": "Hello!"},
}
]
with patch("langchain_ollama.chat_models.Client") as mock_client_class:
mock_client = MagicMock()
mock_client_class.return_value = mock_client
mock_client.chat.return_value = response
# Create ChatOllama with no parameters set
llm = ChatOllama(model="test-model")
llm.invoke([HumanMessage("Hello")])
# Get the options dict that was passed to chat
call_kwargs = mock_client.chat.call_args[1]
options = call_kwargs.get("options", {})
# Options should be empty when no parameters are set
assert options == {}
def test_explicit_options_dict_preserved() -> None:
"""Test that explicitly provided options dict is preserved and not filtered."""
response = [
{
"model": "test-model",
"created_at": "2025-01-01T00:00:00.000000000Z",
"done": True,
"done_reason": "stop",
"message": {"role": "assistant", "content": "Hello!"},
}
]
with patch("langchain_ollama.chat_models.Client") as mock_client_class:
mock_client = MagicMock()
mock_client_class.return_value = mock_client
mock_client.chat.return_value = response
llm = ChatOllama(model="test-model")
# Pass explicit options dict, including None values
llm.invoke(
[HumanMessage("Hello")],
options={"temperature": 0.5, "custom_param": None},
)
# Get the options dict that was passed to chat
call_kwargs = mock_client.chat.call_args[1]
options = call_kwargs.get("options", {})
# Explicit options should be preserved as-is
assert options == {"temperature": 0.5, "custom_param": None}