
     jT                       d Z ddlZddlZddlZddlZddl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mZmZmZmZmZmZmZmZ ddlZddl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%m&Z& ddl'm(Z( ddl)m*Z* ddl+m,Z,m-Z-m.Z. ddl/m0Z0m1Z1m2Z2m3Z3m4Z4m5Z5 ddl6m7Z7 ddl8m9Z9m:Z:m;Z;m<Z<m=Z=m>Z>m?Z?m@Z@mAZAmBZB ddlCmDZDmEZE ddlFmGZG ddlHmIZImJZJmKZKmLZLmMZMmNZNmOZOmPZPmQZQmRZR ddlSmTZTmUZU ddlVmWZW ddlXmYZY ddlZm[Z[ ddl\m]Z] ddl^m_Z_m`Z`maZambZbmcZcmdZdmeZemfZfmgZgmhZhmiZimjZjmkZkmlZlmmZmmnZn ddlompZpmqZqmrZrmsZsmtZt ddlumvZvmwZwmxZxmyZymzZzm{Z{m|Z|m}Z}m~Z~mZ ddlmZ ddlmZ ddlmZmZmZmZmZ dd lmZmZmZmZ  G d! d"          ZdS )#zLangfuse OpenTelemetry integration module.

This module implements Langfuse's core observability functionality on top of the OpenTelemetry (OTel) standard.
    N)datetime)sha256)time_ns)
AnyCallableDictListLiteralOptionalTypeUnioncastoverload)trace)ReadableSpanTracerProvider)SpanExporter)RandomIdGenerator)_AgnosticContextManager_agnosticcontextmanager)Version)
deprecated)LangfuseOtelSpanAttributes&_flatten_and_serialize_metadata_values
_serialize)#LANGFUSE_SDK_EXPERIMENT_ENVIRONMENTObservationTypeGenerationLikeObservationTypeLiteralObservationTypeLiteralNoEventObservationTypeSpanLikeget_observation_types_list)DatasetClient)
LANGFUSE_BASE_URLLANGFUSE_DEBUGLANGFUSE_HOSTLANGFUSE_PUBLIC_KEYLANGFUSE_RELEASELANGFUSE_SAMPLE_RATELANGFUSE_SECRET_KEYLANGFUSE_TIMEOUTLANGFUSE_TRACING_ENABLEDLANGFUSE_TRACING_ENVIRONMENT)PropagatedExperimentAttributes_propagate_attributes)LangfuseResourceManager)
LangfuseAgentLangfuseChainLangfuseEmbeddingLangfuseEvaluatorLangfuseEventLangfuseGenerationLangfuseGuardrailLangfuseRetrieverLangfuseSpanLangfuseTool)get_sha256_hash_hexrun_async_safely)_get_timestamp)get_common_release_envs)handle_fern_exception)PromptCache)CreateChatPromptRequestCreateChatPromptTypeCreateTextPromptRequestDatasetDatasetItemDatasetRunWithItemsDatasetStatusDeleteDatasetRunResponseErrorMapValueNotFoundErrorPaginatedDatasetRunsPrompt_ChatPrompt_Text	ScoreBody	TraceBody)BatchEvaluationResultBatchEvaluationResumeTokenBatchEvaluationRunnerCompositeEvaluatorFunctionMapperFunction)

EvaluationEvaluatorFunctionExperimentDataExperimentItemExperimentItemResultExperimentResultRunEvaluatorFunctionTaskFunction_run_evaluator	_run_task)langfuse_logger)LangfuseMedia)ChatMessageDictChatMessageWithPlaceholdersDictChatPromptClientPromptClientTextPromptClient)MaskFunctionScoreDataType	SpanLevelTraceContextc            -       -   e Zd ZU dZdZee         ed<   dZee	         ed<   e
j        ed<   dddddddddddddddddddddd	ee         d
ee         dee         dee         dee         deej                 dedee         dee         dee         dee         dee         dee         dee         dee	         deee                  deeegef                  deeeef                  dee         dee         f(dZedddddddddddddddee         d ed!ed"         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         d)ee         d*ee         d+eeeef                  d,eeeef                  d-eeeef                  d.ee          d/e!f d0            Z"edd1ddddddd2dee         d ed!ed1         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         d/e#fd3            Z"edddddddd4dee         d ed!ed5         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         d/e$fd6            Z"edddddddd4dee         d ed!ed7         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         d/e%fd8            Z"edddddddd4dee         d ed!ed9         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         d/e&fd:            Z"edddddddd4dee         d ed!ed;         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         d/e'fd<            Z"edddddddd4dee         d ed!ed=         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         d/e(fd>            Z"edddddddddddddddee         d ed!ed?         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         d)ee         d*ee         d+eeeef                  d,eeeef                  d-eeeef                  d.ee          d/e)f d@            Z"edddddddd4dee         d ed!edA         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         d/e*fdB            Z"dd1dddddddddddddCdee         d ed!e+d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         d)ee         d*ee         d+eeeef                  d,eeeef                  d-eeeef                  d.ee          d/e,e#e!e$e%e&e'e(e)e*f	         f dDZ"dddddddddddddEdFe
j-        d!e+d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         d)ee         d*ee         d+eeeef                  d,eeeef                  d-eeeef                  d.ee          d/e,e#e!e$e%e&e'e(e)e*f	         fdGZ.edddddddddddddddHdee         d ed!ed"         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         d)ee         d*ee         d+eeeef                  d,eeeef                  d-eeeef                  d.ee          dIee         d/e/e!         f"dJ            Z0edd1ddddddddK	dee         d ed!ed1         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         dIee         d/e/e#         fdL            Z0edddddddddMdee         d ed!ed5         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         dIee         d/e/e$         fdN            Z0edddddddddMdee         d ed!ed7         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         dIee         d/e/e%         fdO            Z0edddddddddMdee         d ed!ed9         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         dIee         d/e/e&         fdP            Z0edddddddddMdee         d ed!ed;         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         dIee         d/e/e'         fdQ            Z0edddddddddMdee         d ed!ed=         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         dIee         d/e/e(         fdR            Z0edddddddddddddddHdee         d ed!ed?         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         d)ee         d*ee         d+eeeef                  d,eeeef                  d-eeeef                  d.ee          dIee         d/e/e)         f"dS            Z0edddddddddMdee         d ed!edA         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         dIee         d/e/e*         fdT            Z0dd1ddddddddddddddUdee         d ed!e+d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         d)ee         d*ee         d+eeeef                  d,eeeef                  d-eeeef                  d.ee          dIee         d/e,e/e!         e/e#         e/e$         e/e%         e/e&         e/e'         e/e(         e/e)         e/e*         f	         f"dVZ0d!e1d/e,e2e$         e2e%         e2e&         e2e'         e2e(         e2e)         e2e*         e2e!         e2e3         e2e#         f
         fdWZ4e5ddddddddddddddddXd edYee
