o
    iHV                     @   s  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 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 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 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# d dl$m%Z% e	e&Z'dZ(dZ)g dZ*G dd deZ+de,ee- ee- e.e-ef f fd d!Z/dee- fd"d#Z0de.e-ef fd$d%Z1dee- fd&d'Z2d(edee3e.e-ef   fd)d*Z4dee.e-ef  fd+d,Z5d-d. Z6de3fd/d0Z7d(ee de3e.e-ef  fd1d2Z8d3e.e-e3ee-e,e-e-f f  f de3e- fd4d5Z9d6e3e,e-e-f  de:fd7d8Z;d9e.e-ef de.e-e3ee-e,e-e-f f  f fd:d;Z<d<e-d=e.e-e3ee-e,e-e-f f  f fd>d?Z=d@e%de#fdAdBZ>dCS )D    )Any)Iterable)Optional)Union)cast)WeakKeyDictionary)
get_logger)get_argument_value)format_trace_id)LLMObs)AGENT_MANIFEST)INPUT_VALUE)NAME)OUTPUT_VALUE)PARENT_ID_KEY)ROOT_PARENT_ID)	SPAN_KIND)
SPAN_LINKS)BaseLLMIntegration)LANGGRAPH_ASTREAM_OUTPUTformat_langchain_io	_get_attr)_get_nearest_llmobs_ancestor)	_SpanLink)Span__pregel_push__pregel_tasks)
max_tokenstemperaturetop_ptop_kfrequency_penaltypresence_penaltystopnlogprobsecho
logit_biasc                       s  e Zd ZU dZe Zeeeee	f f e
d< e Zee	eee	f f e
d< e Zeee	f e
d< 		d-deded	ef fd
dZ		d.dedee	 deee	f dee	 def
ddZdeee	f d	eeee	f  fddZdeded	eee	f fddZdedefddZ	d/dedededefd d!Zd"edeee	f defd#d$Zd"ededefd%d&Zd"ed'eeeeeeeef f  f d	ee fd(d)Zd"edeee	f d*ee fd+d,Z  ZS )0LangGraphIntegration	langgraph!_graph_nodes_for_graph_by_task_id_agent_manifests_graph_spans_to_graph_instancesFNoperation_idsubmit_to_llmobsreturnc                    s(   t  j||fi |}|r|| j|< |S N)supertracer.   )selfr/   r0   instancekwargsspan	__class__ Z/home/ubuntu/.local/lib/python3.10/site-packages/ddtrace/llmobs/_integrations/langgraph.pyr4   8   s   
zLangGraphIntegration.trace r8   argsr7   response	operationc                 C   sB  | j sd S t||dd}t||ddddpi }t|di }|dd	d
d }	|di dd}
|dks8|
r>| ||	ni }t|g}|dpKg }|rP|}|tpVg }dd }|	t
|dkrddndtt|t||pt||tt|dp|d|jt|| i |dkr| j| }| |||}|t| d S d S )Nr   input   configT)optionalmetadatalanggraph_checkpoint_nsr=   :z_dd.subgraphFnode
span_linksc                 S   s   | d u rd S t | S r2   r   )messagesr;   r;   r<   maybe_format_langchain_ioa   s   zHLangGraphIntegration._llmobs_set_tags.<locals>.maybe_format_langchain_iographagenttaskname)llmobs_enabledr	   r   getsplit_get_node_metadata_from_span_default_span_link_get_ctx_itemr   _set_ctx_itemsr   r   r   r   r   r   rP   r.   _get_agent_manifest_set_ctx_itemr   )r5   r8   r>   r7   r?   r@   inputsrC   rE   instance_idis_subgraphinvoked_noderJ   invoked_node_span_linkscurrent_span_linksrL   rN   agent_manifestr;   r;   r<   _llmobs_set_tagsF   s:   


z%LangGraphIntegration._llmobs_set_tagsrC   c                 C   s   |du rdS | j |}|du r!t|}|jpd|d}|| j |< d|vr)d|d< d|vr5t|dd|d< d|vrUt|trUt|d	krUt|d	 trUt	|d	 
 |d< |S )
