remove some TODOs, simplify some assertions, format

This commit is contained in:
Mason Daugherty 2025-08-06 18:00:44 -04:00
parent eaba6bf650
commit c0cc74db46
No known key found for this signature in database
5 changed files with 60 additions and 168 deletions

View File

@ -570,9 +570,7 @@ def _convert_to_message_v1(message: MessageLikeRepresentation) -> MessageV1:
tool_call = ToolCall( tool_call = ToolCall(
type="tool_call", type="tool_call",
name=message[ name=message["name"],
"name"
], # TODO: revisit, this is the name of the message, not the tool
args=message["args"], args=message["args"],
id=message["id"], id=message["id"],
) )

View File

@ -173,26 +173,27 @@ class TestChatOllamaV1(ChatModelV1IntegrationTests):
def test_agent_loop(self, model: BaseChatModel) -> None: def test_agent_loop(self, model: BaseChatModel) -> None:
super().test_agent_loop(model) super().test_agent_loop(model)
@pytest.mark.xfail( # TODO
reason=( # @pytest.mark.xfail(
"No single Ollama model supports both multimodal content and reasoning. " # reason=(
"Override skips test due to model limitations." # "No single Ollama model supports both multimodal content and reasoning. "
) # "Override skips test due to model limitations."
) # )
def test_multimodal_reasoning(self, model: BaseChatModel) -> None: # )
"""Test complex reasoning with multiple content types. # def test_multimodal_reasoning(self, model: BaseChatModel) -> None:
# """Test complex reasoning with multiple content types.
This test overrides the default model to use a reasoning-capable model # This test overrides the default model to use a reasoning-capable model
with reasoning mode explicitly enabled. Note that this test requires # with reasoning mode explicitly enabled. Note that this test requires
both multimodal support AND reasoning support. # both multimodal support AND reasoning support.
""" # """
if not self.supports_multimodal_reasoning: # if not self.supports_multimodal_reasoning:
pytest.skip("Model does not support multimodal reasoning.") # pytest.skip("Model does not support multimodal reasoning.")
pytest.skip( # pytest.skip(
"TODO: Update this when we have a model that supports both multimodal and " # "TODO: Update this when we have a model that supports both multimodal and " # noqa: E501
"reasoning." # "reasoning."
) # )
@pytest.mark.xfail( @pytest.mark.xfail(
reason=( reason=(

View File

@ -40,7 +40,6 @@ from langchain_core.messages.content_blocks import (
create_plaintext_block, create_plaintext_block,
create_text_block, create_text_block,
create_tool_call, create_tool_call,
create_video_block,
is_reasoning_block, is_reasoning_block,
is_text_block, is_text_block,
is_tool_call_block, is_tool_call_block,
@ -89,26 +88,6 @@ ContentBlock = Union[
] ]
def _get_test_image_base64() -> str:
"""Get a small test image as base64 for testing."""
# 1x1 pixel transparent PNG
return "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==" # noqa: E501
def _get_test_audio_base64() -> str:
"""Get a small test audio file as base64 for testing."""
# Minimal WAV file (1 second of silence)
return (
"UklGRjIAAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQ4AAAAAAAAAAAAAAAAAAA=="
)
def _get_test_video_base64() -> str:
"""Get a small test video file as base64 for testing."""
# Minimal valid video file would be much larger; for testing we use a placeholder
return "PLACEHOLDER_VIDEO_DATA"
def _get_joke_class( def _get_joke_class(
schema_type: Literal["pydantic", "typeddict", "json_schema"], schema_type: Literal["pydantic", "typeddict", "json_schema"],
) -> Any: ) -> Any:
@ -492,11 +471,7 @@ class ChatModelV1IntegrationTests(ChatModelV1Tests):
result = model.invoke("Hello") result = model.invoke("Hello")
assert result is not None assert result is not None
assert isinstance(result, AIMessage) assert isinstance(result, AIMessage)
assert isinstance(result.text, str) assert result.text
assert len(result.content) > 0
text_contentblock = result.content[0]
assert is_text_block(text_contentblock)
async def test_ainvoke(self, model: BaseChatModel) -> None: async def test_ainvoke(self, model: BaseChatModel) -> None:
"""Test to verify that ``await model.ainvoke(simple_message)`` works. """Test to verify that ``await model.ainvoke(simple_message)`` works.
@ -522,11 +497,7 @@ class ChatModelV1IntegrationTests(ChatModelV1Tests):
result = await model.ainvoke("Hello") result = await model.ainvoke("Hello")
assert result is not None assert result is not None
assert isinstance(result, AIMessage) assert isinstance(result, AIMessage)
assert isinstance(result.text, str) assert result.text
assert len(result.content) > 0
text_contentblock = result.content[0]
assert is_text_block(text_contentblock)
def test_stream(self, model: BaseChatModel) -> None: def test_stream(self, model: BaseChatModel) -> None:
"""Test to verify that ``model.stream(simple_message)`` works. """Test to verify that ``model.stream(simple_message)`` works.
@ -616,9 +587,7 @@ class ChatModelV1IntegrationTests(ChatModelV1Tests):
for result in batch_results: for result in batch_results:
assert result is not None assert result is not None
assert isinstance(result, AIMessage) assert isinstance(result, AIMessage)
assert len(result.content) > 0 assert result.text
assert isinstance(result.text, str)
assert len(result.text) > 0
async def test_abatch(self, model: BaseChatModel) -> None: async def test_abatch(self, model: BaseChatModel) -> None:
"""Test to verify that ``await model.abatch([messages])`` works. """Test to verify that ``await model.abatch([messages])`` works.
@ -656,9 +625,7 @@ class ChatModelV1IntegrationTests(ChatModelV1Tests):
for result in batch_results: for result in batch_results:
assert result is not None assert result is not None
assert isinstance(result, AIMessage) assert isinstance(result, AIMessage)
assert len(result.content) > 0 assert result.text
assert isinstance(result.text, str)
assert len(result.text) > 0
def test_conversation(self, model: BaseChatModel) -> None: def test_conversation(self, model: BaseChatModel) -> None:
"""Test to verify that the model can handle multi-turn conversations. """Test to verify that the model can handle multi-turn conversations.
@ -690,9 +657,7 @@ class ChatModelV1IntegrationTests(ChatModelV1Tests):
result = model.invoke(messages) # type: ignore[arg-type] result = model.invoke(messages) # type: ignore[arg-type]
assert result is not None assert result is not None
assert isinstance(result, AIMessage) assert isinstance(result, AIMessage)
assert len(result.content) > 0 assert result.text
assert isinstance(result.text, str)
assert len(result.text) > 0
def test_double_messages_conversation(self, model: BaseChatModel) -> None: def test_double_messages_conversation(self, model: BaseChatModel) -> None:
"""Test to verify that the model can handle double-message conversations. """Test to verify that the model can handle double-message conversations.
@ -731,9 +696,7 @@ class ChatModelV1IntegrationTests(ChatModelV1Tests):
result = model.invoke(messages) # type: ignore[arg-type] result = model.invoke(messages) # type: ignore[arg-type]
assert result is not None assert result is not None
assert isinstance(result, AIMessage) assert isinstance(result, AIMessage)
assert len(result.content) > 0 assert result.text
assert isinstance(result.text, str)
assert len(result.text) > 0
def test_usage_metadata(self, model: BaseChatModel) -> None: def test_usage_metadata(self, model: BaseChatModel) -> None:
"""Test to verify that the model returns correct usage metadata. """Test to verify that the model returns correct usage metadata.
@ -1070,25 +1033,6 @@ class ChatModelV1IntegrationTests(ChatModelV1Tests):
pytest.skip("Test requires tool calling.") pytest.skip("Test requires tool calling.")
tool_choice_value = None if not self.has_tool_choice else "any" tool_choice_value = None if not self.has_tool_choice else "any"
# Emit warning if tool_choice_value property is overridden
# TODO remove since deprecated?
# if inspect.getattr_static(
# self, "tool_choice_value"
# ) is not inspect.getattr_static(
# ChatModelV1IntegrationTests, "tool_choice_value"
# ):
# warn_deprecated(
# "0.3.15",
# message=(
# "`tool_choice_value` will be removed in version 0.3.20. If a "
# "model supports `tool_choice`, it should accept `tool_choice='any' " # noqa: E501
# "and `tool_choice=<string name of tool>`. If the model does not "
# "support `tool_choice`, override the `supports_tool_choice` "
# "property to return `False`."
# ),
# removal="0.3.20",
# )
model_with_tools = model.bind_tools( model_with_tools = model.bind_tools(
[magic_function], tool_choice=tool_choice_value [magic_function], tool_choice=tool_choice_value
@ -2297,54 +2241,6 @@ class ChatModelV1IntegrationTests(ChatModelV1Tests):
# _ = model.bind_tools([random_image]).invoke(messages) # _ = model.bind_tools([random_image]).invoke(messages)
def test_image_content_blocks_with_analysis(self, model: BaseChatModel) -> None:
"""Test image analysis using ``ImageContentBlock``s.
TODO: expand docstring
"""
if not self.supports_image_content_blocks:
pytest.skip("Model does not support image inputs.")
image_block = create_image_block(
base64=_get_test_image_base64(),
mime_type="image/png",
)
text_block = create_text_block("Analyze this image in detail.")
result = model.invoke([HumanMessage([text_block, image_block])])
assert isinstance(result, AIMessage)
text_blocks = [
block
for block in result.content
if isinstance(block, dict) and is_text_block(block)
]
assert len(text_blocks) > 0
if result.text:
assert len(result.text) > 10 # Substantial response
def test_video_content_blocks(self, model: BaseChatModel) -> None:
"""Test video content block processing.
TODO: expand docstring
"""
if not self.supports_video_content_blocks:
pytest.skip("Model does not support video inputs.")
video_block = create_video_block(
base64=_get_test_video_base64(),
mime_type="video/mp4",
)
text_block = create_text_block("Describe what you see in this video.")
result = model.invoke([HumanMessage([text_block, video_block])])
assert isinstance(result, AIMessage)
if result.text:
assert len(result.text) > 10 # Substantial response
def test_anthropic_inputs(self, model: BaseChatModel) -> None: def test_anthropic_inputs(self, model: BaseChatModel) -> None:
"""Test that model can process Anthropic-style message histories. """Test that model can process Anthropic-style message histories.
@ -2767,45 +2663,46 @@ class ChatModelV1IntegrationTests(ChatModelV1Tests):
or "" in customer_name_jp or "" in customer_name_jp
), f"Japanese Unicode characters not found in: {customer_name_jp}" ), f"Japanese Unicode characters not found in: {customer_name_jp}"
def test_multimodal_reasoning(self, model: BaseChatModel) -> None: # TODO
"""Test complex reasoning with multiple content types. # def test_multimodal_reasoning(self, model: BaseChatModel) -> None:
# """Test complex reasoning with multiple content types.
TODO: expand docstring # TODO: expand docstring
""" # """
if not self.supports_multimodal_reasoning: # if not self.supports_multimodal_reasoning:
pytest.skip("Model does not support multimodal reasoning.") # pytest.skip("Model does not support multimodal reasoning.")
content_blocks: list[types.ContentBlock] = [ # content_blocks: list[types.ContentBlock] = [
create_text_block( # create_text_block(
"Compare these media files and provide reasoning analysis:" # "Compare these media files and provide reasoning analysis:"
), # ),
create_image_block( # create_image_block(
base64=_get_test_image_base64(), # base64=_get_test_image_base64(),
mime_type="image/png", # mime_type="image/png",
), # ),
] # ]
if self.supports_audio_content_blocks: # if self.supports_audio_content_blocks:
content_blocks.append( # content_blocks.append(
create_audio_block( # create_audio_block(
base64=_get_test_audio_base64(), # base64=_get_test_audio_base64(),
mime_type="audio/wav", # mime_type="audio/wav",
) # )
) # )
message = HumanMessage(content=cast("list[types.ContentBlock]", content_blocks)) # message = HumanMessage(content=cast("list[types.ContentBlock]", content_blocks)) # noqa: E501
result = model.invoke([message]) # result = model.invoke([message])
assert isinstance(result, AIMessage) # assert isinstance(result, AIMessage)
if self.supports_reasoning_content_blocks: # if self.supports_reasoning_content_blocks:
reasoning_blocks = [ # reasoning_blocks = [
block # block
for block in result.content # for block in result.content
if isinstance(block, dict) and is_reasoning_block(block) # if isinstance(block, dict) and is_reasoning_block(block)
] # ]
assert len(reasoning_blocks) > 0 # assert len(reasoning_blocks) > 0
def test_citation_generation_with_sources(self, model: BaseChatModel) -> None: def test_citation_generation_with_sources(self, model: BaseChatModel) -> None:
"""Test that the model can generate ``Citations`` with source links. """Test that the model can generate ``Citations`` with source links.

View File

@ -67,8 +67,6 @@ class ChatParrotLinkV1(BaseChatModel):
for block in last_message.content: for block in last_message.content:
if isinstance(block, dict) and block.get("type") == "text": if isinstance(block, dict) and block.get("type") == "text":
text_content += str(block.get("text", "")) text_content += str(block.get("text", ""))
# elif isinstance(block, str):
# text_content += block
# Echo the first parrot_buffer_length characters # Echo the first parrot_buffer_length characters
echoed_text = text_content[: self.parrot_buffer_length] echoed_text = text_content[: self.parrot_buffer_length]
@ -136,8 +134,6 @@ class ChatParrotLinkV1(BaseChatModel):
for block in last_message.content: for block in last_message.content:
if isinstance(block, dict) and block.get("type") == "text": if isinstance(block, dict) and block.get("type") == "text":
text_content += str(block.get("text", "")) text_content += str(block.get("text", ""))
# elif isinstance(block, str):
# text_content += block
# Echo the first parrot_buffer_length characters # Echo the first parrot_buffer_length characters
echoed_text = text_content[: self.parrot_buffer_length] echoed_text = text_content[: self.parrot_buffer_length]

View File

@ -1,4 +1,4 @@
"""Test the standard v1 tests on the ChatParrotLinkV1 custom chat model.""" """Test the standard v1 tests on the ``ChatParrotLinkV1`` custom chat model."""
import pytest import pytest