j-                 dZee
j-                 d!e+dIee         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         d)ee         d*ee         d+eeeef                  d,eeeef                  d-eeeef                  d.ee          d/ef$d[            Z6e5ddddddddddddddd\d ed!ee+         dIee         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         d)ee         d*ee         d+eeeef                  d,eeeef                  d-eeeef                  d.ee          d/ef d]            Z7d/ee
j-                 fd^Z8dddddddddddddd_d ee         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         d)ee         d*ee         d+eeeef                  d,eeeef                  d-eeeef                  d.ee          d/dfd`Z9ddddddddad ee         d#ee         d$ee         d%ee         d&ee         d'ee         d(ee         d/dfdbZ: e;dc          ddddd#ee         d$ee         d/dfde            Z<ddfZ=dddddddd4dee         d ed#ee         d$ee         d%ee         d&ee         d'ee         d(ee         d/e3fdgZ>dhediee         d/efdjZ?dhed/efdkZ@dled/efdmZAddndoee         d/efdpZBeCddndoee         d/efdq            ZDdFe
j-        d/efdrZEdFe
j-        d/efdsZFeCdted/efdu            ZGeCdved/efdw            ZHedddddddddddx
d edyedzee         d{ee         dhee         d|ee         d}ee         d~eed                  dee         dee         d%ee         dee         d/dfd            ZIeddddddddddd
d edyedzee         d{ee         dhee         d}ee         d|ee         d~eed                  dee         dee         d%ee         dee         d/dfd            ZIdddddddddddx
d edye,eef         dzee         d{ee         dhee         d|ee         d}ee         d~eeJ         dee         dee         d%ee         dee         d/dfdZIdhedee         d/dfdZKeddddddd edyed}ee         d~eed                  dee         dee         d%ee         d/dfd            ZLeddddddd edyed}ee         d~eed                  dee         dee         d%ee         d/dfd            ZLddddddd edye,eef         d}ee         d~eeJ         dee         dee         d%ee         d/dfdZLeddddddd edyed}ee         d~eed                  dee         dee         d%ee         d/dfd            ZMeddddddd edyed}ee         d~eed                  dee         dee         d%ee         d/dfd            ZMddddddd edye,eef         d}ee         d~eeJ         dee         dee         d%ee         d/dfdZMddZNddZOd/ee         fdZPd/ee         fdZQd/ee         fdZRdddhee         d/ee         fdZSdddd edee         d&ee         d/dfdZTdeded/eUfdZVddddedee         dee         d/eWfdZXdeded/eYfdZZddg dg ddddd edee         dee         de[de\dee]         dee^         dee_         ded%eeeef                  dee         d/e`fdZadddd ededee         de[de\dee]         dee^         dee_         ded%eeeef                  dee         d/e`fdZb	 	 ddecdedee         dee^         dedededee         deeeef                  dee         d/edfdZedddd ee         dee         d/efdZfdddddddddddddded         degdee         dedee         dee         dedee]         dee^         ded%eeeef                  dedeee                  deeh         ded/eif d˄Zjd/efd̄Zkddddd͜d edee         d%ee         dee         dee         d/elfdЄZmddddddddќded#ee         dee         d%ee         dee         dee         deen         dee         d/eofdׄZpdddٜdeded         deded/ef
d߄Zqedddddddd ed&ee         dee         ded         dee         deeer                  dee         dee         d/esfd            Zteddddddddd ed&ee         dee         ded         dee         dee         dee         dee         d/eufd            Ztddddddddd ed&ee         dee         ded         dee         de,eeer                  ee         f         dee         dee         d/e fdZtddddd ed&ee         dee         dee         dedee         d/e fdZvddddee         deded/efdZweg ddddd ed.ee,erexf                  dee         deee                  deed                  dee         dee         d/esfd            Zyeg dddddd ed.edee         deee                  deed                  dee         dee         d/eufd            Zyg dddddd ed.e,eee,erexf                  f         dee         deee                  deed                  dee         dee         d/e fdZyg dd ed&edee         d/efd Zzdddedee         d/efdZ{ddZ|dS (  Langfusea~  Main client for Langfuse tracing and platform features.

    This class provides an interface for creating and managing traces, spans,
    and generations in Langfuse as well as interacting with the Langfuse API.

    The client features a thread-safe singleton pattern for each unique public API key,
    ensuring consistent trace context propagation across your application. It implements
    efficient batching of spans with configurable flush settings and includes background
    thread management for media uploads and score ingestion.

    Configuration is flexible through either direct parameters or environment variables,
    with graceful fallbacks and runtime configuration updates.

    Attributes:
        api: Synchronous API client for Langfuse backend communication
        async_api: Asynchronous API client for Langfuse backend communication
        _otel_tracer: Internal LangfuseTracer instance managing OpenTelemetry components

    Parameters:
        public_key (Optional[str]): Your Langfuse public API key. Can also be set via LANGFUSE_PUBLIC_KEY environment variable.
        secret_key (Optional[str]): Your Langfuse secret API key. Can also be set via LANGFUSE_SECRET_KEY environment variable.
        base_url (Optional[str]): The Langfuse API base URL. Defaults to "https://cloud.langfuse.com". Can also be set via LANGFUSE_BASE_URL environment variable.
        host (Optional[str]): Deprecated. Use base_url instead. The Langfuse API host URL. Defaults to "https://cloud.langfuse.com".
        timeout (Optional[int]): Timeout in seconds for API requests. Defaults to 5 seconds.
        httpx_client (Optional[httpx.Client]): Custom httpx client for making non-tracing HTTP requests. If not provided, a default client will be created.
        debug (bool): Enable debug logging. Defaults to False. Can also be set via LANGFUSE_DEBUG environment variable.
        tracing_enabled (Optional[bool]): Enable or disable tracing. Defaults to True. Can also be set via LANGFUSE_TRACING_ENABLED environment variable.
        flush_at (Optional[int]): Number of spans to batch before sending to the API. Defaults to 512. Can also be set via LANGFUSE_FLUSH_AT environment variable.
        flush_interval (Optional[float]): Time in seconds between batch flushes. Defaults to 5 seconds. Can also be set via LANGFUSE_FLUSH_INTERVAL environment variable.
        environment (Optional[str]): Environment name for tracing. Default is 'default'. Can also be set via LANGFUSE_TRACING_ENVIRONMENT environment variable. Can be any lowercase alphanumeric string with hyphens and underscores that does not start with 'langfuse'.
        release (Optional[str]): Release version/hash of your application. Used for grouping analytics by release.
        media_upload_thread_count (Optional[int]): Number of background threads for handling media uploads. Defaults to 1. Can also be set via LANGFUSE_MEDIA_UPLOAD_THREAD_COUNT environment variable.
        sample_rate (Optional[float]): Sampling rate for traces (0.0 to 1.0). Defaults to 1.0 (100% of traces are sampled). Can also be set via LANGFUSE_SAMPLE_RATE environment variable.
        mask (Optional[MaskFunction]): Function to mask sensitive data in traces before sending to the API.
        blocked_instrumentation_scopes (Optional[List[str]]): Deprecated. Use `should_export_span` instead. Equivalent behavior:
            ```python
            from langfuse.span_filter import is_default_export_span
            blocked = {"sqlite", "requests"}

            should_export_span = lambda span: (
                is_default_export_span(span)
                and (
                    span.instrumentation_scope is None
                    or span.instrumentation_scope.name not in blocked
                )
            )
            ```
        should_export_span (Optional[Callable[[ReadableSpan], bool]]): Callback to decide whether to export a span. If omitted, Langfuse uses the default filter (Langfuse SDK spans, spans with `gen_ai.*` attributes, and known LLM instrumentation scopes).
        additional_headers (Optional[Dict[str, str]]): Additional headers to include in all API requests and in the default OTLPSpanExporter requests. These headers will be merged with default headers. Note: If httpx_client is provided, additional_headers must be set directly on your custom httpx_client as well. If `span_exporter` is provided, these headers are not wired into that exporter and must be configured on the exporter instance directly.
        tracer_provider(Optional[TracerProvider]): OpenTelemetry TracerProvider to use for Langfuse. This can be useful to set to have disconnected tracing between Langfuse and other OpenTelemetry-span emitting libraries. Note: To track active spans, the context is still shared between TracerProviders. This may lead to broken trace trees.
        span_exporter (Optional[SpanExporter]): Custom OpenTelemetry span exporter for the Langfuse span processor. If omitted, Langfuse creates an OTLPSpanExporter pointed at the Langfuse OTLP endpoint. If provided, Langfuse does not wire `base_url`, exporter headers, exporter auth, or exporter timeout into it. Configure endpoint, headers, and timeout on the exporter instance directly. If you are sending spans to Langfuse v4 or using Langfuse Cloud Fast Preview, include `x-langfuse-ingestion-version=4` on the exporter to enable real time processing of exported spans.

    Example:
        ```python
        from langfuse.otel import Langfuse

        # Initialize the client (reads from env vars if not provided)
        langfuse = Langfuse(
            public_key="your-public-key",
            secret_key="your-secret-key",
            host="https://cloud.langfuse.com",  # Optional, default shown
        )

        # Create a trace span
        with langfuse.start_as_current_observation(name="process-query") as span:
            # Your application code here

            # Create a nested generation span for an LLM call
            with span.start_as_current_generation(
                name="generate-response",
                model="gpt-4",
                input={"query": "Tell me about AI"},
                model_parameters={"temperature": 0.7, "max_tokens": 500}
            ) as generation:
                # Generate response here
                response = "AI is a field of computer science..."

                generation.update(
                    output=response,
                    usage_details={"prompt_tokens": 10, "completion_tokens": 50},
                    cost_details={"total_cost": 0.0023}
                )

                # Score the generation (supports NUMERIC, BOOLEAN, CATEGORICAL)
                generation.score(name="relevance", value=0.95, data_type="NUMERIC")
        ```
    N
_resources_mask_otel_tracerFT)
public_key
secret_keybase_urlhosttimeouthttpx_clientdebugtracing_enabledflush_atflush_intervalenvironmentreleasemedia_upload_thread_countsample_ratemaskblocked_instrumentation_scopesshould_export_spanadditional_headerstracer_providerspan_exporterro   rp   rq   rr   rs   rt   ru   rv   rw   rx   ry   rz   r{   r|   r}   r~   r   r   r   r   c                   |pJt           j                            t                    p&|p$t           j                            t          d          | _        |p6t          t          t           j                            t                              | _	        |p2t           j                            t          d           pt                      | _        d | _        |p1t          t           j                            t          d                    }d|cxk    rdk    sn t!          d|           |p1t#          t           j                            t$          d                    }|o:t           j                            t&          d                                          dk    | _        | j        st-          j        d           |r|n/t          j        t2          d                                          dk    }|r3t5          j        d	
           t-          j        t4          j                   |p#t           j                            t<                    }|.t-          j        d           tA          j!                    | _"        d S |p#t           j                            tF                    }|.t-          j        d           tA          j!                    | _"        d S t           j                            dd                                          dk    rt-          j        d           |tI          j%        dtL          d           tO          d$i d|d|d| j        d|d| j	        d|d|	d|
d|d|d|d|d| j        d|d |d!|d"|d#|| _(        | j(        j)        | _*        | j        r| j(        j+        | j(        j+        ntA          j!                    | _"        | j(        j,        | _,        | j(        j-        | _-        d S )%Nzhttps://cloud.langfuse.comg      ?g        z-Sample rate must be between 0.0 and 1.0, got    truefalsezaConfiguration: Langfuse tracing is explicitly disabled. No data will be sent to the Langfuse API.z4%(asctime)s - %(name)s - %(levelname)s - %(message)sformatzAuthentication error: Langfuse client initialized without public_key. Client will be disabled. Provide a public_key parameter or set LANGFUSE_PUBLIC_KEY environment variable. zAuthentication error: Langfuse client initialized without secret_key. Client will be disabled. Provide a secret_key parameter or set LANGFUSE_SECRET_KEY environment variable. OTEL_SDK_DISABLEDz`OTEL_SDK_DISABLED is set. Langfuse tracing will be disabled and no traces will appear in the UI.ag  `blocked_instrumentation_scopes` is deprecated and will be removed in a future release. Use `should_export_span` instead. Example: from langfuse.span_filter import is_default_export_span; blocked={"scope"}; should_export_span=lambda span: is_default_export_span(span) and (span.instrumentation_scope is None or span.instrumentation_scope.name not in blocked).   )
stacklevelro   rp   rq   rs   ry   rz   rw   rx   rt   r{   r|   r}   rv   r~   r   r   r   r    ).osenvirongetr#   r%   	_base_urlr   strr,   _environmentr'   r=   _release_project_idfloatr(   
ValueErrorintr*   r+   lower_tracing_enabledr_   infogetenvr$   loggingbasicConfigsetLevelDEBUGr&   warningotel_trace_api
NoOpTracerrn   r)   warningswarnDeprecationWarningr/   rl   r}   rm   tracerapi	async_api)selfro   rp   rq   rr   rs   rt   ru   rv   rw   rx   ry   rz   r{   r|   r}   r~   r   r   r   r   s                        a/lsinfo/ai/hellotax_ai/base_platform/venv/lib/python3.11/site-packages/langfuse/_client/client.py__init__zLangfuse.__init__   sy   2  Kz~~/00KK z~~m-IJJ	 	 ( 
4 <==,
 ,
  )z~~.55)&(( 	
 +/!UU2:>>:NPS+T+T%U%Uk((((S((((MMM   ES0@!!D!DEE  T
7@@FFHHGS 	 $ 	 s  
 VEE>7!C!C!I!I!K!Kv!U 	  	4M    $W]333F2:>>2E#F#F
#c   !/ 9 ; ;DFF2:>>2E#F#F
#c   !/ 9 ; ;DF:>>-w77==??6II#r   *5MC #	 	 	 	 2 
 
 
!z
!z
 ^^
 G	

 ))
 G
 X
 *>
 &
 '@&?
 $
 
 !11
 ,J+I
  21
   21!
" ,O#
$ (-%
( _)
 $-)-)?)K O""*,, 	
 ?&2    )trace_contextinputoutputmetadataversionlevelstatus_messagecompletion_start_timemodelmodel_parametersusage_detailscost_detailspromptr   nameas_type
generationr   r   r   r   r   r   r   r   r   r   r   r   returnc                    d S Nr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   s                   r   start_observationzLangfuse.start_observationj  s	    & !Sr   span)r   r   r   r   r   r   r   r   c       	             d S r   r   
r   r   r   r   r   r   r   r   r   r   s
             r   r   zLangfuse.start_observation  	     sr   )r   r   r   r   r   r   r   agentc       	             d S r   r   r   s
             r   r   zLangfuse.start_observation  	     r   toolc       	             d S r   r   r   s
             r   r   zLangfuse.start_observation  r   r   chainc       	             d S r   r   r   s
             r   r   zLangfuse.start_observation  r   r   	retrieverc       	             d S r   r   r   s
             r   r   zLangfuse.start_observation  	      Cr   	evaluatorc       	             d S r   r   r   s
             r   r   zLangfuse.start_observation  r   r   	embeddingc                    d S r   r   r   s                   r   r   zLangfuse.start_observation  s	    &  Cr   	guardrailc       	             d S r   r   r   s
             r   r   zLangfuse.start_observation  r   r   )r   r   r   r   r   r   r   r   r   r   r   r   r   r   c                P   |r|                     dd          }|                     dd          }|r|                     ||          }t          j        t	          t          j        |                    5  | j                            |          }|                    t          j
        d           |                     ||||||||	|
|||||          cddd           S # 1 swxY w Y   | j                            |          }|                     ||||||||	|
|||||          S )az  Create a new observation of the specified type.

        This method creates a new observation but does not set it as the current span in the
        context. To create and use an observation within a context, use start_as_current_observation().

        Args:
            trace_context: Optional context for connecting to an existing trace
            name: Name of the observation
            as_type: Type of observation to create (defaults to "span")
            input: Input data for the operation
            output: Output data from the operation
            metadata: Additional metadata to associate with the observation
            version: Version identifier for the code or component
            level: Importance level of the observation
            status_message: Optional status message for the observation
            completion_start_time: When the model started generating (for generation types)
            model: Name/identifier of the AI model used (for generation types)
            model_parameters: Parameters used for the model (for generation types)
            usage_details: Token usage information (for generation types)
            cost_details: Cost information (for generation types)
            prompt: Associated prompt template (for generation types)

        Returns:
            An observation object of the appropriate type that must be ended with .end()
        trace_idNparent_span_idr   r   r   T)	otel_spanr   r   r   r   r   r   r   r   r   r   r   r   r   )r   _create_remote_parent_spanr   use_spanr   Spanrn   
start_spanset_attributer   AS_ROOT"_create_observation_from_otel_span)r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   remote_parent_spanr   s                       r   r   zLangfuse.start_observation  s   l  	$((T::H*../?FFN %)%D%D%n &E & &" $,,.@AA    !% 1 < <$ < G GI++,F,NPTUUUBB"+ '#%!) '#'5.C#)9&3%1% C                 . %00d0;;	66)"7-'% 7 
 
 	
s   4ACC#&C#r   r   r   r   r   r   r   r   r   r   r   r   r   c                @   |t          t                    v rU|                     |          } |di d|d| d| j        d| j        d|d|d|d|d	|d
|d|	d|
d|d|d|d|S |                     |          } ||| | j        | j        ||||||
  
        S )z:Create the appropriate observation type from an OTEL span.r   langfuse_clientry   rz   r   r   r   r   r   r   r   r   r   r   r   r   
r   r   ry   rz   r   r   r   r   r   r   Nr   )r!   r   _get_span_classr   r   )r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   observation_classs                   r   r   z+Langfuse._create_observation_from_otel_spanf  sb   : 01NOOOO $ 4 4W = = %$   #) $ !-- 	
 e v "   e  .~ '<&; e "2!1 ,m *\  v! ( !% 4 4W = = %$# $ -!-   r   )r   r   r   r   r   r   r   r   r   r   r   r   r   end_on_exitr   c                    d S r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   s                    r   start_as_current_observationz%Langfuse.start_as_current_observation  s	    ( 7:cr   )	r   r   r   r   r   r   r   r   r   c       
             d S r   r   r   r   r   r   r   r   r   r   r   r   r   s              r   r   z%Langfuse.start_as_current_observation  	     14r   )r   r   r   r   r   r   r   r   c       
             d S r   r   r   s              r   r   z%Langfuse.start_as_current_observation  	     25r   c       
             d S r   r   r   s              r   r   z%Langfuse.start_as_current_observation  r   r   c       
             d S r   r   r   s              r   r   z%Langfuse.start_as_current_observation  r   r   c       
             d S r   r   r   s              r   r   z%Langfuse.start_as_current_observation  	     69Sr   c       
             d S r   r   r   s              r   r   z%Langfuse.start_as_current_observation  r   r   c                    d S r   r   r   s                    r   r   z%Langfuse.start_as_current_observation$  s	    ( 69Sr   c       
             d S r   r   r   s              r   r   z%Langfuse.start_as_current_observation:  r   r   )r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   c                "   |t          t                    v r|r|                    dd          }|                    dd          }|rx|                     ||          }t	          t
          t          t                   t          t                   f         | 	                    |||d|||||||	|
|||||                    S t	          t
          t          t                   t          t                   f         | 
                    |||||||||	|
|||||                    S |t          t                    v r|r|                    dd          }|                    dd          }|r|                     ||          }t	          t
          t          t                   t          t                   t          t                   t          t                   t          t                    t          t"                   t          t$                   f         | 	                    |||d|||||||	                    S t	          t
          t          t                   t          t                   t          t                   t          t                   t          t                    t          t"                   t          t$                   f         | 
                    |||||||||		  	                  S t'          j        d	| d
           | 
                    d||||||||		  	        S )ai  Create a new observation and set it as the current span in a context manager.

        This method creates a new observation of the specified type and sets it as the
        current span within a context manager. Use this method with a 'with' statement to
        automatically handle the observation lifecycle within a code block.

        The created observation will be the child of the current span in the context.

        Args:
            trace_context: Optional context for connecting to an existing trace
            name: Name of the observation (e.g., function or operation name)
            as_type: Type of observation to create (defaults to "span")
            input: Input data for the operation (can be any JSON-serializable object)
            output: Output data from the operation (can be any JSON-serializable object)
            metadata: Additional metadata to associate with the observation
            version: Version identifier for the code or component
            level: Importance level of the observation (info, warning, error)
            status_message: Optional status message for the observation
            end_on_exit (default: True): Whether to end the span automatically when leaving the context manager. If False, the span must be manually ended to avoid memory leaks.

            The following parameters are available when as_type is: "generation" or "embedding".
            completion_start_time: When the model started generating the response
            model: Name/identifier of the AI model used (e.g., "gpt-4")
            model_parameters: Parameters used for the model (e.g., temperature, max_tokens)
            usage_details: Token usage information (e.g., prompt_tokens, completion_tokens)
            cost_details: Cost information for the model call
            prompt: Associated prompt template from Langfuse prompt management

        Returns:
            A context manager that yields the appropriate observation type based on as_type

        Example:
            ```python
            # Create a span
            with langfuse.start_as_current_observation(name="process-query", as_type="span") as span:
                # Do work
                result = process_data()
                span.update(output=result)

                # Create a child span automatically
                with span.start_as_current_observation(name="sub-operation") as child_span:
                    # Do sub-operation work
                    child_span.update(output="sub-result")

            # Create a tool observation
            with langfuse.start_as_current_observation(name="web-search", as_type="tool") as tool:
                # Do tool work
                results = search_web(query)
                tool.update(output=results)

            # Create a generation observation
            with langfuse.start_as_current_observation(
                name="answer-generation",
                as_type="generation",
                model="gpt-4"
            ) as generation:
                # Generate answer
                response = llm.generate(...)
                generation.update(output=response)
            ```
        r   Nr   r   )r   r   r   parentr   r   r   r   r   r   r   r   r   r   r   r   r   )r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   )r   r   r   r   r   r   r   r   r   r   r   )	r   r   r   r   r   r   r   r   r   zUnknown observation type: z, falling back to spanr   )r!   r   r   r   r   r   r   r5   r2    _create_span_with_parent_context0_start_as_current_otel_span_with_processed_mediar    r8   r0   r9   r1   r7   r3   r6   r_   r   )r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   s                       r   r   z%Langfuse.start_as_current_observationJ  sq   v 01NOOOO !(,,Z>>!.!2!23CT!J!J )-)H)H!). *I * *&  34FG34EFH ==$+!%/A#'(3"'#)%-$+"'+92G"'-=*7)5#)# >    4 +,>?+,=>@ EE# +!%##1*?%5"/!-! F    0 01HIIII  (,,Z>>!.!2!23CT!J!J )-)H)H!). *I * *&  3LA3MB3LA3MB34EF34EF34EFH ==$+!%/A#'(3"'#)%-$+"'+9 >    2 +L9+M:+L9+M:+,=>+,=>+,=>@ EE# +!%##1 F 
 
  0 	HHHH	
 	
 	
 DD#) E 

 

 
	
