
    7|h                        U d 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mZmZmZ ddlmZ ddlm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"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/m0Z0m1Z1m2Z2 ddl3m4Z4m5Z5m6Z6 ddl7m8Z8m9Z9m:Z: ddl;m<Z< ddl=m>Z>m?Z? ddl@mAZAmBZB ddlCmDZDmEZEmFZFmGZG ddlHmIZImJZJmKZK ddlLmMZM ddlNmIZO ddlPmQZQmRZR dZSe	eT   eUd<   dZVe	eT   eUd<   deeeTef      dee+   fdZWd eTd!eXeTef   d"eYdefd#ZZd!eXeTef   deeXeTef      fd$Z[d%eeTef   dee(   fd&Z\d'e(deXfd(Z]d)edeYfd*Z^ G d+ d,e      Z_y)-zOllama chat models.    N)
itemgetter)AnyAsyncIteratorCallableDictFinalIteratorListLiteralMappingOptionalSequenceTupleTypeUnioncast)uuid4)CallbackManagerForLLMRun)AsyncCallbackManagerForLLMRun)OutputParserException)LanguageModelInput)BaseChatModelLangSmithParams)	AIMessageAIMessageChunkBaseMessageBaseMessageChunkHumanMessageSystemMessageToolCallToolMessage)UsageMetadata	tool_call)JsonOutputKeyToolsParserJsonOutputParserPydanticOutputParserPydanticToolsParser)ChatGenerationChatGenerationChunk
ChatResult)RunnableRunnableMapRunnablePassthrough)BaseTool)convert_to_json_schemaconvert_to_openai_tool)TypeBaseModelis_basemodel_subclass)AsyncClientClientMessageOptions)	BaseModelPrivateAttrmodel_validator)JsonSchemaValue)r8   )Selfis_typeddictz<think>DEFAULT_THINK_TOKEN_STARTz</think>DEFAULT_THINK_TOKEN_ENDgeneration_inforeturnc                 x    | y| j                  d      }| j                  d      }||t        ||||z         S y)z7Get usage metadata from ollama generation info mapping.Nprompt_eval_count
eval_count)input_tokensoutput_tokenstotal_tokens)getr"   )r@   rE   rF   s      [/var/www/html/test/engine/venv/lib/python3.12/site-packages/langchain_ollama/chat_models.py(_get_usage_metadata_from_generation_inforJ   C   sY     "1"5"56I"JL#2#6#6|#DMM$=%'%5
 	

     json_stringraw_tool_callskipc                 ,   	 t        j                  |       S # t         j                  $ r1}|r| cY d}~S d|d   d    d|d   d    d| }t        |      |d}~wt        $ r1}|r| cY d}~S d|d   d    d|d   d    d| }t        |      |d}~ww xY w)	a  Attempt to parse a JSON string for tool calling.

    Args:
        json_string: JSON string to parse.
        skip: Whether to ignore parsing errors and return the value anyways.
        raw_tool_call: Raw tool call to include in error message.

    Returns:
        The parsed JSON string.

    Raises:
        OutputParserException: If the JSON string wrong invalid and skip=False.
    Nz	Function functionnamez arguments:

	argumentsz/

are not valid JSON. Received JSONDecodeError z7

are not a string or a dictionary. Received TypeError )jsonloadsJSONDecodeErrorr   	TypeError)rL   rM   rN   emsgs        rI   _parse_json_stringrY   T   s     0zz+&& 0j1&9:/Z(56 7(()s, 	
 $C(a/ 0j1&9:/Z(56 7../S2 	
 $C(a/0s6    BAB#AB"B%B+#BBc                    d| vry| d   d   }i }t        |t              r_|j                         D ]J  \  }}t        |t              r0t	        |d|       }t        |t        t
        f      r|||<   @|||<   F|||<   L |S t	        |d|       }|S )a  Parse arguments by trying to parse any shallowly nested string-encoded JSON.

    Band-aid fix for issue in Ollama with inconsistent tool call argument structure.
    Should be removed/changed if fixed upstream.
    See https://github.com/ollama/ollama/issues/6155
    rP   NrR   T)rN   rM   F)
isinstancedictitemsstrrY   list)rM   rR   parsed_argumentskeyvalueparsed_values         rI   _parse_arguments_from_tool_callrd   z   s     &j)+6I)T"#//+ 
	.JC%%1M  lT4L9,8$S),1$S)(- %
	.  .E
 rK   responsec           
          g }d| v r^| d   j                  d      x}rH|D ]C  }|j                  t        t        t	                     |d   d   t        |      xs i              E |S )z$Get tool calls from ollama response.message
