U
    hy                     @  s  d Z ddlmZ ddlZddlZddlmZ ddlmZm	Z	m
Z
mZmZmZmZmZmZmZmZmZ ddlZddlmZ ddlmZ ddlmZ dd	lmZmZ dd
lmZ ddl m!Z!m"Z" ddl#m$Z$ ddl%m&Z&m'Z'm(Z(m)Z) ddl*m+Z+m,Z,m-Z- ddl.m/Z/ ddl0m1Z1m2Z2 ddl3m4Z4 ddl5m6Z6 ddl7m8Z8 e9e:Z;ede&dZ<ee
e=ef ee< f Z>ee
e<f Z?G dd deZ@dddddZAG dd de8ZBdS )zAzure OpenAI chat wrapper.    )annotationsN)
itemgetter)AnyCallableDictListLiteralOptionalSequenceType	TypedDictTypeVarUnionoverload)LanguageModelInput)LangSmithParams)BaseMessage)JsonOutputParserPydanticOutputParser)OutputParserLike)JsonOutputKeyToolsParserPydanticToolsParser)
ChatResult)	BaseModelField	SecretStrroot_validator)RunnableRunnableMapRunnablePassthrough)BaseTool)from_envsecret_from_env)convert_to_openai_tool)is_basemodel_subclass)BaseChatOpenAI_BM)boundc                   @  s&   e Zd ZU ded< ded< ded< dS )_AllReturnTyper   rawzOptional[_DictOrPydantic]parsedzOptional[BaseException]parsing_errorN)__name__
__module____qualname____annotations__ r0   r0   F/tmp/pip-unpacked-wheel-n8reftby/langchain_openai/chat_models/azure.pyr(   3   s   
r(   r   bool)objreturnc                 C  s   t | tot| S N)
isinstancetyper$   )r3   r0   r0   r1   _is_pydantic_class9   s    r8   c                	      s.  e Zd ZU dZeeddddZded< eddd	Zd
ed< ededdddZ	ded< ede
ddgdddZded< ee
ddddZded< dZded< dZded< eeddddZded< dZd ed!< edd"d	Zded#< ed$d%d&d'Zed(d%d)d*Zed d%d+d,Zed-dd.d/d/d0d1d2Zdd3d4d5d6d7d8 fd9d:ZedZd;dd<d=d>d?d6d@dAdBdCZed[d;d-d<d=d>dDd6dEdAdFdCZd\d;d-d<d=d>d d6dEdAdGdCZedHd% fdIdJZedd%dKdLZedHd%dMdNZd]dOd6dPdQ fdRdSZd^dTdUdVdW fdXdYZ   Z!S )_AzureChatOpenAIu	:  Azure OpenAI chat model integration.

    Setup:
        Head to the https://learn.microsoft.com/en-us/azure/ai-services/openai/chatgpt-quickstart?tabs=command-line%2Cpython-new&pivots=programming-language-python
        to create your Azure OpenAI deployment.

        Then install ``langchain-openai`` and set environment variables
        ``AZURE_OPENAI_API_KEY`` and ``AZURE_OPENAI_ENDPOINT``:

        .. code-block:: bash

            pip install -U langchain-openai

            export AZURE_OPENAI_API_KEY="your-api-key"
            export AZURE_OPENAI_ENDPOINT="https://your-endpoint.openai.azure.com/"

    Key init args — completion params:
        azure_deployment: str
            Name of Azure OpenAI deployment to use.
        temperature: float
            Sampling temperature.
        max_tokens: Optional[int]
            Max number of tokens to generate.
        logprobs: Optional[bool]
            Whether to return logprobs.

    Key init args — client params:
        api_version: str
            Azure OpenAI API version to use. See more on the different versions here:
            https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#rest-api-versioning
        timeout: Union[float, Tuple[float, float], Any, None]
            Timeout for requests.
        max_retries: int
            Max number of retries.
        organization: Optional[str]
            OpenAI organization ID. If not passed in will be read from env
            var OPENAI_ORG_ID.
        model: Optional[str]
            The name of the underlying OpenAI model. Used for tracing and token
            counting. Does not affect completion. E.g. "gpt-4", "gpt-35-turbo", etc.
        model_version: Optional[str]
            The version of the underlying OpenAI model. Used for tracing and token
            counting. Does not affect completion. E.g., "0125", "0125-preview", etc.

    See full list of supported init args and their descriptions in the params section.

    Instantiate:
        .. code-block:: python

            from langchain_openai import AzureChatOpenAI

            llm = AzureChatOpenAI(
                azure_deployment="your-deployment",
                api_version="2024-05-01-preview",
                temperature=0,
                max_tokens=None,
                timeout=None,
                max_retries=2,
                # organization="...",
                # model="gpt-35-turbo",
                # model_version="0125",
                # other params...
            )

    **NOTE**: Any param which is not explicitly supported will be passed directly to the
    ``openai.AzureOpenAI.chat.completions.create(...)`` API every time to the model is
    invoked. For example:
        .. code-block:: python

            from langchain_openai import AzureChatOpenAI
            import openai

            AzureChatOpenAI(..., logprobs=True).invoke(...)

            # results in underlying API call of:

            openai.AzureOpenAI(..).chat.completions.create(..., logprobs=True)

            # which is also equivalent to:

            AzureChatOpenAI(...).invoke(..., logprobs=True)

    Invoke:
        .. code-block:: python

            messages = [
                (
                    "system",
                    "You are a helpful translator. Translate the user sentence to French.",
                ),
                ("human", "I love programming."),
            ]
            llm.invoke(messages)

        .. code-block:: python

            AIMessage(
                content="J'adore programmer.",
                usage_metadata={"input_tokens": 28, "output_tokens": 6, "total_tokens": 34},
                response_metadata={
                    "token_usage": {
                        "completion_tokens": 6,
                        "prompt_tokens": 28,
                        "total_tokens": 34,
                    },
                    "model_name": "gpt-4",
                    "system_fingerprint": "fp_7ec89fabc6",
                    "prompt_filter_results": [
                        {
                            "prompt_index": 0,
                            "content_filter_results": {
                                "hate": {"filtered": False, "severity": "safe"},
                                "self_harm": {"filtered": False, "severity": "safe"},
                                "sexual": {"filtered": False, "severity": "safe"},
                                "violence": {"filtered": False, "severity": "safe"},
                            },
                        }
                    ],
                    "finish_reason": "stop",
                    "logprobs": None,
                    "content_filter_results": {
                        "hate": {"filtered": False, "severity": "safe"},
                        "self_harm": {"filtered": False, "severity": "safe"},
                        "sexual": {"filtered": False, "severity": "safe"},
                        "violence": {"filtered": False, "severity": "safe"},
                    },
                },
                id="run-6d7a5282-0de0-4f27-9cc0-82a9db9a3ce9-0",
            )

    Stream:
        .. code-block:: python

            for chunk in llm.stream(messages):
                print(chunk)

        .. code-block:: python

            AIMessageChunk(content="", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f")
            AIMessageChunk(content="J", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f")
            AIMessageChunk(content="'", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f")
            AIMessageChunk(content="ad", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f")
            AIMessageChunk(content="ore", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f")
            AIMessageChunk(content=" la", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f")
            AIMessageChunk(content=" programm", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f")
            AIMessageChunk(content="ation", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f")
            AIMessageChunk(content=".", id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f")
            AIMessageChunk(
                content="",
                response_metadata={
                    "finish_reason": "stop",
                    "model_name": "gpt-4",
                    "system_fingerprint": "fp_811936bd4f",
                },
                id="run-a6f294d3-0700-4f6a-abc2-c6ef1178c37f",
            )

        .. code-block:: python

            stream = llm.stream(messages)
            full = next(stream)
            for chunk in stream:
                full += chunk
            full

        .. code-block:: python

            AIMessageChunk(
                content="J'adore la programmation.",
                response_metadata={
                    "finish_reason": "stop",
                    "model_name": "gpt-4",
                    "system_fingerprint": "fp_811936bd4f",
                },
                id="run-ba60e41c-9258-44b8-8f3a-2f10599643b3",
            )

    Async:
        .. code-block:: python

            await llm.ainvoke(messages)

            # stream:
            # async for chunk in (await llm.astream(messages))

            # batch:
            # await llm.abatch([messages])

    Tool calling:
        .. code-block:: python

            from langchain_core.pydantic_v1 import BaseModel, Field


            class GetWeather(BaseModel):
                '''Get the current weather in a given location'''

                location: str = Field(
                    ..., description="The city and state, e.g. San Francisco, CA"
                )


            class GetPopulation(BaseModel):
                '''Get the current population in a given location'''

                location: str = Field(
                    ..., description="The city and state, e.g. San Francisco, CA"
                )


            llm_with_tools = llm.bind_tools([GetWeather, GetPopulation])
            ai_msg = llm_with_tools.invoke(
                "Which city is hotter today and which is bigger: LA or NY?"
            )
            ai_msg.tool_calls

        .. code-block:: python

            [
                {
                    "name": "GetWeather",
                    "args": {"location": "Los Angeles, CA"},
                    "id": "call_6XswGD5Pqk8Tt5atYr7tfenU",
                },
                {
                    "name": "GetWeather",
                    "args": {"location": "New York, NY"},
                    "id": "call_ZVL15vA8Y7kXqOy3dtmQgeCi",
                },
                {
                    "name": "GetPopulation",
                    "args": {"location": "Los Angeles, CA"},
                    "id": "call_49CFW8zqC9W7mh7hbMLSIrXw",
                },
                {
                    "name": "GetPopulation",
                    "args": {"location": "New York, NY"},
                    "id": "call_6ghfKxV264jEfe1mRIkS3PE7",
                },
            ]

    Structured output:
        .. code-block:: python

            from typing import Optional

            from langchain_core.pydantic_v1 import BaseModel, Field


            class Joke(BaseModel):
                '''Joke to tell user.'''

                setup: str = Field(description="The setup of the joke")
                punchline: str = Field(description="The punchline to the joke")
                rating: Optional[int] = Field(description="How funny the joke is, from 1 to 10")


            structured_llm = llm.with_structured_output(Joke)
            structured_llm.invoke("Tell me a joke about cats")

        .. code-block:: python

            Joke(
                setup="Why was the cat sitting on the computer?",
                punchline="To keep an eye on the mouse!",
                rating=None,
            )

        See ``AzureChatOpenAI.with_structured_output()`` for more.

    JSON mode:
        .. code-block:: python

            json_llm = llm.bind(response_format={"type": "json_object"})
            ai_msg = json_llm.invoke(
                "Return a JSON object with key 'random_ints' and a value of 10 random ints in [0-99]"
            )
            ai_msg.content

        .. code-block:: python

            '\n{\n  "random_ints": [23, 87, 45, 12, 78, 34, 56, 90, 11, 67]\n}'

    Image input:
        .. code-block:: python

            import base64
            import httpx
            from langchain_core.messages import HumanMessage

            image_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/dd/Gfp-wisconsin-madison-the-nature-boardwalk.jpg/2560px-Gfp-wisconsin-madison-the-nature-boardwalk.jpg"
            image_data = base64.b64encode(httpx.get(image_url).content).decode("utf-8")
            message = HumanMessage(
                content=[
                    {"type": "text", "text": "describe the weather in this image"},
                    {
                        "type": "image_url",
                        "image_url": {"url": f"data:image/jpeg;base64,{image_data}"},
                    },
                ]
            )
            ai_msg = llm.invoke([message])
            ai_msg.content

        .. code-block:: python

            "The weather in the image appears to be quite pleasant. The sky is mostly clear"

    Token usage:
        .. code-block:: python

            ai_msg = llm.invoke(messages)
            ai_msg.usage_metadata

        .. code-block:: python

            {"input_tokens": 28, "output_tokens": 5, "total_tokens": 33}

    Logprobs:
        .. code-block:: python

            logprobs_llm = llm.bind(logprobs=True)
            ai_msg = logprobs_llm.invoke(messages)
            ai_msg.response_metadata["logprobs"]

        .. code-block:: python

            {
                "content": [
                    {
                        "token": "J",
                        "bytes": [74],
                        "logprob": -4.9617593e-06,
                        "top_logprobs": [],
                    },
                    {
                        "token": "'adore",
                        "bytes": [39, 97, 100, 111, 114, 101],
                        "logprob": -0.25202933,
                        "top_logprobs": [],
                    },
                    {
                        "token": " la",
                        "bytes": [32, 108, 97],
                        "logprob": -0.20141791,
                        "top_logprobs": [],
                    },
                    {
                        "token": " programmation",
                        "bytes": [
                            32,
                            112,
                            114,
                            111,
                            103,
                            114,
                            97,
                            109,
                            109,
                            97,
                            116,
                            105,
                            111,
                            110,
                        ],
                        "logprob": -1.9361265e-07,
                        "top_logprobs": [],
                    },
                    {
                        "token": ".",
                        "bytes": [46],
                        "logprob": -1.2233183e-05,
                        "top_logprobs": [],
                    },
                ]
            }

    Response metadata
        .. code-block:: python

            ai_msg = llm.invoke(messages)
            ai_msg.response_metadata

        .. code-block:: python

            {
                "token_usage": {
                    "completion_tokens": 6,
                    "prompt_tokens": 28,
                    "total_tokens": 34,
                },
                "model_name": "gpt-35-turbo",
                "system_fingerprint": None,
                "prompt_filter_results": [
                    {
                        "prompt_index": 0,
                        "content_filter_results": {
                            "hate": {"filtered": False, "severity": "safe"},
                            "self_harm": {"filtered": False, "severity": "safe"},
                            "sexual": {"filtered": False, "severity": "safe"},
                            "violence": {"filtered": False, "severity": "safe"},
                        },
                    }
                ],
                "finish_reason": "stop",
                "logprobs": None,
                "content_filter_results": {
                    "hate": {"filtered": False, "severity": "safe"},
                    "self_harm": {"filtered": False, "severity": "safe"},
                    "sexual": {"filtered": False, "severity": "safe"},
                    "violence": {"filtered": False, "severity": "safe"},
                },
            }
    ZAZURE_OPENAI_ENDPOINTN)default)default_factoryzOptional[str]azure_endpointazure_deployment)r:   aliaszUnion[str, None]deployment_nameapi_versionZOPENAI_API_VERSION)r>   r;   openai_api_versionapi_keyAZURE_OPENAI_API_KEYZOPENAI_API_KEYzOptional[SecretStr]openai_api_keyAZURE_OPENAI_AD_TOKENazure_ad_tokenzUnion[Callable[[], str], None]azure_ad_token_provider strmodel_versionZOPENAI_API_TYPEazureopenai_api_typeTr2   validate_base_urlmodel
model_namez	List[str])r4   c                 C  s
   dddgS )z*Get the namespace of the langchain object.Z	langchainZchat_modelsZazure_openair0   clsr0   r0   r1   get_lc_namespace(  s    z AzureChatOpenAI.get_lc_namespacezDict[str, str]c                 C  s
   dddS )NrC   rE   )rD   rF   r0   selfr0   r0   r1   
lc_secrets-  s    zAzureChatOpenAI.lc_secretsc                 C  s   dS )NTr0   rP   r0   r0   r1   is_lc_serializable4  s    z"AzureChatOpenAI.is_lc_serializableF)preZskip_on_failurer   )valuesr4   c                 C  sn  |d dk rt d|d dkr0|d r0t d|d pJtdpJtd|d< |d	 }|r|d
 rd|krtt d|d rt d|d |d |d |d r|d  nd|d r|d  nd|d |d |d	 |d |d |d |d d}|ds.d|d i}tjf |||d< |d jj|d< |dsjd|d i}tj	f |||d< |d jj|d< |S ) z?Validate that api key and python package exists in environment.n   zn must be at least 1.Z	streamingzn must be 1 when streaming.Zopenai_organizationZOPENAI_ORG_IDZOPENAI_ORGANIZATIONopenai_api_baserM   z/openaizAs of openai>=1.0.0, Azure endpoints should be specified via the `azure_endpoint` param not `openai_api_base` (or alias `base_url`).r?   a  As of openai>=1.0.0, if `azure_deployment` (or alias `deployment_name`) is specified then `base_url` (or alias `openai_api_base`) should not be. If specifying `azure_deployment`/`deployment_name` then use `azure_endpoint` instead of `base_url`.

