
    BTh                       d dl Z d dlZd dlZd dlmZmZmZmZmZ d dl	m
Z
 d dlZd dlZd dlmZ d dlmZ d dlmZmZ d dlmZmZmZmZmZmZmZ d dlmZ d d	lmZ d d
l m!Z!m"Z" d dl#m$Z$m%Z% d dl&m'Z'm(Z( d dl)m'Z* d dl)m(Z+ d dl,m-Z- d dl.m/Z/m0Z0 d dl1m2Z2 d dl3m4Z4 d dl5m6Z6 ded   defdZ7 G d de      Z8 G d de'      Z9 e"e9      de:de:fd       Z;e"de:fd       Z<d eddfd!Z=d eddfd"Z> G d# d$e4      Z?y)%    N)AnyListLiteralOptionalcast)	MagicMock)warn_deprecated)BaseCallbackHandler)BaseChatModelGenericFakeChatModel)	AIMessageAIMessageChunkBaseMessageBaseMessageChunkHumanMessageSystemMessageToolMessage)StrOutputParser)ChatPromptTemplate)BaseTooltool)convert_to_json_schematool_example_to_messages)	BaseModelFieldr   r   )BenchmarkFixture)	Annotated	TypedDict)Cassette)ChatModelTests)PYDANTIC_MAJOR_VERSIONschema_typepydantic	typeddictjson_schemareturnc                      G d dt               dt        dt        ffd} G d dt              }dt        dt        fd}| d	k(  r|fS | d
k(  r||fS | dk(  rj	                         |fS t        d      )z
    :private:
    c                   J    e Zd ZU dZ ed      Zeed<    ed      Zeed<   y)_get_joke_class.<locals>.JokeJoke to tell user.question to set up a jokedescriptionsetupanswer to resolve the joke	punchlineN)	__name__
__module____qualname____doc__r   r1   str__annotations__r3        /var/www/catia.catastroantioquia-mas.com/valormas/lib/python3.12/site-packages/langchain_tests/integration_tests/chat_models.pyJoker,   0   s%     'BCsC+GH	3Hr;   r=   resultr)   c                     t        |       S N)
isinstance)r>   r=   s    r<   validate_jokez&_get_joke_class.<locals>.validate_joke6   s    &$''r;   c                   >    e Zd ZU dZeeddf   ed<   eeddf   ed<   y)!_get_joke_class.<locals>.JokeDictr-   .r.   r1   r2   r3   N)r4   r5   r6   r7   r   r8   r9   r:   r;   r<   JokeDictrD   9   s+     c#>>??S#'CCDDr;   rE   c                 B    t        d | j                         D              S )Nc              3   $   K   | ]  }|d v  
 yw))r1   r3   Nr:   ).0keys     r<   	<genexpr>z>_get_joke_class.<locals>.validate_joke_dict.<locals>.<genexpr>@   s     JS300Js   )allkeys)r>   s    r<   validate_joke_dictz+_get_joke_class.<locals>.validate_joke_dict?   s    JFKKMJJJr;   r&   r'   r(   zInvalid schema type)r   r   boolr    model_json_schema
ValueError)r$   rB   rE   rM   r=   s       @r<   _get_joke_classrQ   )   s    Iy I(c (d (E9 EK3 K4 K j ]""		#+++		%%%');;;.//r;   c                   j     e Zd ZU eee      ed<   d
 fdZdddededeee	ef      deddf
d	Z
 xZS )_TestCallbackHandleroptionsr)   Nc                 0    t         |           g | _        y r@   )super__init__rT   )self	__class__s    r<   rW   z_TestCallbackHandler.__init__Q   s    r;   )rT   
serializedmessageskwargsc                :    | j                   j                  |       y r@   )rT   append)rX   rZ   r[   rT   r\   s        r<   on_chat_model_startz(_TestCallbackHandler.on_chat_model_startU   s     	G$r;   r)   N)r4   r5   r6   listr   dictr9   rW   r   r8   r_   __classcell__)rY   s   @r<   rS   rS   N   s^    (4.!! -1%% %
 $sCx.)% % 