tool_callsrP   rQ   )idrQ   args)rH   appendr$   r^   r   rd   )re   rh   raw_tool_callstcs       rI   _get_tool_calls_from_responsern      s{     JH%i044\BB>B$ !!uw<
^F3<R@FB rK   r$   c                 &    d| d   | d   | d   ddS )NrP   ri   rQ   rj   )rQ   rR   )typeri   rP    r#   s    rI   !_lc_tool_call_to_openai_tool_callrr      s+    of%"6*
 rK   objc                 <    t        | t              xr t        |       S N)r[   rp   r3   )rs   s    rI   _is_pydantic_classrv      s    c4 ?%:3%??rK   c                   6    e Zd ZU dZeed<   	 dZeee	e
eef   f      ed<   	 dZee   ed<   	 dZee   ed<   	 dZee   ed<   	 dZee   ed	<   	 dZee   ed
<   	 dZee   ed<   	 dZee   ed<   	 dZee   ed<   	 dZee   ed<   	 dZee   ed<   	 dZee   ed<   	 dZeee      ed<   	 dZee   ed<   	 dZee   ed<   	 dZee   ed<   	 dZeeed   ef      ed<   	 dZ eeeef      ed<   	 dZ!ee   ed<   	 i Z"ee#   ed<   	  e$d      Z%e&ed<   	  e$d      Z'e(ed<   	 	 dBdee)   deee      de*de+ee*f   fd Z, e-d!"      de.fd#       Z/dee)   de0e1   fd$Z2d%e3d&e	de
e3e	f   fd'Z4	 dBdee)   deee      de*de5ee6ee*f   ef      fd(Z7	 dBdee)   deee      de*de8ee6ee*f   ef      fd)Z9	 	 	 dCdee)   deee      d*ee:   d+e	de*de;fd,Z<	 	 	 dCdee)   deee      d*ee=   d+e	de*de;fd-Z>	 dBdeee      de*de?fd.Z@	 	 dDdee)   deee      d*ee:   de*deAf
d/ZB	 dBdee)   deee      de*de8e;   fd0ZC	 	 dDdee)   deee      d*ee:   de*de8e;   f
d1ZD	 dBdee)   deee      de*de5e;   fd2ZE	 	 dDdee)   deee      d*ee=   de*de5e;   f
d3ZF	 	 dDdee)   deee      d*ee=   de*deAf
d4ZGeHdefd5       ZIdd6d7e0ee+ee*f   eJeKeLf      d8eee#eed9   e	f      de*deMeNe)f   f fd:ZOd;dd<d=ee+ePf   d>ed?   d@e	de*deMeNee+eQf   f   f
dAZR xZSS )E
ChatOllamau[  Ollama chat model integration.

    .. dropdown:: Setup
        :open:

        Install ``langchain-ollama`` and download any models you want to use from ollama.

        .. code-block:: bash

            ollama pull mistral:v0.3
            pip install -U langchain-ollama

    Key init args — completion params:
        model: str
            Name of Ollama model to use.
        temperature: float
            Sampling temperature. Ranges from 0.0 to 1.0.
        num_predict: Optional[int]
            Max number of tokens to generate.

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

    Instantiate:
        .. code-block:: python

            from langchain_ollama import ChatOllama

            llm = ChatOllama(
                model = "llama3",
                temperature = 0.8,
                num_predict = 256,
                # other params ...
            )

    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 le programmation. (Note: "programming" can also refer to the act of writing code, so if you meant that, I could translate it as "J'adore programmer". But since you didn\'t specify, I assumed you were talking about the activity itself, which is what "le programmation" usually refers to.)', response_metadata={'model': 'llama3', 'created_at': '2024-07-04T03:37:50.182604Z', 'message': {'role': 'assistant', 'content': ''}, 'done_reason': 'stop', 'done': True, 'total_duration': 3576619666, 'load_duration': 788524916, 'prompt_eval_count': 32, 'prompt_eval_duration': 128125000, 'eval_count': 71, 'eval_duration': 2656556000}, id='run-ba48f958-6402-41a5-b461-5e250a4ebd36-0')

    Stream:
        .. code-block:: python

            messages = [
                ("human", "Return the words Hello World!"),
            ]
            for chunk in llm.stream(messages):
                print(chunk.text(), end="")


        .. code-block:: python

            content='Hello' id='run-327ff5ad-45c8-49fe-965c-0a93982e9be1'
            content=' World' id='run-327ff5ad-45c8-49fe-965c-0a93982e9be1'
            content='!' id='run-327ff5ad-45c8-49fe-965c-0a93982e9be1'
            content='' response_metadata={'model': 'llama3', 'created_at': '2024-07-04T03:39:42.274449Z', 'message': {'role': 'assistant', 'content': ''}, 'done_reason': 'stop', 'done': True, 'total_duration': 411875125, 'load_duration': 1898166, 'prompt_eval_count': 14, 'prompt_eval_duration': 297320000, 'eval_count': 4, 'eval_duration': 111099000} id='run-327ff5ad-45c8-49fe-965c-0a93982e9be1'


        .. code-block:: python

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

        .. code-block:: python

            AIMessageChunk(content='Je adore le programmation.(Note: "programmation" is the formal way to say "programming" in French, but informally, people might use the phrase "le développement logiciel" or simply "le code")', response_metadata={'model': 'llama3', 'created_at': '2024-07-04T03:38:54.933154Z', 'message': {'role': 'assistant', 'content': ''}, 'done_reason': 'stop', 'done': True, 'total_duration': 1977300042, 'load_duration': 1345709, 'prompt_eval_duration': 159343000, 'eval_count': 47, 'eval_duration': 1815123000}, id='run-3c81a3ed-3e79-4dd3-a796-04064d804890')

    Async:
        .. code-block:: python

            messages = [
                ("human", "Hello how are you!"),
            ]
            await llm.ainvoke(messages)

        .. code-block:: python

            AIMessage(content="Hi there! I'm just an AI, so I don't have feelings or emotions like humans do. But I'm functioning properly and ready to help with any questions or tasks you may have! How can I assist you today?", response_metadata={'model': 'llama3', 'created_at': '2024-07-04T03:52:08.165478Z', 'message': {'role': 'assistant', 'content': ''}, 'done_reason': 'stop', 'done': True, 'total_duration': 2138492875, 'load_duration': 1364000, 'prompt_eval_count': 10, 'prompt_eval_duration': 297081000, 'eval_count': 47, 'eval_duration': 1838524000}, id='run-29c510ae-49a4-4cdd-8f23-b972bfab1c49-0')

        .. code-block:: python

            messages = [
                ("human", "Say hello world!"),
            ]
            async for chunk in llm.astream(messages):
                print(chunk.content)

        .. code-block:: python

            HEL
            LO
            WORLD
            !

        .. code-block:: python

            messages = [
                ("human", "Say hello world!"),
                ("human","Say goodbye world!")
            ]
            await llm.abatch(messages)

        .. code-block:: python

            [AIMessage(content='HELLO, WORLD!', response_metadata={'model': 'llama3', 'created_at': '2024-07-04T03:55:07.315396Z', 'message': {'role': 'assistant', 'content': ''}, 'done_reason': 'stop', 'done': True, 'total_duration': 1696745458, 'load_duration': 1505000, 'prompt_eval_count': 8, 'prompt_eval_duration': 111627000, 'eval_count': 6, 'eval_duration': 185181000}, id='run-da6c7562-e25a-4a44-987a-2c83cd8c2686-0'),
            AIMessage(content="It's been a blast chatting with you! Say goodbye to the world for me, and don't forget to come back and visit us again soon!", response_metadata={'model': 'llama3', 'created_at': '2024-07-04T03:55:07.018076Z', 'message': {'role': 'assistant', 'content': ''}, 'done_reason': 'stop', 'done': True, 'total_duration': 1399391083, 'load_duration': 1187417, 'prompt_eval_count': 20, 'prompt_eval_duration': 230349000, 'eval_count': 31, 'eval_duration': 1166047000}, id='run-96cad530-6f3e-4cf9-86b4-e0f8abba4cdb-0')]

    JSON mode:
        .. code-block:: python


            json_llm = ChatOllama(format="json")
            messages = [
                ("human", "Return a query for the weather in a random location and time of day with two keys: location and time_of_day. Respond using JSON only."),
            ]
            llm.invoke(messages).content

        .. code-block:: python

            '{"location": "Pune, India", "time_of_day": "morning"}'

    Tool Calling:

        .. code-block:: python

            from langchain_ollama import ChatOllama
            from pydantic import BaseModel, Field

            class Multiply(BaseModel):
                a: int = Field(..., description="First integer")
                b: int = Field(..., description="Second integer")

            ans = await chat.invoke("What is 45*67")
            ans.tool_calls

        .. code-block:: python

            [{'name': 'Multiply',
            'args': {'a': 45, 'b': 67},
            'id': '420c3f3b-df10-4188-945f-eb3abdb40622',
            'type': 'tool_call'}]
    modelFextract_reasoningNmirostatmirostat_etamirostat_taunum_ctxnum_gpu
num_threadnum_predictrepeat_last_nrepeat_penaltytemperatureseedstoptfs_ztop_ktop_p) rS   format
keep_alivebase_urlclient_kwargs)default_client_async_clientmessageskwargsrA   c                    | j                  |      }| j                  |t        d      | j                  | j                  }|j                  d| j                  | j
                  | j                  | j                  | j                  | j                  | j                  | j                  | j                  | j                  | j                  || j                  n|| j                  | j                   | j"                  d      }||j                  dd      |j                  d| j$                        |j                  d| j&                        t)        di ||j                  d| j*                        d	|}|j-                  d
      x}r||d
