
    j7G                        U d Z ddl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 dd	lmZ dd
lmZ ed         Zed         Zg dZee	eef                  e d<    G d de          Z!deddfdZ"dddddddddee#         dee#         deee#e#f                  dee#         deee#                  dee#         de$dee         fdZ%eddddddddddee#         dee#         deee#e#f                  dee#         deee#                  dee#         de$d ee!         deeeef         fd!            Z&d"ej'        dee#e	e#ee#         f         f         fd#Z(d$e#d%e	e#ee#         ee#e#f         f         d"ej'        d&ej)        de$dej'        fd'Z*d%ed$e#dee	e#ee#         f                  fd(Z+d%e#d$e#de$fd)Z,d$e#de#fd*Z-d+Z.d$e#de#fd,Z/d$e#dee#         fd-Z0d$e#de#fd.Z1dS )/a  Attribute propagation utilities for Langfuse OpenTelemetry integration.

This module provides the `propagate_attributes` context manager for setting trace-level
attributes (user_id, session_id, metadata) that automatically propagate to all child spans
within the context.
    )	AnyDict	GeneratorListLiteralOptional	TypedDictUnioncast)baggagecontext)trace)_RUNTIME_CONTEXT)_AgnosticContextManager_agnosticcontextmanager)LangfuseOtelSpanAttributes)#LANGFUSE_SDK_EXPERIMENT_ENVIRONMENT)langfuse_logger)user_id
session_idmetadataversiontags
trace_name)experiment_idexperiment_nameexperiment_metadataexperiment_dataset_idexperiment_item_idexperiment_item_metadata#experiment_item_root_observation_id)r   r   r   r   r   r   r   r   r   r   r    r!   r"   propagated_keysc                       e Zd ZU eed<   eed<   eeeef                  ed<   ee         ed<   eed<   eeeef                  ed<   eed<   dS )	PropagatedExperimentAttributesr   r   r   r   r    r!   r"   N)__name__
__module____qualname__str__annotations__r   r        l/lsinfo/ai/hellotax_ai/data_center/backend/venv/lib/python3.11/site-packages/langfuse/_client/propagation.pyr%   r%   D   s         !$sCx.1111#C=(((&tCH~6666),,,,,,r,   r%   tokenreturnNc                 R    	 t          j        |            dS # t          $ r Y dS w xY w)a  Detach a context token without emitting noisy async teardown errors.

    OpenTelemetry tokens are backed by ``contextvars`` and must be detached in the
    same execution context where they were attached. Async frameworks can legitimately
    end spans or unwind context managers in a different task/context, in which case
    detach raises and the public OpenTelemetry helper logs an error. At that point the
    observation is already completed, so the mismatch is safe to ignore.
    N)r   detach	Exception)r.   s    r-   _detach_context_token_safelyr3   N   s@    &&&&&   s    