%r;   rS   c                   .    e Zd ZU  eddd      Zeed<   y)_MagicFunctionSchema.ii  )gtltinputN)r4   r5   r6   r   rh   intr9   r:   r;   r<   re   re   `   s    su.E3.r;   re   )args_schemarh   c                     | dz   S )z%Applies a magic function to an input.   r:   )rh   s    r<   magic_functionrm   d   s     19r;   c                       y)zCalculates a magic function.   r:   r:   r;   r<   magic_function_no_argsrp   j   s     r;   messagec                     t        | t              sJ t        | j                        dk(  sJ | j                  d   }|d   dk(  sJ |d   ddik(  sJ |d   J |d	   d
k(  sJ y )N   r   namerm   argsrh      idtype	tool_callrA   r   len
tool_callsrq   ry   s     r<   _validate_tool_call_messager~   p   s    gy)))w!!"a'''""1%IV 0000V!,,,T?&&&V+++r;   c                     t        | t              sJ t        | j                        dk(  sJ | j                  d   }|d   dk(  sJ |d   i k(  sJ |d   J |d   dk(  sJ y )	Nrs   r   rt   rp   ru   rw   rx   ry   rz   r}   s     r<   #_validate_tool_call_message_no_argsr   z   s    gy)))w!!"a'''""1%IV 8888V"""T?&&&V+++r;   c            
          e Zd ZdZedefd       ZdeddfdZdeddfdZ	deddfdZ
deddfd	Zdeddfd
ZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZdeddfdZej2                  j5                  dg d      dededdfd       Zej2                  j5                  dg d      dededdfd       Zej2                  j=                  edk7  d      deddfd       Z deddfdZ!deddfdZ"ded e#ddfd!Z$ded e#ddfd"Z%ded e#ddfd#Z&deddfd$Z'deddfd%Z(deddfd&Z)deddfd'Z*deddfd(Z+ded e#ddfd)Z,deddfd*Z-deddfd+Z.ej2                  j^                  ej2                  j`                  ded,e1d-e2ddfd.              Z3d/d0d1e4de5fd2Z6d/d0d1e4de5fd3Z7d/d0d1e4de5fd4Z8d/d0d1e4de5fd5Z9d/d0d1e4de5fd6Z:y)7ChatModelIntegrationTestsaD  Base class for chat model integration tests.

    Test subclasses must implement the ``chat_model_class`` and
    ``chat_model_params`` properties to specify what model to test and its
    initialization parameters.

    Example:

    .. code-block:: python

        from typing import Type

        from langchain_tests.integration_tests import ChatModelIntegrationTests
        from my_package.chat_models import MyChatModel


        class TestMyChatModelIntegration(ChatModelIntegrationTests):
            @property
            def chat_model_class(self) -> Type[MyChatModel]:
                # Return the chat model class to test here
                return MyChatModel

            @property
            def chat_model_params(self) -> dict:
                # Return initialization parameters for the model.
                return {"model": "model-001", "temperature": 0}

    .. note::
          API references for individual test methods include troubleshooting tips.


    Test subclasses must implement the following two properties:

    chat_model_class
        The chat model class to test, e.g., ``ChatParrotLink``.

        Example:

        .. code-block:: python

            @property
            def chat_model_class(self) -> Type[ChatParrotLink]:
                return ChatParrotLink

    chat_model_params
        Initialization parameters for the chat model.

        Example:

        .. code-block:: python

            @property
            def chat_model_params(self) -> dict:
                return {"model": "bird-brain-001", "temperature": 0}

    In addition, test subclasses can control what features are tested (such as tool
    calling or multi-modality) by selectively overriding the following properties.
    Expand to see details:

    .. dropdown:: has_tool_calling

        Boolean property indicating whether the chat model supports tool calling.

        By default, this is determined by whether the chat model's `bind_tools` method
        is overridden. It typically does not need to be overridden on the test class.

        Example override:

        .. code-block:: python

            @property
            def has_tool_calling(self) -> bool:
                return True

    .. dropdown:: tool_choice_value

        Value to use for tool choice when used in tests.

        .. warning:: Deprecated since version 0.3.15:
           This property will be removed in version 0.3.20. If a model supports
           ``tool_choice``, it should accept ``tool_choice="any"`` and
           ``tool_choice=<string name of tool>``. If a model does not
           support forcing tool calling, override the ``has_tool_choice`` property to
           return ``False``.

        Example:

        .. code-block:: python

            @property
            def tool_choice_value(self) -> Optional[str]:
                return "any"

    .. dropdown:: has_tool_choice

        Boolean property indicating whether the chat model supports forcing tool
        calling via a ``tool_choice`` parameter.

        By default, this is determined by whether the parameter is included in the
        signature for the corresponding ``bind_tools`` method.

        If ``True``, the minimum requirement for this feature is that
        ``tool_choice="any"`` will force a tool call, and ``tool_choice=<tool name>``
        will force a call to a specific tool.

        Example override:

        .. code-block:: python

            @property
            def has_tool_choice(self) -> bool:
                return False

    .. dropdown:: has_structured_output

        Boolean property indicating whether the chat model supports structured
        output.

        By default, this is determined by whether the chat model's
        ``with_structured_output`` method is overridden. If the base implementation is
        intended to be used, this method should be overridden.

        See: https://python.langchain.com/docs/concepts/structured_outputs/

        Example:

        .. code-block:: python

            @property
            def has_structured_output(self) -> bool:
                return True

    .. dropdown:: structured_output_kwargs

        Dict property that can be used to specify additional kwargs for
        ``with_structured_output``. Useful for testing different models.

        Example:

        .. code-block:: python

            @property
            def structured_output_kwargs(self) -> dict:
                return {"method": "function_calling"}

    .. dropdown:: supports_json_mode

        Boolean property indicating whether the chat model supports JSON mode in
        ``with_structured_output``.

        See: https://python.langchain.com/docs/concepts/structured_outputs/#json-mode

        Example:

        .. code-block:: python

            @property
            def supports_json_mode(self) -> bool:
                return True

    .. dropdown:: supports_image_inputs

        Boolean property indicating whether the chat model supports image inputs.
        Defaults to ``False``.

        If set to ``True``, the chat model will be tested using content blocks of the
        form

        .. code-block:: python

            {
                "type": "image",
                "source_type": "base64",
                "data": "<base64 image data>",
                "mime_type": "image/jpeg",  # or appropriate mime-type
            }

        In addition to OpenAI-style content blocks:

        .. code-block:: python

            {
                "type": "image_url",
                "image_url": {"url": f"data:image/jpeg;base64,{image_data}"},
            }

        See https://python.langchain.com/docs/concepts/multimodality/

        Example:

        .. code-block:: python

            @property
            def supports_image_inputs(self) -> bool:
                return True

    .. dropdown:: supports_image_urls

        Boolean property indicating whether the chat model supports image inputs from
        URLs. Defaults to ``False``.

        If set to ``True``, the chat model will be tested using content blocks of the
        form

        .. code-block:: python

            {
                "type": "image",
                "source_type": "url",
                "url": "https://...",
            }

        See https://python.langchain.com/docs/concepts/multimodality/

        Example:

        .. code-block:: python

            @property
            def supports_image_urls(self) -> bool:
                return True

    .. dropdown:: supports_pdf_inputs

        Boolean property indicating whether the chat model supports PDF inputs.
        Defaults to ``False``.

        If set to ``True``, the chat model will be tested using content blocks of the
        form

        .. code-block:: python

            {
                "type": "file",
                "source_type": "base64",
                "data": "<base64 file data>",
                "mime_type": "application/pdf",
            }

        See https://python.langchain.com/docs/concepts/multimodality/

        Example:

        .. code-block:: python

            @property
            def supports_pdf_inputs(self) -> bool:
                return True

    .. dropdown:: supports_audio_inputs

        Boolean property indicating whether the chat model supports audio inputs.
        Defaults to ``False``.

        If set to ``True``, the chat model will be tested using content blocks of the
        form

        .. code-block:: python

            {
                "type": "audio",
                "source_type": "base64",
                "data": "<base64 audio data>",
                "mime_type": "audio/wav",  # or appropriate mime-type
            }

        See https://python.langchain.com/docs/concepts/multimodality/

        Example:

        .. code-block:: python

            @property
            def supports_audio_inputs(self) -> bool:
                return True

    .. dropdown:: supports_video_inputs

        Boolean property indicating whether the chat model supports image inputs.
        Defaults to ``False``. No current tests are written for this feature.

    .. dropdown:: returns_usage_metadata

        Boolean property indicating whether the chat model returns usage metadata
        on invoke and streaming responses.

        ``usage_metadata`` is an optional dict attribute on AIMessages that track input
        and output tokens: https://python.langchain.com/api_reference/core/messages/langchain_core.messages.ai.UsageMetadata.html

        Example:

        .. code-block:: python

            @property
            def returns_usage_metadata(self) -> bool:
                return False

        Models supporting ``usage_metadata`` should also return the name of the
        underlying model in the ``response_metadata`` of the AIMessage.

    .. dropdown:: supports_anthropic_inputs

        Boolean property indicating whether the chat model supports Anthropic-style
        inputs.

        These inputs might feature "tool use" and "tool result" content blocks, e.g.,

        .. code-block:: python

            [
                {"type": "text", "text": "Hmm let me think about that"},
                {
                    "type": "tool_use",
                    "input": {"fav_color": "green"},
                    "id": "foo",
                    "name": "color_picker",
                },
            ]

        If set to ``True``, the chat model will be tested using content blocks of this
        form.

        Example:

        .. code-block:: python

            @property
            def supports_anthropic_inputs(self) -> bool:
                return False

    .. dropdown:: supports_image_tool_message

        Boolean property indicating whether the chat model supports ToolMessages
        that include image content, e.g.,

        .. code-block:: python

            ToolMessage(
                content=[
                    {
                        "type": "image_url",
                        "image_url": {"url": f"data:image/jpeg;base64,{image_data}"},
                    },
                ],
                tool_call_id="1",
                name="random_image",
            )

        (OpenAI Chat Completions format), as well as

        .. code-block:: python

            ToolMessage(
                content=[
                    {
                        "type": "image",
                        "source_type": "base64",
                        "data": image_data,
                        "mime_type": "image/jpeg",
                    },
                ],
                tool_call_id="1",
                name="random_image",
            )

        (standard format).

        If set to ``True``, the chat model will be tested with message sequences that
        include ToolMessages of this form.

        Example:

        .. code-block:: python

            @property
            def supports_image_tool_message(self) -> bool:
                return False

    .. dropdown:: supported_usage_metadata_details

        Property controlling what usage metadata details are emitted in both invoke
        and stream.

        ``usage_metadata`` is an optional dict attribute on AIMessages that track input
        and output tokens: https://python.langchain.com/api_reference/core/messages/langchain_core.messages.ai.UsageMetadata.html

        It includes optional keys ``input_token_details`` and ``output_token_details``
        that can track usage details associated with special types of tokens, such as
        cached, audio, or reasoning.

        Only needs to be overridden if these details are supplied.

    .. dropdown:: enable_vcr_tests

        Property controlling whether to enable select tests that rely on
        `VCR <https://vcrpy.readthedocs.io/en/latest/>`_ caching of HTTP calls, such
        as benchmarking tests.

        To enable these tests, follow these steps:

        1. Override the ``enable_vcr_tests`` property to return ``True``:

            .. code-block:: python

                @property
                def enable_vcr_tests(self) -> bool:
                    return True

        2. Configure VCR to exclude sensitive headers and other information from cassettes.

            .. important::
                VCR will by default record authentication headers and other sensitive
                information in cassettes. Read below for how to configure what
                information is recorded in cassettes.

            To add configuration to VCR, add a ``conftest.py`` file to the ``tests/``
            directory and implement the ``vcr_config`` fixture there.

            ``langchain-tests`` excludes the headers ``"authorization"``,
            ``"x-api-key"``, and ``"api-key"`` from VCR cassettes. To pick up this
            configuration, you will need to add ``conftest.py`` as shown below. You can
            also exclude additional headers, override the default exclusions, or apply
            other customizations to the VCR configuration. See example below:

            .. code-block:: python
                :caption: tests/conftest.py

                import pytest
                from langchain_tests.conftest import _base_vcr_config as _base_vcr_config

                _EXTRA_HEADERS = [
                    # Specify additional headers to redact
                    ("user-agent", "PLACEHOLDER"),
                ]


                def remove_response_headers(response: dict) -> dict:
                    # If desired, remove or modify headers in the response.
                    response["headers"] = {}
                    return response


                @pytest.fixture(scope="session")
                def vcr_config(_base_vcr_config: dict) -> dict:  # noqa: F811
                    """Extend the default configuration from langchain_tests."""
                    config = _base_vcr_config.copy()
                    config.setdefault("filter_headers", []).extend(_EXTRA_HEADERS)
                    config["before_record_response"] = remove_response_headers

                    return config

            .. dropdown:: Compressing cassettes

                ``langchain-tests`` includes a custom VCR serializer that compresses
                cassettes using gzip. To use it, register the ``"yaml.gz"`` serializer
                to your VCR fixture and enable this serializer in the config. See
                example below:

                .. code-block:: python
                    :caption: tests/conftest.py

                    import pytest
                    from langchain_tests.conftest import CustomPersister, CustomSerializer
                    from langchain_tests.conftest import _base_vcr_config as _base_vcr_config
                    from vcr import VCR

                    _EXTRA_HEADERS = [
                        # Specify additional headers to redact
                        ("user-agent", "PLACEHOLDER"),
                    ]


                    def remove_response_headers(response: dict) -> dict:
                        # If desired, remove or modify headers in the response.
                        response["headers"] = {}
                        return response


                    @pytest.fixture(scope="session")
                    def vcr_config(_base_vcr_config: dict) -> dict:  # noqa: F811
                        """Extend the default configuration from langchain_tests."""
                        config = _base_vcr_config.copy()
                        config.setdefault("filter_headers", []).extend(_EXTRA_HEADERS)
                        config["before_record_response"] = remove_response_headers
                        # New: enable serializer and set file extension
                        config["serializer"] = "yaml.gz"
                        config["path_transformer"] = VCR.ensure_suffix(".yaml.gz")

                        return config


                    def pytest_recording_configure(config: dict, vcr: VCR) -> None:
                        vcr.register_persister(CustomPersister())
                        vcr.register_serializer("yaml.gz", CustomSerializer())


                You can inspect the contents of the compressed cassettes (e.g., to
                ensure no sensitive information is recorded) using

                .. code-block:: bash

                    gunzip -k /path/to/tests/cassettes/TestClass_test.yaml.gz

                or by using the serializer:

                .. code-block:: python

                    from langchain_tests.conftest import CustomPersister, CustomSerializer

                    cassette_path = "/path/to/tests/cassettes/TestClass_test.yaml.gz"
                    requests, responses = CustomPersister().load_cassette(path, CustomSerializer())

        3. Run tests to generate VCR cassettes.

            Example:

            .. code-block:: bash

                uv run python -m pytest tests/integration_tests/test_chat_models.py::TestMyModel::test_stream_time

            This will generate a VCR cassette for the test in
            ``tests/integration_tests/cassettes/``.

            .. important::
                You should inspect the generated cassette to ensure that it does not
                contain sensitive information. If it does, you can modify the
                ``vcr_config`` fixture to exclude headers or modify the response
                before it is recorded.

            You can then commit the cassette to your repository. Subsequent test runs
            will use the cassette instead of making HTTP calls.
    r)   c                     i S z	:private:r:   )rX   s    r<   standard_chat_model_paramsz4ChatModelIntegrationTests.standard_chat_model_params  s	     	r;   modelNc                     |j                  d      }|J t        |t              sJ t        |j                         t              sJ t        |j                        dkD  sJ y)a\  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
            does not raise any exceptions, and that it returns a valid
            :class:`~langchain_core.outputs.chat_result.ChatResult` like so:

            .. code-block:: python

                return ChatResult(
                    generations=[ChatGeneration(
                        message=AIMessage(content="Output text")
                    )]
                )
        HelloNr   )invokerA   r   textr8   r{   contentrX   r   r>   s      r<   test_invokez%ChatModelIntegrationTests.test_invoke  s[    & g&!!!&),,,&++----6>>"Q&&&r;   c                    K   |j                  d       d{   }|J t        |t              sJ t        |j                         t              sJ t        |j                        dkD  sJ y7 Uw)a  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
        in an async context.

        .. dropdown:: Troubleshooting

            First, debug
            :meth:`~langchain_tests.integration_tests.chat_models.ChatModelIntegrationTests.test_invoke`.
            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
            method does not raise any exceptions, and that it returns a valid
            :class:`~langchain_core.outputs.chat_result.ChatResult` like so:

            .. code-block:: python

                return ChatResult(
                    generations=[ChatGeneration(
                        message=AIMessage(content="Output text")
                    )]
                )
        r   Nr   )ainvokerA   r   r   r8   r{   r   r   s      r<   test_ainvokez&ChatModelIntegrationTests.test_ainvoke  sg     4 }}W--!!!&),,,&++----6>>"Q&&&	 .s   A/A-AA/c                     d}|j                  d      D ]0  }|J t        |t              sJ |t        |j                        z  }2 |dkD  sJ y)a  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
        streaming context.

        .. dropdown:: Troubleshooting

            First, debug
            :meth:`~langchain_tests.integration_tests.chat_models.ChatModelIntegrationTests.test_invoke`.
            because `stream` has a default implementation that calls `invoke` and yields
            the result as a single chunk.

            If that test passes but not this one, you should make sure your _stream
            method does not raise any exceptions, and that it yields valid
            :class:`~langchain_core.outputs.chat_generation.ChatGenerationChunk`
            objects like so:

            .. code-block:: python

                yield ChatGenerationChunk(
                    message=AIMessageChunk(content="chunk text")
                )
        r   r   N)streamrA   r   r{   r   rX   r   
num_tokenstokens       r<   test_streamz%ChatModelIntegrationTests.test_stream  s^    2 
\\'* 	-E$$$e^444#emm,,J	- A~~r;   c                    K   d}|j                  d      2 3 d{   }|J t        |t              sJ |t        |j                        z  }87 36 |dkD  sJ yw)a  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
        be used in an async streaming context.

        .. dropdown:: Troubleshooting

            First, debug
            :meth:`~langchain_tests.integration_tests.chat_models.ChatModelIntegrationTests.test_stream`.
            and
            :meth:`~langchain_tests.integration_tests.chat_models.ChatModelIntegrationTests.test_ainvoke`.
            because `astream` has a default implementation that calls `_stream` in an
            async context if it is implemented, or `ainvoke` and yields the result as a
            single chunk if not.

            If those tests pass but not this one, you should make sure your _astream
            method does not raise any exceptions, and that it yields valid
            :class:`~langchain_core.outputs.chat_generation.ChatGenerationChunk`
            objects like so:

            .. code-block:: python

                yield ChatGenerationChunk(
                    message=AIMessageChunk(content="chunk text")
                )
        r   r   N)astreamrA   r   r{   r   r   s       r<   test_astreamz&ChatModelIntegrationTests.test_astream  sk     8 
 ==1 	- 	-%$$$e^444#emm,,J	-1 A~~s%   AAAA0AA	Ac                 $   |j                  ddg      }|J t        |t              sJ t        |      dk(  sJ |D ]R  }|J t        |t              sJ t        |j                         t              sJ t        |j                        dkD  rRJ  y)a  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.

        .. dropdown:: Troubleshooting

            First, debug
            :meth:`~langchain_tests.integration_tests.chat_models.ChatModelIntegrationTests.test_invoke`
            because `batch` has a default implementation that calls `invoke` for each
            message in the batch.

            If that test passes but not this one, you should make sure your `batch`
            method does not raise any exceptions, and that it returns a list of valid
            :class:`~langchain_core.messages.AIMessage` objects.
        r   HeyNrl   r   )batchrA   ra   r{   r   r   r8   r   rX   r   batch_resultsr>   s       r<   
test_batchz$ChatModelIntegrationTests.test_batch  s    " We$45(((-...=!Q&&&# 	+F%%%fi000fkkmS111v~~&***		+r;   c                 @  K   |j                  ddg       d{   }|J t        |t              sJ t        |      dk(  sJ |D ]R  }|J t        |t              sJ t        |j                         t              sJ t        |j                        dkD  rRJ  y7 w)a^  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.

        .. dropdown:: Troubleshooting

            First, debug
            :meth:`~langchain_tests.integration_tests.chat_models.ChatModelIntegrationTests.test_batch`
            and
            :meth:`~langchain_tests.integration_tests.chat_models.ChatModelIntegrationTests.test_ainvoke`
            because `abatch` has a default implementation that calls `ainvoke` for each
            message in the batch.

            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.
        r   r   Nrl   r   )abatchrA   ra   r{   r   r   r8   r   r   s       r<   test_abatchz%ChatModelIntegrationTests.test_abatch6  s     & $llGU+;<<(((-...=!Q&&&# 	+F%%%fi000fkkmS111v~~&***		+	 =s   BBA=BBc                    t        d      t        d      t        d      g}|j                  |      }|J t        |t              sJ t        |j	                         t
              sJ t        |j                        dkD  sJ y)a  Test to verify that the model can handle multi-turn conversations.

        This should pass for all integrations. Tests the model's ability to process
        a sequence of alternating human and AI messages as context for generating
        the next response.

        .. dropdown:: Troubleshooting

            First, debug
            :meth:`~langchain_tests.integration_tests.chat_models.ChatModelIntegrationTests.test_invoke`
            because this test also uses `model.invoke()`.

            If that test passes but not this one, you should verify that:
            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`
        hellohow are youNr   )r   r   r   rA   r   r8   r{   r   rX   r   r[   r>   s       r<   test_conversationz+ChatModelIntegrationTests.test_conversationS  s|    & !g'

 h'!!!&),,,&++----6>>"Q&&&r;   c           	      V   t        d      t        d      t        d      t        d      t        d      t        d      t        d      g}|j                  |      }|J t	        |t              sJ t	        |j                         t              sJ t        |j                        dkD  sJ y)a  
        Test to verify that the model can handle double-message conversations.

        This should pass for all integrations. Tests the model's ability to process
        a sequence of double-system, double-human, and double-ai messages as context
        for generating the next response.

        .. dropdown:: Troubleshooting

            First, debug
            :meth:`~langchain_tests.integration_tests.chat_models.ChatModelIntegrationTests.test_invoke`
            because this test also uses `model.invoke()`.

            Second, debug
            :meth:`~langchain_tests.integration_tests.chat_models.ChatModelIntegrationTests.test_conversation`
            because this test is the "basic case" without double messages.

            If that test passes those but not this one, you should verify that:
            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`
        r   r   Nr   )	r   r   r   r   rA   r   r8   r{   r   r   s       r<   !test_double_messages_conversationz;ChatModelIntegrationTests.test_double_messages_conversationp  s    0 '"'"!!gg'
 h'!!!&),,,&++----6>>"Q&&&r;   c                 *   | j                   st        j                  d       |j                  d      }|J t	        |t
              sJ |j                  J t	        |j                  d   t              sJ t	        |j                  d   t              sJ t	        |j                  d   t              sJ |j                  j                  d      }t	        |t              sJ |sJ d| j                  d	   v r| j                         }|j                  J |j                  d
   J t	        |j                  d
   d   t              sJ |j                  d   t        d |j                  d
   j                         D              k\  sJ d| j                  d	   v r| j                         }|j                  J |j                  d   J t	        |j                  d   d   t              sJ t        |j                  d         t        d |j                  d   j                         D              k\  sJ d| j                  d	   v r| j!                         }|j                  J |j                  d   J t	        |j                  d   d   t              sJ |j                  d   t        d |j                  d   j                         D              k\  sJ d| j                  d	   v r| j#                         }|j                  J |j                  d
   J t	        |j                  d
   d   t              sJ |j                  d   t        d |j                  d
   j                         D              k\  sJ d| j                  d	   v r| j%                         }|j                  J |j                  d
   J t	        |j                  d
   d   t              sJ |j                  d   t        d |j                  d
   j                         D              k\  sJ yy)a(  Test to verify that the model returns correct usage metadata.

        This test is optional and should be skipped if the model does not return
        usage metadata (see Configuration below).

        .. versionchanged:: 0.3.17

            Additionally check for the presence of `model_name` in the response
            metadata, which is needed for usage tracking in callback handlers.

        .. dropdown:: Configuration

            By default, this test is run.
            To disable this feature, set `returns_usage_metadata` to False in your
            test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def returns_usage_metadata(self) -> bool:
                        return False

            This test can also check the format of specific kinds of usage metadata
            based on the `supported_usage_metadata_details` property. This property
            should be configured as follows with the types of tokens that the model
            supports tracking:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def supported_usage_metadata_details(self) -> dict:
                        return {
                            "invoke": [
                                "audio_input",
                                "audio_output",
                                "reasoning_output",
                                "cache_read_input",
                                "cache_creation_input",
                            ],
                            "stream": [
                                "audio_input",
                                "audio_output",
                                "reasoning_output",
                                "cache_read_input",
                                "cache_creation_input",
                            ],
                        }


        .. dropdown:: Troubleshooting

            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`:

            .. code-block:: python

                return ChatResult(
                    generations=[ChatGeneration(
                        message=AIMessage(
                            content="Output text",
                            usage_metadata={
                                "input_tokens": 350,
                                "output_tokens": 240,
                                "total_tokens": 590,
                                "input_token_details": {
                                    "audio": 10,
                                    "cache_creation": 200,
                                    "cache_read": 100,
                                },
                                "output_token_details": {
                                    "audio": 10,
                                    "reasoning": 200,
                                }
                            }
                        )
                    )]
                )

            Check also that the response includes a ``"model_name"`` key in its
            ``usage_metadata``.
        Not implemented.r   Ninput_tokensoutput_tokenstotal_tokens
model_nameaudio_inputr   input_token_detailsaudioc              3   (   K   | ]
  }|xs d   ywr   Nr:   rH   vs     r<   rJ   z@ChatModelIntegrationTests.test_usage_metadata.<locals>.<genexpr>         = a=   audio_outputoutput_token_detailsc              3   (   K   | ]
  }|xs d   ywr   r:   r   s     r<   rJ   z@ChatModelIntegrationTests.test_usage_metadata.<locals>.<genexpr>	  s       C aCr   reasoning_output	reasoningc              3   (   K   | ]
  }|xs d   ywr   r:   r   s     r<   rJ   z@ChatModelIntegrationTests.test_usage_metadata.<locals>.<genexpr>  s      > a>r   cache_read_input
cache_readc              3   (   K   | ]
  }|xs d   ywr   r:   r   s     r<   rJ   z@ChatModelIntegrationTests.test_usage_metadata.<locals>.<genexpr>!  r   r   cache_creation_inputcache_creationc              3   (   K   | ]
  }|xs d   ywr   r:   r   s     r<   rJ   z@ChatModelIntegrationTests.test_usage_metadata.<locals>.<genexpr>-  r   r   )returns_usage_metadatapytestskipr   rA   r   usage_metadatari   response_metadatagetr8    supported_usage_metadata_detailsinvoke_with_audio_inputsumvaluesinvoke_with_audio_outputinvoke_with_reasoning_outputinvoke_with_cache_read_input invoke_with_cache_creation_input)rX   r   r>   r   msgs        r<   test_usage_metadataz-ChatModelIntegrationTests.test_usage_metadata  s   j **KK*+g&!!!&),,,$$000&//?EEE&//@#FFF&//?EEE --11,?
*c***zDAA(KK..0C%%111%%&;<HHHc001FGPRUVVV%%n5 =++,ABIIK= :    TBB8LL//1C%%111%%&<=IIIc001GHQSVWWWs))/:;s C++,BCJJLC @    !F!Fx!PP335C%%111%%&<=III""#9:;G   %%o6# >++,BCJJL> ;    !F!Fx!PP335C%%111%%&;<HHH""#89,G   %%n5 =++,ABIIK= :    "T%J%J8%TT779C%%111%%&;<HHH""#89:JK   %%n5 =++,ABIIK= :    Ur;   c                 V   | j                   st        j                  d       d}|j                  d      D ]j  }t	        |t
              sJ |r=|j                  r1|j                  d   r"|j                  r|j                  d   rJ d       ||nt        t
        ||z         }l t	        |t
              sJ |j                  J t	        |j                  d   t              sJ t	        |j                  d   t              sJ t	        |j                  d   t              sJ |j                  j                  d      }t	        |t              sJ |sJ d	| j                  d
   v r4| j                  d      }t	        |j                  d   d   t              sJ d| j                  d
   v r4| j                  d      }t	        |j                  d   d   t              sJ d| j                  d
   v r4| j                  d      }t	        |j                  d   d   t              sJ d| j                  d
   v r4| j!                  d      }t	        |j                  d   d   t              sJ d| j                  d
   v r5| j#                  d      }t	        |j                  d   d   t              sJ yy)a  
        Test to verify that the model returns correct usage metadata in streaming mode.

        .. versionchanged:: 0.3.17

            Additionally check for the presence of `model_name` in the response
            metadata, which is needed for usage tracking in callback handlers.

        .. dropdown:: Configuration

            By default, this test is run.
            To disable this feature, set `returns_usage_metadata` to False in your
            test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def returns_usage_metadata(self) -> bool:
                        return False

            This test can also check the format of specific kinds of usage metadata
            based on the `supported_usage_metadata_details` property. This property
            should be configured as follows with the types of tokens that the model
            supports tracking:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def supported_usage_metadata_details(self) -> dict:
                        return {
                            "invoke": [
                                "audio_input",
                                "audio_output",
                                "reasoning_output",
                                "cache_read_input",
                                "cache_creation_input",
                            ],
                            "stream": [
                                "audio_input",
                                "audio_output",
                                "reasoning_output",
                                "cache_read_input",
                                "cache_creation_input",
                            ],
                        }

        .. dropdown:: Troubleshooting

            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`
            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.

            `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

                yield ChatResult(
                    generations=[ChatGeneration(
                        message=AIMessage(
                            content="Output text",
                            usage_metadata={
                                "input_tokens": (
                                    num_input_tokens if is_first_chunk else 0
                                ),
                                "output_tokens": 11,
                                "total_tokens": (
                                    11+num_input_tokens if is_first_chunk else 11
                                ),
                                "input_token_details": {
                                    "audio": 10,
                                    "cache_creation": 200,
                                    "cache_read": 100,
                                },
                                "output_token_details": {
                                    "audio": 10,
                                    "reasoning": 200,
                                }
                            }
                        )
                    )]
                )

            Check also that the aggregated response includes a ``"model_name"`` key
            in its ``usage_metadata``.
        r   Nz+Write me 2 haikus. Only include the haikus.r   zDOnly one chunk should set input_tokens, the rest should be 0 or Noner   r   r   r   r   Tr   r   r   r   r   r   r   r   r   r   r   )r   r   r   r   rA   r   r   r   ri   r   r   r8   r   r   r   r   r   r   )rX   r   fullchunkr   r   s         r<   test_usage_metadata_streamingz7ChatModelIntegrationTests.test_usage_metadata_streaming2  s   ~ **KK*+)-\\"OP 	QEe^444 ++0C0CN0S,,E4H4H4X4X
 !L5d>4%<.PD	Q $///""...$--n=sCCC$--o>DDD$--n=sCCC ++//=
*c***zDAA(KK..d.;Cc001FGPRUVVVTBB8LL//t/<Cc001GHQSVWWW!F!Fx!PP3343@C""#9:;G   !F!Fx!PP3343@C""#89,G   "T%J%J8%TT77t7DC""#89:JK   Ur;   c                     |j                  ddg      }t        |t              sJ  | j                  di i | j                  ddgi}|j                  d      }t        |t              sJ y)ar  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

        This should pass for all integrations.

        .. dropdown:: Troubleshooting

            If this test fails, check that the function signature for ``_generate``
            (as well as ``_stream`` and async variants) accepts the ``stop`` parameter:

            .. code-block:: python

                def _generate(
                    self,
                    messages: List[BaseMessage],
                    stop: Optional[List[str]] = None,
                    run_manager: Optional[CallbackManagerForLLMRun] = None,
                    **kwargs: Any,
                ) -> ChatResult:
        hiyou)stopr   Nr:   )r   rA   r   chat_model_classchat_model_params)rX   r   r>   custom_models       r<   test_stop_sequencez,ChatModelIntegrationTests.test_stop_sequence  s    . d%1&),,,,t,, 
((
 $$T*&),,,r;   c                    | j                   st        j                  d       | j                  sd}nd}t	        j
                  | d      t	        j
                  t        d      urt        ddd       |j                  t        g|	      }d
}|j                  |      }t        |       d}|j                  |      D ]  }||n||z   } t        |t              sJ t        |       y)a8  Test that the model generates tool calls. This test is skipped if the
        ``has_tool_calling`` property on the test class is set to False.

        This test is optional and should be skipped if the model does not support
        tool calling (see Configuration below).

        .. dropdown:: Configuration

            To disable tool calling tests, set ``has_tool_calling`` to False in your
            test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def has_tool_calling(self) -> bool:
                        return False

        .. dropdown:: Troubleshooting

            If this test fails, check that ``bind_tools`` is implemented to correctly
            translate LangChain tool objects into the appropriate schema for your
            chat model.

            This test may fail if the chat model does not support a ``tool_choice``
            parameter. This parameter can be used to force a tool call. If
            ``tool_choice`` is not supported and the model consistently fails this
            test, you can ``xfail`` the test:

            .. code-block:: python

                @pytest.mark.xfail(reason=("Does not support tool_choice."))
                def test_tool_calling(self, model: BaseChatModel) -> None:
                    super().test_tool_calling(model)

            Otherwise, in the case that only one tool is bound, ensure that
            ``tool_choice`` supports the string ``"any"`` to force calling that tool.
        Test requires tool calling.Nanytool_choice_valuez0.3.15a  `tool_choice_value` will be removed in version 0.3.20. If a model supports `tool_choice`, it should accept `tool_choice='any' and `tool_choice=<string name of tool>`. If the model does not support `tool_choice`, override the `supports_tool_choice` property to return `False`.z0.3.20)rq   removaltool_choice5What is the value of magic_function(3)? Use the tool.)has_tool_callingr   r   has_tool_choiceinspectgetattr_staticr   r	   