r   c                 <   |                                 }|dk    rt          S |dk    rt          S |dk    rt          S |dk    rt          S |dk    rt
          S |dk    rt          S |dk    rt          S |dk    rt          S |d	k    rt          S |d
k    rt          S t          S )z0Get the appropriate span class based on as_type.r   r   r   r   r   r   r   r   eventr   )r   r0   r9   r1   r7   r3   r2   r6   r5   r4   r8   )r   r   normalized_types      r   r   zLangfuse._get_span_class+  s      "--//g%%  &&''  ++$$++$$++$$++$$,,%%''  &&r   )r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   c             #   j  K   |pt          t          j        |          }t          j        |          5  |                     |||||||	|
|||||||          5 }|%|j                            t          j        d           |V  d d d            n# 1 swxY w Y   d d d            d S # 1 swxY w Y   d S )N)r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   T)	r   r   r   r   r   
_otel_spanr   r   r   )r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   parent_spanlangfuse_spans                       r   r   z)Langfuse._create_span_with_parent_contextT  sj     , M^%8:L M M$[11 	$ 	$FF'!-&;!1+) G   $  %1!,::2:D   $###-$ $ $ $ $ $ $ $ $ $ $ $ $ $ $	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$ 	$s5   %B(,BB(B	B(B	B((B,/B,)r   r   r   r   r   r   r   r   r   r   r   r   r   r   c             #   D  K   | j                             |||nd          5 }|                     |pd          }|| | j        | j        ||||||	d
}|t
          t          fv r|                    |
|||||d            |di |V  d d d            d S # 1 swxY w Y   d S )NT)r   r   r   r   )r   r   r   r   r   r   r   )rn   start_as_current_spanr   r   r   r5   r2   update)r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   
span_classcommon_argss                      r   r   z9Langfuse._start_as_current_otel_span_with_processed_media  sV     ( 44'2'>D 5 
 
 $	, --'< J '#'#0= $""0 K "!   ""1F!&,<)6(4"( 	 	 	 *++{+++++I$	, $	, $	, $	, $	, $	, $	, $	, $	, $	, $	, $	, $	, $	, $	, $	, $	, $	,s   A%BBBc                 t    t          j                    }|t           j        u rt          j        d           d S |S )NzContext error: No active span in current context. Operations that depend on an active span will be skipped. Ensure spans are created with start_as_current_observation() or that you're operating within an active span context.)r   get_current_spanINVALID_SPANr_   r   )r   current_spans     r   _get_current_otel_spanzLangfuse._get_current_otel_span  sF    %688>666#G   4r   )r   r   r   r   r   r   r   r   r   r   r   r   r   c                   | j         st          j        d           dS |                                 }|Kt	          ||           }|r|                    |           |                    ||||||||	|
|||           dS dS )a[  Update the current active generation span with new information.

        This method updates the current generation span in the active context with
        additional information. It's useful for adding output, usage stats, or other
        details that become available during or after model generation.

        Args:
            name: The generation name
            input: Updated input data for the model
            output: Output from the model (e.g., completions)
            metadata: Additional metadata to associate with the generation
            version: Version identifier for the model or component
            level: Importance level of the generation (info, warning, error)
            status_message: Optional status message for the generation
            completion_start_time: When the model started generating the response
            model: Name/identifier of the AI model used (e.g., "gpt-4")
            model_parameters: Parameters used for the model (e.g., temperature, max_tokens)
            usage_details: Token usage information (e.g., prompt_tokens, completion_tokens)
            cost_details: Cost information for the model call
            prompt: Associated prompt template from Langfuse prompt management

        Example:
            ```python
            with langfuse.start_as_current_generation(name="answer-query") as generation:
                # Initial setup and API call
                response = llm.generate(...)

                # Update with results that weren't available at creation time
                langfuse.update_current_generation(
                    output=response.text,
                    usage_details={
                        "prompt_tokens": response.usage.prompt_tokens,
                        "completion_tokens": response.usage.completion_tokens
                    }
                )
            ```
        z^Operation skipped: update_current_generation - Tracing is disabled or client is in no-op mode.N)r   r   r   )r   r_   ru   r  r5   update_namer  )r   r   r   r   r   r   r   r   r   r   r   r   r   r   current_otel_spanr   s                   r   update_current_generationz"Langfuse.update_current_generation  s    l $ 	!p   F 7799(++T  J  4!--d333!-&;!1+)       )(r   )r   r   r   r   r   r   r   c                   | j         st          j        d           dS |                                 }|Qt	          || | j        | j                  }	|r|                    |           |	                    ||||||           dS dS )a  Update the current active span with new information.

        This method updates the current span in the active context with
        additional information. It's useful for adding outputs or metadata
        that become available during execution.

        Args:
            name: The span name
            input: Updated input data for the operation
            output: Output data from the operation
            metadata: Additional metadata to associate with the span
            version: Version identifier for the code or component
            level: Importance level of the span (info, warning, error)
            status_message: Optional status message for the span

        Example:
            ```python
            with langfuse.start_as_current_observation(name="process-data") as span:
                # Initial processing
                result = process_first_part()

                # Update with intermediate results
                langfuse.update_current_span(metadata={"intermediate_result": result})

                # Continue processing
                final_result = process_second_part(result)

                # Final update
                langfuse.update_current_span(output=final_result)
            ```
        zXOperation skipped: update_current_span - Tracing is disabled or client is in no-op mode.Nr   r   ry   rz   )r   r   r   r   r   r   )	r   r_   ru   r  r8   r   r   r  r  )
r   r   r   r   r   r   r   r   r  r   s
             r   update_current_spanzLangfuse.update_current_span   s    T $ 	!j   F 7799(+ $ -	  D  4!--d333KK!-       )(r   zTrace-level input/output is deprecated. For trace attributes (user_id, session_id, tags, etc.), use propagate_attributes() instead. This method will be removed in a future major version.r   r   c                p   | j         st          j        d           dS |                                 }||                                rn|j                            t          j        d          }| 	                    |          } ||| | j
        | j                  }|                    ||           dS dS dS )ax  Set trace-level input and output for the current span's trace.

        .. deprecated::
            This is a legacy method for backward compatibility with Langfuse platform
            features that still rely on trace-level input/output (e.g., legacy LLM-as-a-judge
            evaluators). It will be removed in a future major version.

            For setting other trace attributes (user_id, session_id, metadata, tags, version),
            use :meth:`propagate_attributes` instead.

        Args:
            input: Input data to associate with the trace.
            output: Output data to associate with the trace.
        zYOperation skipped: set_current_trace_io - Tracing is disabled or client is in no-op mode.Nr   r  r  )r   r_   ru   r  is_recording