&&Fr   r   r   r   r   r   
as_baggager   r   r   r   r   r   r5   c           	      .    t          | ||||||          S )u  Propagate trace-level attributes to all spans created within this context.

    This context manager sets attributes on the currently active span AND automatically
    propagates them to all new child spans created within the context. This is the
    recommended way to set trace-level attributes like user_id, session_id, and metadata
    dimensions that should be consistently applied across all observations in a trace.

    **IMPORTANT**: Call this as early as possible within your trace/workflow. Only the
    currently active span and spans created after entering this context will have these
    attributes. Pre-existing spans will NOT be retroactively updated.

    **Why this matters**: Langfuse aggregation queries (e.g., total cost by user_id,
    filtering by session_id) only include observations that have the attribute set.
    If you call `propagate_attributes` late in your workflow, earlier spans won't be
    included in aggregations for that attribute.

    Args:
        user_id: User identifier to associate with all spans in this context.
            Must be US-ASCII string, ≤200 characters. Use this to track which user
            generated each trace and enable e.g. per-user cost/performance analysis.
        session_id: Session identifier to associate with all spans in this context.
            Must be US-ASCII string, ≤200 characters. Use this to group related traces
            within a user session (e.g., a conversation thread, multi-turn interaction).
        metadata: Additional key-value metadata to propagate to all spans.
            - Keys and values must be US-ASCII strings
            - All values must be ≤200 characters
            - Use for dimensions like internal correlating identifiers
            - AVOID: large payloads, sensitive data, non-string values (will be dropped with warning)
        version: Version identfier for parts of your application that are independently versioned, e.g. agents
        tags: List of tags to categorize the group of observations
        trace_name: Name to assign to the trace. Must be US-ASCII string, ≤200 characters.
            Use this to set a consistent trace name for all spans created within this context.
        as_baggage: If True, propagates attributes using OpenTelemetry baggage for
            cross-process/service propagation. **Security warning**: When enabled,
            attribute values are added to HTTP headers on ALL outbound requests.
            Only enable if values are safe to transmit via HTTP headers and you need
            cross-service tracing. Default: False.

    Returns:
        Context manager that propagates attributes to all child spans.

    Example:
        Basic usage with user and session tracking:

        ```python
        from langfuse import Langfuse

        langfuse = Langfuse()

        # Set attributes early in the trace
        with langfuse.start_as_current_observation(name="user_workflow") as span:
            with langfuse.propagate_attributes(
                user_id="user_123",
                session_id="session_abc",
                metadata={"experiment": "variant_a", "environment": "production"}
            ):
                # All spans created here will have user_id, session_id, and metadata
                with langfuse.start_observation(name="llm_call") as llm_span:
                    # This span inherits: user_id, session_id, experiment, environment
                    ...

                with langfuse.start_generation(name="completion") as gen:
                    # This span also inherits all attributes
                    ...
        ```

        Late propagation (anti-pattern):

        ```python
        with langfuse.start_as_current_observation(name="workflow") as span:
            # These spans WON'T have user_id
            early_span = langfuse.start_observation(name="early_work")
            early_span.end()

            # Set attributes in the middle
            with langfuse.propagate_attributes(user_id="user_123"):
                # Only spans created AFTER this point will have user_id
                late_span = langfuse.start_observation(name="late_work")
                late_span.end()

            # Result: Aggregations by user_id will miss "early_work" span
        ```

        Cross-service propagation with baggage (advanced):

        ```python
        # Service A - originating service
        with langfuse.start_as_current_observation(name="api_request"):
            with langfuse.propagate_attributes(
                user_id="user_123",
                session_id="session_abc",
                as_baggage=True  # Propagate via HTTP headers
            ):
                # Make HTTP request to Service B
                response = requests.get("https://service-b.example.com/api")
                # user_id and session_id are now in HTTP headers

        # Service B - downstream service
        # OpenTelemetry will automatically extract baggage from HTTP headers
        # and propagate to spans in Service B
        ```

    Note:
        - **Validation**: All attribute values (user_id, session_id, metadata values)
          must be strings ≤200 characters. Invalid values will be dropped with a
          warning logged. Ensure values meet constraints before calling.
        - **OpenTelemetry**: This uses OpenTelemetry context propagation under the hood,
          making it compatible with other OTel-instrumented libraries.

    Raises:
        No exceptions are raised. Invalid values are logged as warnings and dropped.
    r4   )_propagate_attributesr4   s          r-   propagate_attributesr8   ^   s1    t !   r,   )r   r   r   r   r   r   r5   
experimentr9   c              #     K   t          j                    }t          j                    }	| ||||d}
d|i}|r|                                D ]\  }}|dv r6t          t          t          t          t          f                  |          ||<   ?t          t          t          t          t          t                   f                  |          |
|<   d |
                                D             }
|
                                D ],\  }}t          ||          }|t          ||||	|          }-|                                D ]U\  }}|i }|                                D ] \  }}t          || d|           r|||<   !|rt          ||||	|          }Vt          j        |          }	 d V  t          |           d S # t          |           w xY w)	N)r   r   r   r   r   r   )r   r!   c                     i | ]