bind_toolsrm   r   r~   r   rA   r   rX   r   r   model_with_toolsqueryr>   r   r   s           r<   test_tool_callingz+ChatModelIntegrationTests.test_tool_calling  s    N $$KK56## $ %!!%
''(ACVWX 2 !
 !++*; , 

 H!((/#F+ ,0%,,U3 	;E L5dUlD	;$	***#D)r;   c                 l   | j                   r| j                  st        j                  d       t        dt
        dt
        fd       }dD ]i  }|j                  t        |g|      }|j                  d      }t        |t              sJ |j                  sJ |dk(  sS|j                  d	   d
   dk(  riJ  y)a.  Test that the model can force tool calling via the ``tool_choice``
        parameter. This test is skipped if the ``has_tool_choice`` property on the
        test class is set to False.

        This test is optional and should be skipped if the model does not support
        tool calling (see Configuration below).

        .. dropdown:: Configuration

            To disable tool calling tests, set ``has_tool_choice`` to False in your
            test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def has_tool_choice(self) -> bool:
                        return False

        .. dropdown:: Troubleshooting

            If this test fails, check whether the ``test_tool_calling`` test is passing.
            If it is not, refer to the troubleshooting steps in that test first.

            If ``test_tool_calling`` is passing, check that the underlying model
            supports forced tool calling. If it does, ``bind_tools`` should accept a
            ``tool_choice`` parameter that can be used to force a tool call.

            It should accept (1) the string ``"any"`` to force calling the bound tool,
            and (2) the string name of the tool to force calling that tool.

        zTest requires tool choice.locationr)   c                      y)zGet weather at a location.It's sunny.r:   r   s    r<   get_weatherz?ChatModelIntegrationTests.test_tool_choice.<locals>.get_weatherZ       !r;   )r   rm   r   zHello!rm   r   rt   N)r   r   r   r   r   r8   r   rm   r   rA   r   r|   )rX   r   r   r   r   r>   s         r<   test_tool_choicez*ChatModelIntegrationTests.test_tool_choice6  s    B ##4+@+@KK45		!# 	!# 	! 
	! 5 	HK$//-;  0   &,,X6Ffi000$$$$..((+F37GGGG	Hr;   c                 |  K   | j                   st        j                  d       | j                  sd}nd}|j	                  t
        g|      }d}|j                  |       d{   }t        |       d}|j                  |      2 3 d{   }||n||z   }7 57 6 t        |t              sJ t        |       yw)aP  Test that the model generates tool calls. This test is skipped if the
        ``has_tool_calling`` property on the test class is set to False.

        This test is optional and should be skipped if the model does not support
        tool calling (see Configuration below).

        .. dropdown:: Configuration

            To disable tool calling tests, set ``has_tool_calling`` to False in your
            test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def has_tool_calling(self) -> bool:
                        return False

        .. dropdown:: Troubleshooting

            If this test fails, check that ``bind_tools`` is implemented to correctly
            translate LangChain tool objects into the appropriate schema for your
            chat model.

            This test may fail if the chat model does not support a ``tool_choice``
            parameter. This parameter can be used to force a tool call. If
            ``tool_choice`` is not supported and the model consistently fails this
            test, you can ``xfail`` the test:

            .. code-block:: python

                @pytest.mark.xfail(reason=("Does not support tool_choice."))
                async def test_tool_calling_async(self, model: BaseChatModel) -> None:
                    await super().test_tool_calling_async(model)

            Otherwise, in the case that only one tool is bound, ensure that
            ``tool_choice`` supports the string ``"any"`` to force calling that tool.
        r   Nr   r   r   )r   r   r   r   r   rm   r   r~   r   rA   r   r   s           r<   test_tool_calling_asyncz1ChatModelIntegrationTests.test_tool_calling_asynci  s     N $$KK56## $ % ++*; , 

 H'//66#F+ ,0+33E: 	; 	;% L5dUlD 7
	;:$	***#D)s6   A!B<#B$"B<B