<   |S )Nz2`stop` found in both the input and default params.options)r{   r|   r}   r~   r   r   r   r   r   r   r   r   r   r   r   streamTry   r   r   )r   r   ry   r   r   r   toolsrq   )$_convert_messages_to_ollama_messagesr   
ValueErrorpopr{   r|   r}   r~   r   r   r   r   r   r   r   r   r   r   ry   r   r7   r   rH   )selfr   r   r   ollama_messagesoptions_dictparamsr   s           rI   _chat_paramszChatOllama._chat_params  s\    CCHM99 T%5QRRYY"99Dzz MM $ 1 1 $ 1 1<<<<"oo#//!%!3!3"&"5"5#//		%)\		t
, (jj40ZZ4jj4;;7.. **\4??C
 
 JJw''5'#F7OrK   after)modec                     | j                   xs i }t        dd| j                  i|| _        t	        dd| j                  i|| _        | S )zSet clients to use for ollama.hostrq   )r   r5   r   r   r4   r   )r   r   s     rI   _set_clientszChatOllama._set_clients  sJ     **0bB4==BMB(MdmmM}MrK   c                 8   g }|D ]  }d }d }t        |t              rd}nt        |t              r4d}|j                  r#|j                  D cg c]  }t	        |       c}nd }n=t        |t
              rd}n*t        |t              rd}|j                  }nt        d      d}g }	t        |j                  t              r|j                  }n t        t        t           |j                        D ]  }