\  }}|||S Nr+   ).0kvs      r-   
<dictcomp>z)_propagate_attributes.<locals>.<dictcomp>
  s&     $ $ $A1r,   valuekey)rC   rB   r   spanr5   .r   )otel_context_apiget_currentotel_trace_apiget_current_spanitemsr   r   r   r)   r
   r   _validate_propagated_value_set_propagated_attribute_validate_string_valueattachr3   )r   r   r   r   r   r   r5   r9   r   current_spanpropagated_string_attributespropagated_metadata_attributesrC   rB   validated_valuemetadata_keymetadata_valuevalidated_metadatar.   s                      r-   r7   r7      sf      *,,G!244L   P P  	HK"  	$**,, 	 	JCIII6:T#s(^,e7 7.s33 59U3S	>23U5 5,S11
$ $5;;==$ $ $  388:: 
 

U45cJJJ&/%!%  G )G(L(L(N(N  $n!-/(..00 	0 	0JC%E,7N7N7N7NOOO 0*/"3' 	/ (!%  G #G444E, 	%U+++++$U++++s   *F? ?Gr   c                    i }t          j        |           }|                                D ]`\  }}|                    t                    rAt          |          }|r0t          |t          t          f          r|nt          |          ||<   at          D ]}t          |          }t          j        ||           }|*t          |t                    r4t          |          }|                                D ]\  }	}
|
|| d|	 <   st          |          }t          |t          t          f          r|nt          |          ||<   t          j        |v rt"          |t          j        <   |S )Nr   )rC   r   rE   )r   get_allrJ   
startswithLANGFUSE_BAGGAGE_PREFIX_get_span_key_from_baggage_key
isinstancer)   listr#   _get_propagated_context_keyrF   	get_valuedict_get_propagated_span_keyr   #EXPERIMENT_ITEM_ROOT_OBSERVATION_IDr   ENVIRONMENT)r   propagated_attributesbaggage_entriesbaggage_keybaggage_valuespan_keyrC   context_keyrB   r>   r?   s              r-   '_get_propagated_attributes_from_contextri   7  s    ?A og666O&5&;&;&=&= 	 	"]!!"9:: 	5kBBH  "-#t==,MM]++ &h/   1#66 *{GLLL=eT"" 	/44H = =1;<%&7&7A&7&788= 044H $EC;77GSZZ "(++
 	#F 	! 	! 0 	8DE ! r,   rC   rB   rD   c                    t          |           }t          |           }t          |           }t          |t                    r.t          t          t          j        |          pi           }||z  }t          |t                    r[t          t          t          j        |          pg           t                    }	|		                    fd|D                        |	}t          j
        |||          }|w|                                rct          |t                    r7|                                D ]!\  }
}|                    | d|
 |           "n|                    ||           |rct          |t                    r7|                                D ]!\  }
}t          j        | d|
 ||          }"nt          j        |||          }|S )Nc              3   $   K   | ]
}|v|V  d S r<   r+   )r=   tagexisting_tags_in_contexts     r-   	<genexpr>z,_set_propagated_attribute.<locals>.<genexpr>  s.      WW33>V3V3V33V3V3V3VWWr,   )rC   rB   r   rE   rC   rB   _)namerB   r   )r]   r`   _get_propagated_baggage_keyr[   r_   r   rF   r^   r\   extend	set_valueis_recordingrJ   set_attributeotel_baggage_apiset_baggage)rC   rB   r   rD   r5   rh   rg   re   existing_metadata_in_contextmerged_tagsr>   r?   rm   s               @r-   rL   rL   j  s9    .c22K',,H-c22K % 5'+",[99?R(
 (
$ -u4 % #'",[99?R$
 $
  344WWWW%WWWWWW (  G D--//eT"" 		:  1""#))a)) #     85999  
eT"" 		  1*6'--!--Q  
 '2 w  G Nr,   c                 T   t          | t                    r%fd| D             }t          |          dk    r|nd S t          | t                    st	          j        d d           d S t          |           dk    r*t	          j        d dt          |            d           d S | S )Nc                 6    g | ]}t          |           |S )ro   )rM   )r=   r?   rC   s     r-   
<listcomp>z._validate_propagated_value.<locals>.<listcomp>  s<     
 
 
 63a H H H

 
 
r,   r   Propagated attribute '(' value is not a string. Dropping value.    ' value is over 200 characters ( chars). Dropping value.)r[   r\   lenr)   r   warning)rB   rC   validated_valuess    ` r-   rK   rK     s     % G
 
 
 

 
 
 $''7#8#81#<#<$FeS!! RSRRR	
 	
 	
 t