attributesr   r   OBSERVATION_TYPEr   r   r   set_trace_io)r   r   r   r  existing_observation_typer  r   s          r   set_current_trace_iozLangfuse.set_current_trace_iof  s    2 $ 	!k   F 7799(->-K-K-M-M((9(D(H(H*;V) )% --.GHHJ:+ $ -	  D        )(((r   c                 ^   | j         st          j        d           dS |                                 }|w|                                re|j                            t          j        d          }| 	                    |          } ||| | j
                  }|                                 dS dS dS )a*  Make the current trace publicly accessible via its URL.

        When a trace is published, anyone with the trace link can view the full trace
        without needing to be logged in to Langfuse. This action cannot be undone
        programmatically - once published, the entire trace becomes public.

        This is a convenience method that publishes the trace from the currently
        active span context. Use this when you want to make a trace public from
        within a traced function without needing direct access to the span object.
        z`Operation skipped: set_current_trace_as_public - Tracing is disabled or client is in no-op mode.Nr   )r   r   ry   )r   r_   ru   r  r  r  r   r   r  r   r   set_trace_as_public)r   r  r  r  r   s        r   set_current_trace_as_publicz$Langfuse.set_current_trace_as_public  s     $ 	!r   F 7799(->-K-K-M-M((9(D(H(H*;V) )% --.GHHJ:+ $ -  D $$&&&&& )(((r   c                   t                      }	|r|                    dd          }
|                    dd          }|
r|                     |
|          }t          j        t          t          j        |                    5  | j                            ||	          }|	                    t          j        d           t          t          t          || | j        | j        ||||||
  
                            |	                    cddd           S # 1 swxY w Y   | j                            ||	          }t          t          t          || | j        | j        ||||||
  
                            |	                    S )	a  Create a new Langfuse observation of type 'EVENT'.

        The created Langfuse Event observation will be the child of the current span in the context.

        Args:
            trace_context: Optional context for connecting to an existing trace
            name: Name of the span (e.g., function or operation name)
            input: Input data for the operation (can be any JSON-serializable object)
            output: Output data from the operation (can be any JSON-serializable object)
            metadata: Additional metadata to associate with the span
            version: Version identifier for the code or component
            level: Importance level of the span (info, warning, error)
            status_message: Optional status message for the span

        Returns:
            The Langfuse Event object

        Example:
            ```python
            event = langfuse.create_event(name="process-event")
            ```
        r   Nr   r   )r   
start_timeTr   )end_time)r   r   r   r   r   r   r   rn   r   r   r   r   r4   r   r   end)r   r   r   r   r   r   r   r   r   	timestampr   r   r   r   s                 r   create_eventzLangfuse.create_event  s   D II	 	$((T::H*../?FFN %)%D%D%n &E & &" $,,.@AA    !% 1 < <!i != ! !I ++,F,NPTUUU%%&/,0(,(9$(M"'#)%-$+"'+9   #y#11                0 %00dy0QQ	# $ -!-   c9c%%
 
 	
s   BDDDr   r   c                   |                      |          st          j        d| d           |r-|                     |          st          j        d| d           t	          |d          }|rt	          |d          nt                                                      }t          j        ||t          j	        d          d          }t          j
        |          S )	NzPassed trace ID 'zL' is not a valid 32 lowercase hex char Langfuse trace id. Ignoring trace ID.zPassed span ID 'zQ' is not a valid 16 lowercase hex char Langfuse span id. Ignoring parent span ID.      F)r   span_idtrace_flags	is_remote)_is_valid_trace_idr_   r   _is_valid_span_idr   r   generate_span_idr   SpanContext
TraceFlagsNonRecordingSpan)r   r   r   int_trace_idint_parent_span_idspan_contexts         r   r   z#Langfuse._create_remote_parent_span  s    &&x00 	#zHzzz    	$"8"8"H"H 	# E>  E  E  E   8R(( 8C###"$$5577 	 &1!&&1$77	
 
 
 .|<<<r   c                 J    d}t          t          j        ||                    S )Nz^[0-9a-f]{32}$boolrematch)r   r   patterns      r   r-  zLangfuse._is_valid_trace_id.  s!    #BHWh//000r   r*  c                 J    d}t          t          j        ||                    S )Nz^[0-9a-f]{16}$r7  )r   r*  r;  s      r   r.  zLangfuse._is_valid_span_id3  s!    #BHWg..///r   )seedr=  c                   |s5t                                                      }|                     |          S t          |                    d                                                    dd                                         S )a  Create a unique observation ID for use with Langfuse.

        This method generates a unique observation ID (span ID in OpenTelemetry terms)
        for use with various Langfuse APIs. It can either generate a random ID or
        create a deterministic ID based on a seed string.

        Observation IDs must be 16 lowercase hexadecimal characters, representing 8 bytes.
        This method ensures the generated ID meets this requirement. If you need to
        correlate an external ID with a Langfuse observation ID, use the external ID as
        the seed to get a valid, deterministic observation ID.

        Args:
            seed: Optional string to use as a seed for deterministic ID generation.
                 If provided, the same seed will always produce the same ID.
                 If not provided, a random ID will be generated.

        Returns:
            A 16-character lowercase hexadecimal string representing the observation ID.

        Example:
            ```python
            # Generate a random observation ID
            obs_id = langfuse.create_observation_id()

            # Generate a deterministic ID based on a seed
            user_obs_id = langfuse.create_observation_id(seed="user-123-feedback")

            # Correlate an external item ID with a Langfuse observation ID
            item_id = "item-789012"
            correlated_obs_id = langfuse.create_observation_id(seed=item_id)

            # Use the ID with Langfuse APIs
            langfuse.create_score(
                name="relevance",
                value=0.95,
                trace_id=trace_id,
                observation_id=obs_id
            )
            ```
        utf-8N   )r   r/  _format_otel_span_idr   encodedigesthex)r   r=  span_id_ints      r   _create_observation_idzLangfuse._create_observation_id8  ss    R  	:+-->>@@K,,[999dkk'**++2244RaR8<<>>>r   c                    | s:t                                                      }t                              |          S t	          |                     d                                                    dd                                         S )a  Create a unique trace ID for use with Langfuse.

        This method generates a unique trace ID for use with various Langfuse APIs.
        It can either generate a random ID or create a deterministic ID based on
        a seed string.

        Trace IDs must be 32 lowercase hexadecimal characters, representing 16 bytes.
        This method ensures the generated ID meets this requirement. If you need to
        correlate an external ID with a Langfuse trace ID, use the external ID as the
        seed to get a valid, deterministic Langfuse trace ID.

        Args:
            seed: Optional string to use as a seed for deterministic ID generation.
                 If provided, the same seed will always produce the same ID.
                 If not provided, a random ID will be generated.

        Returns:
            A 32-character lowercase hexadecimal string representing the Langfuse trace ID.

        Example:
            ```python
            # Generate a random trace ID
            trace_id = langfuse.create_trace_id()

            # Generate a deterministic ID based on a seed
            session_trace_id = langfuse.create_trace_id(seed="session-456")

            # Correlate an external ID with a Langfuse trace ID
            external_id = "external-system-123456"
            correlated_trace_id = langfuse.create_trace_id(seed=external_id)

            # Use the ID with trace context
            with langfuse.start_as_current_observation(
                name="process-request",
                trace_context={"trace_id": trace_id}
            ) as span:
                # Operation will be part of the specific trace
                pass
            ```
        r?  Nr(  )r   generate_trace_idrk   _format_otel_trace_idr   rB  rC  rD  )r=  trace_id_ints     r   create_trace_idzLangfuse.create_trace_idh  st    T  	@,..@@BBL11,???dkk'**++2244SbS9==???r   c                 ^    |                                 }|                     |j                  S r   )get_span_contextrI  r   r   r   r5  s      r   _get_otel_trace_idzLangfuse._get_otel_trace_id  s*     1133)),*?@@@r   c                 ^    |                                 }|                     |j                  S r   )rM  rA  r*  rN  s      r   _get_otel_span_idzLangfuse._get_otel_span_id  s*     1133(()=>>>r   rE  c                 "    t          | d          S )a}  Format an integer span ID to a 16-character lowercase hex string.

        Internal method to convert an OpenTelemetry integer span ID to the standard
        W3C Trace Context format (16-character lowercase hex string).

        Args:
            span_id_int: 64-bit integer representing a span ID

        Returns:
            A 16-character lowercase hexadecimal string
        016xr   )rE  s    r   rA  zLangfuse._format_otel_span_id  s     k6***r   rJ  c                 "    t          | d          S )a  Format an integer trace ID to a 32-character lowercase hex string.

        Internal method to convert an OpenTelemetry integer trace ID to the standard
        W3C Trace Context format (32-character lowercase hex string).

        Args:
            trace_id_int: 128-bit integer representing a trace ID

        Returns:
            A 32-character lowercase hexadecimal string
        032xr   )rJ  s    r   rI  zLangfuse._format_otel_trace_id  s     lF+++r   )

session_iddataset_run_idr   observation_idscore_id	data_typecomment	config_idr   r%  valuerV  rW  rX  rY  rZ  )NUMERICBOOLEANr[  r\  r%  c                    d S r   r   )r   r   r]  rV  rW  r   rX  rY  rZ  r[  r\  r   r%  s                r   create_scorezLangfuse.create_score  	      sr   CATEGORICAL)
rV  rW  r   rY  rX  rZ  r[  r\  r   r%  rc  TEXTc                    d S r   r   )r   r   r]  rV  rW  r   rY  rX  rZ  r[  r\  r   r%  s                r   ra  zLangfuse.create_score  rb  r   c                   | j         sdS |p|                                 }	 t          |||||||||	|
| j        |          }|                                 d|pt                      |d}| j        8|r|                     |           nd}| j                            ||           dS dS # t          $ r(}t          j        d| d| d	|            Y d}~dS d}~ww xY w)
a  Create a score for a specific trace or observation.

        This method creates a score for evaluating a Langfuse trace or observation. Scores can be
        used to track quality metrics, user feedback, or automated evaluations.

        Args:
            name: Name of the score (e.g., "relevance", "accuracy")
            value: Score value (can be numeric for NUMERIC/BOOLEAN types or string for CATEGORICAL/TEXT)
            session_id: ID of the Langfuse session to associate the score with
            dataset_run_id: ID of the Langfuse dataset run to associate the score with
            trace_id: ID of the Langfuse trace to associate the score with
            observation_id: Optional ID of the specific observation to score. Trace ID must be provided too.
            score_id: Optional custom ID for the score (auto-generated if not provided)
            data_type: Type of score (NUMERIC, BOOLEAN, CATEGORICAL, or TEXT)
            comment: Optional comment or explanation for the score
            config_id: Optional ID of a score config defined in Langfuse
            metadata: Optional metadata to be attached to the score
            timestamp: Optional timestamp for the score (defaults to current UTC time)

        Example:
            ```python
            # Create a numeric score for accuracy
            langfuse.create_score(
                name="accuracy",
                value=0.92,
                trace_id="abcdef1234567890abcdef1234567890",
                data_type="NUMERIC",
                comment="High accuracy with minor irrelevant details"
            )

            # Create a categorical score for sentiment
            langfuse.create_score(
                name="sentiment",
                value="positive",
                trace_id="abcdef1234567890abcdef1234567890",
                observation_id="abcdef1234567890",
                data_type="CATEGORICAL"
            )
            ```
        N)id	sessionIddatasetRunIdtraceIdobservationIdr   r]  dataTyper[  configIdry   r   zscore-createrh  typer%  bodyT)force_samplezAError creating score: Failed to process score event for trace_id=z, name=	. Error: )r   rF  rN   r   rK  r<   rl   r-  add_score_task	Exceptionr_   	exception)r   r   r]  rV  rW  r   rX  rY  rZ  r[  r\  r   r%  new_bodyr   rr  es                    r   ra  zLangfuse.create_score  sw   p $ 	F<t::<<%	 $+ ,"" -!  H  **,,&&:.*:*: 	 E * >FO//99994  ..!- /      +*  	 	 	%wT\wweiwwtuww        	s   BB) )
C3CCtagsc                Z   | j         sdS t          |          dk    rdS 	 t          ||          }|                                 dt	                      |d}| j        | j                            |           dS dS # t          $ r%}t          j	        d| d|            Y d}~dS d}~ww xY w)zEPrivate helper to enqueue trace tag updates via ingestion API events.Nr   )rh  ry  ztrace-createro  zMError updating trace tags: Failed to process trace update event for trace_id=rs  )
r   lenrO   rK  r<   rl   add_trace_taskru  r_   rv  )r   r   ry  rw  r   rx  s         r    _create_trace_tags_via_ingestionz)Langfuse._create_trace_tags_via_ingestionI  s    $ 	Ft99>>F	   H **,,&+-- 	 E *..u55555 +* 	 	 	%v`hvvstvv        	s   AA; ;
B*B%%B*)rY  rZ  r[  r\  r   c                    d S r   r   r   r   r]  rY  rZ  r[  r\  r   s           r   score_current_spanzLangfuse.score_current_spanj  	     sr   c                    d S r   r   r  s           r   r  zLangfuse.score_current_spanw  r  r   c                ^   |                                  }||                     |          }	|                     |          }
t          j        d| d| d|
 d|	            |                     |	|
|t          t          |          |t          t          d         |          |||	  	         dS dS )a  Create a score for the current active span.

        This method scores the currently active span in the context. It's a convenient
        way to score the current operation without needing to know its trace and span IDs.

        Args:
            name: Name of the score (e.g., "relevance", "accuracy")
            value: Score value (can be numeric for NUMERIC/BOOLEAN types or string for CATEGORICAL/TEXT)
            score_id: Optional custom ID for the score (auto-generated if not provided)
            data_type: Type of score (NUMERIC, BOOLEAN, CATEGORICAL, or TEXT)
            comment: Optional comment or explanation for the score
            config_id: Optional ID of a score config defined in Langfuse
            metadata: Optional metadata to be attached to the score

        Example:
            ```python
            with langfuse.start_as_current_generation(name="answer-query") as generation:
                # Generate answer
                response = generate_answer(...)
                generation.update(output=response)

                # Score the generation
                langfuse.score_current_span(
                    name="relevance",
                    value=0.85,
                    data_type="NUMERIC",
                    comment="Mostly relevant but contains some tangential information",
                    metadata={"model": "gpt-4", "prompt_version": "v2"}
                )
            ```
        NScore: Creating score name='' value=z for current span (z) in trace rd  )	r   rX  r   r]  rY  rZ  r[  r\  r   )	r  rO  rQ  r_   r   ra  r   r   r
   )r   r   r]  rY  rZ  r[  r\  r   r  r   rX  s              r   r  zLangfuse.score_current_span  s    T 2244#..|<<H!33LAAN |t||U||We||rz||   !-3&&!w'<=yII#!  
 
 
 
 
 $#r   c                    d S r   r   r  s           r   score_current_tracezLangfuse.score_current_trace  r  r   c                    d S r   r   r  s           r   r  zLangfuse.score_current_trace  r  r   c          
      ,   |                                  }|}|                     |          }	t          j        d| d| d|	            |                     |	|t          t          |          |t          t          d         |          |||           dS dS )a	  Create a score for the current trace.

        This method scores the trace of the currently active span. Unlike score_current_span,
        this method associates the score with the entire trace rather than a specific span.
        It's useful for scoring overall performance or quality of the entire operation.

        Args:
            name: Name of the score (e.g., "user_satisfaction", "overall_quality")
            value: Score value (can be numeric for NUMERIC/BOOLEAN types or string for CATEGORICAL/TEXT)
            score_id: Optional custom ID for the score (auto-generated if not provided)
            data_type: Type of score (NUMERIC, BOOLEAN, CATEGORICAL, or TEXT)
            comment: Optional comment or explanation for the score
            config_id: Optional ID of a score config defined in Langfuse
            metadata: Optional metadata to be attached to the score

        Example:
            ```python
            with langfuse.start_as_current_observation(name="process-user-request") as span:
                # Process request
                result = process_complete_request()
                span.update(output=result)

                # Score the overall trace
                langfuse.score_current_trace(
                    name="overall_quality",
                    value=0.95,
                    data_type="NUMERIC",
                    comment="High quality end-to-end response",
                    metadata={"evaluator": "gpt-4", "criteria": "comprehensive"}
                )
            ```
        Nr  r  z for entire trace rd  )r   r   r]  rY  rZ  r[  r\  r   )r  rO  r_   r   ra  r   r   r
   )
r   r   r]  rY  rZ  r[  r\  r   r  r   s
             r   r  zLangfuse.score_current_trace  s    V 2244#..|<<H `t``U``V^``   !3&&!w'<=yII#!  	 	 	 	 	 $#r   c                 J    | j         | j                                          dS dS )a  Force flush all pending spans and events to the Langfuse API.

        This method manually flushes any pending spans, scores, and other events to the
        Langfuse API. It's useful in scenarios where you want to ensure all data is sent
        before proceeding, without waiting for the automatic flush interval.

        Example:
            ```python
            # Record some spans and scores
            with langfuse.start_as_current_observation(name="operation") as span:
                # Do work...
                pass

            # Ensure all data is sent to Langfuse before proceeding
            langfuse.flush()

            # Continue with other work
            ```
        N)rl   flushr   s    r   r  zLangfuse.flush  s.    ( ?&O!!##### '&r   c                 J    | j         | j                                          dS dS )a,  Shut down the Langfuse client and flush all pending data.

        This method cleanly shuts down the Langfuse client, ensuring all pending data
        is flushed to the API and all background threads are properly terminated.

        It's important to call this method when your application is shutting down to
        prevent data loss and resource leaks. For most applications, using the client
        as a context manager or relying on the automatic shutdown via atexit is sufficient.

        Example:
            ```python
            # Initialize Langfuse
            langfuse = Langfuse(public_key="...", secret_key="...")

            # Use Langfuse throughout your application
            # ...

            # When application is shutting down
            langfuse.shutdown()
            ```
        N)rl   shutdownr  s    r   r  zLangfuse.shutdown4  s.    , ?&O$$&&&&& '&r   c                     | j         st          j        d           dS |                                 }|r|                     |          ndS )a  Get the trace ID of the current active span.

        This method retrieves the trace ID from the currently active span in the context.
        It can be used to get the trace ID for referencing in logs, external systems,
        or for creating related operations.

        Returns:
            The current trace ID as a 32-character lowercase hexadecimal string,
            or None if there is no active span.

        Example:
            ```python
            with langfuse.start_as_current_observation(name="process-request") as span:
                # Get the current trace ID for reference
                trace_id = langfuse.get_current_trace_id()

                # Use it for external correlation
                log.info(f"Processing request with trace_id: {trace_id}")

                # Or pass to another system
                external_system.process(data, trace_id=trace_id)
            ```
        zYOperation skipped: get_current_trace_id - Tracing is disabled or client is in no-op mode.N)r   r_   ru   r  rO  r   r  s     r   get_current_trace_idzLangfuse.get_current_trace_idM  s`    0 $ 	!k   4 7799=NXt&&'8999TXXr   c                     | j         st          j        d           dS |                                 }|r|                     |          ndS )a  Get the observation ID (span ID) of the current active span.

        This method retrieves the observation ID from the currently active span in the context.
        It can be used to get the observation ID for referencing in logs, external systems,
        or for creating scores or other related operations.

        Returns:
            The current observation ID as a 16-character lowercase hexadecimal string,
            or None if there is no active span.

        Example:
            ```python
            with langfuse.start_as_current_observation(name="process-user-query") as span:
                # Get the current observation ID
                observation_id = langfuse.get_current_observation_id()

                # Store it for later reference
                cache.set(f"query_{query_id}_observation", observation_id)

                # Process the query...
            ```
        z_Operation skipped: get_current_observation_id - Tracing is disabled or client is in no-op mode.N)r   r_   ru   r  rQ  r  s     r   get_current_observation_idz#Langfuse.get_current_observation_ido  s`    . $ 	!q   4 7799<MWt%%&7888SWWr   c                     | j         sP| j        j                                        }|j        r|j        d         j        sdS |j        d         j        | _         | j         S )zxFetch and return the current project id. Persisted across requests. Returns None if no project id is found for api keys.r   N)r   r   projectsr   datarh  )r   projs     r   _get_project_idzLangfuse._get_project_id  sZ     	/8$((**D9 DIaLO t#y|Dr   )r   c                    |p|                                  }|sdS |                                 }|r|r| j         d| d| ndS )aI  Get the URL to view a trace in the Langfuse UI.

        This method generates a URL that links directly to a trace in the Langfuse UI.
        It's useful for providing links in logs, notifications, or debugging tools.

        Args:
            trace_id: Optional trace ID to generate a URL for. If not provided,
                     the trace ID of the current active span will be used.

        Returns:
            A URL string pointing to the trace in the Langfuse UI,
            or None if the project ID couldn't be retrieved or no trace ID is available.

        Example:
            ```python
            # Get URL for the current trace
            with langfuse.start_as_current_observation(name="process-request") as span:
                trace_url = langfuse.get_trace_url()
                log.info(f"Processing trace: {trace_url}")

            # Get URL for a specific trace
            specific_trace_url = langfuse.get_trace_url(trace_id="1234567890abcdef1234567890abcdef")
            send_notification(f"Review needed for trace: {specific_trace_url}")
            ```
        N	/project/z/traces/)r  r  r   )r   r   final_trace_id