|
j                  d      dk(  r|d	|
d    z  }#|
j                  d      d
k(  r8|
j                  d      dk(  rd }|
j                  d      }t        |t              r|}n8t        |t              rd|v rt        |d   t              r|d   }nt        d      |j!                  d      }t#        |      dkD  r|	j%                  |d          |	j%                  |d          t        d       |||	d}|r||d<   |r||d<   |j%                  |        |S c c}w )Nuser	assistantsystemtoolz-Received unsupported message type for Ollama.r   rp   text
tool_use	image_urlurlzSOnly string image_url or dict with string 'url' inside content parts are supported.,   r   zsUnsupported message content type. Must either have type 'text' or type 'image_url' with a string 'image_url' field.)rolecontentimagesrh   tool_call_id)r[   r   r   rh   rr   r   r!   r   r   r   r^   r   r
   r   rH   r\   splitlenrk   )r   r   r   rg   r   rh   r   r$   r   r   content_partr   temp_image_urlimage_url_componentsrX   s                  rI   r   z/ChatOllama._convert_messages_to_ollama_messages  sK    !# K	(G*.L9=J'<0GY/" )) *1););% :)D
   G]3G[1&33 !PQQGF'//3/!//$(dW__$E #L#''/69RV(<'=#>>%))&1Z? %))&1[@$(	)5)9)9+)F%nc:(6I&~t< % 7 *>%+@# F(6u(=I",!F# 
 09s/C, 34q8"MM*>q*AB"MM*>q*AB )? ?#L " C
 $.L!&2N#""3'WK	(Z Is   Hmessage_chunkis_thinkingc                    | j                   s||fS | j                   du rt        }t        }nt        t        | j                         \  }}||j
                  v rd}|j
                  }|r||j                  d<   d|_        ||v rd}||fS )z4Mutate a message chunk to extract reasoning content.Treasoning_contentr   F)rz   r>   r?   r   tupler   additional_kwargs)r   r   r   start_token	end_tokenr   s         rI   _extract_reasoningzChatOllama._extract_reasoningH  s     %% +--##t+3K/I%)%1G1G%H"K-///K''CJM++,?@$&M!Kk))rK   c                  K    | j                   ||fi |}|d   r3 | j                  j                  di | d {   2 3 d {   }|  | j                  j                  di | d {    y 7 ;7 46 y 7 wNr   rq   )r   r   chat)r   r   r   r   chat_paramsparts         rI   _acreate_chat_streamzChatOllama._acreate_chat_stream^  s      (d''$A&Ax $;D$6$6$;$;$Jk$JJ  d