BBB<BB<c                 P   | j                   st        j                  d       | j                  sd}nd}|j	                  t
        g|      }d}|j                  |      }t        |       d}|j                  |      D ]  }||n||z   } t        |t              sJ t        |       y)a  Test that the model generates tool calls for tools with no arguments.
        This test is skipped if the ``has_tool_calling`` property on the test class
        is set to False.

        This test is optional and should be skipped if the model does not support
        tool calling (see Configuration below).

        .. dropdown:: Configuration

            To disable tool calling tests, set ``has_tool_calling`` to False in your
            test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def has_tool_calling(self) -> bool:
                        return False

        .. dropdown:: Troubleshooting

            If this test fails, check that ``bind_tools`` is implemented to correctly
            translate LangChain tool objects into the appropriate schema for your
            chat model. It should correctly handle the case where a tool has no
            arguments.

            This test may fail if the chat model does not support a ``tool_choice``
            parameter. This parameter can be used to force a tool call. It may also
            fail if a provider does not support this form of tool. In these cases,
            you can ``xfail`` the test:

            .. code-block:: python

                @pytest.mark.xfail(reason=("Does not support tool_choice."))
                def test_tool_calling_with_no_arguments(self, model: BaseChatModel) -> None:
                    super().test_tool_calling_with_no_arguments(model)

            Otherwise, in the case that only one tool is bound, ensure that
            ``tool_choice`` supports the string ``"any"`` to force calling that tool.
        r   Nr   r   z<What is the value of magic_function_no_args()? Use the tool.)r   r   r   r   r   rp   r   r   r   rA   r   r   s           r<   #test_tool_calling_with_no_argumentsz=ChatModelIntegrationTests.test_tool_calling_with_no_arguments  s    R $$KK56## $ % ++#$2C , 
 O!((/+F3+/%,,U3 	;E L5dUlD	;$	***+D1r;   c                    | j                   st        j                  d       t        j                  dg      }t        t        dg            }||z  t               z  }|j                  dd      }| j                  rd}nd	}|j                  |g|
      }d}|j                  |      }	t        |	t              sJ |	j                  sJ |	j                  d   }