project_ids       r   get_trace_urlzLangfuse.get_trace_url  so    4 "@T%>%>%@%@ 	4))++
 ,t~LL
LLNLLL	
r   2   )fetch_items_page_sizer   r  r"   c                   	 t          j        d|            | j        j                            |                     |                    }g }d}	 | j        j                            |                     |d          |||          }|                    |j	                   |j
        j        |k    rn|dz  }it          ||||           S # t          $ r}t          |           |d}~ww xY w)	a}  Fetch a dataset by its name.

        Args:
            name (str): The name of the dataset to fetch.
            fetch_items_page_size (Optional[int]): All items of the dataset will be fetched in chunks of this size. Defaults to 50.
            version (Optional[datetime]): Retrieve dataset items as they existed at this specific point in time (UTC).
                If provided, returns the state of items at the specified UTC timestamp.
                If not provided, returns the latest version. Must be a timezone-aware datetime object in UTC.

        Returns:
            DatasetClient: The dataset with the given name.
        zGetting datasets )dataset_namer)  Tis_url_param)r  pagelimitr   )datasetitemsr   r   N)r_   ru   r   datasetsr   _url_encodedataset_itemslistextendr  metatotal_pagesr"   rH   r>   )	r   r   r  r   r  r  r  	new_itemsrx  s	            r   get_datasetzLangfuse.get_dataset  s)   &	!"<d"<"<===h'++9I9I$9O9O+PPGMD H277!%!1!1$T!1!J!J/#	 8  	 $$Y^444>-55	 !# $	     	 	 	!!$$$G	s   C
C 
C-C((C-r  run_namec          	          	 t          t          | j        j                            |                     |          |                     |          d                    S # t          $ r}t          |           |d}~ww xY w)a  Fetch a dataset run by dataset name and run name.

        Args:
            dataset_name (str): The name of the dataset.
            run_name (str): The name of the run.

        Returns:
            DatasetRunWithItems: The dataset run with its items.
        Nr  r  request_options)r   rE   r   r  get_runr  rH   r>   r   r  r  rx  s       r   get_dataset_runzLangfuse.get_dataset_run  s    	#!))!%!1!1,!?!?!--h77$( *      	 	 	!!$$$G	   AA 
A='A88A=)r  r  r  r  c          	          	 t          t          | j        j                            |                     |          ||d                    S # t          $ r}t          |           |d}~ww xY w)a:  Fetch all runs for a dataset.

        Args:
            dataset_name (str): The name of the dataset.
            page (Optional[int]): Page number, starts at 1.
            limit (Optional[int]): Limit of items per page.

        Returns:
            PaginatedDatasetRuns: Paginated list of dataset runs.
        N)r  r  r  r  )r   rK   r   r  get_runsr  rH   r>   )r   r  r  r  rx  s        r   get_dataset_runszLangfuse.get_dataset_runs	  s    "	$!**!%!1!1,!?!?$(	 +      	 	 	!!$$$G	s   AA 
A+A&&A+c          	          	 t          t          | j        j                            |                     |          |                     |          d                    S # t          $ r}t          |           |d}~ww xY w)a  Delete a dataset run and all its run items. This action is irreversible.

        Args:
            dataset_name (str): The name of the dataset.
            run_name (str): The name of the run.

        Returns:
            DeleteDatasetRunResponse: Confirmation of deletion.
        Nr  )r   rG   r   r  