zFGets the agent manifest for a given agent at the end of its execution.N	LangGraph)rP   tools	frameworkmax_iterationsrecursion_limit   dependenciesr   )r-   rR   _get_tools_from_graphrP   r   
isinstancetuplelendictlistkeys)r5   rN   r>   rC   r`   rc   r;   r;   r<   rX   v   s&   
z(LangGraphIntegration._get_agent_manifestr[   c                 C   sD   t |}|r| j|i |i ni }t|jt|jd|d< |S )z
        Get the node metadata for a given span and its node instance id.
        Additionally, set the span and trace ids on its metadata should another node in a later
        tick of the graph need to be linked to this node.
        )trace_idspan_idr8   )r   r,   
setdefaultr
   rp   strrq   )r5   r8   r[   parent_spanr]   r;   r;   r<   rT      s   z1LangGraphIntegration._get_node_metadata_from_spanc                 C   s   | j sdS t||ddd}t|\}}}t||dddpg }t|}	t|d}
|d}i }|r6||d< |r<||d	< |rB||d
< |	rH|	|d< |
rN|
|d< |rT||d< || j|< dS )z
        Handles the agent manifest for a given react agent (defined through `langgraph.prebuilt.create_react_agent`),
        and caches it for use when tagging the graph/agent span in the `_get_agent_manifest`.
        Nr   modelTrB   rc   promptrP   model_providermodel_settingsinstructions)rQ   r	   _get_model_info_get_tools_from_react_agent#_get_system_prompt_from_react_agentrR   r-   )r5   rN   r>   r7   ru   
model_namerw   rx   agent_toolsrc   system_promptrP   r`   r;   r;   r<   llmobs_handle_agent_manifest   s2   

z1LangGraphIntegration.llmobs_handle_agent_manifestfinished_tasks
next_tasks
more_tasksis_subgraph_nodec              
   C   s   z(| j sW dS tj }|du rW dS |s| ||| W dS | ||| W dS  tyB } ztjd|d W Y d}~dS d}~ww )zp
        Compute incoming and outgoing span links between finished tasks and queued tasks in the graph.
        Nz)Error in LangGraph span linking operation)exc_info)	rQ   r   	_instance_current_span_handle_finished_graph_handle_intermediary_graph_tick	Exceptionloggerdebug)r5   r   r   r   r   
graph_spaner;   r;   r<   llmobs_handle_pregel_loop_tick   s   z3LangGraphIntegration.llmobs_handle_pregel_loop_tickr   c           	         s    rt  nd} fdd| D } tpg } t||  |durK|sK|tp/g }tt jp7dt j	ddddg}|t||  dS )a  Create the span links for a finished pregel graph from all finished tasks as the graph span's outputs.
        Generate the output-to-output span links for the last nodes in a pregel graph.
        If the graph isn't a subgraph, add a span link from the graph span to the calling LLMObs parent span.
        Note: is_subgraph_node denotes whether the graph is a subgraph node,
         not whether it is a standalone graph (called internally during a node execution).
        Nc                    sD   g | ]}t j  | d  d j  | d  d ddddqS )r8   rq   rp   outputfromtorq   rp   
attributes)r   r,   ).0task_idr   r5   r;   r<   
<listcomp>   s    z?LangGraphIntegration._handle_finished_graph.<locals>.<listcomp>	undefinedr   r   r   )
r   ro   rV   r   rY   r   rs   rq   r
   rp   )	r5   r   r   r   graph_caller_spanoutput_span_linksgraph_span_span_linksgraph_caller_span_linksrJ   r;   r   r<   r      s    z+LangGraphIntegration._handle_finished_graphc           
      C   sr   t |}t }| D ]$\}}| j|i |i }t|dd|d< | ||||}	||	 q| ||| dS )a-  
        Handle graph ticks that aren't at the end of the graph execution. Link all next tasks to their trigger tasks
        from the dict of finished tasks. Any finished tasks that do not trigger any queued tasks should be
        linked as output --> output links for the outer graph span.
        rP   r=   N)	)_map_channel_writes_to_finished_tasks_idssetitemsr,   rr   r   _link_task_to_triggersupdate_link_standalone_terminal_tasks)