For example, you could specify:

azure_endpoint="https://xxx.openai.azure.com/", azure_deployment="my-deployment"

Or you can equivalently specify:

base_url="https://xxx.openai.azure.com/openai/deployments/my-deployment"rA   r<   rD   NrF   rG   request_timeoutmax_retriesdefault_headersdefault_query)r@   r<   r=   rB   rF   rG   Zorganizationbase_urltimeoutr]   r^   r_   clienthttp_clientZroot_clientZasync_clientZhttp_async_clientZroot_async_client)

ValueErrorosgetenvZget_secret_valuegetopenaiZAzureOpenAIZchatZcompletionsZAsyncAzureOpenAI)rQ   rX   r[   Zclient_paramsZsync_specificZasync_specificr0   r0   r1   validate_environment8  s`     
z$AzureChatOpenAI.validate_environmenttool_choicez9Sequence[Union[Dict[str, Any], Type, Callable, BaseTool]]zNOptional[Union[dict, str, Literal[('auto', 'none', 'required', 'any')], bool]]r   z)Runnable[LanguageModelInput, BaseMessage])toolsrk   kwargsr4   c                  sR   |dkr:t |dkr&td|dnt|d d d }t j|fd|i|S )	N)anyrequiredTrZ   z4Azure OpenAI does not currently support tool_choice=zC. Should be one of 'auto', 'none', or the name of the tool to call.r   functionnamerk   )lenrd   r#   super
bind_tools)rT   rl   rk   rm   	__class__r0   r1   rt     s    
zAzureChatOpenAI.bind_toolsfunction_calling)methodinclude_rawzOptional[_DictOrPydanticClass]z*Literal[('function_calling', 'json_mode')]zLiteral[True]z,Runnable[LanguageModelInput, _AllReturnType])schemarx   ry   rm   r4   c                K  s   d S r5   r0   rT   rz   rx   ry   rm   r0   r0   r1   with_structured_output  s    z&AzureChatOpenAI.with_structured_outputzLiteral[False]z-Runnable[LanguageModelInput, _DictOrPydantic]c                K  s   d S r5   r0   r{   r0   r0   r1   r|     s    c                K  s  |rt d| t|}|dkrt|dkr2t dt|d d }| j|g|d}|rft|gdd	}qt|dd
}n>|dkr| jddid}|rt|dnt }nt d| d|r t	j
td|B dd d}	t	j
dd d}
|	j|
gdd}t|d|B S ||B S dS )ao+  Model wrapper that returns outputs formatted to match the given schema.

        Args:
            schema:
                The output schema. Can be passed in as:
                    - an OpenAI function/tool schema,
                    - a JSON Schema,
                    - a TypedDict class,
                    - or a Pydantic class.
                If ``schema`` is a Pydantic class then the model output will be a
                Pydantic instance of that class, and the model-generated fields will be
                validated by the Pydantic class. Otherwise the model output will be a
                dict and will not be validated. See :meth:`langchain_core.utils.function_calling.convert_to_openai_tool`
                for more on how to properly specify types and descriptions of
                schema fields when specifying a Pydantic or TypedDict class.
            method:
                The method for steering model generation, either "function_calling"
                or "json_mode". If "function_calling" then the schema will be converted
                to an OpenAI function and the returned model will make use of the
                function-calling API. If "json_mode" then OpenAI's JSON mode will be
                used. Note that if using "json_mode" then you must include instructions
                for formatting the output into the desired schema into the model call.
            include_raw:
                If False then only the parsed structured output is returned. If
                an error occurs during model output parsing it will be raised. If True
                then both the raw model response (a BaseMessage) and the parsed model
                response will be returned. If an error occurs during output parsing it
                will be caught and returned as well. The final output is always a dict
                with keys "raw", "parsed", and "parsing_error".

        Returns:
            A Runnable that takes same inputs as a :class:`langchain_core.language_models.chat.BaseChatModel`.

            If ``include_raw`` is False and ``schema`` is a Pydantic class, Runnable outputs
            an instance of ``schema`` (i.e., a Pydantic object).

            Otherwise, if ``include_raw`` is False then Runnable outputs a dict.

            If ``include_raw`` is True, then Runnable outputs a dict with keys:
                - ``"raw"``: BaseMessage
                - ``"parsed"``: None if there was a parsing error, otherwise the type depends on the ``schema`` as described above.
                - ``"parsing_error"``: Optional[BaseException]

        Example: schema=Pydantic class, method="function_calling", include_raw=False:
            .. code-block:: python

                from typing import Optional

                from langchain_openai import AzureChatOpenAI
                from langchain_core.pydantic_v1 import BaseModel, Field


                class AnswerWithJustification(BaseModel):
                    '''An answer to the user question along with justification for the answer.'''

                    answer: str
                    # If we provide default values and/or descriptions for fields, these will be passed
                    # to the model. This is an important part of improving a model's ability to
                    # correctly return structured outputs.
                    justification: Optional[str] = Field(
                        default=None, description="A justification for the answer."
                    )


                llm = AzureChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
                structured_llm = llm.with_structured_output(AnswerWithJustification)

                structured_llm.invoke(
                    "What weighs more a pound of bricks or a pound of feathers"
                )

                # -> AnswerWithJustification(
                #     answer='They weigh the same',
                #     justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'
                # )

        Example: schema=Pydantic class, method="function_calling", include_raw=True:
            .. code-block:: python

                from langchain_openai import AzureChatOpenAI
                from langchain_core.pydantic_v1 import BaseModel


                class AnswerWithJustification(BaseModel):
                    '''An answer to the user question along with justification for the answer.'''

                    answer: str
                    justification: str


                llm = AzureChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
                structured_llm = llm.with_structured_output(
                    AnswerWithJustification, include_raw=True
                )

                structured_llm.invoke(
                    "What weighs more a pound of bricks or a pound of feathers"
                )
                # -> {
                #     'raw': AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_Ao02pnFYXD6GN1yzc0uXPsvF', 'function': {'arguments': '{"answer":"They weigh the same.","justification":"Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ."}', 'name': 'AnswerWithJustification'}, 'type': 'function'}]}),
                #     'parsed': AnswerWithJustification(answer='They weigh the same.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume or density of the objects may differ.'),
                #     'parsing_error': None
                # }

        Example: schema=TypedDict class, method="function_calling", include_raw=False:
            .. code-block:: python

                # IMPORTANT: If you are using Python <=3.8, you need to import Annotated
                # from typing_extensions, not from typing.
                from typing_extensions import Annotated, TypedDict

                from langchain_openai import AzureChatOpenAI


                class AnswerWithJustification(TypedDict):
                    '''An answer to the user question along with justification for the answer.'''

                    answer: str
                    justification: Annotated[
                        Optional[str], None, "A justification for the answer."
                    ]


                llm = AzureChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
                structured_llm = llm.with_structured_output(AnswerWithJustification)

                structured_llm.invoke(
                    "What weighs more a pound of bricks or a pound of feathers"
                )
                # -> {
                #     'answer': 'They weigh the same',
                #     'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.'
                # }

        Example: schema=OpenAI function schema, method="function_calling", include_raw=False:
            .. code-block:: python

                from langchain_openai import AzureChatOpenAI

                oai_schema = {
                    'name': 'AnswerWithJustification',
                    'description': 'An answer to the user question along with justification for the answer.',
                    'parameters': {
                        'type': 'object',
                        'properties': {
                            'answer': {'type': 'string'},
                            'justification': {'description': 'A justification for the answer.', 'type': 'string'}
                        },
                       'required': ['answer']
                   }
               }

                llm = AzureChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
                structured_llm = llm.with_structured_output(oai_schema)

                structured_llm.invoke(
                    "What weighs more a pound of bricks or a pound of feathers"
                )
                # -> {
                #     'answer': 'They weigh the same',
                #     'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The weight is the same, but the volume and density of the two substances differ.'
                # }

        Example: schema=Pydantic class, method="json_mode", include_raw=True:
            .. code-block::

                from langchain_openai import AzureChatOpenAI
                from langchain_core.pydantic_v1 import BaseModel

                class AnswerWithJustification(BaseModel):
                    answer: str
                    justification: str

                llm = AzureChatOpenAI(model="gpt-3.5-turbo-0125", temperature=0)
                structured_llm = llm.with_structured_output(
                    AnswerWithJustification,
                    method="json_mode",
                    include_raw=True
                )

                structured_llm.invoke(
                    "Answer the following question. "
                    "Make sure to return a JSON blob with keys 'answer' and 'justification'.

"
                    "What's heavier a pound of bricks or a pound of feathers?"
                )
                # -> {
                #     'raw': AIMessage(content='{
    "answer": "They are both the same weight.",
    "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight." 
}'),
                #     'parsed': AnswerWithJustification(answer='They are both the same weight.', justification='Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.'),
                #     'parsing_error': None
                # }

        Example: schema=None, method="json_mode", include_raw=True:
            .. code-block::

                structured_llm = llm.with_structured_output(method="json_mode", include_raw=True)

                structured_llm.invoke(
                    "Answer the following question. "
                    "Make sure to return a JSON blob with keys 'answer' and 'justification'.

"
                    "What's heavier a pound of bricks or a pound of feathers?"
                )
                # -> {
                #     'raw': AIMessage(content='{
    "answer": "They are both the same weight.",
    "justification": "Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight." 
}'),
                #     'parsed': {
                #         'answer': 'They are both the same weight.',
                #         'justification': 'Both a pound of bricks and a pound of feathers weigh one pound. The difference lies in the volume and density of the materials, not the weight.'
                #     },
                #     'parsing_error': None
                # }
        zReceived unsupported arguments rw   NzJschema must be specified when method is 'function_calling'. Received None.rp   rq   rj   T)rl   first_tool_only)Zkey_namer}   Z	json_moder7   Zjson_object)Zresponse_format)Zpydantic_objectz\Unrecognized method argument. Expected one of 'function_calling' or 'json_mode'. Received: ''r)   c                 S  s   d S r5   r0   _r0   r0   r1   <lambda>      z8AzureChatOpenAI.with_structured_output.<locals>.<lambda>)r*   r+   c                 S  s   d S r5   r0   r   r0   r0   r1   r     r   )r*   r+   )Zexception_key)r)   )rd   r8   r#   rt   r   r   bindr   r   r   Zassignr   Zwith_fallbacksr   )rT   rz   rx   ry   rm   Zis_pydantic_schemaZ	tool_nameZllmZoutput_parserZparser_assignZparser_noneZparser_with_fallbackr0   r0   r1   r|     sR     [ 

  zDict[str, Any]c                   s   d| j it jS )zGet the identifying parameters.r=   )r?   rs   _identifying_paramsrS   ru   r0   r1   r     s    z#AzureChatOpenAI._identifying_paramsc                 C  s   dS )Nzazure-openai-chatr0   rS   r0   r0   r1   	_llm_type  s    zAzureChatOpenAI._llm_typec                 C  s   | j | jdS )NrL   rA   r   rS   r0   r0   r1   lc_attributes  s    zAzureChatOpenAI.lc_attributeszOptional[List[str]]r   )stoprm   r4   c                   sr   t  jf d|i|}d|d< | jr^| jrR| j| jkrR| jd | jd |d< qn| j|d< n| jrn| j|d< |S )z,Get the parameters used to invoke the model.r   rK   Zls_provider-Zls_model_name)rs   _get_ls_paramsrO   rJ   lstripr?   )rT   r   rm   paramsru   r0   r1   r     s    
zAzureChatOpenAI._get_ls_paramszUnion[dict, openai.BaseModel]zOptional[Dict]r   )responsegeneration_infor4   c                   s   t |ts| }|d D ]}|dd dkrtdqt ||}d|kr|d }| jrl| d| j }|jpti |_||jd< d|kr|jpi |_|d |jd< t	|j
|d D ]&\}}|jpi |_|d	i |jd	< q|S )
NchoicesZfinish_reasonZcontent_filterzKAzure has not provided the response due to a content filter being triggeredrN   r   rO   Zprompt_filter_resultsZcontent_filter_results)r6   dictZ
model_dumprg   rd   rs   _create_chat_resultrJ   Z
llm_outputzipZgenerationsr   )rT   r   r   resZchat_resultrN   Zchat_genZresponse_choiceru   r0   r1   r     s:    


  z#AzureChatOpenAI._create_chat_result)N)N)N)N)N)"r,   r-   r.   __doc__r   r!   r<   r/   r?   rA   r"   rD   rF   rG   rJ   rL   rM   rO   classmethodrR   propertyrU   rV   r   ri   rt   r   r|   r   r   r   r   r   __classcell__r0   r0   ru   r1   r9   =   s   
   "
	
 



L 	    	  r9   )Cr   
__future__r   loggingre   operatorr   typingr   r   r   r   r   r	   r
   r   r   r   r   r   rh   Zlangchain_core.language_modelsr   Z*langchain_core.language_models.chat_modelsr   Zlangchain_core.messagesr   Zlangchain_core.output_parsersr   r   Z"langchain_core.output_parsers.baser   Z*langchain_core.output_parsers.openai_toolsr   r   Zlangchain_core.outputsr   Zlangchain_core.pydantic_v1r   r   r   r   Zlangchain_core.runnablesr   r   r   Zlangchain_core.toolsr    Zlangchain_core.utilsr!   r"   Z%langchain_core.utils.function_callingr#   Zlangchain_core.utils.pydanticr$   Z!langchain_openai.chat_models.baser%   	getLoggerr,   loggerr&   rI   Z_DictOrPydanticClassZ_DictOrPydanticr(   r8   r9   r0   r0   r0   r1   <module>   s6   8