delete_runr  rH   r>   r  s       r   delete_dataset_runzLangfuse.delete_dataset_run,	  s    	(!,,!%!1!1,!?!?!--h77$( -      	 	 	!!$$$G	r  )r  description
evaluatorscomposite_evaluatorrun_evaluatorsmax_concurrencyr   _dataset_versionr  r  taskr  r  r  r  r  c                    t          t          t          |                     ||                     ||          ||||pg ||pg |	|
|                              S )a"  Run an experiment on a dataset with automatic tracing and evaluation.

        This method executes a task function on each item in the provided dataset,
        automatically traces all executions with Langfuse for observability, runs
        item-level and run-level evaluators on the outputs, and returns comprehensive
        results with evaluation metrics.

        The experiment system provides:
        - Automatic tracing of all task executions
        - Concurrent processing with configurable limits
        - Comprehensive error handling that isolates failures
        - Integration with Langfuse datasets for experiment tracking
        - Flexible evaluation framework supporting both sync and async evaluators

        Args:
            name: Human-readable name for the experiment. Used for identification
                in the Langfuse UI.
            run_name: Optional exact name for the experiment run. If provided, this will be
                used as the exact dataset run name if the `data` contains Langfuse dataset items.
                If not provided, this will default to the experiment name appended with an ISO timestamp.
            description: Optional description explaining the experiment's purpose,
                methodology, or expected outcomes.
            data: Array of data items to process. Can be either:
                - List of dict-like items with 'input', 'expected_output', 'metadata' keys
                - List of Langfuse DatasetItem objects from dataset.items
            task: Function that processes each data item and returns output.
                Must accept 'item' as keyword argument and can return sync or async results.
                The task function signature should be: task(*, item, **kwargs) -> Any
            evaluators: List of functions to evaluate each item's output individually.
                Each evaluator receives input, output, expected_output, and metadata.
                Can return single Evaluation dict or list of Evaluation dicts.
            composite_evaluator: Optional function that creates composite scores from item-level evaluations.
                Receives the same inputs as item-level evaluators (input, output, expected_output, metadata)
                plus the list of evaluations from item-level evaluators. Useful for weighted averages,
                pass/fail decisions based on multiple criteria, or custom scoring logic combining multiple metrics.
            run_evaluators: List of functions to evaluate the entire experiment run.
                Each run evaluator receives all item_results and can compute aggregate metrics.
                Useful for calculating averages, distributions, or cross-item comparisons.
            max_concurrency: Maximum number of concurrent task executions (default: 50).
                Controls the number of items processed simultaneously. Adjust based on
                API rate limits and system resources.
            metadata: Optional metadata dictionary to attach to all experiment traces.
                This metadata will be included in every trace created during the experiment.
                If `data` are Langfuse dataset items, the metadata will be attached to the dataset run, too.

        Returns:
            ExperimentResult containing:
            - run_name: The experiment run name. This is equal to the dataset run name if experiment was on Langfuse dataset.
            - item_results: List of results for each processed item with outputs and evaluations
            - run_evaluations: List of aggregate evaluation results for the entire run
            - experiment_id: Stable identifier for the experiment run across all items
            - dataset_run_id: ID of the dataset run (if using Langfuse datasets)
            - dataset_run_url: Direct URL to view results in Langfuse UI (if applicable)

        Raises:
            ValueError: If required parameters are missing or invalid
            Exception: If experiment setup fails (individual item failures are handled gracefully)

        Examples:
            Basic experiment with local data:
            ```python
            def summarize_text(*, item, **kwargs):
                return f"Summary: {item['input'][:50]}..."

            def length_evaluator(*, input, output, expected_output=None, **kwargs):
                return {
                    "name": "output_length",
                    "value": len(output),
                    "comment": f"Output contains {len(output)} characters"
                }

            result = langfuse.run_experiment(
                name="Text Summarization Test",
                description="Evaluate summarization quality and length",
                data=[
                    {"input": "Long article text...", "expected_output": "Expected summary"},
                    {"input": "Another article...", "expected_output": "Another summary"}
                ],
                task=summarize_text,
                evaluators=[length_evaluator]
            )

            print(f"Processed {len(result.item_results)} items")
            for item_result in result.item_results:
                print(f"Input: {item_result.item['input']}")
                print(f"Output: {item_result.output}")
                print(f"Evaluations: {item_result.evaluations}")
            ```

            Advanced experiment with async task and multiple evaluators:
            ```python
            async def llm_task(*, item, **kwargs):
                # Simulate async LLM call
                response = await openai_client.chat.completions.create(
                    model="gpt-4",
                    messages=[{"role": "user", "content": item["input"]}]
                )
                return response.choices[0].message.content

            def accuracy_evaluator(*, input, output, expected_output=None, **kwargs):
                if expected_output and expected_output.lower() in output.lower():
                    return {"name": "accuracy", "value": 1.0, "comment": "Correct answer"}
                return {"name": "accuracy", "value": 0.0, "comment": "Incorrect answer"}

            def toxicity_evaluator(*, input, output, expected_output=None, **kwargs):
                # Simulate toxicity check
                toxicity_score = check_toxicity(output)  # Your toxicity checker
                return {
                    "name": "toxicity",
                    "value": toxicity_score,
                    "comment": f"Toxicity level: {'high' if toxicity_score > 0.7 else 'low'}"
                }

            def average_accuracy(*, item_results, **kwargs):
                accuracies = [
                    eval.value for result in item_results
                    for eval in result.evaluations
                    if eval.name == "accuracy"
                ]
                return {
                    "name": "average_accuracy",
                    "value": sum(accuracies) / len(accuracies) if accuracies else 0,
                    "comment": f"Average accuracy across {len(accuracies)} items"
                }

            result = langfuse.run_experiment(
                name="LLM Safety and Accuracy Test",
                description="Evaluate model accuracy and safety across diverse prompts",
                data=test_dataset,  # Your dataset items
                task=llm_task,
                evaluators=[accuracy_evaluator, toxicity_evaluator],
                run_evaluators=[average_accuracy],
                max_concurrency=5,  # Limit concurrent API calls
                metadata={"model": "gpt-4", "temperature": 0.7}
            )
            ```

            Using with Langfuse datasets:
            ```python
            # Get dataset from Langfuse
            dataset = langfuse.get_dataset("my-eval-dataset")

            result = dataset.run_experiment(
                name="Production Model Evaluation",
                description="Monthly evaluation of production model performance",
                task=my_production_task,
                evaluators=[accuracy_evaluator, latency_evaluator]
            )

            # Results automatically linked to dataset in Langfuse UI
            print(f"View results: {result['dataset_run_url']}")
            ```

        Note:
            - Task and evaluator functions can be either synchronous or asynchronous
            - Individual item failures are logged but don't stop the experiment
            - All executions are automatically traced and visible in Langfuse UI
            - When using Langfuse datasets, results are automatically linked for easy comparison
            - This method works in both sync and async contexts (Jupyter notebooks, web apps, etc.)
            - Async execution is handled automatically with smart event loop detection
        r   r  )r   r  r  r  r  r  r  r  r  r   dataset_version)r   rZ   r;   _run_experiment_async_create_experiment_run_name)r   r   r  r  r  r  r  r  r  r  r   r  s               r   run_experimentzLangfuse.run_experimentE	  s    ` **!==!H >   !,)/R(;#1#7R$3%$4 +   
 
 	
r   )r   r  r  c                <   
K   t          j        d d dt          |           d                                            t	          j        |	          dt          dt          f
 fdfd|D             }t	          j        |d	d
i d {V }g }t          |          D ]_\  }}t          |t                    rt          j        d| d|            5t          |t                    r|                    |           `g }|D ]]}	 t          ||           d {V }|                    |           0# t          $ r!}t          j        d|            Y d }~Vd }~ww xY wt!          d |D             d           }d }|ri|rg	 |d         }d }t#          |d          rt%          |dd           }|r(                                 }|r j         d| d| d| }n# t          $ r Y nw xY w|D ]o}	 |r<                     ||j        pd|j        |j        |j        |j        |j                   B# t          $ r!}t          j        d|            Y d }~hd }~ww xY w                                  t;          |||p||          S )NzStarting experiment 'z' run 'z' with z itemsitemr   c                    K   	4 d {V                       | 

  
         d {V cd d d           d {V  S # 1 d {V swxY w Y   d S r   )_process_experiment_item)r  r  r  r  r  r   r   r  r   	semaphoreshared_fallback_experiment_idr  s    r   process_itemz4Langfuse._run_experiment_async.<locals>.process_item#
  s              !::'1#                                    s   $A
AAc                 &    g | ]} |          S r   r   ).0r  r  s     r   
<listcomp>z2Langfuse._run_experiment_async.<locals>.<listcomp>3
  s#    555d##555r   return_exceptionsTzItem z	 failed: )item_resultszRun evaluator failed: c              3   2   K   | ]}|j         	|j         V  d S r   )rW  )r  results     r   	<genexpr>z1Langfuse._run_experiment_async.<locals>.<genexpr>K
  sB        (%     r   r   
dataset_idr  z
/datasets/z/runs/z	<unknown>)rW  r   r]  r[  r   rZ  r\  z Failed to store run evaluation: )r   r  r  r  run_evaluationsexperiment_idrW  dataset_run_url)r_   ru   r{  rF  asyncio	SemaphorerX   rY   gather	enumerate
isinstanceru  errorappendr]   r  nexthasattrgetattrr  r   ra  r   r]  r[  r   rZ  r\  r  rZ   )r   r   r  r  r  r  r  r  r  r  r   r  tasksr  valid_resultsir  r  run_evaluatorevaluationsrx  rW  r  
first_itemr  r  
evaluationr  r  r  s   ```` ```  ``               @@@r   r  zLangfuse._run_experiment_async

  s4      	SDSSSS#d))SSS	
 	
 	
 )-(C(C(E(E% %o66		^ 	8L 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	  6555555$^UKdKKKKKKKK 57"<00 	- 	-IAv&),, -%&Ba&B&B&&B&BCCCCF$899 -$$V,,, -/+ 	D 	DMD$2!% % %        &&{3333 D D D%&Bq&B&BCCCCCCCCD  +  
 
 
  	d 	!!W
!
:|44 I!(\4!H!HJ !%!5!5!7!7J! -1^*~*~j*~*~\f*~*~n|*~*~    * 	N 	NJN! 	%%'5'_;(. * 2!+!4","6","6 &     N N N%&L&L&LMMMMMMMMN 	

#&+(I,I)+	
 	
 	
 		
sC   ,E


E5E00E5AG/ /
G<;G<>I
I.I))I.r  fallback_experiment_idexperiment_nameexperiment_run_nameexperiment_descriptionexperiment_metadatac                 l  K   d}|                      |          5 }	 t          |t                    r|                    d          nt	          |dd           }|t          d          t          |t                    r|                    d          nt	          |dd           }t          |t                    r|                    d          nt	          |dd           }||d|	pi }|j        }d }d }d }t          |d          rt          |d	          rr	 t          j	        | j
        j        j        |||	|j        ||j        |

           d {V }|j        }n.# t          $ r!}t!          j        d|            Y d }~nd }~ww xY wt          |t                    sFt          |d	          r6t          |d          r&|j        }|j        }|                    ||d           t          |t                    r|                    |           |p|}|p#t)          t+          |                    d d         }|j                            d t0          j        t4          t0          j        |t0          j        t+          |          i                                D                        t=          ||t?          |	          ||t?          t          |t                    r|nd           |j                  }tA          |          5  tC          ||           d {V }d d d            n# 1 swxY w Y   |                    |||           nI# t          $ r<}|                    dtE          |           dtE          |                     |d }~ww xY wg }|D ]}	 d }t          |t                    r|                    d          }nt          |d          r|j#        }tA          |          5  tI          |||||           d {V }|%                    |           |D ]B}| &                    ||j        |j'        |j(        |j)        |j#        |j*        |j+                   C	 d d d            n# 1 swxY w Y   # t          $ r"}t!          j        d|            Y d }~d }~ww xY w|r_|r\	 d } t          |t                    r|                    d          } nt          |d          r|j#        } tA          |          5   ||||| |          }!t          j,        |!          r|! d {V }!g }"t          |!t          tZ          f          r|!g}"nt          |!t\                    r|!}"|"D ]W}#| &                    ||j        |#j'        |#j(        |#j)        |#j#        |#j*        |#j+                   |/                    |#           X	 d d d            n# 1 swxY w Y   n.# t          $ r!}t!          j        d|            Y d }~nd }~ww xY wta          |||||          cd d d            S # 1 swxY w Y   d S )Nzexperiment-item-runr   r   z0Experiment Item is missing input. Skipping item.expected_outputr   )r  r  rh  r  )r  run_descriptionr   dataset_item_idr   rX  r  z#Failed to create dataset run item: )r  r  r(  c                     i | ]
\  }}|||S r   r   )r  kvs      r   
<dictcomp>z5Langfuse._process_experiment_item.<locals>.<dictcomp>
  s/     
 
 
 Aq = 1 )==r   )r  r  r  experiment_dataset_idexperiment_item_idexperiment_item_metadata#experiment_item_root_observation_id)
experiment)r   r   r   zError: ERROR)r   r   r   )r   r   r  r   )r   rX  r   r]  r[  r   r\  rZ  zEvaluator failed: )r   r   r  r   r  zComposite evaluator failed: )r  r   r  r   rW  )1r   r  dictr   r  r   r   r  r  	to_threadr   dataset_run_itemscreaterh  rW  ru  r_   r  r  r  r:   r   r  set_attributesr   ENVIRONMENTr   EXPERIMENT_DESCRIPTIONEXPERIMENT_ITEM_EXPECTED_OUTPUTr  r-   r   r.   r^   r   r   r]   r  ra  r   r]  r[  r\  rZ  iscoroutinerU   r  r  rY   )$r   r  r  r  r  r  r  r  r  r  r  	span_namer   
input_datar  item_metadatafinal_observation_metadatar   r  r  rW  dataset_run_itemrx  r  r   propagated_experiment_attributesr   r  r   eval_metadataeval_resultsr  composite_eval_metadatar  composite_evalscomposite_evaluations$                                       r   r  z!Langfuse._process_experiment_item
  sh	      *	..I.>> S	$r "$--6DHHW%%% w55  %$%WXXX "$--@DHH./// '8$??   "$--9DHHZ((( z488  (7+>. . +0b.*  =!
"&!% 4&& Y74+F+F YY 291B H6=%8,B%8,0G%-+/7,;	2 	2 	2 	, 	, 	, 	, 	, 	,( *:)H$ Y Y Y'-.WTU.W.WXXXXXXXXY #4..
l33
  d++

 "&J&*gO.55'1oVV   mT22 E.55mDDD . H2H#W'::j;Q;Q'R'RSVTVSV'W # ..
 
 7BDg6MOe6VXb /Y Y%  %''
 
 
   4R"/$7(N+) ) +5'9-S)3M4)H)HRd. . 9=4 4 40 +6VWWW 9 9#,T4#8#8888888F9 9 9 9 9 9 9 9 9 9 9 9 9 9 9 $!7         -SVV--WSQRVV     	 K' #D #D	"D>BM!$-- 6(,(<(< z22 6(,.#C     .<%",#),;%2. . . ( ( ( ( ( ( $**<888 +7 
 
J --)1/3w%/_&0&6(2(:)3)<*4*>*4*> . 	 	 	 	
              2 ! D D D#)*Bq*B*BCCCCCCCCD # -N{ -N,NHL+!$-- @26((:2F2F// z22 @26-/.#C   "E "E "5!4",#),;%<(3" " " #.v66 2+1\\\\\\F =?%ftZ.@AA 5/5hOO'55 5.4O 5D E E0 --)1/3w%9%>&:&@(<(D)=)F*>*H*>*H . 	 	 	 (../CDDDDE/"E "E "E "E "E "E "E "E "E "E "E "E "E "E "EH ! N N N#)*L*L*LMMMMMMMMN ('!-  [S	 S	 S	 S	 S	 S	 S	 S	 S	 S	 S	 S	 S	 S	 S	 S	 S	 S	s%  X)C8MAEM
F#E?:M?FE:M>L!M!L%	%M(L%	)MX)
N7NN
X)AQ8*A5Q, Q8,Q00Q83Q04Q87X)8
R$RX)R$$	X).AWC WWW	WW	WX)
X%X<X)XX))X-0X-r  c                    |r|S t                                                                          dd          }| d| S )Nz+00:00Zz - )r<   	isoformatreplace)r   r   r  iso_timestamps       r   r  z$Langfuse._create_experiment_run_nameh  sL      	O&((2244<<XsKK**=***r      r   )filterfetch_batch_sizefetch_trace_fields	max_itemsmax_retriesr  r  r    _add_observation_scores_to_trace_additional_trace_tagsresume_fromverbosescope)tracesobservationsmapperr  r  r  r   r!  r"  r#  r$  r%  c                    t          |           }t          t          t          |                    ||||||||
|	||||||                              S )up%  Fetch traces or observations and run evaluations on each item.

        This method provides a powerful way to evaluate existing data in Langfuse at scale.
        It fetches items based on filters, transforms them using a mapper function, runs
        evaluators on each item, and creates scores that are linked back to the original
        entities. This is ideal for:

        - Running evaluations on production traces after deployment
        - Backtesting new evaluation metrics on historical data
        - Batch scoring of observations for quality monitoring
        - Periodic evaluation runs on recent data

        The method uses a streaming/pipeline approach to process items in batches, making
        it memory-efficient for large datasets. It includes comprehensive error handling,
        retry logic, and resume capability for long-running evaluations.

        Args:
            scope: The type of items to evaluate. Must be one of:
                - "traces": Evaluate complete traces with all their observations
                - "observations": Evaluate individual observations (spans, generations, events)
            mapper: Function that transforms API response objects into evaluator inputs.
                Receives a trace/observation object and returns an EvaluatorInputs
                instance with input, output, expected_output, and metadata fields.
                Can be sync or async.
            evaluators: List of evaluation functions to run on each item. Each evaluator
                receives the mapped inputs and returns Evaluation object(s). Evaluator
                failures are logged but don't stop the batch evaluation.
            filter: Optional JSON filter string for querying items (same format as Langfuse API). Examples:
                - '{"tags": ["production"]}'
                - '{"user_id": "user123", "timestamp": {"operator": ">", "value": "2024-01-01"}}'
                Default: None (fetches all items).
            fetch_batch_size: Number of items to fetch per API call and hold in memory.
                Larger values may be faster but use more memory. Default: 50.
            fetch_trace_fields: Comma-separated list of fields to include when fetching traces. Available field groups: 'core' (always included), 'io' (input, output, metadata), 'scores', 'observations', 'metrics'. If not specified, all fields are returned. Example: 'core,scores,metrics'. Note: Excluded 'observations' or 'scores' fields return empty arrays; excluded 'metrics' returns -1 for 'totalCost' and 'latency'. Only relevant if scope is 'traces'.
            max_items: Maximum total number of items to process. If None, processes all
                items matching the filter. Useful for testing or limiting evaluation runs.
                Default: None (process all).
            max_concurrency: Maximum number of items to evaluate concurrently. Controls
                parallelism and resource usage. Default: 5.
            composite_evaluator: Optional function that creates a composite score from
                item-level evaluations. Receives the original item and its evaluations,
                returns a single Evaluation. Useful for weighted averages or combined metrics.
                Default: None.
            metadata: Optional metadata dict to add to all created scores. Useful for
                tracking evaluation runs, versions, or other context. Default: None.
            max_retries: Maximum number of retry attempts for failed batch fetches.
                Uses exponential backoff (1s, 2s, 4s). Default: 3.
            verbose: If True, logs progress information to console. Useful for monitoring
                long-running evaluations. Default: False.
            resume_from: Optional resume token from a previous incomplete run. Allows
                continuing evaluation after interruption or failure. Default: None.


        Returns:
            BatchEvaluationResult containing:
                - total_items_fetched: Number of items fetched from API
                - total_items_processed: Number of items successfully evaluated
                - total_items_failed: Number of items that failed evaluation
                - total_scores_created: Scores created by item-level evaluators
                - total_composite_scores_created: Scores created by composite evaluator
                - total_evaluations_failed: Individual evaluator failures
                - evaluator_stats: Per-evaluator statistics (success rate, scores created)
                - resume_token: Token for resuming if incomplete (None if completed)
                - completed: True if all items processed
                - duration_seconds: Total execution time
                - failed_item_ids: IDs of items that failed
                - error_summary: Error types and counts
                - has_more_items: True if max_items reached but more exist

        Raises:
            ValueError: If invalid scope is provided.

        Examples:
            Basic trace evaluation:
            ```python
            from langfuse import Langfuse, EvaluatorInputs, Evaluation

            client = Langfuse()

            # Define mapper to extract fields from traces
            def trace_mapper(trace):
                return EvaluatorInputs(
                    input=trace.input,
                    output=trace.output,
                    expected_output=None,
                    metadata={"trace_id": trace.id}
                )

            # Define evaluator
            def length_evaluator(*, input, output, expected_output, metadata):
                return Evaluation(
                    name="output_length",
                    value=len(output) if output else 0
                )

            # Run batch evaluation
            result = client.run_batched_evaluation(
                scope="traces",
                mapper=trace_mapper,
                evaluators=[length_evaluator],
                filter='{"tags": ["production"]}',
                max_items=1000,
                verbose=True
            )

            print(f"Processed {result.total_items_processed} traces")
            print(f"Created {result.total_scores_created} scores")
            ```

            Evaluation with composite scorer:
            ```python
            def accuracy_evaluator(*, input, output, expected_output, metadata):
                # ... evaluation logic
                return Evaluation(name="accuracy", value=0.85)

            def relevance_evaluator(*, input, output, expected_output, metadata):
                # ... evaluation logic
                return Evaluation(name="relevance", value=0.92)

            def composite_evaluator(*, item, evaluations):
                # Weighted average of evaluations
                weights = {"accuracy": 0.6, "relevance": 0.4}
                total = sum(
                    e.value * weights.get(e.name, 0)
                    for e in evaluations
                    if isinstance(e.value, (int, float))
                )
                return Evaluation(
                    name="composite_score",
                    value=total,
                    comment=f"Weighted average of {len(evaluations)} metrics"
                )

            result = client.run_batched_evaluation(
                scope="traces",
                mapper=trace_mapper,
                evaluators=[accuracy_evaluator, relevance_evaluator],
                composite_evaluator=composite_evaluator,
                filter='{"user_id": "important_user"}',
                verbose=True
            )
            ```

            Handling incomplete runs with resume:
            ```python
            # Initial run that may fail or timeout
            result = client.run_batched_evaluation(
                scope="observations",
                mapper=obs_mapper,
                evaluators=[my_evaluator],
                max_items=10000,
                verbose=True
            )

            # Check if incomplete
            if not result.completed and result.resume_token:
                print(f"Processed {result.resume_token.items_processed} items before interruption")

                # Resume from where it left off
                result = client.run_batched_evaluation(
                    scope="observations",
                    mapper=obs_mapper,
                    evaluators=[my_evaluator],
                    resume_from=result.resume_token,
                    verbose=True
                )

            print(f"Total items processed: {result.total_items_processed}")
            ```

            Monitoring evaluator performance:
            ```python
            result = client.run_batched_evaluation(...)

            for stats in result.evaluator_stats:
                success_rate = stats.successful_runs / stats.total_runs
                print(f"{stats.name}:")
                print(f"  Success rate: {success_rate:.1%}")
                print(f"  Scores created: {stats.total_scores_created}")

                if stats.failed_runs > 0:
                    print(f"  ⚠️  Failed {stats.failed_runs} times")
            ```

        Note:
            - Evaluator failures are logged but don't stop the batch evaluation
            - Individual item failures are tracked but don't stop processing
            - Fetch failures are retried with exponential backoff
            - All scores are automatically flushed to Langfuse at the end
            - The resume mechanism uses timestamp-based filtering to avoid duplicates
        )r&  r)  r  r  r  r  r   r  r  r   r"  r#  r!  r%  r$  )rR   r   rP   r;   	run_async)r   r&  r)  r  r  r  r   r!  r  r  r  r   r"  r#  r$  r%  runners                    r   run_batched_evaluationzLangfuse.run_batched_evaluationr  sy    d 't,,!  !)!%5'9'$3(;%5U+A +# + !   
 
 	
r   c                    	 | j         j                                        }t          j        dt          |j                   d           t          |j                  dk    rt          d          dS # t          $ r"}t          j	        d|            Y d}~dS d}~wt          $ r}t          |           |d}~ww xY w)	a  Check if the provided credentials (public and secret key) are valid.

        Raises:
            Exception: If no projects were found for the provided credentials.

        Note:
            This method is blocking. It is discouraged to use it in production code.
        zAuth check successful, found z	 projectsr   z:Auth check failed, no project found for the keys provided.Tz;Auth check failed: Client not properly initialized. Error: NF)r   r  r   r_   ru   r{  r  ru  AttributeErrorr   rH   r>   )r   r  rx  s      r   
auth_checkzLangfuse.auth_check]  s    	x(,,..H!MHM0B0BMMM   8=!!Q&&P   4 	 	 	#QaQQ   55555 	 	 	!!$$$G	s$   A/A3 3
B==BB='B88B=)r  r   input_schemaexpected_output_schemar1  r2  c                    	 t          j        d|            | j        j                            |||||          }t          t          |          S # t          $ r}t          |           |d}~ww xY w)a  Create a dataset with the given name on Langfuse.

        Args:
            name: Name of the dataset to create.
            description: Description of the dataset. Defaults to None.
            metadata: Additional metadata. Defaults to None.
            input_schema: JSON Schema for validating dataset item inputs. When set, all new items will be validated against this schema.
            expected_output_schema: JSON Schema for validating dataset item expected outputs. When set, all new items will be validated against this schema.

        Returns:
            Dataset: The created dataset as returned by the Langfuse API.
        zCreating datasets )r   r  r   r1  r2  N)	r_   ru   r   r  r  r   rC   rH   r>   )r   r   r  r   r1  r2  r  rx  s           r   create_datasetzLangfuse.create_dataset{  s    *	!"=t"="=>>>X&--'!)'= .  F ((( 	 	 	!!$$$G	s   AA 
A2A--A2)r   r  r   source_trace_idsource_observation_idstatusrh  r  r5  r6  r7  rh  c          
          	 t          j        d|            | j        j                            ||||||||          }	t          t          |	          S # t          $ r}
t          |
           |
d}
~
ww xY w)a  Create a dataset item.

        Upserts if an item with id already exists.

        Args:
            dataset_name: Name of the dataset in which the dataset item should be created.
            input: Input data. Defaults to None. Can contain any dict, list or scalar.
            expected_output: Expected output data. Defaults to None. Can contain any dict, list or scalar.
            metadata: Additional metadata. Defaults to None. Can contain any dict, list or scalar.
            source_trace_id: Id of the source trace. Defaults to None.
            source_observation_id: Id of the source observation. Defaults to None.
            status: Status of the dataset item. Defaults to ACTIVE for newly created items.
            id: Id of the dataset item. Defaults to None. Provide your own id if you want to dedupe dataset items. Id needs to be globally unique and cannot be reused across datasets.

        Returns:
            DatasetItem: The created dataset item as returned by the Langfuse API.

        Example:
            ```python
            from langfuse import Langfuse

            langfuse = Langfuse()

            # Uploading items to the Langfuse dataset named "capital_cities"
            langfuse.create_dataset_item(
                dataset_name="capital_cities",
                input={"input": {"country": "Italy"}},
                expected_output={"expected_output": "Rome"},
                metadata={"foo": "bar"}
            )
            ```
        z"Creating dataset item for dataset )r  r   r  r   r5  r6  r7  rh  N)	r_   ru   r   r  r  r   rD   rH   r>   )r   r  r   r  r   r5  r6  r7  rh  r  rx  s              r   create_dataset_itemzLangfuse.create_dataset_item  s    X	!"U|"U"UVVVX+22) /! /&; 3 	 	F V,,, 	 	 	!!$$$G	s   AA 
A5A00A5
   )	max_depthcontent_fetch_timeout_secondsobjresolve_withbase64_data_urir;  r<  c                4    t          j        | ||||          S )a  Replace media reference strings in an object with base64 data URIs.

        This method recursively traverses an object (up to max_depth) looking for media reference strings
        in the format "@@@langfuseMedia:...@@@". When found, it (synchronously) fetches the actual media content using
        the provided Langfuse client and replaces the reference string with a base64 data URI.

        If fetching media content fails for a reference string, a warning is logged and the reference
        string is left unchanged.

        Args:
            obj: The object to process. Can be a primitive value, array, or nested object.
                If the object has a __dict__ attribute, a dict will be returned instead of the original object type.
            resolve_with: The representation of the media content to replace the media reference string with.
                Currently only "base64_data_uri" is supported.
            max_depth: int: The maximum depth to traverse the object. Default is 10.
            content_fetch_timeout_seconds: int: The timeout in seconds for fetching media content. Default is 5.

        Returns:
            A deep copy of the input object with all media references replaced with base64 data URIs where possible.
            If the input object has a __dict__ attribute, a dict will be returned instead of the original object type.

        Example:
            obj = {
                "image": "@@@langfuseMedia:type=image/jpeg|id=123|source=bytes@@@",
                "nested": {
                    "pdf": "@@@langfuseMedia:type=application/pdf|id=456|source=bytes@@@"
                }
            }

            result = await LangfuseMedia.resolve_media_references(obj, langfuse_client)

            # Result:
            # {
            #     "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
            #     "nested": {
            #         "pdf": "data:application/pdf;base64,JVBERi0xLjcK..."
            #     }
            # }
        )r   r=  r>  r;  r<  )r`   resolve_media_references)r   r=  r>  r;  r<  s        r   rA  z!Langfuse.resolve_media_references  s.    ^ 5 %*G
 
 
 	