/**//>+>>> K J ?s?   9A?A7A? A;A9A;&A?.A=/	A?9A;;A?c              +      K    | j                   ||fi |}|d   r% | j                  j                  di |E d {    y  | j                  j                  di | y 7 $wr   )r   r   r   )r   r   r   r   r   s        rI   _create_chat_streamzChatOllama._create_chat_streaml  se      (d''$A&Ax (t||((7;777#$,,##2k22 8s   9A"A %A"run_managerverbosec                     d } | j                   ||fi |D ]-  }||}n||z  }|s|j                  |j                  ||       / |t        d      |S N)chunkr   z$No data received from Ollama stream.)_iterate_over_streamon_llm_new_tokenr   r   r   r   r   r   r   r   final_chunkr   s           rI   _chat_stream_with_aggregationz(ChatOllama._chat_stream_with_aggregationy  s~     .T..xHH 
	E"#u$,,JJ# - 
	 CDDrK   c                    K   d } | j                   ||fi |2 3 d {   }||}n||z  }|s|j                  |j                  ||       d {    =7 87 6 |t        d      |S wr   )_aiterate_over_streamr   r   r   r   s           rI   _achat_stream_with_aggregationz)ChatOllama._achat_stream_with_aggregation  s      5455hOO 
	 
	%"#u$!22JJ# 3   
	 P CDDs<   A*AAAA*!A*AA*AA*A*c           	           | j                   dd|i|}t        d| j                  d|j                  d| j                              }|xs  |j                  dd      xs | j
                  x}r||d<   |S )	z Get standard params for tracing.r   ollamar   r   )ls_providerls_model_namels_model_typels_temperatureNls_stoprq   )_get_invocation_paramsr   ry   rH   r   r   )r   r   r   r   	ls_paramsr   s         rI   _get_ls_paramszChatOllama._get_ls_params  s     -,,A$A&A# ** !::mT5E5EF	
	 Cfjj6C$))C7C#*Ii rK   c           
      n    | j                   |||fd| j                  i|}|j                  }t        t	        |j
                  t        t        |j                        j                  t        t        |j                        j                  |j                  j                        |      }t        |g      S Nr   )r   usage_metadatarh   r   rg   r@   )generations)r   r   r@   r)   r   r   r   r   rg   r   rh   r   r+   r   r   r   r   r   r   r@   chat_generations           rI   	_generatezChatOllama._generate  s     9d88dK
