diff --git a/libs/partners/ollama/langchain_ollama/__init__.py b/libs/partners/ollama/langchain_ollama/__init__.py
index d514785b7a2..6cf0d9a7c2c 100644
--- a/libs/partners/ollama/langchain_ollama/__init__.py
+++ b/libs/partners/ollama/langchain_ollama/__init__.py
@@ -10,6 +10,7 @@ service.
exist locally. This is useful for ensuring that the model is available before
attempting to use it, especially in environments where models may not be
pre-downloaded.
+
"""
from importlib import metadata
@@ -20,6 +21,8 @@ from langchain_ollama.embeddings import OllamaEmbeddings
from langchain_ollama.llms import OllamaLLM
try:
+ if __package__ is None:
+ raise metadata.PackageNotFoundError
__version__ = metadata.version(__package__)
except metadata.PackageNotFoundError:
# Case where package metadata is not available.
diff --git a/libs/partners/ollama/langchain_ollama/_compat.py b/libs/partners/ollama/langchain_ollama/_compat.py
index 9e1d2c235d2..5f9278d093d 100644
--- a/libs/partners/ollama/langchain_ollama/_compat.py
+++ b/libs/partners/ollama/langchain_ollama/_compat.py
@@ -53,8 +53,8 @@ def _convert_content_blocks_to_ollama_format(
text_content += text_block["text"]
elif block_type == "image":
image_block = cast(ImageContentBlock, block)
- if image_block.get("source_type") == "base64":
- images.append(image_block.get("data", ""))
+ if image_block.get("base64"):
+ images.append(image_block.get("base64", ""))
else:
msg = "Only base64 image data is supported by Ollama"
raise ValueError(msg)
diff --git a/libs/partners/ollama/langchain_ollama/chat_models_v1.py b/libs/partners/ollama/langchain_ollama/chat_models_v1.py
index 301d358ee8a..28024f5150a 100644
--- a/libs/partners/ollama/langchain_ollama/chat_models_v1.py
+++ b/libs/partners/ollama/langchain_ollama/chat_models_v1.py
@@ -2,6 +2,8 @@
This implementation provides native support for v1 messages with structured
content blocks and always returns AIMessageV1 format responses.
+
+.. versionadded:: 1.0.0
"""
from __future__ import annotations
diff --git a/libs/partners/ollama/tests/integration_tests/chat_models/test_chat_models_reasoning.py b/libs/partners/ollama/tests/integration_tests/chat_models/test_chat_models_reasoning.py
index 83914979dad..84ad466a32c 100644
--- a/libs/partners/ollama/tests/integration_tests/chat_models/test_chat_models_reasoning.py
+++ b/libs/partners/ollama/tests/integration_tests/chat_models/test_chat_models_reasoning.py
@@ -35,12 +35,10 @@ def test_stream_no_reasoning(model: str) -> None:
result += chunk
assert isinstance(result, AIMessageChunk)
assert result.content
- assert "reasoning_content" not in result.additional_kwargs
assert "" not in result.content and "" not in result.content
- # Only check additional_kwargs for v0 format (content as string)
- if not isinstance(result.content, list):
- assert "" not in result.additional_kwargs.get("reasoning_content", "")
- assert "" not in result.additional_kwargs.get("reasoning_content", "")
+ if hasattr(result, "additional_kwargs"):
+ # v0 format
+ assert "reasoning_content" not in result.additional_kwargs
@pytest.mark.parametrize(("model"), [("deepseek-r1:1.5b")])
@@ -62,12 +60,10 @@ async def test_astream_no_reasoning(model: str) -> None:
result += chunk
assert isinstance(result, AIMessageChunk)
assert result.content
- assert "reasoning_content" not in result.additional_kwargs
assert "" not in result.content and "" not in result.content
- # Only check additional_kwargs for v0 format (content as string)
- if not isinstance(result.content, list):
- assert "" not in result.additional_kwargs.get("reasoning_content", "")
- assert "" not in result.additional_kwargs.get("reasoning_content", "")
+ if hasattr(result, "additional_kwargs"):
+ # v0 format
+ assert "reasoning_content" not in result.additional_kwargs
@pytest.mark.parametrize(("model"), [("deepseek-r1:1.5b")])
@@ -91,8 +87,8 @@ def test_stream_reasoning_none(model: str) -> None:
assert result.content
assert "reasoning_content" not in result.additional_kwargs
assert "" in result.content and "" in result.content
- # Only check additional_kwargs for v0 format (content as string)
if not isinstance(result.content, list):
+ # v0 format (content as string)
assert "" not in result.additional_kwargs.get("reasoning_content", "")
assert "" not in result.additional_kwargs.get("reasoning_content", "")
@@ -118,8 +114,8 @@ async def test_astream_reasoning_none(model: str) -> None:
assert result.content
assert "reasoning_content" not in result.additional_kwargs
assert "" in result.content and "" in result.content
- # Only check additional_kwargs for v0 format (content as string)
if not isinstance(result.content, list):
+ # v0 format (content as string)
assert "" not in result.additional_kwargs.get("reasoning_content", "")
assert "" not in result.additional_kwargs.get("reasoning_content", "")
@@ -146,8 +142,8 @@ def test_reasoning_stream(model: str) -> None:
assert "reasoning_content" in result.additional_kwargs
assert len(result.additional_kwargs["reasoning_content"]) > 0
assert "" not in result.content and "" not in result.content
- # Only check additional_kwargs for v0 format (content as string)
if not isinstance(result.content, list):
+ # v0 format (content as string)
assert "" not in result.additional_kwargs["reasoning_content"]
assert "" not in result.additional_kwargs["reasoning_content"]
@@ -174,8 +170,8 @@ async def test_reasoning_astream(model: str) -> None:
assert "reasoning_content" in result.additional_kwargs
assert len(result.additional_kwargs["reasoning_content"]) > 0
assert "" not in result.content and "" not in result.content
- # Only check additional_kwargs for v0 format (content as string)
if not isinstance(result.content, list):
+ # v0 format (content as string)
assert "" not in result.additional_kwargs["reasoning_content"]
assert "" not in result.additional_kwargs["reasoning_content"]
@@ -188,10 +184,9 @@ def test_invoke_no_reasoning(model: str) -> None:
result = llm.invoke([message])
assert result.content
assert "" not in result.content and "" not in result.content
- # Only check additional_kwargs for v0 format (content as string)
- if not isinstance(result.content, list):
- assert "" not in result.additional_kwargs.get("reasoning_content", "")
- assert "" not in result.additional_kwargs.get("reasoning_content", "")
+ if hasattr(result, "additional_kwargs"):
+ # v0 format
+ assert "reasoning_content" not in result.additional_kwargs
@pytest.mark.parametrize(("model"), [("deepseek-r1:1.5b")])
@@ -202,10 +197,9 @@ async def test_ainvoke_no_reasoning(model: str) -> None:
result = await llm.ainvoke([message])
assert result.content
assert "" not in result.content and "" not in result.content
- # Only check additional_kwargs for v0 format (content as string)
- if not isinstance(result.content, list):
- assert "" not in result.additional_kwargs.get("reasoning_content", "")
- assert "" not in result.additional_kwargs.get("reasoning_content", "")
+ if hasattr(result, "additional_kwargs"):
+ # v0 format
+ assert "reasoning_content" not in result.additional_kwargs
@pytest.mark.parametrize(("model"), [("deepseek-r1:1.5b")])
@@ -217,8 +211,8 @@ def test_invoke_reasoning_none(model: str) -> None:
assert result.content
assert "reasoning_content" not in result.additional_kwargs
assert "" in result.content and "" in result.content
- # Only check additional_kwargs for v0 format (content as string)
if not isinstance(result.content, list):
+ # v0 format (content as string)
assert "" not in result.additional_kwargs.get("reasoning_content", "")
assert "" not in result.additional_kwargs.get("reasoning_content", "")
@@ -232,8 +226,8 @@ async def test_ainvoke_reasoning_none(model: str) -> None:
assert result.content
assert "reasoning_content" not in result.additional_kwargs
assert "" in result.content and "" in result.content
- # Only check additional_kwargs for v0 format (content as string)
if not isinstance(result.content, list):
+ # v0 format (content as string)
assert "" not in result.additional_kwargs.get("reasoning_content", "")
assert "" not in result.additional_kwargs.get("reasoning_content", "")
@@ -248,8 +242,8 @@ def test_reasoning_invoke(model: str) -> None:
assert "reasoning_content" in result.additional_kwargs
assert len(result.additional_kwargs["reasoning_content"]) > 0
assert "" not in result.content and "" not in result.content
- # Only check additional_kwargs for v0 format (content as string)
if not isinstance(result.content, list):
+ # v0 format (content as string)
assert "" not in result.additional_kwargs["reasoning_content"]
assert "" not in result.additional_kwargs["reasoning_content"]
diff --git a/libs/partners/ollama/tests/unit_tests/test_base_v1.py b/libs/partners/ollama/tests/unit_tests/test_base_v1.py
index 592d0059bec..65a12858cf8 100644
--- a/libs/partners/ollama/tests/unit_tests/test_base_v1.py
+++ b/libs/partners/ollama/tests/unit_tests/test_base_v1.py
@@ -13,6 +13,8 @@ from langchain_ollama._compat import (
)
from langchain_ollama.chat_models_v1 import ChatOllamaV1
+MODEL_NAME = "llama3.1"
+
class TestMessageConversion:
"""Test v1 message conversion utilities."""
@@ -34,11 +36,10 @@ class TestMessageConversion:
message = HumanMessageV1(
content=[
TextContentBlock(type="text", text="Describe this image:"),
- ImageContentBlock( # type: ignore[typeddict-unknown-key]
+ ImageContentBlock(
type="image",
mime_type="image/jpeg",
- data="base64imagedata",
- source_type="base64",
+ base64="base64imagedata",
),
]
)
@@ -74,7 +75,7 @@ class TestMessageConversion:
def test_convert_from_ollama_format(self) -> None:
"""Test converting Ollama response to AIMessageV1."""
ollama_response = {
- "model": "llama3",
+ "model": MODEL_NAME,
"created_at": "2024-01-01T00:00:00Z",
"message": {
"role": "assistant",
@@ -93,13 +94,13 @@ class TestMessageConversion:
assert len(result.content) == 1
assert result.content[0]["type"] == "text"
assert result.content[0]["text"] == "Hello! How can I help you today?"
- assert result.response_metadata["model_name"] == "llama3"
- assert result.response_metadata.get("done") is True # type: ignore[typeddict-item]
+ assert result.response_metadata["model_name"] == MODEL_NAME # type: ignore[typeddict-not-required-key]
+ assert result.response_metadata.get("done") is True
def test_convert_chunk_to_v1(self) -> None:
"""Test converting Ollama streaming chunk to AIMessageChunkV1."""
chunk = {
- "model": "llama3",
+ "model": MODEL_NAME,
"created_at": "2024-01-01T00:00:00Z",
"message": {"role": "assistant", "content": "Hello"},
"done": False,
@@ -127,14 +128,14 @@ class TestChatOllamaV1:
def test_initialization(self) -> None:
"""Test ChatOllamaV1 initialization."""
- llm = ChatOllamaV1(model="llama3")
+ llm = ChatOllamaV1(model=MODEL_NAME)
- assert llm.model == "llama3"
+ assert llm.model == MODEL_NAME
assert llm._llm_type == "chat-ollama-v1"
def test_chat_params(self) -> None:
"""Test _chat_params method."""
- llm = ChatOllamaV1(model="llama3", temperature=0.7)
+ llm = ChatOllamaV1(model=MODEL_NAME, temperature=0.7)
messages: list[MessageV1] = [
HumanMessageV1(content=[TextContentBlock(type="text", text="Hello")])
@@ -142,26 +143,28 @@ class TestChatOllamaV1:
params = llm._chat_params(messages)
- assert params["model"] == "llama3"
+ assert params["model"] == MODEL_NAME
assert len(params["messages"]) == 1
assert params["messages"][0]["role"] == "user"
assert params["messages"][0]["content"] == "Hello"
+
+ # Ensure options carry thru
assert params["options"].temperature == 0.7
def test_ls_params(self) -> None:
"""Test LangSmith parameters."""
- llm = ChatOllamaV1(model="llama3", temperature=0.5)
+ llm = ChatOllamaV1(model=MODEL_NAME, temperature=0.5)
ls_params = llm._get_ls_params()
- assert ls_params["ls_provider"] == "ollama"
- assert ls_params["ls_model_name"] == "llama3"
- assert ls_params["ls_model_type"] == "chat"
- assert ls_params["ls_temperature"] == 0.5
+ assert ls_params["ls_provider"] == "ollama" # type: ignore[typeddict-not-required-key]
+ assert ls_params["ls_model_name"] == MODEL_NAME # type: ignore[typeddict-not-required-key]
+ assert ls_params["ls_model_type"] == "chat" # type: ignore[typeddict-not-required-key]
+ assert ls_params["ls_temperature"] == 0.5 # type: ignore[typeddict-not-required-key]
def test_bind_tools_basic(self) -> None:
"""Test basic tool binding functionality."""
- llm = ChatOllamaV1(model="llama3")
+ llm = ChatOllamaV1(model=MODEL_NAME)
def test_tool(query: str) -> str:
"""A test tool."""