|
d   j                  d      sJ |
d   dk(  sJ y	)ad  Test that the model generates tool calls for tools that are derived from
        LangChain runnables. This test is skipped if the ``has_tool_calling`` property
        on the test class is set to False.

        This test is optional and should be skipped if the model does not support
        tool calling (see Configuration below).

        .. dropdown:: Configuration

            To disable tool calling tests, set ``has_tool_calling`` to False in your
            test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def has_tool_calling(self) -> bool:
                        return False

        .. dropdown:: Troubleshooting

            If this test fails, check that ``bind_tools`` is implemented to correctly
            translate LangChain tool objects into the appropriate schema for your
            chat model.

            This test may fail if the chat model does not support a ``tool_choice``
            parameter. This parameter can be used to force a tool call. If
            ``tool_choice`` is not supported and the model consistently fails this
            test, you can ``xfail`` the test:

            .. code-block:: python

                @pytest.mark.xfail(reason=("Does not support tool_choice."))
                def test_bind_runnables_as_tools(self, model: BaseChatModel) -> None:
                    super().test_bind_runnables_as_tools(model)

            Otherwise, ensure that the ``tool_choice_value`` property is correctly
            specified on the test class.
        r   )humanz5Hello. Please respond in the style of {answer_style}.zhello matey)r[   greeting_generatorz6Generate a greeting in a particular style of speaking.)rt   r0   r   Nr   z+Using the tool, generate a Pirate greeting.r   ru   answer_stylerx   ry   )r   r   r   r   from_messagesr   iterr   as_toolr   r   r   rA   r   r|   r   )rX   r   promptllmchaintool_r   r   r   r>   ry   s              r<   test_bind_runnables_as_toolsz6ChatModelIntegrationTests.test_bind_runnables_as_tools  s   P $$KK56#11OP
 #D-,AB00%P  
 ).KK ++UG+M=!((/&),,,    %%a(	 $$^444 K///r;   r$   r%   c                    | j                   st        j                  d       t        |      \  }} |j                  |fi | j
                  }t               }t               |_        t               }|j                  dd|gi      } ||       t        |j                        dk(  sJ d       t        |j                  d   t              sJ t        |j                  d   d   d	   t              sJ |j                  d   d   d	   t        |      k(  sJ t               }	|j                  dd|	gi      D ]
  }
 ||
        
sJ t        |	j                        dk(  sJ d       t        |	j                  d   t              sJ t        |	j                  d   d   d	   t              sJ |	j                  d   d   d	   t        |      k(  sJ y
)l  Test to verify structured output is generated both on invoke and stream.

        This test is optional and should be skipped if the model does not support
        structured output (see Configuration below).

        .. dropdown:: Configuration

            To disable structured output tests, set ``has_structured_output`` to False
            in your test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def has_structured_output(self) -> bool:
                        return False

            By default, ``has_structured_output`` is True if a model overrides the
            ``with_structured_output`` or ``bind_tools`` methods.

        .. dropdown:: Troubleshooting

            If this test fails, ensure that the model's ``bind_tools`` method
            properly handles both JSON Schema and Pydantic V2 models.
            ``langchain_core`` implements 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
         Test requires structured output.Tell me a joke about cats.	callbacksconfigrs   .Expected on_chat_model_start to be called oncer   ls_structured_output_formatschemaN)has_structured_outputr   r   rQ   with_structured_outputstructured_output_kwargsr   r_   rS   r   r{   rT   rA   rb   r   r   )rX   r   r$   r  validation_functionchatmock_callbackinvoke_callbackr>   stream_callbackr   s              r<   test_structured_outputz0ChatModelIntegrationTests.test_structured_output#  s   > ))KK:;&5k&B##+u++FTd6S6ST!,5K).0(+?P1Q  
 	F#?**+q0 	
<	
0 /11!4d;;;##A&'DEhOQU
 	
 
 &&q)*GH
#F+, 	, , /0[[(+?P1Q ! 
 	'E  &	' u?**+q0 	
<	
0 /11!4d;;;##A&'DEhOQU
 	
 
 &&q)*GH
#F+, 	, ,r;   c                   K   | j                   st        j                  d       t        |      \  }} |j                  |fi | j
                  }t               }|j                  dd|gi       d{   } ||       t        |j                        dk(  sJ d       t        |j                  d   t              sJ t        |j                  d   d	   d
   t              sJ |j                  d   d	   d
   t        |      k(  sJ t               }|j                  dd|gi      2 3 d{   }	 ||	       7 7 6 	sJ t        |j                        dk(  sJ d       t        |j                  d   t              sJ t        |j                  d   d	   d
   t              sJ |j                  d   d	   d
   t        |      k(  sJ yw)r  r  r  r  r  Nrs   r  r   r  r  )r  r   r   rQ   r  r  rS   r   r{   rT   rA   rb   r   r   )
rX   r   r$   r  r  r  ainvoke_callbackr>   astream_callbackr   s
             r<   test_structured_output_asyncz6ChatModelIntegrationTests.test_structured_output_asynco  s)    B ))KK:;&5k&B##+u++FTd6S6ST/1||(+@P?Q1R $ 
 
 	F##++,1 	
<	
1 *2215t<<<$$Q'(EFxPRV
 	
 
  ''*+HI
#F+, 	, , 01<<(+@P?Q1R ( 
 	' 	'%  &+
$	' 
 u#++,1 	
<	
1 *2215t<<<$$Q'(EFxPRV
 	
 
  ''*+HI