15
AG
 &55(#((#NK4G4GHWW0C0CDOO"-"5"5"G"G	 ,
 &788rK   c           
   +     K   d} | j                   ||fi |D ]  }t        |t              rt        t	        d|v rd|d   v r|d   d   ndt        |      t        |            |j                  d      du rt        |      nd       }| j                  r&| j                  |j                  |      \  }}||_        |  y w	NFrg   r   r   )r   r   rh   doneTr   )r   r[   r^   r*   r   rJ   rn   rH   r\   rz   r   rg   r   r   r   r   r   stream_respr   rg   s           rI   r   zChatOllama._iterate_over_stream  s      3433HdMfM 	Kk3/+*  )K7 )[-C C (	29= "$'O'( $A#M .9__V-D-L[)RV" ))+/+B+B{,(G[ %,EM1	s   *CBCc              +      K    | j                   ||fi |D ]/  }|r'|j                  |j                  | j                         | 1 y wN)r   )r   r   r   r   r   r   r   r   r   r   s         rI   _streamzChatOllama._stream  sV      /T..xHH 	E,,JJ LL -  K	s   AA
c           
       K   d} | j                   ||fi |2 3 d {   }t        |t              rt        t	        d|v rd|d   v r|d   d   ndt        |      t        |            |j                  d      du rt        |      nd       }| j                  r&| j                  |j                  |      \  }}||_        | 7 6 y wr   )r   r[   r^   r*   r   rJ   rn   rH   r\   rz   r   rg   r   s           rI   r   z ChatOllama._aiterate_over_stream   s      !:!:!:8T!TV!T 	 	+k3/+*  )K7 )[-C C (	29= "$'O'( $A#M .9__V-D-L[)RV" ))+/+B+B{,(G[ %,EM1	!Ts+   CC	CC	CBCC		Cc                   K    | j                   ||fi |2 3 d {   }|r/|j                  |j                  | j                         d {    | @7 ;7 6 y wr   )r   r   r   r   r   s         rI   _astreamzChatOllama._astream!  sr      6455hOO 	 	%!22JJ LL 3    K	 Ps7   AAAA-AA
AAAAc           
        K    | j                   |||fd| j                  i| d {   }|j                  }t        t	        |j
                  t        t        |j                        j                  t        t        |j                        j                  |j                  j                        |      }t        |g      S 7 wr   )r   r   r@   r)   r   r   r   r   rg   r   rh   r   r+   r   s           rI   