r   )r   labelcache_ttl_secondsfallbackr!  fetch_timeout_secondsrB  rp  chatrC  rD  rE  c                    d S r   r   	r   r   r   rB  rp  rC  rD  r!  rE  s	            r   
get_promptzLangfuse.get_prompt  	     3r   text)r   rB  rp  rC  rD  r!  rE  c                    d S r   r   rH  s	            r   rI  zLangfuse.get_prompt%  rJ  r   )rF  rK  c          	           j         t          d          t          d          st          d          t          j                  }	                     |dd          t          j        d	|	 d
            j         j        	                    |	          }
|
dk    rt          j        d|	 d           	  
                              S # t          $ r}|rwt          j        d|	 d|            ||pdi rgng g d}|dk    r t          t          d i |d          cY d}~S |dk    r t          t!          d i |d          cY d}~S |d}~ww xY w|
                                rt          j        d|	 d           	 t          j        d|	 d           d! fd} j         j                            |	|
|           t          j        d|	 d           |
j        S # t          $ r+}t          j        d|	 d|            |
j        cY d}~S d}~ww xY w|
j        S )"a@	  Get a prompt.

        This method attempts to fetch the requested prompt from the local cache. If the prompt is not found
        in the cache or if the cached prompt has expired, it will try to fetch the prompt from the server again
        and update the cache. If fetching the new prompt fails, and there is an expired prompt in the cache, it will
        return the expired prompt as a fallback.

        Args:
            name (str): The name of the prompt to retrieve.

        Keyword Args:
            version (Optional[int]): The version of the prompt to retrieve. If no label and version is specified, the `production` label is returned. Specify either version or label, not both.
            label: Optional[str]: The label of the prompt to retrieve. If no label and version is specified, the `production` label is returned. Specify either version or label, not both.
            cache_ttl_seconds: Optional[int]: Time-to-live in seconds for caching the prompt. Must be specified as a
            keyword argument. If not set, defaults to 60 seconds. Disables caching if set to 0.
            type: Literal["chat", "text"]: The type of the prompt to retrieve. Defaults to "text".
            fallback: Union[Optional[List[ChatMessageDict]], Optional[str]]: The prompt string to return if fetching the prompt fails. Important on the first call where no cached prompt is available. Follows Langfuse prompt formatting with double curly braces for variables. Defaults to None.
            max_retries: Optional[int]: The maximum number of retries in case of API/network errors. Defaults to 2. The maximum value is 4. Retries have an exponential backoff with a maximum delay of 10 seconds.
            fetch_timeout_seconds: Optional[int]: The timeout in milliseconds for fetching the prompt. Defaults to the default timeout set on the SDK, which is 5 seconds per default.

        Returns:
            The prompt object retrieved from the cache or directly fetched if not cached or expired of type
            - TextPromptClient, if type argument is 'text'.
            - ChatPromptClient, if type argument is 'chat'.

        Raises:
            Exception: Propagates any exceptions raised during the fetching of a new prompt, unless there is an
            expired prompt in the cache, in which case it logs a warning and returns the expired prompt.
        NzGSDK is not correctly initialized. Check the init logs for more details.z7Cannot specify both version and label at the same time.zPrompt name cannot be empty.r   rB  r      default_max_retriesmax_retries_upper_boundzGetting prompt ''r   Prompt 'z)' not found in cache or caching disabled.r   rB  ttl_secondsr!  rE  zReturning fallback prompt for 'z' due to fetch error: )r   r   rp  r   configlabelsry  rK  T)r   is_fallbackrF  zStale prompt 'z' found in cache.zRefreshing prompt 'z' in background.r   c                  >                                     d S )NrU  )_fetch_prompt_and_update_cache)bounded_max_retriesrC  rE  rB  r   r   r   s   r   refresh_taskz)Langfuse.get_prompt.<locals>.refresh_task  s<    77 '#$5$7.C 8     r   zReturning stale prompt 'z' from cache.z%Error when refreshing cached prompt 'z$', returning cached version. Error: r   r   N)rl   rH   r   r?   generate_cache_key_get_bounded_max_retriesr_   ru   prompt_cacher   r[  ru  r   re   rM   rc   rL   
is_expired"add_refresh_prompt_task_if_currentr]  )r   r   r   rB  rp  rC  rD  r!  rE  	cache_keycached_promptrx  fallback_client_argsr]  r\  s   ```` `  `     @r   rI  zLangfuse.get_prompt3  s   R ?"Y   5#4VWWW 	=;<<<24PUVVV	";;Q < 
 
 	====>>>488CC $5$:$:!O9OOO  %::# 1 3*? ;        #+^)^^[\^^  
 !%"* $#*<a"$-2":5'' "< <( v~~/#.#F#F1E#F#F(,           
 v~~/#.#F#F1E#F#F(,           
 9< ##%%  	+!"O9"O"O"OPPP+%&WI&W&W&WXXX            ,OO!   
  %GyGGG   %** + + +'nInnklnn   %*******+ ""sP   	C$ $
E..AE);E. E)!E.'E))E.A#H 
H7 H2,H72H7)r   rB  rV  rV  c          	          t          j                  }t          j        d| d           	 t	          j        t          j        t          |dz   d           dt          f fd            } |            }	|	j	        dk    rt          |	          }
nt          |	          }
 j        ! j        j                            ||
|           |
S # t          $ rE}t          j        d	| d
            j         j        j                            |           |d }~wt          $ r.}t          j        d| dt'          |                      |d }~ww xY w)NrN  zFetching prompt 'z' from server...r)  )	max_triesloggerr   c                  |    j         j                                                           d ind           S )Ntimeout_in_seconds)r   rB  r  )r   promptsr   r  )rE  rB  r   r   r   s   r   fetch_promptsz>Langfuse._fetch_prompt_and_update_cache.<locals>.fetch_prompts  s]     x'++$$T**# -8 -.C% %  , 	 	 	r   rF  rT  z0' not found during refresh, evicting from cache.zError while fetching prompt 'z': )r?   r_  r_   ru   backoffon_exceptionconstantru  r   rp  rc   re   rl   ra  setrJ   r   deleter  r   )r   r   r   rB  rV  r!  rE  rd  rm  prompt_responser   not_found_errorrx  s   ````  `      r   r[  z'Langfuse._fetch_prompt_and_update_cache  s     24PUVVV	M)MMMNNN*	! ){Qt  
3 
 
 
 
 
 
 
 
 
 
 ,mooO #v--)/::)/::*,00FKPPPM 	" 	" 	"#V9VVV   *,33I>>>!! 	 	 	!F	FFc!ffFF   G		s%   BC 