#F+, 	, ,s8   A0G2D73B2G%D;)D9*D;-G9D;;BGrl   zTest requires pydantic 2.)reasonc                    | j                   st        j                  d        G d dt              } |j                  |fi | j
                  }|j                  d      }t        ||      sJ |j                  d      D ]  }t        ||      rJ   |j                  |j                         fi | j
                  }|j                  d      }t        |t              sJ t        |j                               ddhk(  sJ |j                  d      D ]  }t        |t              rJ  t        t              sJ t        |j                               ddhk(  sJ y)a  Test to verify we can generate structured output using
        pydantic.v1.BaseModel.

        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).

        .. dropdown:: Configuration

            To disable structured output tests, set ``has_structured_output`` to False
            in your test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def has_structured_output(self) -> bool:
                        return False

            By default, ``has_structured_output`` is True if a model overrides the
            ``with_structured_output`` or ``bind_tools`` methods.

        .. dropdown:: Troubleshooting

            If this test fails, ensure that the model's ``bind_tools`` method
            properly handles both JSON Schema and Pydantic V1 models.
            ``langchain_core`` implements 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
        r  c                   J    e Zd ZU dZ ed      Zeed<    ed      Zeed<   y)LChatModelIntegrationTests.test_structured_output_pydantic_2_v1.<locals>.Joker-   r.   r/   r1   r2   r3   N)	r4   r5   r6   r7   FieldV1r1   r8   r9   r3   r:   r;   r<   r=   r%    s%    $ -HIE3I$1MNIsNr;   r=   r  r1   r3   N)r  r   r   BaseModelV1r  r  r   rA   r   r  rb   setrL   )rX   r   r=   r  r>   r   s         r<   $test_structured_output_pydantic_2_v1z>ChatModelIntegrationTests.test_structured_output_pydantic_2_v1  sQ   D ))KK:;	O; 	O ,u++DRD4Q4QR9:&$'''[[!=> 	+EeT***	+ ,u++KKM
!::
 9:&$'''6;;=!g{%;;;;[[!=> 	+EeT***	+%&&&5::< Wk$::::r;   c                 T   | j                   st        j                  d        G d dt              } |j                  |fi | j
                  }|j                  d      }t        ||      sJ |j                  d      }t        ||      sJ  |j                  |j                         fi | j
                  }|j                  d      }t        |t              sJ  G d dt              } |j                  |fi | j
                  }|j                  d      }t        |t              sJ y	)
a  Test to verify we can generate structured output that includes optional
        parameters.

        This test is optional and should be skipped if the model does not support
        structured output (see Configuration below).

        .. dropdown:: Configuration

            To disable structured output tests, set ``has_structured_output`` to False
            in your test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def has_structured_output(self) -> bool:
                        return False

            By default, ``has_structured_output`` is True if a model overrides the
            ``with_structured_output`` or ``bind_tools`` methods.

        .. dropdown:: Troubleshooting

            If this test fails, ensure that the model's ``bind_tools`` method
            properly handles Pydantic V2 models with optional parameters.
            ``langchain_core`` implements 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
        r  c                   R    e Zd ZU dZ ed      Zeed<    edd      Ze	e   ed<   y)	MChatModelIntegrationTests.test_structured_output_optional_param.<locals>.Joker-   r.   r/   r1   Nr2   )defaultr0   r3   )
r4   r5   r6   r7   r   r1   r8   r9   r3   r   r:   r;   r<   r=   r,    s0    $+FGE3G',*F(Ix} r;   r=   z5Give me the setup to a joke about cats, no punchline.z1Give me a joke about cats, include the punchline.r  c                   D    e Zd ZU dZeeddf   ed<   eee   ddf   ed<   y)QChatModelIntegrationTests.test_structured_output_optional_param.<locals>.JokeDictr-   .r.   r1   Nr2   r3   )r4   r5   r6   r7   r   r8   r9   r   r:   r;   r<   rE   r/  8  s/    $S#'BBCC #6R!RSSr;   rE   N)r  r   r   r   r  r  r   rA   rO   rb   r    )rX   r   r=   r  setup_resultjoke_resultr>   rE   s           r<   %test_structured_output_optional_paramz?ChatModelIntegrationTests.test_structured_output_optional_param  s"   > ))KK:;	9 	 ,u++DRD4Q4QR{{C
 ,---kk"UV+t,,, ,u++""$
(,(E(E
 9:&$'''	Ty 	T ,u++HV8U8UV9:&$'''r;   c                    | j                   st        j                  d       ddlm} ddlm  G fdd|      }|j                  |d      }d	}|j                  |      }t        ||      sJ |j                  |      D ]  }t        ||      rJ  |j                  |j                         d      }|j                  |      }t        |t              sJ t        |j                               d
dhk(  sJ |j                  |      D ]  }t        |t              rJ  t        t              sJ t        |j                               d
dhk(  sJ y)aw  Test structured output via `JSON mode. <https://python.langchain.com/docs/concepts/structured_outputs/#json-mode>`_

        This test is optional and should be skipped if the model does not support
        the JSON mode feature (see Configuration below).

        .. dropdown:: Configuration

            To disable this test, set ``supports_json_mode`` to False in your
            test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def supports_json_mode(self) -> bool:
                        return False

        .. dropdown:: Troubleshooting

            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
        z Test requires json mode support.r   r   r   c                   P    e Zd ZU dZ W  d      Zeed<    W  d      Zeed<   y)6ChatModelIntegrationTests.test_json_mode.<locals>.Joker-   r.   r/   r1   r2   r3   N)r4   r5   r6   r7   r1   r8   r9   r3   )FieldPropers   r<   r=   r5  ^  s&    $$1LME3M(5QRIsRr;   r=   	json_mode)methodzyTell me a joke about cats. Return the result as a JSON with 'setup' and 'punchline' keys. Return nothing other than JSON.r1   r3   N)supports_json_moder   r   r&   r   r   r  r   rA   r   rO   rb   r(  rL   )	rX   r   BaseModelProperr=   r  r   r>   r   r6  s	           @r<   test_json_modez(ChatModelIntegrationTests.test_json_modeB  sR   , &&KK:;91	S? 	S ++D+E@ 	 S!&$'''[[% 	+EeT***	+ ++""$[ , 
 S!&$'''6;;=!g{%;;;;[[% 	+EeT***	+%&&&5::< Wk$::::r;   my_adder_toolc           	      >   | j                   st        j                  d       |j                  |g      }d}ddd}t	        d      t        d||dd	d
g      t        t        j                  ddi      |d      g}|j                  |      }t        |t
              sJ y)a6  Test that message histories are compatible with string tool contents
        (e.g. OpenAI format). If a model passes this test, it should be compatible
        with messages generated from providers following OpenAI format.

        This test should be skipped if the model does not support tool calling
        (see Configuration below).

        .. dropdown:: Configuration

            To disable tool calling tests, set ``has_tool_calling`` to False in your
            test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def has_tool_calling(self) -> bool:
                        return False

        .. dropdown:: Troubleshooting

            If this test fails, check that:

            1. The model can correctly handle message histories that include AIMessage objects with ``""`` 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
            is not supported.

            .. code-block:: python

                @pytest.mark.xfail(reason=("Not implemented."))
                def test_tool_message_histories_string_content(self, *args: Any) -> None:
                    super().test_tool_message_histories_string_content(*args)
        r   r<  12abWhat is 1 + 2 abc123ry   rt   ru   rw   rx   r|   r>   rv   rt   tool_call_idNr   r   r   r   r   r   r   jsondumpsr   rA   )rX   r   r<  r   function_namefunction_argsmessages_string_contentresult_string_contents           r<   *test_tool_message_histories_string_contentzDChatModelIntegrationTests.test_tool_message_histories_string_content  s    N $$KK56 ++]O<'!, ) !. -& +	
 

Ha=)"%#
( !1 7 78O P/;;;r;   c           	      R   | j                   st        j                  d       |j                  |g      }d}ddd}t	        d      t        ddd	d
d||dg||dddg      t        t        j                  ddi      |d      g}|j                  |      }t        |t
              sJ y)a{  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
        content blocks, e.g.,

        .. code-block:: python

            [
                {"type": "text", "text": "Hmm let me think about that"},
                {
                    "type": "tool_use",
                    "input": {"fav_color": "green"},
                    "id": "foo",
                    "name": "color_picker",
                },
            ]

        This test should be skipped if the model does not support tool calling
        (see Configuration below).

        .. dropdown:: Configuration

            To disable tool calling tests, set ``has_tool_calling`` to False in your
            test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def has_tool_calling(self) -> bool:
                        return False

        .. dropdown:: Troubleshooting

            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.
            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
            is not supported.

            .. code-block:: python

                @pytest.mark.xfail(reason=("Not implemented."))
                def test_tool_message_histories_list_content(self, *args: Any) -> None:
                    super().test_tool_message_histories_list_content(*args)
        r   r<  rs   rl   r@  rC  r   z	some textrx   r   tool_userE  )rx   rw   rt   rh   ry   rF  rG  r>   rv   rH  NrJ  )rX   r   r<  r   rM  rN  messages_list_contentresult_list_contents           r<   (test_tool_message_histories_list_contentzBChatModelIntegrationTests.test_tool_message_histories_list_content  s    n $$KK56 ++]O<'a( )#[9 *& -!.	 !. -& +	& 

Ha=)"%-!
8 /556KL-y999r;   c                    | j                   st        j                  d       |j                  |gd      }t	        j
                  ddi      }|j                  }t        |t              rt        |t              sJ t        d |dd	      g|g|
      }|t        d      gz   }|j                  |      }t        |t              sJ y)a  Test that the model can process few-shot examples with tool calls.

        These are represented as a sequence of messages of the following form:

        - ``HumanMessage`` with string content;
        - ``AIMessage`` with the ``tool_calls`` attribute populated;
        - ``ToolMessage`` with string content;
        - ``AIMessage`` with string content (an answer);
        - ``HuamnMessage`` with string content (a follow-up question).

        This test should be skipped if the model does not support tool calling
        (see Configuration below).

        .. dropdown:: Configuration

            To disable tool calling tests, set ``has_tool_calling`` to False in your
            test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def has_tool_calling(self) -> bool:
                        return False

        .. dropdown:: Troubleshooting

            This test uses a utility function in ``langchain_core`` to generate a
            sequence of messages representing "few-shot" examples: https://python.langchain.com/api_reference/core/utils/langchain_core.utils.function_calling.tool_example_to_messages.html

            If this test fails, check that the model can correctly handle this
            sequence of messages.

            You can ``xfail`` the test if tool calling is implemented but this format
            is not supported.

            .. code-block:: python

                @pytest.mark.xfail(reason=("Not implemented."))
                def test_structured_few_shot_examples(self, *args: Any) -> None:
                    super().test_structured_few_shot_examples(*args)
        r   r   r   r>   rv   rC  rs   rl   r@  )tool_outputsai_responsezWhat is 3 + 4N)r   r   r   r   rK  rL  rj   rA   rx   
