This commit is contained in:
Mason Daugherty
2025-08-05 16:15:28 -04:00
parent 85511893f7
commit 2087294760
4 changed files with 57 additions and 47 deletions

View File

@@ -70,7 +70,7 @@ The module defines several types of content blocks, including:
- ``TextContentBlock``: Standard text.
- ``ImageContentBlock``, ``Audio...``, ``Video...``, ``PlainText...``, ``File...``: For multimodal data.
- ``ToolCallContentBlock``, ``ToolOutputContentBlock``: For function calling.
- ``ToolCallContentBlock``: For function calling.
- ``ReasoningContentBlock``: To capture a model's thought process.
- ``Citation``: For annotations that link generated text to a source document.

View File

@@ -135,6 +135,7 @@ def unicode_customer(customer_name: str, description: str) -> str:
Returns:
A confirmation message about the customer creation.
"""
return f"Created customer: {customer_name} - {description}"
@@ -680,13 +681,13 @@ class ChatModelIntegrationTests(ChatModelTests):
return {}
def test_invoke(self, model: BaseChatModel) -> None:
"""Test to verify that `model.invoke(simple_message)` works.
"""Test to verify that ``model.invoke(simple_message)`` works.
This should pass for all integrations.
.. dropdown:: Troubleshooting
If this test fails, you should make sure your _generate method
If this test fails, you should make sure your ``_generate`` method
does not raise any exceptions, and that it returns a valid
:class:`~langchain_core.outputs.chat_result.ChatResult` like so:
@@ -706,7 +707,7 @@ class ChatModelIntegrationTests(ChatModelTests):
assert len(result.content) > 0
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.
This should pass for all integrations. Passing this test does not indicate
a "natively async" implementation, but rather that the model can be used
@@ -716,7 +717,7 @@ class ChatModelIntegrationTests(ChatModelTests):
First, debug
:meth:`~langchain_tests.integration_tests.chat_models.ChatModelIntegrationTests.test_invoke`.
because `ainvoke` has a default implementation that calls `invoke` in an
because ``ainvoke`` has a default implementation that calls ``invoke`` in an
async context.
If that test passes but not this one, you should make sure your _agenerate
@@ -739,7 +740,7 @@ class ChatModelIntegrationTests(ChatModelTests):
assert len(result.content) > 0
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.
This should pass for all integrations. Passing this test does not indicate
a "streaming" implementation, but rather that the model can be used in a
@@ -772,7 +773,7 @@ class ChatModelIntegrationTests(ChatModelTests):
assert num_chunks > 0
async def test_astream(self, model: BaseChatModel) -> None:
"""Test to verify that `await model.astream(simple_message)` works.
"""Test to verify that ``await model.astream(simple_message)`` works.
This should pass for all integrations. Passing this test does not indicate
a "natively async" or "streaming" implementation, but rather that the model can
@@ -809,7 +810,7 @@ class ChatModelIntegrationTests(ChatModelTests):
assert num_chunks > 0
def test_batch(self, model: BaseChatModel) -> None:
"""Test to verify that `model.batch([messages])` works.
"""Test to verify that ``model.batch([messages])`` works.
This should pass for all integrations. Tests the model's ability to process
multiple prompts in a single batch.
@@ -836,7 +837,7 @@ class ChatModelIntegrationTests(ChatModelTests):
assert len(result.content) > 0
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.
This should pass for all integrations. Tests the model's ability to process
multiple prompts in a single batch asynchronously.
@@ -853,6 +854,7 @@ class ChatModelIntegrationTests(ChatModelTests):
If those tests pass but not this one, you should make sure your `abatch`
method does not raise any exceptions, and that it returns a list of valid
:class:`~langchain_core.messages.AIMessage` objects.
"""
batch_results = await model.abatch(["Hello", "Hey"])
assert batch_results is not None
@@ -881,6 +883,7 @@ class ChatModelIntegrationTests(ChatModelTests):
1. Your model correctly processes the message history
2. The model maintains appropriate context from previous messages
3. The response is a valid :class:`~langchain_core.messages.AIMessage`
"""
messages = [
HumanMessage("hello"),
@@ -914,6 +917,7 @@ class ChatModelIntegrationTests(ChatModelTests):
1. Your model API can handle double messages, or the integration should
merge messages before sending them to the API.
2. The response is a valid :class:`~langchain_core.messages.AIMessage`
"""
messages = [
SystemMessage("hello"),
@@ -938,7 +942,7 @@ class ChatModelIntegrationTests(ChatModelTests):
.. versionchanged:: 0.3.17
Additionally check for the presence of `model_name` in the response
Additionally check for the presence of ``model_name`` in the response
metadata, which is needed for usage tracking in callback handlers.
.. dropdown:: Configuration
@@ -987,7 +991,7 @@ class ChatModelIntegrationTests(ChatModelTests):
If this test fails, first verify that your model returns
:class:`~langchain_core.messages.ai.UsageMetadata` dicts
attached to the returned AIMessage object in `_generate`:
attached to the returned AIMessage object in ``_generate``:
.. code-block:: python
@@ -1105,7 +1109,7 @@ class ChatModelIntegrationTests(ChatModelTests):
.. versionchanged:: 0.3.17
Additionally check for the presence of `model_name` in the response
Additionally check for the presence of ``model_name`` in the response
metadata, which is needed for usage tracking in callback handlers.
.. dropdown:: Configuration
@@ -1152,16 +1156,16 @@ class ChatModelIntegrationTests(ChatModelTests):
If this test fails, first verify that your model yields
:class:`~langchain_core.messages.ai.UsageMetadata` dicts
attached to the returned AIMessage object in `_stream`
attached to the returned AIMessage object in ``_stream``
that sum up to the total usage metadata.
Note that `input_tokens` should only be included on one of the chunks
(typically the first or the last chunk), and the rest should have 0 or None
to avoid counting input tokens multiple times.
Note that ``input_tokens`` should only be included on one of the chunks
(typically the first or the last chunk), and the rest should have ``0`` or
``None`` to avoid counting input tokens multiple times.
`output_tokens` typically count the number of tokens in each chunk, not the
sum. This test will pass as long as the sum of `output_tokens` across all
chunks is not 0.
``output_tokens`` typically count the number of tokens in each chunk, not
the sum. This test will pass as long as the sum of ``output_tokens`` across
all chunks is not ``0``.
.. code-block:: python
@@ -1261,7 +1265,7 @@ class ChatModelIntegrationTests(ChatModelTests):
"""Test that model does not fail when invoked with the ``stop`` parameter,
which is a standard parameter for stopping generation at a certain token.
More on standard parameters here: https://python.langchain.com/docs/concepts/chat_models/#standard-parameters
`More on standard parameters <https://python.langchain.com/docs/concepts/chat_models/#standard-parameters>`__
This should pass for all integrations.
@@ -1569,7 +1573,7 @@ class ChatModelIntegrationTests(ChatModelTests):
"""Test that message histories are compatible with list tool contents
(e.g. Anthropic format).
These message histories will include AIMessage objects with "tool use" and
These message histories will include ``AIMessage`` objects with "tool use" and
content blocks, e.g.,
.. code-block:: python
@@ -1603,8 +1607,8 @@ class ChatModelIntegrationTests(ChatModelTests):
If this test fails, check that:
1. The model can correctly handle message histories that include AIMessage objects with list content.
2. The ``tool_calls`` attribute on AIMessage objects is correctly handled and passed to the model in an appropriate format.
1. The model can correctly handle message histories that include ``AIMessage`` objects with list content.
2. The ``tool_calls`` attribute on ``AIMessage`` objects is correctly handled and passed to the model in an appropriate format.
3. The model can correctly handle ToolMessage objects with string content and arbitrary string values for ``tool_call_id``.
You can ``xfail`` the test if tool calling is implemented but this format
@@ -2052,9 +2056,9 @@ class ChatModelIntegrationTests(ChatModelTests):
@pytest.mark.skipif(PYDANTIC_MAJOR_VERSION != 2, reason="Test requires pydantic 2.")
def test_structured_output_pydantic_2_v1(self, model: BaseChatModel) -> None:
"""Test to verify we can generate structured output using
pydantic.v1.BaseModel.
``pydantic.v1.BaseModel``.
pydantic.v1.BaseModel is available in the pydantic 2 package.
``pydantic.v1.BaseModel`` is available in the pydantic 2 package.
This test is optional and should be skipped if the model does not support
structured output (see Configuration below).
@@ -2686,7 +2690,7 @@ class ChatModelIntegrationTests(ChatModelTests):
1. The model can correctly handle message histories that include message objects with list content.
2. The ``tool_calls`` attribute on AIMessage objects is correctly handled and passed to the model in an appropriate format.
3. HumanMessages with "tool_result" content blocks are correctly handled.
3. ``HumanMessage``s with "tool_result" content blocks are correctly handled.
Otherwise, if Anthropic tool call and result formats are not supported,
set the ``supports_anthropic_inputs`` property to False.
@@ -2792,7 +2796,7 @@ class ChatModelIntegrationTests(ChatModelTests):
assert isinstance(response, AIMessage)
def test_message_with_name(self, model: BaseChatModel) -> None:
"""Test that HumanMessage with values for the ``name`` field can be handled.
"""Test that ``HumanMessage`` with values for the ``name`` field can be handled.
These messages may take the form:
@@ -2953,11 +2957,12 @@ class ChatModelIntegrationTests(ChatModelTests):
Args:
model: The chat model to test
tool_choice: Tool choice parameter to pass to bind_tools (provider-specific)
force_tool_call: Whether to force a tool call (use tool_choice=True if None)
force_tool_call: Whether to force a tool call (use ``tool_choice=True`` if None)
Tests that Unicode characters in tool call arguments are preserved correctly,
not escaped as \\uXXXX sequences.
"""
not escaped as ``\\uXXXX`` sequences.
""" # noqa: E501
if not self.has_tool_calling:
pytest.skip("Test requires tool calling support.")

View File

@@ -26,6 +26,7 @@ def generate_schema_pydantic_v1_from_2() -> Any:
"""Use to generate a schema from v1 namespace in pydantic 2.
:private:
"""
if PYDANTIC_MAJOR_VERSION != 2:
msg = "This function is only compatible with Pydantic v2."
@@ -44,6 +45,7 @@ def generate_schema_pydantic() -> Any:
"""Works with either pydantic 1 or 2.
:private:
"""
class PersonA(BaseModel):
@@ -65,6 +67,7 @@ class ChatModelTests(BaseStandardTests):
"""Base class for chat model tests.
:private:
"""
@property
@@ -148,16 +151,12 @@ class ChatModelTests(BaseStandardTests):
@property
def supports_image_inputs(self) -> bool:
"""(bool) whether the chat model supports image inputs, defaults to
``False``.
"""
"""(bool) whether the chat model supports image inputs, defaults to ``False``.""" # noqa: E501
return False
@property
def supports_image_urls(self) -> bool:
"""(bool) whether the chat model supports image inputs from URLs, defaults to
``False``.
"""
"""(bool) whether the chat model supports image inputs from URLs, defaults to ``False``.""" # noqa: E501
return False
@property
@@ -167,23 +166,21 @@ class ChatModelTests(BaseStandardTests):
@property
def supports_audio_inputs(self) -> bool:
"""(bool) whether the chat model supports audio inputs, defaults to
``False``.
"""
"""(bool) whether the chat model supports audio inputs, defaults to ``False``.""" # noqa: E501
return False
@property
def supports_video_inputs(self) -> bool:
"""(bool) whether the chat model supports video inputs, defaults to ``False``.
No current tests are written for this feature.
"""
return False
@property
def returns_usage_metadata(self) -> bool:
"""(bool) whether the chat model returns usage metadata on invoke and streaming
responses.
"""
"""(bool) whether the chat model returns usage metadata on invoke and streaming responses.""" # noqa: E501
return True
@property
@@ -193,9 +190,7 @@ class ChatModelTests(BaseStandardTests):
@property
def supports_image_tool_message(self) -> bool:
"""(bool) whether the chat model supports ``ToolMessage``s that include image
content.
"""
"""(bool) whether the chat model supports ``ToolMessage``s that include image content.""" # noqa: E501
return False
@property
@@ -205,6 +200,7 @@ class ChatModelTests(BaseStandardTests):
.. important::
See ``enable_vcr_tests`` dropdown :class:`above <ChatModelTests>` for more
information.
"""
return False
@@ -806,6 +802,7 @@ class ChatModelUnitTests(ChatModelTests):
def init_from_env_params(self) -> tuple[dict, dict, dict]:
"""(tuple) environment variables, additional initialization args, and expected
instance attributes for testing initialization from environment variables.
"""
return {}, {}, {}
@@ -818,6 +815,7 @@ class ChatModelUnitTests(ChatModelTests):
1. ``chat_model_params`` is specified and the model can be initialized from those params;
2. The model accommodates `standard parameters <https://python.langchain.com/docs/concepts/chat_models/#standard-parameters>`__
""" # noqa: E501
model = self.chat_model_class(
**{
@@ -837,6 +835,7 @@ class ChatModelUnitTests(ChatModelTests):
If this test fails, ensure that ``init_from_env_params`` is specified
correctly and that model parameters are properly set from environment
variables during initialization.
"""
env_params, model_params, expected_attrs = self.init_from_env_params
if not env_params:
@@ -861,6 +860,7 @@ class ChatModelUnitTests(ChatModelTests):
If this test fails, ensure that the model can be initialized with a
boolean ``streaming`` parameter.
"""
model = self.chat_model_class(
**{
@@ -887,6 +887,7 @@ class ChatModelUnitTests(ChatModelTests):
a utility function that will accommodate most formats: https://python.langchain.com/api_reference/core/utils/langchain_core.utils.function_calling.convert_to_openai_tool.html
See example implementation of ``bind_tools`` here: https://python.langchain.com/api_reference/_modules/langchain_openai/chat_models/base.html#BaseChatOpenAI.bind_tools
"""
if not self.has_tool_calling:
return
@@ -927,6 +928,7 @@ class ChatModelUnitTests(ChatModelTests):
a utility function that will accommodate most formats: https://python.langchain.com/api_reference/core/utils/langchain_core.utils.function_calling.convert_to_openai_tool.html
See example implementation of ``with_structured_output`` here: https://python.langchain.com/api_reference/_modules/langchain_openai/chat_models/base.html#BaseChatOpenAI.with_structured_output
"""
if not self.has_structured_output:
return
@@ -949,6 +951,7 @@ class ChatModelUnitTests(ChatModelTests):
Check also that the model class is named according to convention
(e.g., ``ChatProviderName``).
"""
class ExpectedParams(BaseModelV1):
@@ -986,6 +989,7 @@ class ChatModelUnitTests(ChatModelTests):
If this test fails, check that the ``init_from_env_params`` property is
correctly set on the test class.
"""
if not self.chat_model_class.is_lc_serializable():
pytest.skip("Model is not serializable.")
@@ -1005,6 +1009,7 @@ class ChatModelUnitTests(ChatModelTests):
def test_init_time(self, benchmark: BenchmarkFixture) -> None:
"""Test initialization time of the chat model. If this test fails, check that
we are not introducing undue overhead in the model's initialization.
"""
def _init_in_loop() -> None:

View File

@@ -102,8 +102,8 @@ class TestChatParrotLinkV1Unit(ChatModelV1UnitTests):
return False
@property
def supports_enhanced_tool_calls(self) -> bool:
"""``ChatParrotLinkV1`` does not support enhanced tool calls."""
def supports_tool_calls(self) -> bool:
"""``ChatParrotLinkV1`` does not support tool calls."""
return False
@property