EA DE,)EEr   rO  rP  rQ  rR  c                J    ||S t          t          |d          |          }|S )Nr   )minmax)r   r!  rQ  rR  r\  s        r   r`  z!Langfuse._get_bounded_max_retries  s8     &&!Q#
 

 #"r   )rX  ry  rW  commit_messagerX  rW  rx  c                    d S r   r   r   r   r   rX  ry  rp  rW  rx  s           r   create_promptzLangfuse.create_prompt  	     3r   )rX  ry  rp  rW  rx  c                    d S r   r   rz  s           r   r{  zLangfuse.create_prompt  r|  r   c          	      V   	 t          j        d|d|           |dk    rt          |t                    st	          d          t          |t          t          |          |||pi |t          j	                  }| j
        j                            |          }	| j        | j        j                            |           t!          t          t"          |	                    S t          |t$                    st	          d	          t'          |||||pi |
          }| j
        j                            |          }	| j        | j        j                            |           t)          t          t*          |	                    S # t,          $ r}
t/          |
           |
d}
~
ww xY w)a   Create a new prompt in Langfuse.

        Keyword Args:
            name : The name of the prompt to be created.
            prompt : The content of the prompt to be created.
            is_active [DEPRECATED] : A flag indicating whether the prompt is active or not. This is deprecated and will be removed in a future release. Please use the 'production' label instead.
            labels: The labels of the prompt. Defaults to None. To create a default-served prompt, add the 'production' label.
            tags: The tags of the prompt. Defaults to None. Will be applied to all versions of the prompt.
            config: Additional structured data to be saved with the prompt. Defaults to None.
            type: The type of the prompt to be created. "chat" vs. "text". Defaults to "text".
            commit_message: Optional string describing the change.

        Returns:
            TextPromptClient: The prompt if type argument is 'text'.
            ChatPromptClient: The prompt if type argument is 'chat'.
        zCreating prompt name=z	, labels=rF  z[For 'chat' type, 'prompt' must be a list of chat messages with role and content attributes.)r   r   rX  ry  rW  rx  rp  )requestN)r   z+For 'text' type, 'prompt' must be a string.)r   r   rX  ry  rW  rx  )r_   ru   r  r  r   r@   r   r   rA   CHATr   rl  r  rl   ra  
invalidaterc   rL   r   rB   re   rM   rH   r>   )r   r   r   rX  ry  rp  rW  rx  r  server_promptrx  s              r   r{  zLangfuse.create_prompt!  s   :/	!"GT"G"Gf"G"GHHHv~~!&$// $u   ,!#C00%!%|'516    !% 0 7 7 7 H H?.O0;;DAAA'tK/O/OPPPPfc** P !NOOO-|-  G !H,33G3DDM*,77===#4]+K+KLLLL 	 	 	!!$$$G	s   C!F $B#F 
F(F##F()
new_labelsr  c                    | j         j                            |                     |          ||          }| j        | j        j                            |           |S )a3  Update an existing prompt version in Langfuse. The Langfuse SDK prompt cache is invalidated for all prompts witht he specified name.

        Args:
            name (str): The name of the prompt to update.
            version (int): The version number of the prompt to update.
            new_labels (List[str], optional): New labels to assign to the prompt version. Labels are unique across versions. The "latest" label is reserved and managed by Langfuse. Defaults to [].

        Returns:
            Prompt: The updated prompt from the Langfuse API.

        )r   r   r  )r   prompt_versionr  r  rl   ra  r  )r   r   r   r  updated_prompts        r   update_promptzLangfuse.update_prompto  sc    $ 077!!$''! 8 
 
 ?&O(33D999r   r  urlr  c                    |r,t          t          j                  t          d          k    r|S t          j                            |d          S )Nz0.28.0 )safe)r   httpx__version__urllibparsequote)r   r  r  s      r   r  zLangfuse._url_encode  sK    
  	GE$566'(:K:KKKJ
 |!!#B!///r   c                 T    | j          | j         j                                         dS dS )a   Clear the entire prompt cache, removing all cached prompts.

        This method is useful when you want to force a complete refresh of all
        cached prompts, for example after major updates or when you need to
        ensure the latest versions are fetched from the server.
        N)rl   ra  clearr  s    r   clear_prompt_cachezLangfuse.clear_prompt_cache  s1     ?&O(..00000 '&r   r^  )NN)}__name__
__module____qualname____doc__rl   r   r/   __annotations__rm   rf   r   Tracerr   r   r  Clientr8  r   r	   r   r   r   r   r   r   r   ri   r
   r   rh   r   rI   rd   r5   r   r8   r0   r9   r1   r7   r3   r2   r6   r   r   r   r   r   r   r   r   r4   r   r   r   r   r  r  r  r   r  r   r&  r   r-  r.  rF  staticmethodrK  rO  rQ  rA  rI  ra  rg   r}  r  r  r  r  r  r  r  r  r  rE   r  rK   r  rG   r  rW   r\   rV   rS   r[   rZ   r  r  rX   rY   r  r  rT   rQ   rP   r-  r0  rC   r4  rF   rD   r9  rA  ra   rc   rI  re   r[  r`  rb   r{  r  r  r  r   r   r   rk   rk      sI4        V Vp 59J01888$(E8L!((( ''''
 %)$("&"!%/3*."&*.%)!%37'+'+>BGK7;4804-A3 A3 A3 SMA3 SM	A3
 3-A3 smA3 #A3 u|,A3 A3 "$A3 3-A3 !A3 c]A3 #A3 $,C=A3  e_!A3" |$#A3$ )1c(;%A3& %X|nd.B%CD'A3( %T#s(^4)A3* ".1+A3,  --A3 A3 A3 A3F  15  $ $"&!%%)(,48#:>2637)-#! ! !  -! 	!
 &! }! ! 3-! #! 	"! !!  (1! }! #4X#67!  S#X/!  tCJ/0!!" &#!$ 
%! ! ! X!(  15#)# $"&!%%)(,    - 	
  }  3- # 	" ! 
   X  15  $ $"&!%%)(,    - 	
 ! }  3- # 	" ! 
   X  15  $ $"&!%%)(,    - 	
  }  3- # 	" ! 
   X  15  $ $"&!%%)(,    - 	
 ! }  3- # 	" ! 
   X  15  $ $"&!%%)(,       -  	 
 %  }    3-  #  	"  !  
      X   15  $ $"&!%%)(,       -  	 
 %  }    3-  #  	"  !  
      X   15  $ $"&!%%)(,48#:>2637)-#       -  	 
 %  }    3-  #  	"  !   (1  }  #4X#67   S#X/   tCJ/0! " &# $ 
%      X (  15  $ $"&!%%)(,       -  	 
 %  }    3-  #  	"  !  
      X " 1517# $"&!%%)(,48#:>2637)-#g
 g
 g
  -g
 	g

 /g
 }g
 g
 3-g
 #g
 	"g
 !g
  (1g
 }g
 #4X#67g
  S#X/g
  tCJ/0!g
" &#g
$ 
	

%g
 g
 g
 g
\  $ $"&!%%)(,48#:>2637)-!C C C "&C /	C
 }C C 3-C #C 	"C !C  (1C }C #4X#67C  S#X/C tCJ/0C  &!C" 
	

#C C C CP  15  $ $"&!%%)(,48#:>2637)-&*%: : :  -: 	:
 &: }: : 3-: #: 	": !:  (1: }: #4X#67:  S#X/:  tCJ/0!:" &#:$ d^%:& 
!!3	4': : : X:*  15#)# $"&!%%)(,&*4 4 4  -4 	4
 4 }4 4 3-4 #4 	"4 !4 d^4 
!	.4 4 4 X4  15  $ $"&!%%)(,&*5 5 5  -5 	5
 !5 }5 5 3-5 #5 	"5 !5 d^5 
!	/5 5 5 X5  15  $ $"&!%%)(,&*4 4 4  -4 	4
 4 }4 4 3-4 #4 	"4 !4 d^4 
!	.4 4 4 X4  15  $ $"&!%%)(,&*5 5 5  -5 	5
 !5 }5 5 3-5 #5 	"5 !5 d^5 
!	/5 5 5 X5  15  $ $"&!%%)(,&*9 9 9  -9 	9
 %9 }9 9 3-9 #9 	"9 !9 d^9 
!!2	39 9 9 X9  15  $ $"&!%%)(,&*9 9 9  -9 	9
 %9 }9 9 3-9 #9 	"9 !9 d^9 
!!2	39 9 9 X9  15  $ $"&!%%)(,48#:>2637)-&*%9 9 9  -9 	9
 %9 }9 9 3-9 #9 	"9 !9  (19 }9 #4X#679  S#X/9  tCJ/0!9" &#9$ d^%9& 
!!2	3'9 9 9 X9*  15  $ $"&!%%)(,&*9 9 9  -9 	9
 %9 }9 9 3-9 #9 	"9 !9 d^9 
!!2	39 9 9 X9$ 1517# $"&!%%)(,48#:>2637)-&*%_
 _
 _
  -_
 	_

 /_
 }_
 _
 3-_
 #_
 	"_
 !_
  (1_
 }_
 #4X#67_
  S#X/_
  tCJ/0!_
" &#_
$ d^%_
& 
 23-.-. 12 12 12 12	4

'_
 _
 _
 _
B' ''  
]\] ]\		
'  '  '  ' R 
 15<@&*# $"&!%%)(,48#:>2637)-'.$ .$ .$ .$ ,-	.$
 %^%89.$ /.$ d^.$ }.$ .$ 3-.$ #.$ 	".$ !.$  (1.$ }.$  #4X#67!.$"  S#X/#.$$ tCJ/0%.$& &'.$( 
).$ .$ .$ .$` 
 <@&*# $"&!%%)(,48#:>2637)-#7, 7, 7, 7, 78	7,
 d^7, }7, 7, 3-7, #7, 	"7, !7,  (17, }7, #4X#677,  S#X/7,  tCJ/0!7," &#7,$ 
%7, 7, 7, 7,r
1D(E 
 
 
 
 ## $"&!%%)(,48#:>2637)-S S S smS }	S
 S 3-S #S 	"S !S  (1S }S #4X#67S  S#X/S tCJ/0S &S  
!S S S Sp ## $"&!%%)(,D D D smD }	D
 D 3-D #D 	"D !D 
D D D DL Z	A   $ $	, , , }, 	,
 
, , , 
,\' ' ' 'H 15# $"&!%%)(,U
 U
 U
  -U
 	U

 }U
 U
 3-U
 #U
 	"U
 !U
 
U
 U
 U
 U
n==08=	= = = =:13 14 1 1 1 1
0 0 0 0 0 0
 ?C .? .? .?hsm .?s .? .? .? .?` 15 .@ .@ .@# .@# .@ .@ .@ \.@`AN,? AC A A A A
?>+> ?3 ? ? ? ?
 +# +# + + + \+ ,C ,C , , , \,  %)(,"&(,"&=A!%#'"&(,    	
 SM ! 3- ! 3- G$89: # C= 3- H% 
   X"  %)(,"&"&(,>K!%#'"&(,    	
 SM ! 3- 3- ! G$9:; # C= 3- H% 
   X, %)(,"&(,"&-1!%#'"&(,b b b b UCZ 	b
 SMb !b 3-b !b 3-b M*b #b C=b 3-b H%b 
b b b bH  3i	
 
   B  #'=A!%#'"&
 
 
 
 	

 3-
 G$89:
 #
 C=
 3-
 

 
 
 X
  #'>K!%#'"&
 
 
 
 	

 3-
 G$9:;
 #
 C=
 3-
 

 
 
 X
" #'-1!%#'"&> > > > UCZ 	>
 3-> M*> #> C=> 3-> 
> > > >@  #'=A!%#'"&
 
 
 
 	

 3-
 G$89:
 #
 C=
 3-
 

 
 
 X
  #'>K!%#'"&
 
 
 
 	

 3-
 G$9:;
 #
 C=
 3-
 

 
 
 X
" #'-1!%#'"&= = = = UCZ 	=
 3-= M*= #= C== 3-= 
= = = =~$ $ $ $.' ' ' '2 Yhsm  Y  Y  Y  YDXHSM X X X XB	 # 	  	  	  	  :> $
 $
 $
# $
(3- $
 $
 $
 $
T 02&*1 1 11  (}	1
 (#1 
1 1 1 1f".1	   : ##    sm	
 } 
   >".1	!   : #'%) /1DH57!-1/3C
 C
 C
 C
 3-	C

 c]C
 C
 C
 *+C
 &&@AC
 12C
 C
 4S>*C
 #8,C
 
C
 C
 C
 C
b .2.2x
 x
 x
 x
 	x

 c]x
 x
 x
 *+x
 &&@Ax
 12x
 x
 4S>*x
 "(+x
 
x
 x
 x
 x
H 9=.2b bb b N	b
 &&@Ab !$b b !b !)b &d38n5b "(+b 
b b b bJ (,t+ + +}+7?}+	+ + + + !% ",0#'DH -1166:<@#i
 i
 i
 /0i
 	i

 i
 i
 %SMi
 C=i
 i
 *+i
 &&@Ai
 i
 4S>*i
 +/i
 !)c 3i
  89!i
" #i
$ 
%i
 i
 i
 i
VD    D &*"&&*04$ $ $ $ c]	$
 3-$ sm$ !)$ 
$ $ $ $T  $)-"&)-/3*. = = = = }	=
 "#= 3-= "#=  (}= '= SM= 
= = = =H -.5
 5
 5
 5
 /0	5

 5
 (+5
 
5
 5
 5
 5
n 
 "&#+/48%)/3   #	
 } fo $C= 401 c]  (} 
   X 
 "&# &+/"&%)/3   #	
 } fo $C= 3- c]  (} 
   X" "&#(.+/JN%)/3H# H# H#H# #	H#
 }H# n%H# $C=H# o!67#FGH# c]H#  (}H# 
H# H# H# H#\ "&#%)7 7 77 #	7
 }7 c]7 7  (}7 
7 7 7 7z $%'(# # #c]# !	#
 "%# 
# # # #"  $( $(,
 
 
 
 U?,KKLM	

 S	
 tCy!
 wv'
 
 !
 

 
 
 X
  $(*0 $(,
 
 
 
 	

 S	
 tCy!
 wv'
 
 !
 

 
 
 X
& $(28 $(,L L L L eO-LLMNN
	L S	L tCy!L w~./L L !L 
L L L Lf !#    	
 I 
   : GL 0 0 0s 0Xd^ 0PS 0 0 0 01 1 1 1 1 1r   rk   )r  r  r   r   r9  urllib.parser  r   r   hashlibr   timer   typingr   r   r   r	   r
   r   r   r   r   r   rn  r  opentelemetryr   r   opentelemetry.sdk.tracer   r   opentelemetry.sdk.trace.exportr   $opentelemetry.sdk.trace.id_generatorr   opentelemetry.util._decoratorr   r   packaging.versionr   typing_extensionsr   langfuse._client.attributesr   r   r   langfuse._client.constantsr   r   r   r   r    r!   langfuse._client.datasetsr"   &langfuse._client.environment_variablesr#   r$   r%   r&   r'   r(   r)   r*   r+   r,   langfuse._client.propagationr-   r.   !langfuse._client.resource_managerr/   langfuse._client.spanr0   r1   r2   r3   r4   r5   r6   r7   r8   r9   langfuse._client.utilsr:   r;   langfuse._utilsr<   langfuse._utils.environmentr=   langfuse._utils.parse_errorr>   langfuse._utils.prompt_cacher?   langfuse.apir@   rA   rB   rC   rD   rE   rF   rG   rH   rI   rJ   rK   rL   rM   rN   rO   langfuse.batch_evaluationrP   rQ   rR   rS   rT   langfuse.experimentrU   rV   rW   rX   rY   rZ   r[   r\   r]   r^   langfuse.loggerr_   langfuse.mediar`   langfuse.modelra   rb   rc   rd   re   langfuse.typesrf   rg   rh   ri   rk   r   r   r   <module>r     s   
   				 				                                                 1 1 1 1 1 1 @ @ @ @ @ @ @ @ 7 7 7 7 7 7 B B B B B B        & % % % % % ( ( ( ( ( (         
                4 3 3 3 3 3                               F E E E E E                        I H H H H H H H * * * * * * ? ? ? ? ? ? = = = = = = 4 4 4 4 4 4                                   $                                     , + + + + + ( ( ( ( ( (              P O O O O O O O O O O OW81 W81 W81 W81 W81 W81 W81 W81 W81 W81r   