issubclassr   r   r   r   r   )	rX   r   r<  r   function_resulttool_schemafew_shot_messagesr[   r>   s	            r<   !test_structured_few_shot_examplesz;ChatModelIntegrationTests.test_structured_few_shot_examples   s    Z $$KK56 ++]O+O**h]3#//+t,K1SSS41"#)*'	
 %_(E'FF!((2&),,,r;   c                 p   | j                   st        j                  d       d}t        j                  t        j                  |      j                        j                  d      }t        dddddd	|d
g      }|j                  |g      }t        dddddd| ddg      }|j                  |g      }y)at  Test that the model can process PDF inputs.

        This test should be skipped (see Configuration below) if the model does not
        support PDF inputs. These will take the form:

        .. code-block:: python

            {
                "type": "image",
                "source_type": "base64",
                "data": "<base64 image data>",
                "mime_type": "application/pdf",
            }

        See https://python.langchain.com/docs/concepts/multimodality/

        .. dropdown:: Configuration

            To disable this test, set ``supports_pdf_inputs`` to False in your
            test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):

                    @property
                    def supports_pdf_inputs(self) -> bool:
                        return False

        .. dropdown:: Troubleshooting

            If this test fails, check that the model can correctly handle messages
            with pdf content blocks, including base64-encoded files. Otherwise, set
            the ``supports_pdf_inputs`` property to False.
        z"Model does not support PDF inputs.zGhttps://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdfutf-8r   zSummarize this document:rS  filebase64zapplication/pdfrx   source_type	mime_typedataztest file.pdfzdata:application/pdf;base64,)filename	file_data)rx   rb  N)supports_pdf_inputsr   r   rc  	b64encodehttpxr   r   decoder   r   )rX   r   urlpdf_datarq   _s         r<   test_pdf_inputsz)ChatModelIntegrationTests.test_pdf_inputs_  s    H ''KK<=W##EIIcN$:$:;BB7K #6
 ##+!2$	
 LL'#  #6
 #$3'CH:%N
 LL'#r;   c                 j   | j                   st        j                  d       d}t        j                  t        j                  |      j                        j                  d      }t        dddddd	|d
g      }|j                  |g      }t        dddd|dddg      }|j                  |g      }y)a  Test that the model can process audio inputs.

        This test should be skipped (see Configuration below) if the model does not
        support audio inputs. These will take the form:

        .. code-block:: python

            {
                "type": "audio",
                "source_type": "base64",
                "data": "<base64 audio data>",
                "mime_type": "audio/wav",  # or appropriate mime-type
            }

        See https://python.langchain.com/docs/concepts/multimodality/

        .. dropdown:: Configuration

            To disable this test, set ``supports_audio_inputs`` to False in your
            test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):

                    @property
                    def supports_audio_inputs(self) -> bool:
                        return False

        .. dropdown:: Troubleshooting

            If this test fails, check that the model can correctly handle messages
            with audio content blocks, specifically base64-encoded files. Otherwise,
            set the ``supports_audio_inputs`` property to False.
        z$Model does not support audio inputs.zhttps://upload.wikimedia.org/wikipedia/commons/3/3d/Alcal%C3%A1_de_Henares_%28RPS_13-04-2024%29_canto_de_ruise%C3%B1or_%28Luscinia_megarhynchos%29_en_el_Soto_del_Henares.wavra  r   zDescribe this audio:rS  r   rc  z	audio/wavrd  input_audiowav)rg  format)rx   rs  N)supports_audio_inputsr   r   rc  rk  rl  r   r   rm  r   r   )rX   r   rn  
audio_datarq   rp  s         r<   test_audio_inputsz+ChatModelIntegrationTests.test_audio_inputs  s    H ))KK>? ~%%eiin&<&<=DDWM
 #2
 $#+!,&	
 LL'#  #2
 *,6%#H	
 LL'#r;   c                    | j                   st        j                  d       d}t        j                  t        j                  |      j                        j                  d      }t        dddddd	| id
g      }|j                  |g      }t        dddddd|dg      }|j                  |g      }| j                  r(t        ddddd|dg      }|j                  |g      }yy)a$  Test that the model can process image inputs.

        This test should be skipped (see Configuration below) if the model does not
        support image inputs. These will take the form:

        .. code-block:: python

            {
                "type": "image",
                "source_type": "base64",
                "data": "<base64 image data>",
                "mime_type": "image/jpeg",  # or appropriate mime-type
            }

        For backward-compatibility, we must also support OpenAI-style
        image content blocks:

        .. code-block:: python

            [
                {"type": "text", "text": "describe the weather in this image"},
                {
                    "type": "image_url",
                    "image_url": {"url": f"data:image/jpeg;base64,{image_data}"},
                },
            ]

        See https://python.langchain.com/docs/concepts/multimodality/

        If the property ``supports_image_urls`` is set to True, the test will also
        check that we can process content blocks of the form:

        .. code-block:: python

            {
                "type": "image",
                "source_type": "url",
                "url": "<url>",
            }

        .. dropdown:: Configuration

            To disable this test, set ``supports_image_inputs`` to False in your
            test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def supports_image_inputs(self) -> bool:
                        return False

                    # Can also explicitly disable testing image URLs:
                    @property
                    def supports_image_urls(self) -> bool:
                        return False

        .. dropdown:: Troubleshooting

            If this test fails, check that the model can correctly handle messages
            with image content blocks, including base64-encoded images. Otherwise, set
            the ``supports_image_inputs`` property to False.
        z%Model does not support image message.https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpgra  r   z"describe the weather in this imagerS  	image_urlrn  data:image/jpeg;base64,rx   r{  )r   imagerc  
image/jpegrd  )rx   re  rn  N)supports_image_inputsr   r   rc  rk  rl  r   r   rm  r   r   supports_image_urls)rX   r   r{  
image_datarq   rp  s         r<   test_image_inputsz+ChatModelIntegrationTests.test_image_inputs  s   @ ))KK?@ u	%%eii	&:&B&BCJJ7S
 )MN'"'+B:,)O!P
 LL'# )MN##+!-&	

 LL'# ##"#-QR '',(	G gY'A $r;   c           
         | j                   st        j                  d       d}t        j                  t        j                  |      j                        j                  d      }t        ddd| idgdd	
      }t        dd|ddgdd	
      }||fD ]L  }t        d      t        g ddd	i dg      |g}dt        fd}|j                  |g      j                  |      }	N y)a  Test that the model can process ToolMessages with image inputs.

        This test should be skipped if the model does not support messages of the
        form:

        .. code-block:: python

            ToolMessage(
                content=[
                    {
                        "type": "image_url",
                        "image_url": {"url": f"data:image/jpeg;base64,{image_data}"},
                    },
                ],
                tool_call_id="1",
                name="random_image",
            )

        containing image content blocks in OpenAI Chat Completions format, in addition
        to messages of the form:

        .. code-block:: python

            ToolMessage(
                content=[
                    {
                        "type": "image",
                        "source_type": "base64",
                        "data": image_data,
                        "mime_type": "image/jpeg",
                    },
                ],
                tool_call_id="1",
                name="random_image",
            )

        containing image content blocks in standard format.

        This test can be skipped by setting the ``supports_image_tool_message`` property
        to False (see Configuration below).

        .. dropdown:: Configuration

            To disable this test, set ``supports_image_tool_message`` to False in your
            test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def supports_image_tool_message(self) -> bool:
                        return False

        .. dropdown:: Troubleshooting

            If this test fails, check that the model can correctly handle messages
            with image content blocks in ToolMessages, including base64-encoded
            images. Otherwise, set the ``supports_image_tool_message`` property to
            False.
        z*Model does not support image tool message.rz  ra  r{  rn  r|  r}  r>  random_image)r   rI  rt   r~  rc  r  )rx   re  rg  rf  z:get a random image using the tool and describe the weatherry   )rx   rw   rt   ru   rG  r)   c                       y)zReturn a random image.rD  r:   r:   r;   r<   r  zGChatModelIntegrationTests.test_image_tool_message.<locals>.random_image	  s    r;   N)supports_image_tool_messager   r   rc  rk  rl  r   r   rm  r   r   r   r8   r   r   )
rX   r   r{  r  oai_format_messagestandard_format_messagetool_messager[   r  rp  s
             r<   test_image_tool_messagez1ChatModelIntegrationTests.test_image_tool_message_	  s.   z //KKDE u	%%eii	&:&B&BCJJ7S
 ) ("'+B:,)O!P 	
 #. $#+&!-	 #
 01HI 	BLP  %0"%$2$&	 
 H$#    ,077AA/	Br;   c           
         | j                   st        j                  d        G d dt              }dddg}| j                  r[d}t        j                  t        j                  |      j                        j                  d      }|j                  d	d