r5   r   r   r   'task_trigger_channels_to_finished_tasksused_finished_task_idsr   rO   queued_nodetrigger_idsr;   r;   r<   r      s   z4LangGraphIntegration._handle_intermediary_graph_tickr   c           
      C   s~   t ||}|D ]5}|du rq| j|i |i d}|sqt|dd|dddddd	}|d
g }	|	| q|S )z
        Create the span links for a queued task from its triggering trigger tasks.

        Returns the finished task ids used as trigger tasks.
        Nr8   rq   r=   rp   r   rA   r   r   rJ   )$_get_trigger_ids_from_finished_tasksr,   rR   r   rr   append)
r5   rO   r   r   r   r   node_idtrigger_node_span	span_linkrJ   r;   r;   r<   r     s   


z+LangGraphIntegration._link_task_to_triggersused_finished_tasks_idsc           	   	   C   s   t | | }|tpg }|D ]0}| j|i |}|du r"q|d}|du r,q|t|dd|dddddd q|t| dS )	z~
        Default handler that links any finished tasks not used as triggers for queued tasks to the outer graph span.
        Nr8   rq   r=   rp   r   r   r   )	r   ro   rV   r   r,   rR   r   r   rY   )	r5   r   r   r   standalone_terminal_task_idsgraph_span_linksfinished_task_idrI   r8   r;   r;   r<   r   /  s"   


z4LangGraphIntegration._link_standalone_terminal_tasks)FN)Nr=   )F)__name__
__module____qualname___integration_namer   r,   r   rm   rs   r   __annotations__r-   r.   boolr4   rn   r   ra   rX   rT   rk   r   r   r   r   r   r   r   r   __classcell__r;   r;   r9   r<   r*   2   sp   
 

&0'

 
r*   r1   c                 C   sL   t | tr| jddd\}}||i fS t| dd}t| }t| }|||fS )z?Get the model name, provider, and settings from a langchain llmrG   rB   )maxsplitr}   N)rj   rs   rS   r   _get_model_provider_get_model_settings)ru   model_provider_strmodel_name_strr}   rw   rx   r;   r;   r<   rz   J  s   


rz   c                 C   s2   t | dd}|du st|sdS | }|ddS )z+Get the model provider from a langchain llm_get_ls_paramsNls_provider)r   callablerR   )ru   model_provider_info_fnmodel_provider_infor;   r;   r<   r   W  s
   r   c                 C   s:   t | dd}|du st|si S |  }dd | D S )z+Get the model settings from a langchain llmrm   Nc                 S   s"   i | ]\}}|t v r|r||qS r;   )ALLOWED_MODEL_SETTINGS_KEYS)r   keyvaluer;   r;   r<   
<dictcomp>h  s   " z'_get_model_settings.<locals>.<dictcomp>)r   r   rm   r   )ru   model_dict_fn
model_dictr;   r;   r<   r   a  s
   r   c                 C   s&   | du rdS t | tr| S t| ddS )a!  
    Get the system prompt from a react agent.

    The system prompt can be:
    - a string
    - a dict with a "content" key
    - a Callable that returns a string or dict

    In the case of a Callable (which is dynamic as a function of state and config), we end up returning None.
    Ncontent)rj   rs   r   )r   r;   r;   r<   r|   k  s
   
r|   rc   c                 C   s(   t | rt| di }t| } t| S )a:  
    Get the tools for the agent manifest passed into the react agent.

    Tools can be:
    - a ToolNode
    - a list of BaseTools (langchain tools)
    - a list of Callables
    - a list of dicts

    In the case of a Callable (which is dynamic as a function of state and config), we end up returning None.
    tools_by_name)_is_tool_noder   rn   values_extract_tools)rc   r   r;   r;   r<   r{     s   r{   c                 C   s:   | du s	t | trdS t| ddt| ddt| di dS )z6Get the tool representation from a langchain base toolNrP   r=   descriptionr>   )rP   r   
parameters)rj   rm   r   )toolr;   r;   r<   '_get_tool_repr_from_langchain_base_tool  s   