5zzCnSnn#e**nnn	
 	
 	
 tLr,   c                     t          | t                    st          j        d| d           dS t	          |           dk    r*t          j        d| dt	          |            d           dS dS )Nr~   r   Fr   r   r   T)r[   r)   r   r   r   rA   s     r-   rM   rM     s    eS!! RSRRR	
 	
 	
 u
5zzCnSnn#e**nnn	
 	
 	
 u4r,   c                     d|  S )Nzlangfuse.propagated.r+   rC   s    r-   r]   r]     s    '#'''r,   	langfuse_c                     t            |  S r<   )rY   r   s    r-   rr   rr     s    %,s,,,r,   c                 $   |                      t                    sd S | t          t                    d          }dD ]G}| d}|                     |          r+t          |           d|t          |          d           c S Ht          |          S )N)r   r   r!   rp   rE   )rX   rY   r   r`   )rC   suffixrS   baggage_metadata_prefixs       r-   rZ   rZ     s    >>122 t ,--//0FW  %1"4"4"4455 	+L99 < <#566889< <  	 $F+++r,   c                 n   t           j        t           j        t           j        t           j        t           j        t           j        t           j        t           j        t           j	        t           j
        t           j        t           j        t           j        d                    |           pt           j         d|  S )N)r   r   r   r   r   r   r   r   r   r   r    r!   r"   rE   )r   TRACE_SESSION_IDTRACE_USER_IDVERSION
TRACE_TAGS
TRACE_NAMETRACE_METADATAEXPERIMENT_IDEXPERIMENT_NAMEEXPERIMENT_METADATAEXPERIMENT_DATASET_IDEXPERIMENT_ITEM_IDEXPERIMENT_ITEM_METADATAra   getr   s    r-   r`   r`     s    0A-;-5*50;.=3A5E9M!;!Q8K$>$W/I/m  
c#hhG 0>FFFFGr,   )2__doc__typingr   r   r   r   r   r   r	   r
   r   opentelemetryr   rw   r   rF   r   rH   opentelemetry.contextr   opentelemetry.util._decoratorr   r   langfuse._client.attributesr   langfuse._client.constantsr   langfuse.loggerr   PropagatedKeysInternalPropagatedKeysr#   r*   r%   r3   r)   boolr8   r7   Contextri   SpanrL   rK   rM   r]   rY   rr   rZ   r`   r+   r,   r-   <module>r      sE     Y X X X X X X X X X X X X X X X X X X X X X                     3 2 2 2 2 2       
 C B B B B B J J J J J J + + + + + + !+ H H HeN,BBCD   "- - - - -Y - - -     $ " $)-! $ $B B Bc]B B tCH~&	B
 c]B 49
B B B S!B B B BJ  " $)-! $ $;?P, P, P,c]P, P, tCH~&	P,
 c]P, 49
P, P, P, 78P, sC}P, P, P, P,f0!%0!	#uS$s)^$
$%0! 0! 0! 0!f?	? d3ic3h/0? %	?
 
? ? ? ? ? ?DeCcN#$   2S s t     (S (S ( ( ( ( & -S -S - - - -, , , , , ,&G# G# G G G G G Gr,   