d|dd       t        d      t        |      t        ddddddidddgdddidddg      t        dd      g}|j!                  |g      j#                  |      }t%        |t              sJ t        dddg      t        dddddddg      t        dd dg      g}|j#                  |      }t%        |t              sJ y!)"a	  Test that model can process Anthropic-style message histories.

        These message histories will include ``AIMessage`` objects with ``tool_use``
        content blocks, e.g.,

        .. code-block:: python

            AIMessage(
                [
                    {"type": "text", "text": "Hmm let me think about that"},
                    {
                        "type": "tool_use",
                        "input": {"fav_color": "green"},
                        "id": "foo",
                        "name": "color_picker",
                    },
                ]
            )

        as well as ``HumanMessage`` objects containing ``tool_result`` content blocks:

        .. code-block:: python

            HumanMessage(
                [
                    {
                        "type": "tool_result",
                        "tool_use_id": "foo",
                        "content": [
                            {
                                "type": "text",
                                "text": "green is a great pick! that's my sister's favorite color",  # noqa: E501
                            }
                        ],
                        "is_error": False,
                    },
                    {"type": "text", "text": "what's my sister's favorite color"},
                ]
            )

        This test should be skipped if the model does not support messages of this
        form (or doesn't support tool calling generally). See Configuration below.

        .. dropdown:: Configuration

            To disable this test, set ``supports_anthropic_inputs`` to False in your
            test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def supports_anthropic_inputs(self) -> bool:
                        return False

        .. dropdown:: Troubleshooting

            If this test fails, check that:

            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.

            Otherwise, if Anthropic tool call and result formats are not supported,
            set the ``supports_anthropic_inputs`` property to False.
        z3Model does not explicitly support Anthropic inputs.c                       e Zd ZU dZeed<   y)EChatModelIntegrationTests.test_anthropic_inputs.<locals>.color_pickerz4Input your fav color and get a random fact about it.	fav_colorN)r4   r5   r6   r7   r8   r9   r:   r;   r<   color_pickerr  
  s
    FNr;   r  r   z(what's your favorite color in this imagerS  rz  ra  r~  rc  r  )rx   
media_typerg  )rx   sourcezyou're a good assistantzHmm let me think about thatrT  r  greenfoo)rx   rh   rw   rt   ry   rF  rG  zThat's a great pick!)rI  r   thinkingzI'm thinking...rE  )rx   r  	signaturezHello, how are you?zWell, thanks.N)supports_anthropic_inputsr   r   r'  r  rc  rk  rl  r   r   rm  r^   r   r   r   r   r   r   rA   )rX   r   r  human_contentr{  r  r[   responses           r<   test_anthropic_inputsz/ChatModelIntegrationTests.test_anthropic_inputs	  s   F --KKMN	; 	 B%
 %% yI))%))I*>*F*FGNNwWJ  # (&2 *	 34'#-JK *"-w!7# .	 !/!,g 6# +	& .UC-
0 ##\N3::8D(I...  !' '  !+$5%- !' 5
  !' /-
> <<)(I...r;   c           	         | j                   st        j                  d       |j                  |g      }t	        d      t        ddddiddd	g
      t        dddd      g}|j                  |      }t        |t
              sJ y)a  Test that ToolMessage with ``status="error"`` can be handled.

        These messages may take the form:

        .. code-block:: python

            ToolMessage(
                "Error: Missing required argument 'b'.",
                name="my_adder_tool",
                tool_call_id="abc123",
                status="error",
            )

        If possible, the ``status`` field should be parsed and passed appropriately
        to the model.

        This test is optional and should be skipped if the model does not support
        tool calling (see Configuration below).

        .. dropdown:: Configuration

            To disable tool calling tests, set ``has_tool_calling`` to False in your
            test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def has_tool_calling(self) -> bool:
                        return False

        .. dropdown:: Troubleshooting

            If this test fails, check that the ``status`` field on ``ToolMessage``
            objects is either ignored or passed to the model appropriately.
        r   rC  rD  r<  rA  rs   rE  ry   rF  rG  z%Error: Missing required argument 'b'.error)rt   rI  statusN)	r   r   r   r   r   r   r   r   rA   )rX   r   r<  r   r[   r>   s         r<   test_tool_message_error_statusz8ChatModelIntegrationTests.test_tool_message_error_statuso
  s    N $$KK56 ++]O<) !0!$a& +	
 7$%	
( "((2&),,,r;   c                     |j                  t        dd      g      }|J t        |t              sJ t        |j	                         t
              sJ t        |j                        dkD  sJ y)a%  Test that HumanMessage with values for the ``name`` field can be handled.

        These messages may take the form:

        .. code-block:: python

            HumanMessage("hello", name="example_user")

        If possible, the ``name`` field should be parsed and passed appropriately
        to the model. Otherwise, it should be ignored.

        .. dropdown:: Troubleshooting

            If this test fails, check that the ``name`` field on ``HumanMessage``
            objects is either ignored or passed to the model appropriately.
        r   example_user)rt   Nr   )r   r   rA   r   r   r8   r{   r   r   s      r<   test_message_with_namez0ChatModelIntegrationTests.test_message_with_name
  se    " |G.IJK!!!&),,,&++----6>>"Q&&&r;   c                    | j                   st        j                  d       t        dt        dt        fd       }|j                  |g      }t        d      }|j                  |g      }t        |t              sJ |j                  }t        |      dk(  sJ |d   }|j                  |      }t        |t              sJ |j                  |||g      }	t        |	t              sJ y)	a  Test that the model supports a simple ReAct agent loop. This test is skipped
        if the ``has_tool_calling`` property on the test class is set to False.

        This test is optional and should be skipped if the model does not support
        tool calling (see Configuration below).

        .. dropdown:: Configuration

            To disable tool calling tests, set ``has_tool_calling`` to False in your
            test class:

            .. code-block:: python

                class TestMyChatModelIntegration(ChatModelIntegrationTests):
                    @property
                    def has_tool_calling(self) -> bool:
                        return False

        .. dropdown:: Troubleshooting

            If this test fails, check that ``bind_tools`` is implemented to correctly
            translate LangChain tool objects into the appropriate schema for your
            chat model.

            Check also that all required information (e.g., tool calling identifiers)
            from AIMessage objects is propagated correctly to model payloads.

            This test may fail if the chat model does not consistently generate tool
            calls in response to an appropriate query. In these cases you can ``xfail``
            the test:

            .. code-block:: python

                @pytest.mark.xfail(reason=("Does not support tool_choice."))
                def test_agent_loop(self, model: BaseChatModel) -> None:
                    super().test_agent_loop(model)

        r   r   r)   c                      y)zCall to surf the web.r   r:   r   s    r<   r   z>ChatModelIntegrationTests.test_agent_loop.<locals>.get_weather
  r   r;   z)What is the weather in San Francisco, CA?rs   r   N)r   r   r   r   r8   r   r   r   rA   r   r|   r{   r   )
rX   r   r   llm_with_toolsinput_messagetool_call_messager|   ry   r  r  s
             r<   test_agent_loopz)ChatModelIntegrationTests.test_agent_loop
  s    N $$KK56		!# 	!# 	! 
	! ));-8$%PQ*11=/B+Y777&11
:!###qM	")))4,444!((!
 (I...r;   	benchmarkvcrc                     | j                   st        j                  d       dfd}|j                  s |        y ||       y)a`  Test that streaming does not introduce undue overhead.

        See ``enable_vcr_tests`` dropdown :class:`above <ChatModelIntegrationTests>`
        for more information.

        .. dropdown:: Configuration

            This test can be enabled or disabled using the ``enable_vcr_tests``
            property. For example, to disable the test, set this property to ``False``:

            .. code-block:: python

                @property
                def enable_vcr_tests(self) -> bool:
                    return False

            .. important::

                VCR will by default record authentication headers and other sensitive
                information in cassettes. See ``enable_vcr_tests`` dropdown
                :class:`above <ChatModelIntegrationTests>` for how to configure what
                information is recorded in cassettes.

        zVCR not set up.Nc                  2    j                  d      D ]  }  y )NzWrite a story about a cat.r   )rp  r   s    r<   _runz8ChatModelIntegrationTests.test_stream_time.<locals>._run(  s    \\">? r;   r`   )enable_vcr_testsr   r   	responses)rX   r   r  r  r  s    `   r<   test_stream_timez*ChatModelIntegrationTests.test_stream_time  s5    : $$KK)*	 }}FdOr;   Fr   r   c                    t               r   NotImplementedErrorrX   r   s     r<   r   z1ChatModelIntegrationTests.invoke_with_audio_input1      !##r;   c                    t               r   r  r  s     r<   r   z2ChatModelIntegrationTests.invoke_with_audio_output5  r  r;   c                    t               r   r  r  s     r<   r   z6ChatModelIntegrationTests.invoke_with_reasoning_output9  r  r;   c                    t               r   r  r  s     r<   r   z6ChatModelIntegrationTests.invoke_with_cache_read_input=  r  r;   c                    t               r   r  r  s     r<   r   z:ChatModelIntegrationTests.invoke_with_cache_creation_inputA  r  r;   );r4   r5   r6   r7   propertyrb   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r
  r   markparametrizer8   r  r!  skipifr#   r)  r2  r;  r   rQ  rW  r_  rq  rx  r  r  r  r  r  r  r  r  r   r!   r  rN   r   r   r   r   r   r   r:   r;   r<   r   r      sJ   Sj D  ' '4 '2' '$ '@ 4 @! !$ !F+ +$ +6+} + +:'} ' ':$'} $' $'LZ Z4 ZxS= ST Sj!- !-$ !-FJ*} J* J*X1Hm 1H 1Hf;*= ;*T ;*z:2 :24 :2x?0- ?0D ?0B [[],TUI,M I, I,PT I, VI,V [[],TUJ,"J,14J,	J, VJ,X [[.!3<WX=;- =;D =; Y=;~D(= D(T D(L<;M <;d <;|B<"B<3;B<	B<HZ:Z:  Z: 
	Z:x=-"=-3;=-	=-~I$] I$t I$VF$} F$ F$Pk(} k( k(ZrB] rBt rBhZ/= Z/T Z/x?-"?-3;?-	?-B'M 'd '.?/] ?/t ?/B [[[[__%"%/?%FN%	%  %N 9> $ $) $ :? $$ $9 $ >C $d $y $ >C $d $y $ BG $$ $9 $r;   r   )@rc  r   rK  typingr   r   r   r   r   unittest.mockr   rl  r   langchain_core._apir	   langchain_core.callbacksr
   langchain_core.language_modelsr   r   langchain_core.messagesr   r   r   r   r   r   r   langchain_core.output_parsersr   langchain_core.promptsr   langchain_core.toolsr   r   %langchain_core.utils.function_callingr   r   r&   r   r   pydantic.v1r'  r&  pytest_benchmark.fixturer   typing_extensionsr   r    vcr.cassetter!   &langchain_tests.unit_tests.chat_modelsr"   langchain_tests.utils.pydanticr#   rQ   rS   re   ri   rm   rp   r~   r   r   r:   r;   r<   <module>r     s      5 5 #   / 8 N   : 5 / & 0 ( 5 2 ! B"0?@"0"0J%. %$/9 / &'# #  (
   
, , ,, , ,*$ *$r;   