r   c                 C   s   t | ddduS )z@Check if a node is a tool node without a specific instance checkr   Nr   )maybe_tool_noder;   r;   r<   r     s   r   c                 C   s   g }| du r|S t | dd}|du r|S t |dd}|du s#t|ts%|S | D ]!}t |dd}|du r6q)t|s;q)t |di }|t|  q)|S )z4Get the tools from the ToolNode(s) of an agent/graphNbuildernodesrunnabler   )r   rj   rm   r   r   extendr   )rN   graph_toolsr   r   rI   r   r   r;   r;   r<   ri     s$   ri   c                 C   s(   g }| D ]}t |}|r|| q|S )z5Extract the tool representations from a list of tools)r   r   )rc   
tools_reprr   	tool_reprr;   r;   r<   r     s   
r   r   c           
      C   s   t | dg }|p	g }g }|D ]:}|tkr9tttttf  |tg }t| |}|dkr8|	|\}}	|
|	 q|ttt ||pFg  q|S )a  
    Return the set of task ids that are responsible for triggering the queued task, returning all the trigger nodes
    that wrote to the channel that the queued task consumes from.

    The one caveat is nodes queued up via `Send` commands. These nodes will have a `__pregel_push` as their trigger, and
    consume from the `__pregel_tasks` channel. We want to pop these instances and associate them one at a time with each
    of the queued tasks.
    triggersrH   )r   PREGEL_PUSHr   rn   rk   rs   rR   PREGEL_TASKS_find_pregel_push_indexpopr   r   )
queued_tasksr   task_triggers_from_tasktask_triggersr   triggerpregel_pushespregel_push_index_
trigger_idr;   r;   r<   r     s   

 r   r   c                 C   s2   t |D ]\}\}}|t| ddkr|  S qdS )zX
    Find the index of a specific pregel push node in the list of pregel push nodes
    rP   r=   rH   )	enumerater   )rO   r   ipregel_push_noder   r;   r;   r<   r     s
   r   r   c                 C   s<   i }|   D ]\}}t|dg }|D ]}t||| qq|S )a  
    Maps channel writes for finished tasks to the list of finished tasks ids that wrote to that channel.
    For `__pregel_tasks` writes, we append both the node name for the `Send` object, and the finished task id
    to be used in `_get_trigger_ids_from_finished_tasks`.
    writes)r   r   +_append_finished_task_to_channel_writes_map)r   #channel_names_to_finished_tasks_idsr   finished_taskr   writer;   r;   r<   r     s   r   r   r   c                 C   sn   t |trt|dkrdS |\}}||g }|tkr0t|dd}|du r'dS ||| f dS ||  dS )aM  
    Appends the finished task id to the map of channel names to finished tasks ids. If the write represents a
    `__pregel_tasks` write, then append both the node name it's writing to, and the finished task id.
    Otherwise, just append the finished task id to the list of channel writes that the finished task is writing to.
       NrI   )rj   rk   rl   rr   r   r   r   )r   r   r   channel_write_namechannel_write_argtasks_for_triggerpregel_task_noder;   r;   r<   r     s   r   r8   c                 C   s$   t | tptt| jddddS )z
    Create a default input-to-input span link for a given span, if there are no
    referenced spans that represent the causal link. In this case, we assume
    the span is linked to its parent's input.
    rA   r   r   )r   rV   r   r   r
   rp   )r8   r;   r;   r<   rU     s
   rU   N)?typingr   r   r   r   r   weakrefr   ddtrace.internal.loggerr   ddtrace.internal.utilsr	   ddtrace.internal.utils.formatsr
   ddtrace.llmobsr   ddtrace.llmobs._constantsr   r   r   r   r   r   r   r   !ddtrace.llmobs._integrations.baser   &ddtrace.llmobs._integrations.constantsr   "ddtrace.llmobs._integrations.utilsr   ddtrace.llmobs._utilsr   r   ddtrace.llmobs.typesr   ddtrace.tracer   r   r   r   r   r   r*   rk   rs   rm   rz   r   r   r|   rn   r{   r   r   ri   r   r   intr   r   r   rU   r;   r;   r;   r<   <module>   st      (

""