_ageneratezChatOllama._agenerate0  s      @D??dK
15
AG
 
 &55(#((#NK4G4GHWW0C0CDOO"-"5"5"G"G	 ,
 &788
s   %CCBCc                      y)zReturn type of chat model.zchat-ollamarq   )r   s    rI   	_llm_typezChatOllama._llm_typeF  s     rK   )tool_choicer   r   )autoanyc                `    |D cg c]  }t        |       }}t        |   dd|i|S c c}w )ai  Bind tool-like objects to this chat model.

        Assumes model is compatible with OpenAI tool-calling API.

        Args:
            tools: A list of tool definitions to bind to this chat model.
                Supports any tool definition handled by
                :meth:`langchain_core.utils.function_calling.convert_to_openai_tool`.
            tool_choice: If provided, which tool for model to call. **This parameter
                is currently ignored as it is not supported by Ollama.**
            kwargs: Any additional parameters are passed directly to
                ``self.bind(**kwargs)``.
        r   rq   )r1   superbind)r   r   r   r   r   formatted_tools	__class__s         rI   
bind_toolszChatOllama.bind_toolsK  s=    ( EJJD1$7JJw|</<V<< Ks   +json_schema)methodinclude_rawschemar  )function_calling	json_moder  r  c                >   |j                  dd      }|rt        d|       t        |      }|dk(  r[|t        d      t        |      }|d   d   }| j	                  |g|d|i|d	
      }	|rt        |gd      }
n;t        |d      }
n,|dk(  r1| j                  dd|i|d	      }	|rt        |      n	t               }
n|dk(  r|t        d      |rft        t        |      }t        |t              r|j                         }n|j                         }| j                  |d|i|d	      }	t        |      }
n|t!        |      r/t#        |      }d|vr0t%        |d   j'                               |d<   nt        t(        |      }| j                  |d|i|d	      }	t               }
nt        d| d      |r^t+        j,                  t/        d      |
z  d       }t+        j,                  d       }|j1                  |gd      }t3        |	      |z  S |	|
z  S )a+  Model wrapper that returns outputs formatted to match the given schema.

        Args:
            schema:
                The output schema. Can be passed in as:

                - a Pydantic class,
                - a JSON schema
                - a TypedDict class
                - an OpenAI function/tool schema.

                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, one of:

                - "json_schema":
                    Uses Ollama's structured output API: https://ollama.com/blog/structured-outputs
                - "function_calling":
                    Uses Ollama's tool-calling API
                - "json_mode":
                    Specifies ``format="json"``. 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".

            kwargs: Additional keyword args aren't supported.

        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]

        .. versionchanged:: 0.2.2

            Added support for structured output API via ``format`` parameter.

        .. versionchanged:: 0.3.0

            Updated default ``method`` to ``"json_schema"``.

        .. dropdown:: Example: schema=Pydantic class, method="json_schema", include_raw=False

            .. code-block:: python

                from typing import Optional

                from langchain_ollama import ChatOllama
                from pydantic import BaseModel, Field


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

                    answer: str
                    justification: Optional[str] = Field(
                        default=..., description="A justification for the answer."
                    )


                llm = ChatOllama(model="llama3.1", 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.'
                # )

        .. dropdown:: Example: schema=Pydantic class, method="json_schema", include_raw=True

            .. code-block:: python

                from langchain_ollama import ChatOllama
                from pydantic import BaseModel


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

                    answer: str
                    justification: str


                llm = ChatOllama(model="llama3.1", 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
                # }

        .. dropdown:: Example: schema=Pydantic class, method="function_calling", include_raw=False

            .. code-block:: python

                from typing import Optional

                from langchain_ollama import ChatOllama
                from pydantic import BaseModel, Field


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

                    answer: str
                    justification: Optional[str] = Field(
                        default=..., description="A justification for the answer."
                    )


                llm = ChatOllama(model="llama3.1", temperature=0)
                structured_llm = llm.with_structured_output(
                    AnswerWithJustification, method="function_calling"
                )

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

        .. dropdown:: 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_ollama import ChatOllama


                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 = ChatOllama(model="llama3.1", 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.'
                # }

        .. dropdown:: Example: schema=OpenAI function schema, method="function_calling", include_raw=False

            .. code-block:: python

                from langchain_ollama import ChatOllama

                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 = ChatOllama(model="llama3.1", 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.'
                # }

        .. dropdown:: Example: schema=Pydantic class, method="json_mode", include_raw=True

            .. code-block::

                from langchain_ollama import ChatOllama
                from pydantic import BaseModel

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

                llm = ChatOllama(model="llama3.1", 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'.\n\n"
                    "What's heavier a pound of bricks or a pound of feathers?"
                )
                # -> {
                #     'raw': AIMessage(content='{\n    "answer": "They are both the same weight.",\n    "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." \n}'),
                #     '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
                # }
        strictNzReceived unsupported arguments r  zGschema must be specified when method is not 'json_mode'. Received None.rP   rQ   r  )r   r  )r   ls_structured_output_formatT)r   first_tool_only)key_namer  r  rS   )r   r  )pydantic_objectr  required
propertieszlUnrecognized method argument. Expected one of 'function_calling', 'json_schema', or 'json_mode'. Received: ''rawc                      y ru   rq   _s    rI   <lambda>z3ChatOllama.with_structured_output.<locals>.<lambda>      rK   )parsedparsing_errorc                      y ru   rq   r  s    rI   r  z3ChatOllama.with_structured_output.<locals>.<lambda>  r  rK   )r  r  )exception_key)r  )r   r   rv   r1   r  r(   r%   r   r'   r&   r   r2   
issubclassBaseModelV1r  model_json_schemar=   r0   r_   keysr\   r.   assignr   with_fallbacksr-   )r   r  r  r  r   r  is_pydantic_schemaformatted_tool	tool_namellmoutput_parserresponse_formatparser_assignparser_noneparser_with_fallbacks                  rI   with_structured_outputz!ChatOllama.with_structured_outputb  s   v JJx&>vhGHH/7''~ %  4F;N&z26:I//%'0,- " C "*=!($(+
 !9&! {"))'0$-  C & %V<%' 
 }$~ %  "mV4fk2&,mmoO&,&>&>&@Oii*#+V"4"(1    !5V L'&<V&DO!86:+L9>>@7
3
 '+4&8Oii*#+V"4"11    !1 2==CHAG 
 /66!%(=8M .44NKK#0#?#?_ $@ $  3'*>>>&&rK   ru   )NNF)NN)T__name__
__module____qualname____doc__r^   __annotations__rz   r   r   boolr   r{   intr|   floatr}   r~   r   r   r   r   r   r   r   r   r
   r   r   r   r   r   r;   r   r   r   r\   r9   r   r5   r   r4   r   r   r   r   r:   r<   r   r   r6   r   r   r   r   r   r   r	   r   r   r*   r   r   r   r   r   r+   r   r   r   r   r   r   propertyr   r   r   r/   r,   r   r  rp   r8   r+  __classcell__)r  s   @rI   rx   rx      s   Wr J@ExdE#s(O&; <=E #Hhsm"B %)L(5/(5
 %)L(5/(% "GXc]!$ "GXc]!+ !%J$N
 "&K#%C $(M8C='= '+NHUO*, $(K%'= D(3- !%D(49
$&!E8E?!M  E8C=0 "E8E?!C EIFHU7:.?@AHJ,0JsCx)0:"Hhsm"-$&M8D>& "$/GV/ "-T!:M;: %)/{#/ tCy!/ 	/
 
c3h/b '"d  #Q[)Q	'	Qf*-*<@*	%	&*2 %)?{#? tCy!? 	?
 
uWS#X.34	5?" %)3{#3 tCy!3 	3
 
%S)3./	03  %):>{# tCy! 67	
   
8 %)?C{# tCy! ;<	
   
4 +/T#Y':=	$ %):>	9{#9 tCy!9 67	9
 9 
92 %){# tCy! 	
 
%	&H %):>	{# tCy! 67	
  
%	&$ %){# tCy! 	
 
*	+H %)?C	{# tCy! ;<	
  
*	+$ %)?C	9{#9 tCy!9 ;<	9
 9 
9, 3   QU	=d38ndHhFGH= eD#w}/Et$KLM	=
 = 
$k1	2=6 KX!['dDj![' FG	['
 [' [' 
$eD)O&<<	=['rK   rx   )`r/  rS   operatorr   typingr   r   r   r   r   r	   r
   r   r   r   r   r   r   r   r   uuidr   langchain_core.callbacksr    langchain_core.callbacks.managerr   langchain_core.exceptionsr   langchain_core.language_modelsr   *langchain_core.language_models.chat_modelsr   r   langchain_core.messagesr   r   r   r   r   r   r    r!   langchain_core.messages.air"   langchain_core.messages.toolr$   langchain_core.output_parsersr%   r&   r'   r(   langchain_core.outputsr)   r*   r+   langchain_core.runnablesr,   r-   r.   langchain_core.toolsr/   %langchain_core.utils.function_callingr0   r1   langchain_core.utils.pydanticr2   r3   r   r4   r5   r6   r7   pydanticr8   r9   r:   pydantic.json_schemar;   pydantic.v1r  typing_extensionsr<   r=   r>   r^   r0  r?   rJ   r\   r1  rY   rd   rn   rr   rv   rx   rq   rK   rI   <module>rK     s         "  K ; = U	 	 	 5 2  S R O O ) O 8 8 < < 0 0 0(1 5: 1&0 s 0gc3h/0m"#0#0%)#s(^#0;?#0#0LS>d38n@c3h	(^$ d @C @D @B' B'rK   