langfuse

Langfuse GitHub Banner

Langfuse Python SDK

MIT License CI test status PyPI Version GitHub Repo stars Discord YC W23

Installation

Important

The SDK was rewritten in v3 and released in June 2025. Refer to the v3 migration guide for instructions on updating your code.

pip install langfuse

Docs

Please see our docs for detailed information on this SDK.

 1""".. include:: ../README.md"""
 2
 3from langfuse.experiment import Evaluation
 4
 5from ._client import client as _client_module
 6from ._client.attributes import LangfuseOtelSpanAttributes
 7from ._client.constants import ObservationTypeLiteral
 8from ._client.get_client import get_client
 9from ._client.observe import observe
10from ._client.propagation import propagate_attributes
11from ._client.span import (
12    LangfuseAgent,
13    LangfuseChain,
14    LangfuseEmbedding,
15    LangfuseEvaluator,
16    LangfuseEvent,
17    LangfuseGeneration,
18    LangfuseGuardrail,
19    LangfuseRetriever,
20    LangfuseSpan,
21    LangfuseTool,
22)
23
24Langfuse = _client_module.Langfuse
25
26__all__ = [
27    "Langfuse",
28    "get_client",
29    "observe",
30    "propagate_attributes",
31    "ObservationTypeLiteral",
32    "LangfuseSpan",
33    "LangfuseGeneration",
34    "LangfuseEvent",
35    "LangfuseOtelSpanAttributes",
36    "LangfuseAgent",
37    "LangfuseTool",
38    "LangfuseChain",
39    "LangfuseEmbedding",
40    "LangfuseEvaluator",
41    "LangfuseRetriever",
42    "LangfuseGuardrail",
43    "Evaluation",
44    "experiment",
45    "api",
46]
class Langfuse:
 121class Langfuse:
 122    """Main client for Langfuse tracing and platform features.
 123
 124    This class provides an interface for creating and managing traces, spans,
 125    and generations in Langfuse as well as interacting with the Langfuse API.
 126
 127    The client features a thread-safe singleton pattern for each unique public API key,
 128    ensuring consistent trace context propagation across your application. It implements
 129    efficient batching of spans with configurable flush settings and includes background
 130    thread management for media uploads and score ingestion.
 131
 132    Configuration is flexible through either direct parameters or environment variables,
 133    with graceful fallbacks and runtime configuration updates.
 134
 135    Attributes:
 136        api: Synchronous API client for Langfuse backend communication
 137        async_api: Asynchronous API client for Langfuse backend communication
 138        _otel_tracer: Internal LangfuseTracer instance managing OpenTelemetry components
 139
 140    Parameters:
 141        public_key (Optional[str]): Your Langfuse public API key. Can also be set via LANGFUSE_PUBLIC_KEY environment variable.
 142        secret_key (Optional[str]): Your Langfuse secret API key. Can also be set via LANGFUSE_SECRET_KEY environment variable.
 143        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.
 144        host (Optional[str]): Deprecated. Use base_url instead. The Langfuse API host URL. Defaults to "https://cloud.langfuse.com".
 145        timeout (Optional[int]): Timeout in seconds for API requests. Defaults to 5 seconds.
 146        httpx_client (Optional[httpx.Client]): Custom httpx client for making non-tracing HTTP requests. If not provided, a default client will be created.
 147        debug (bool): Enable debug logging. Defaults to False. Can also be set via LANGFUSE_DEBUG environment variable.
 148        tracing_enabled (Optional[bool]): Enable or disable tracing. Defaults to True. Can also be set via LANGFUSE_TRACING_ENABLED environment variable.
 149        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.
 150        flush_interval (Optional[float]): Time in seconds between batch flushes. Defaults to 5 seconds. Can also be set via LANGFUSE_FLUSH_INTERVAL environment variable.
 151        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'.
 152        release (Optional[str]): Release version/hash of your application. Used for grouping analytics by release.
 153        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.
 154        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.
 155        mask (Optional[MaskFunction]): Function to mask sensitive data in traces before sending to the API.
 156        blocked_instrumentation_scopes (Optional[List[str]]): List of instrumentation scope names to block from being exported to Langfuse. Spans from these scopes will be filtered out before being sent to the API. Useful for filtering out spans from specific libraries or frameworks. For exported spans, you can see the instrumentation scope name in the span metadata in Langfuse (`metadata.scope.name`)
 157        additional_headers (Optional[Dict[str, str]]): Additional headers to include in all API requests and 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.
 158        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.
 159
 160    Example:
 161        ```python
 162        from langfuse.otel import Langfuse
 163
 164        # Initialize the client (reads from env vars if not provided)
 165        langfuse = Langfuse(
 166            public_key="your-public-key",
 167            secret_key="your-secret-key",
 168            host="https://cloud.langfuse.com",  # Optional, default shown
 169        )
 170
 171        # Create a trace span
 172        with langfuse.start_as_current_span(name="process-query") as span:
 173            # Your application code here
 174
 175            # Create a nested generation span for an LLM call
 176            with span.start_as_current_generation(
 177                name="generate-response",
 178                model="gpt-4",
 179                input={"query": "Tell me about AI"},
 180                model_parameters={"temperature": 0.7, "max_tokens": 500}
 181            ) as generation:
 182                # Generate response here
 183                response = "AI is a field of computer science..."
 184
 185                generation.update(
 186                    output=response,
 187                    usage_details={"prompt_tokens": 10, "completion_tokens": 50},
 188                    cost_details={"total_cost": 0.0023}
 189                )
 190
 191                # Score the generation (supports NUMERIC, BOOLEAN, CATEGORICAL)
 192                generation.score(name="relevance", value=0.95, data_type="NUMERIC")
 193        ```
 194    """
 195
 196    _resources: Optional[LangfuseResourceManager] = None
 197    _mask: Optional[MaskFunction] = None
 198    _otel_tracer: otel_trace_api.Tracer
 199
 200    def __init__(
 201        self,
 202        *,
 203        public_key: Optional[str] = None,
 204        secret_key: Optional[str] = None,
 205        base_url: Optional[str] = None,
 206        host: Optional[str] = None,
 207        timeout: Optional[int] = None,
 208        httpx_client: Optional[httpx.Client] = None,
 209        debug: bool = False,
 210        tracing_enabled: Optional[bool] = True,
 211        flush_at: Optional[int] = None,
 212        flush_interval: Optional[float] = None,
 213        environment: Optional[str] = None,
 214        release: Optional[str] = None,
 215        media_upload_thread_count: Optional[int] = None,
 216        sample_rate: Optional[float] = None,
 217        mask: Optional[MaskFunction] = None,
 218        blocked_instrumentation_scopes: Optional[List[str]] = None,
 219        additional_headers: Optional[Dict[str, str]] = None,
 220        tracer_provider: Optional[TracerProvider] = None,
 221    ):
 222        self._base_url = (
 223            base_url
 224            or os.environ.get(LANGFUSE_BASE_URL)
 225            or host
 226            or os.environ.get(LANGFUSE_HOST, "https://cloud.langfuse.com")
 227        )
 228        self._environment = environment or cast(
 229            str, os.environ.get(LANGFUSE_TRACING_ENVIRONMENT)
 230        )
 231        self._project_id: Optional[str] = None
 232        sample_rate = sample_rate or float(os.environ.get(LANGFUSE_SAMPLE_RATE, 1.0))
 233        if not 0.0 <= sample_rate <= 1.0:
 234            raise ValueError(
 235                f"Sample rate must be between 0.0 and 1.0, got {sample_rate}"
 236            )
 237
 238        timeout = timeout or int(os.environ.get(LANGFUSE_TIMEOUT, 5))
 239
 240        self._tracing_enabled = (
 241            tracing_enabled
 242            and os.environ.get(LANGFUSE_TRACING_ENABLED, "true").lower() != "false"
 243        )
 244        if not self._tracing_enabled:
 245            langfuse_logger.info(
 246                "Configuration: Langfuse tracing is explicitly disabled. No data will be sent to the Langfuse API."
 247            )
 248
 249        debug = (
 250            debug if debug else (os.getenv(LANGFUSE_DEBUG, "false").lower() == "true")
 251        )
 252        if debug:
 253            logging.basicConfig(
 254                format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
 255            )
 256            langfuse_logger.setLevel(logging.DEBUG)
 257
 258        public_key = public_key or os.environ.get(LANGFUSE_PUBLIC_KEY)
 259        if public_key is None:
 260            langfuse_logger.warning(
 261                "Authentication error: Langfuse client initialized without public_key. Client will be disabled. "
 262                "Provide a public_key parameter or set LANGFUSE_PUBLIC_KEY environment variable. "
 263            )
 264            self._otel_tracer = otel_trace_api.NoOpTracer()
 265            return
 266
 267        secret_key = secret_key or os.environ.get(LANGFUSE_SECRET_KEY)
 268        if secret_key is None:
 269            langfuse_logger.warning(
 270                "Authentication error: Langfuse client initialized without secret_key. Client will be disabled. "
 271                "Provide a secret_key parameter or set LANGFUSE_SECRET_KEY environment variable. "
 272            )
 273            self._otel_tracer = otel_trace_api.NoOpTracer()
 274            return
 275
 276        if os.environ.get("OTEL_SDK_DISABLED", "false").lower() == "true":
 277            langfuse_logger.warning(
 278                "OTEL_SDK_DISABLED is set. Langfuse tracing will be disabled and no traces will appear in the UI."
 279            )
 280
 281        # Initialize api and tracer if requirements are met
 282        self._resources = LangfuseResourceManager(
 283            public_key=public_key,
 284            secret_key=secret_key,
 285            base_url=self._base_url,
 286            timeout=timeout,
 287            environment=self._environment,
 288            release=release,
 289            flush_at=flush_at,
 290            flush_interval=flush_interval,
 291            httpx_client=httpx_client,
 292            media_upload_thread_count=media_upload_thread_count,
 293            sample_rate=sample_rate,
 294            mask=mask,
 295            tracing_enabled=self._tracing_enabled,
 296            blocked_instrumentation_scopes=blocked_instrumentation_scopes,
 297            additional_headers=additional_headers,
 298            tracer_provider=tracer_provider,
 299        )
 300        self._mask = self._resources.mask
 301
 302        self._otel_tracer = (
 303            self._resources.tracer
 304            if self._tracing_enabled and self._resources.tracer is not None
 305            else otel_trace_api.NoOpTracer()
 306        )
 307        self.api = self._resources.api
 308        self.async_api = self._resources.async_api
 309
 310    def start_span(
 311        self,
 312        *,
 313        trace_context: Optional[TraceContext] = None,
 314        name: str,
 315        input: Optional[Any] = None,
 316        output: Optional[Any] = None,
 317        metadata: Optional[Any] = None,
 318        version: Optional[str] = None,
 319        level: Optional[SpanLevel] = None,
 320        status_message: Optional[str] = None,
 321    ) -> LangfuseSpan:
 322        """Create a new span for tracing a unit of work.
 323
 324        This method creates a new span but does not set it as the current span in the
 325        context. To create and use a span within a context, use start_as_current_span().
 326
 327        The created span will be the child of the current span in the context.
 328
 329        Args:
 330            trace_context: Optional context for connecting to an existing trace
 331            name: Name of the span (e.g., function or operation name)
 332            input: Input data for the operation (can be any JSON-serializable object)
 333            output: Output data from the operation (can be any JSON-serializable object)
 334            metadata: Additional metadata to associate with the span
 335            version: Version identifier for the code or component
 336            level: Importance level of the span (info, warning, error)
 337            status_message: Optional status message for the span
 338
 339        Returns:
 340            A LangfuseSpan object that must be ended with .end() when the operation completes
 341
 342        Example:
 343            ```python
 344            span = langfuse.start_span(name="process-data")
 345            try:
 346                # Do work
 347                span.update(output="result")
 348            finally:
 349                span.end()
 350            ```
 351        """
 352        return self.start_observation(
 353            trace_context=trace_context,
 354            name=name,
 355            as_type="span",
 356            input=input,
 357            output=output,
 358            metadata=metadata,
 359            version=version,
 360            level=level,
 361            status_message=status_message,
 362        )
 363
 364    def start_as_current_span(
 365        self,
 366        *,
 367        trace_context: Optional[TraceContext] = None,
 368        name: str,
 369        input: Optional[Any] = None,
 370        output: Optional[Any] = None,
 371        metadata: Optional[Any] = None,
 372        version: Optional[str] = None,
 373        level: Optional[SpanLevel] = None,
 374        status_message: Optional[str] = None,
 375        end_on_exit: Optional[bool] = None,
 376    ) -> _AgnosticContextManager[LangfuseSpan]:
 377        """Create a new span and set it as the current span in a context manager.
 378
 379        This method creates a new span and sets it as the current span within a context
 380        manager. Use this method with a 'with' statement to automatically handle span
 381        lifecycle within a code block.
 382
 383        The created span will be the child of the current span in the context.
 384
 385        Args:
 386            trace_context: Optional context for connecting to an existing trace
 387            name: Name of the span (e.g., function or operation name)
 388            input: Input data for the operation (can be any JSON-serializable object)
 389            output: Output data from the operation (can be any JSON-serializable object)
 390            metadata: Additional metadata to associate with the span
 391            version: Version identifier for the code or component
 392            level: Importance level of the span (info, warning, error)
 393            status_message: Optional status message for the span
 394            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.
 395
 396        Returns:
 397            A context manager that yields a LangfuseSpan
 398
 399        Example:
 400            ```python
 401            with langfuse.start_as_current_span(name="process-query") as span:
 402                # Do work
 403                result = process_data()
 404                span.update(output=result)
 405
 406                # Create a child span automatically
 407                with span.start_as_current_span(name="sub-operation") as child_span:
 408                    # Do sub-operation work
 409                    child_span.update(output="sub-result")
 410            ```
 411        """
 412        return self.start_as_current_observation(
 413            trace_context=trace_context,
 414            name=name,
 415            as_type="span",
 416            input=input,
 417            output=output,
 418            metadata=metadata,
 419            version=version,
 420            level=level,
 421            status_message=status_message,
 422            end_on_exit=end_on_exit,
 423        )
 424
 425    @overload
 426    def start_observation(
 427        self,
 428        *,
 429        trace_context: Optional[TraceContext] = None,
 430        name: str,
 431        as_type: Literal["generation"],
 432        input: Optional[Any] = None,
 433        output: Optional[Any] = None,
 434        metadata: Optional[Any] = None,
 435        version: Optional[str] = None,
 436        level: Optional[SpanLevel] = None,
 437        status_message: Optional[str] = None,
 438        completion_start_time: Optional[datetime] = None,
 439        model: Optional[str] = None,
 440        model_parameters: Optional[Dict[str, MapValue]] = None,
 441        usage_details: Optional[Dict[str, int]] = None,
 442        cost_details: Optional[Dict[str, float]] = None,
 443        prompt: Optional[PromptClient] = None,
 444    ) -> LangfuseGeneration: ...
 445
 446    @overload
 447    def start_observation(
 448        self,
 449        *,
 450        trace_context: Optional[TraceContext] = None,
 451        name: str,
 452        as_type: Literal["span"] = "span",
 453        input: Optional[Any] = None,
 454        output: Optional[Any] = None,
 455        metadata: Optional[Any] = None,
 456        version: Optional[str] = None,
 457        level: Optional[SpanLevel] = None,
 458        status_message: Optional[str] = None,
 459    ) -> LangfuseSpan: ...
 460
 461    @overload
 462    def start_observation(
 463        self,
 464        *,
 465        trace_context: Optional[TraceContext] = None,
 466        name: str,
 467        as_type: Literal["agent"],
 468        input: Optional[Any] = None,
 469        output: Optional[Any] = None,
 470        metadata: Optional[Any] = None,
 471        version: Optional[str] = None,
 472        level: Optional[SpanLevel] = None,
 473        status_message: Optional[str] = None,
 474    ) -> LangfuseAgent: ...
 475
 476    @overload
 477    def start_observation(
 478        self,
 479        *,
 480        trace_context: Optional[TraceContext] = None,
 481        name: str,
 482        as_type: Literal["tool"],
 483        input: Optional[Any] = None,
 484        output: Optional[Any] = None,
 485        metadata: Optional[Any] = None,
 486        version: Optional[str] = None,
 487        level: Optional[SpanLevel] = None,
 488        status_message: Optional[str] = None,
 489    ) -> LangfuseTool: ...
 490
 491    @overload
 492    def start_observation(
 493        self,
 494        *,
 495        trace_context: Optional[TraceContext] = None,
 496        name: str,
 497        as_type: Literal["chain"],
 498        input: Optional[Any] = None,
 499        output: Optional[Any] = None,
 500        metadata: Optional[Any] = None,
 501        version: Optional[str] = None,
 502        level: Optional[SpanLevel] = None,
 503        status_message: Optional[str] = None,
 504    ) -> LangfuseChain: ...
 505
 506    @overload
 507    def start_observation(
 508        self,
 509        *,
 510        trace_context: Optional[TraceContext] = None,
 511        name: str,
 512        as_type: Literal["retriever"],
 513        input: Optional[Any] = None,
 514        output: Optional[Any] = None,
 515        metadata: Optional[Any] = None,
 516        version: Optional[str] = None,
 517        level: Optional[SpanLevel] = None,
 518        status_message: Optional[str] = None,
 519    ) -> LangfuseRetriever: ...
 520
 521    @overload
 522    def start_observation(
 523        self,
 524        *,
 525        trace_context: Optional[TraceContext] = None,
 526        name: str,
 527        as_type: Literal["evaluator"],
 528        input: Optional[Any] = None,
 529        output: Optional[Any] = None,
 530        metadata: Optional[Any] = None,
 531        version: Optional[str] = None,
 532        level: Optional[SpanLevel] = None,
 533        status_message: Optional[str] = None,
 534    ) -> LangfuseEvaluator: ...
 535
 536    @overload
 537    def start_observation(
 538        self,
 539        *,
 540        trace_context: Optional[TraceContext] = None,
 541        name: str,
 542        as_type: Literal["embedding"],
 543        input: Optional[Any] = None,
 544        output: Optional[Any] = None,
 545        metadata: Optional[Any] = None,
 546        version: Optional[str] = None,
 547        level: Optional[SpanLevel] = None,
 548        status_message: Optional[str] = None,
 549        completion_start_time: Optional[datetime] = None,
 550        model: Optional[str] = None,
 551        model_parameters: Optional[Dict[str, MapValue]] = None,
 552        usage_details: Optional[Dict[str, int]] = None,
 553        cost_details: Optional[Dict[str, float]] = None,
 554        prompt: Optional[PromptClient] = None,
 555    ) -> LangfuseEmbedding: ...
 556
 557    @overload
 558    def start_observation(
 559        self,
 560        *,
 561        trace_context: Optional[TraceContext] = None,
 562        name: str,
 563        as_type: Literal["guardrail"],
 564        input: Optional[Any] = None,
 565        output: Optional[Any] = None,
 566        metadata: Optional[Any] = None,
 567        version: Optional[str] = None,
 568        level: Optional[SpanLevel] = None,
 569        status_message: Optional[str] = None,
 570    ) -> LangfuseGuardrail: ...
 571
 572    def start_observation(
 573        self,
 574        *,
 575        trace_context: Optional[TraceContext] = None,
 576        name: str,
 577        as_type: ObservationTypeLiteralNoEvent = "span",
 578        input: Optional[Any] = None,
 579        output: Optional[Any] = None,
 580        metadata: Optional[Any] = None,
 581        version: Optional[str] = None,
 582        level: Optional[SpanLevel] = None,
 583        status_message: Optional[str] = None,
 584        completion_start_time: Optional[datetime] = None,
 585        model: Optional[str] = None,
 586        model_parameters: Optional[Dict[str, MapValue]] = None,
 587        usage_details: Optional[Dict[str, int]] = None,
 588        cost_details: Optional[Dict[str, float]] = None,
 589        prompt: Optional[PromptClient] = None,
 590    ) -> Union[
 591        LangfuseSpan,
 592        LangfuseGeneration,
 593        LangfuseAgent,
 594        LangfuseTool,
 595        LangfuseChain,
 596        LangfuseRetriever,
 597        LangfuseEvaluator,
 598        LangfuseEmbedding,
 599        LangfuseGuardrail,
 600    ]:
 601        """Create a new observation of the specified type.
 602
 603        This method creates a new observation but does not set it as the current span in the
 604        context. To create and use an observation within a context, use start_as_current_observation().
 605
 606        Args:
 607            trace_context: Optional context for connecting to an existing trace
 608            name: Name of the observation
 609            as_type: Type of observation to create (defaults to "span")
 610            input: Input data for the operation
 611            output: Output data from the operation
 612            metadata: Additional metadata to associate with the observation
 613            version: Version identifier for the code or component
 614            level: Importance level of the observation
 615            status_message: Optional status message for the observation
 616            completion_start_time: When the model started generating (for generation types)
 617            model: Name/identifier of the AI model used (for generation types)
 618            model_parameters: Parameters used for the model (for generation types)
 619            usage_details: Token usage information (for generation types)
 620            cost_details: Cost information (for generation types)
 621            prompt: Associated prompt template (for generation types)
 622
 623        Returns:
 624            An observation object of the appropriate type that must be ended with .end()
 625        """
 626        if trace_context:
 627            trace_id = trace_context.get("trace_id", None)
 628            parent_span_id = trace_context.get("parent_span_id", None)
 629
 630            if trace_id:
 631                remote_parent_span = self._create_remote_parent_span(
 632                    trace_id=trace_id, parent_span_id=parent_span_id
 633                )
 634
 635                with otel_trace_api.use_span(
 636                    cast(otel_trace_api.Span, remote_parent_span)
 637                ):
 638                    otel_span = self._otel_tracer.start_span(name=name)
 639                    otel_span.set_attribute(LangfuseOtelSpanAttributes.AS_ROOT, True)
 640
 641                    return self._create_observation_from_otel_span(
 642                        otel_span=otel_span,
 643                        as_type=as_type,
 644                        input=input,
 645                        output=output,
 646                        metadata=metadata,
 647                        version=version,
 648                        level=level,
 649                        status_message=status_message,
 650                        completion_start_time=completion_start_time,
 651                        model=model,
 652                        model_parameters=model_parameters,
 653                        usage_details=usage_details,
 654                        cost_details=cost_details,
 655                        prompt=prompt,
 656                    )
 657
 658        otel_span = self._otel_tracer.start_span(name=name)
 659
 660        return self._create_observation_from_otel_span(
 661            otel_span=otel_span,
 662            as_type=as_type,
 663            input=input,
 664            output=output,
 665            metadata=metadata,
 666            version=version,
 667            level=level,
 668            status_message=status_message,
 669            completion_start_time=completion_start_time,
 670            model=model,
 671            model_parameters=model_parameters,
 672            usage_details=usage_details,
 673            cost_details=cost_details,
 674            prompt=prompt,
 675        )
 676
 677    def _create_observation_from_otel_span(
 678        self,
 679        *,
 680        otel_span: otel_trace_api.Span,
 681        as_type: ObservationTypeLiteralNoEvent,
 682        input: Optional[Any] = None,
 683        output: Optional[Any] = None,
 684        metadata: Optional[Any] = None,
 685        version: Optional[str] = None,
 686        level: Optional[SpanLevel] = None,
 687        status_message: Optional[str] = None,
 688        completion_start_time: Optional[datetime] = None,
 689        model: Optional[str] = None,
 690        model_parameters: Optional[Dict[str, MapValue]] = None,
 691        usage_details: Optional[Dict[str, int]] = None,
 692        cost_details: Optional[Dict[str, float]] = None,
 693        prompt: Optional[PromptClient] = None,
 694    ) -> Union[
 695        LangfuseSpan,
 696        LangfuseGeneration,
 697        LangfuseAgent,
 698        LangfuseTool,
 699        LangfuseChain,
 700        LangfuseRetriever,
 701        LangfuseEvaluator,
 702        LangfuseEmbedding,
 703        LangfuseGuardrail,
 704    ]:
 705        """Create the appropriate observation type from an OTEL span."""
 706        if as_type in get_observation_types_list(ObservationTypeGenerationLike):
 707            observation_class = self._get_span_class(as_type)
 708            # Type ignore to prevent overloads of internal _get_span_class function,
 709            # issue is that LangfuseEvent could be returned and that classes have diff. args
 710            return observation_class(  # type: ignore[return-value,call-arg]
 711                otel_span=otel_span,
 712                langfuse_client=self,
 713                environment=self._environment,
 714                input=input,
 715                output=output,
 716                metadata=metadata,
 717                version=version,
 718                level=level,
 719                status_message=status_message,
 720                completion_start_time=completion_start_time,
 721                model=model,
 722                model_parameters=model_parameters,
 723                usage_details=usage_details,
 724                cost_details=cost_details,
 725                prompt=prompt,
 726            )
 727        else:
 728            # For other types (e.g. span, guardrail), create appropriate class without generation properties
 729            observation_class = self._get_span_class(as_type)
 730            # Type ignore to prevent overloads of internal _get_span_class function,
 731            # issue is that LangfuseEvent could be returned and that classes have diff. args
 732            return observation_class(  # type: ignore[return-value,call-arg]
 733                otel_span=otel_span,
 734                langfuse_client=self,
 735                environment=self._environment,
 736                input=input,
 737                output=output,
 738                metadata=metadata,
 739                version=version,
 740                level=level,
 741                status_message=status_message,
 742            )
 743            # span._observation_type = as_type
 744            # span._otel_span.set_attribute("langfuse.observation.type", as_type)
 745            # return span
 746
 747    def start_generation(
 748        self,
 749        *,
 750        trace_context: Optional[TraceContext] = None,
 751        name: str,
 752        input: Optional[Any] = None,
 753        output: Optional[Any] = None,
 754        metadata: Optional[Any] = None,
 755        version: Optional[str] = None,
 756        level: Optional[SpanLevel] = None,
 757        status_message: Optional[str] = None,
 758        completion_start_time: Optional[datetime] = None,
 759        model: Optional[str] = None,
 760        model_parameters: Optional[Dict[str, MapValue]] = None,
 761        usage_details: Optional[Dict[str, int]] = None,
 762        cost_details: Optional[Dict[str, float]] = None,
 763        prompt: Optional[PromptClient] = None,
 764    ) -> LangfuseGeneration:
 765        """Create a new generation span for model generations.
 766
 767        DEPRECATED: This method is deprecated and will be removed in a future version.
 768        Use start_observation(as_type='generation') instead.
 769
 770        This method creates a specialized span for tracking model generations.
 771        It includes additional fields specific to model generations such as model name,
 772        token usage, and cost details.
 773
 774        The created generation span will be the child of the current span in the context.
 775
 776        Args:
 777            trace_context: Optional context for connecting to an existing trace
 778            name: Name of the generation operation
 779            input: Input data for the model (e.g., prompts)
 780            output: Output from the model (e.g., completions)
 781            metadata: Additional metadata to associate with the generation
 782            version: Version identifier for the model or component
 783            level: Importance level of the generation (info, warning, error)
 784            status_message: Optional status message for the generation
 785            completion_start_time: When the model started generating the response
 786            model: Name/identifier of the AI model used (e.g., "gpt-4")
 787            model_parameters: Parameters used for the model (e.g., temperature, max_tokens)
 788            usage_details: Token usage information (e.g., prompt_tokens, completion_tokens)
 789            cost_details: Cost information for the model call
 790            prompt: Associated prompt template from Langfuse prompt management
 791
 792        Returns:
 793            A LangfuseGeneration object that must be ended with .end() when complete
 794
 795        Example:
 796            ```python
 797            generation = langfuse.start_generation(
 798                name="answer-generation",
 799                model="gpt-4",
 800                input={"prompt": "Explain quantum computing"},
 801                model_parameters={"temperature": 0.7}
 802            )
 803            try:
 804                # Call model API
 805                response = llm.generate(...)
 806
 807                generation.update(
 808                    output=response.text,
 809                    usage_details={
 810                        "prompt_tokens": response.usage.prompt_tokens,
 811                        "completion_tokens": response.usage.completion_tokens
 812                    }
 813                )
 814            finally:
 815                generation.end()
 816            ```
 817        """
 818        warnings.warn(
 819            "start_generation is deprecated and will be removed in a future version. "
 820            "Use start_observation(as_type='generation') instead.",
 821            DeprecationWarning,
 822            stacklevel=2,
 823        )
 824        return self.start_observation(
 825            trace_context=trace_context,
 826            name=name,
 827            as_type="generation",
 828            input=input,
 829            output=output,
 830            metadata=metadata,
 831            version=version,
 832            level=level,
 833            status_message=status_message,
 834            completion_start_time=completion_start_time,
 835            model=model,
 836            model_parameters=model_parameters,
 837            usage_details=usage_details,
 838            cost_details=cost_details,
 839            prompt=prompt,
 840        )
 841
 842    def start_as_current_generation(
 843        self,
 844        *,
 845        trace_context: Optional[TraceContext] = None,
 846        name: str,
 847        input: Optional[Any] = None,
 848        output: Optional[Any] = None,
 849        metadata: Optional[Any] = None,
 850        version: Optional[str] = None,
 851        level: Optional[SpanLevel] = None,
 852        status_message: Optional[str] = None,
 853        completion_start_time: Optional[datetime] = None,
 854        model: Optional[str] = None,
 855        model_parameters: Optional[Dict[str, MapValue]] = None,
 856        usage_details: Optional[Dict[str, int]] = None,
 857        cost_details: Optional[Dict[str, float]] = None,
 858        prompt: Optional[PromptClient] = None,
 859        end_on_exit: Optional[bool] = None,
 860    ) -> _AgnosticContextManager[LangfuseGeneration]:
 861        """Create a new generation span and set it as the current span in a context manager.
 862
 863        DEPRECATED: This method is deprecated and will be removed in a future version.
 864        Use start_as_current_observation(as_type='generation') instead.
 865
 866        This method creates a specialized span for model generations and sets it as the
 867        current span within a context manager. Use this method with a 'with' statement to
 868        automatically handle the generation span lifecycle within a code block.
 869
 870        The created generation span will be the child of the current span in the context.
 871
 872        Args:
 873            trace_context: Optional context for connecting to an existing trace
 874            name: Name of the generation operation
 875            input: Input data for the model (e.g., prompts)
 876            output: Output from the model (e.g., completions)
 877            metadata: Additional metadata to associate with the generation
 878            version: Version identifier for the model or component
 879            level: Importance level of the generation (info, warning, error)
 880            status_message: Optional status message for the generation
 881            completion_start_time: When the model started generating the response
 882            model: Name/identifier of the AI model used (e.g., "gpt-4")
 883            model_parameters: Parameters used for the model (e.g., temperature, max_tokens)
 884            usage_details: Token usage information (e.g., prompt_tokens, completion_tokens)
 885            cost_details: Cost information for the model call
 886            prompt: Associated prompt template from Langfuse prompt management
 887            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.
 888
 889        Returns:
 890            A context manager that yields a LangfuseGeneration
 891
 892        Example:
 893            ```python
 894            with langfuse.start_as_current_generation(
 895                name="answer-generation",
 896                model="gpt-4",
 897                input={"prompt": "Explain quantum computing"}
 898            ) as generation:
 899                # Call model API
 900                response = llm.generate(...)
 901
 902                # Update with results
 903                generation.update(
 904                    output=response.text,
 905                    usage_details={
 906                        "prompt_tokens": response.usage.prompt_tokens,
 907                        "completion_tokens": response.usage.completion_tokens
 908                    }
 909                )
 910            ```
 911        """
 912        warnings.warn(
 913            "start_as_current_generation is deprecated and will be removed in a future version. "
 914            "Use start_as_current_observation(as_type='generation') instead.",
 915            DeprecationWarning,
 916            stacklevel=2,
 917        )
 918        return self.start_as_current_observation(
 919            trace_context=trace_context,
 920            name=name,
 921            as_type="generation",
 922            input=input,
 923            output=output,
 924            metadata=metadata,
 925            version=version,
 926            level=level,
 927            status_message=status_message,
 928            completion_start_time=completion_start_time,
 929            model=model,
 930            model_parameters=model_parameters,
 931            usage_details=usage_details,
 932            cost_details=cost_details,
 933            prompt=prompt,
 934            end_on_exit=end_on_exit,
 935        )
 936
 937    @overload
 938    def start_as_current_observation(
 939        self,
 940        *,
 941        trace_context: Optional[TraceContext] = None,
 942        name: str,
 943        as_type: Literal["generation"],
 944        input: Optional[Any] = None,
 945        output: Optional[Any] = None,
 946        metadata: Optional[Any] = None,
 947        version: Optional[str] = None,
 948        level: Optional[SpanLevel] = None,
 949        status_message: Optional[str] = None,
 950        completion_start_time: Optional[datetime] = None,
 951        model: Optional[str] = None,
 952        model_parameters: Optional[Dict[str, MapValue]] = None,
 953        usage_details: Optional[Dict[str, int]] = None,
 954        cost_details: Optional[Dict[str, float]] = None,
 955        prompt: Optional[PromptClient] = None,
 956        end_on_exit: Optional[bool] = None,
 957    ) -> _AgnosticContextManager[LangfuseGeneration]: ...
 958
 959    @overload
 960    def start_as_current_observation(
 961        self,
 962        *,
 963        trace_context: Optional[TraceContext] = None,
 964        name: str,
 965        as_type: Literal["span"] = "span",
 966        input: Optional[Any] = None,
 967        output: Optional[Any] = None,
 968        metadata: Optional[Any] = None,
 969        version: Optional[str] = None,
 970        level: Optional[SpanLevel] = None,
 971        status_message: Optional[str] = None,
 972        end_on_exit: Optional[bool] = None,
 973    ) -> _AgnosticContextManager[LangfuseSpan]: ...
 974
 975    @overload
 976    def start_as_current_observation(
 977        self,
 978        *,
 979        trace_context: Optional[TraceContext] = None,
 980        name: str,
 981        as_type: Literal["agent"],
 982        input: Optional[Any] = None,
 983        output: Optional[Any] = None,
 984        metadata: Optional[Any] = None,
 985        version: Optional[str] = None,
 986        level: Optional[SpanLevel] = None,
 987        status_message: Optional[str] = None,
 988        end_on_exit: Optional[bool] = None,
 989    ) -> _AgnosticContextManager[LangfuseAgent]: ...
 990
 991    @overload
 992    def start_as_current_observation(
 993        self,
 994        *,
 995        trace_context: Optional[TraceContext] = None,
 996        name: str,
 997        as_type: Literal["tool"],
 998        input: Optional[Any] = None,
 999        output: Optional[Any] = None,
1000        metadata: Optional[Any] = None,
1001        version: Optional[str] = None,
1002        level: Optional[SpanLevel] = None,
1003        status_message: Optional[str] = None,
1004        end_on_exit: Optional[bool] = None,
1005    ) -> _AgnosticContextManager[LangfuseTool]: ...
1006
1007    @overload
1008    def start_as_current_observation(
1009        self,
1010        *,
1011        trace_context: Optional[TraceContext] = None,
1012        name: str,
1013        as_type: Literal["chain"],
1014        input: Optional[Any] = None,
1015        output: Optional[Any] = None,
1016        metadata: Optional[Any] = None,
1017        version: Optional[str] = None,
1018        level: Optional[SpanLevel] = None,
1019        status_message: Optional[str] = None,
1020        end_on_exit: Optional[bool] = None,
1021    ) -> _AgnosticContextManager[LangfuseChain]: ...
1022
1023    @overload
1024    def start_as_current_observation(
1025        self,
1026        *,
1027        trace_context: Optional[TraceContext] = None,
1028        name: str,
1029        as_type: Literal["retriever"],
1030        input: Optional[Any] = None,
1031        output: Optional[Any] = None,
1032        metadata: Optional[Any] = None,
1033        version: Optional[str] = None,
1034        level: Optional[SpanLevel] = None,
1035        status_message: Optional[str] = None,
1036        end_on_exit: Optional[bool] = None,
1037    ) -> _AgnosticContextManager[LangfuseRetriever]: ...
1038
1039    @overload
1040    def start_as_current_observation(
1041        self,
1042        *,
1043        trace_context: Optional[TraceContext] = None,
1044        name: str,
1045        as_type: Literal["evaluator"],
1046        input: Optional[Any] = None,
1047        output: Optional[Any] = None,
1048        metadata: Optional[Any] = None,
1049        version: Optional[str] = None,
1050        level: Optional[SpanLevel] = None,
1051        status_message: Optional[str] = None,
1052        end_on_exit: Optional[bool] = None,
1053    ) -> _AgnosticContextManager[LangfuseEvaluator]: ...
1054
1055    @overload
1056    def start_as_current_observation(
1057        self,
1058        *,
1059        trace_context: Optional[TraceContext] = None,
1060        name: str,
1061        as_type: Literal["embedding"],
1062        input: Optional[Any] = None,
1063        output: Optional[Any] = None,
1064        metadata: Optional[Any] = None,
1065        version: Optional[str] = None,
1066        level: Optional[SpanLevel] = None,
1067        status_message: Optional[str] = None,
1068        completion_start_time: Optional[datetime] = None,
1069        model: Optional[str] = None,
1070        model_parameters: Optional[Dict[str, MapValue]] = None,
1071        usage_details: Optional[Dict[str, int]] = None,
1072        cost_details: Optional[Dict[str, float]] = None,
1073        prompt: Optional[PromptClient] = None,
1074        end_on_exit: Optional[bool] = None,
1075    ) -> _AgnosticContextManager[LangfuseEmbedding]: ...
1076
1077    @overload
1078    def start_as_current_observation(
1079        self,
1080        *,
1081        trace_context: Optional[TraceContext] = None,
1082        name: str,
1083        as_type: Literal["guardrail"],
1084        input: Optional[Any] = None,
1085        output: Optional[Any] = None,
1086        metadata: Optional[Any] = None,
1087        version: Optional[str] = None,
1088        level: Optional[SpanLevel] = None,
1089        status_message: Optional[str] = None,
1090        end_on_exit: Optional[bool] = None,
1091    ) -> _AgnosticContextManager[LangfuseGuardrail]: ...
1092
1093    def start_as_current_observation(
1094        self,
1095        *,
1096        trace_context: Optional[TraceContext] = None,
1097        name: str,
1098        as_type: ObservationTypeLiteralNoEvent = "span",
1099        input: Optional[Any] = None,
1100        output: Optional[Any] = None,
1101        metadata: Optional[Any] = None,
1102        version: Optional[str] = None,
1103        level: Optional[SpanLevel] = None,
1104        status_message: Optional[str] = None,
1105        completion_start_time: Optional[datetime] = None,
1106        model: Optional[str] = None,
1107        model_parameters: Optional[Dict[str, MapValue]] = None,
1108        usage_details: Optional[Dict[str, int]] = None,
1109        cost_details: Optional[Dict[str, float]] = None,
1110        prompt: Optional[PromptClient] = None,
1111        end_on_exit: Optional[bool] = None,
1112    ) -> Union[
1113        _AgnosticContextManager[LangfuseGeneration],
1114        _AgnosticContextManager[LangfuseSpan],
1115        _AgnosticContextManager[LangfuseAgent],
1116        _AgnosticContextManager[LangfuseTool],
1117        _AgnosticContextManager[LangfuseChain],
1118        _AgnosticContextManager[LangfuseRetriever],
1119        _AgnosticContextManager[LangfuseEvaluator],
1120        _AgnosticContextManager[LangfuseEmbedding],
1121        _AgnosticContextManager[LangfuseGuardrail],
1122    ]:
1123        """Create a new observation and set it as the current span in a context manager.
1124
1125        This method creates a new observation of the specified type and sets it as the
1126        current span within a context manager. Use this method with a 'with' statement to
1127        automatically handle the observation lifecycle within a code block.
1128
1129        The created observation will be the child of the current span in the context.
1130
1131        Args:
1132            trace_context: Optional context for connecting to an existing trace
1133            name: Name of the observation (e.g., function or operation name)
1134            as_type: Type of observation to create (defaults to "span")
1135            input: Input data for the operation (can be any JSON-serializable object)
1136            output: Output data from the operation (can be any JSON-serializable object)
1137            metadata: Additional metadata to associate with the observation
1138            version: Version identifier for the code or component
1139            level: Importance level of the observation (info, warning, error)
1140            status_message: Optional status message for the observation
1141            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.
1142
1143            The following parameters are available when as_type is: "generation" or "embedding".
1144            completion_start_time: When the model started generating the response
1145            model: Name/identifier of the AI model used (e.g., "gpt-4")
1146            model_parameters: Parameters used for the model (e.g., temperature, max_tokens)
1147            usage_details: Token usage information (e.g., prompt_tokens, completion_tokens)
1148            cost_details: Cost information for the model call
1149            prompt: Associated prompt template from Langfuse prompt management
1150
1151        Returns:
1152            A context manager that yields the appropriate observation type based on as_type
1153
1154        Example:
1155            ```python
1156            # Create a span
1157            with langfuse.start_as_current_observation(name="process-query", as_type="span") as span:
1158                # Do work
1159                result = process_data()
1160                span.update(output=result)
1161
1162                # Create a child span automatically
1163                with span.start_as_current_span(name="sub-operation") as child_span:
1164                    # Do sub-operation work
1165                    child_span.update(output="sub-result")
1166
1167            # Create a tool observation
1168            with langfuse.start_as_current_observation(name="web-search", as_type="tool") as tool:
1169                # Do tool work
1170                results = search_web(query)
1171                tool.update(output=results)
1172
1173            # Create a generation observation
1174            with langfuse.start_as_current_observation(
1175                name="answer-generation",
1176                as_type="generation",
1177                model="gpt-4"
1178            ) as generation:
1179                # Generate answer
1180                response = llm.generate(...)
1181                generation.update(output=response)
1182            ```
1183        """
1184        if as_type in get_observation_types_list(ObservationTypeGenerationLike):
1185            if trace_context:
1186                trace_id = trace_context.get("trace_id", None)
1187                parent_span_id = trace_context.get("parent_span_id", None)
1188
1189                if trace_id:
1190                    remote_parent_span = self._create_remote_parent_span(
1191                        trace_id=trace_id, parent_span_id=parent_span_id
1192                    )
1193
1194                    return cast(
1195                        Union[
1196                            _AgnosticContextManager[LangfuseGeneration],
1197                            _AgnosticContextManager[LangfuseEmbedding],
1198                        ],
1199                        self._create_span_with_parent_context(
1200                            as_type=as_type,
1201                            name=name,
1202                            remote_parent_span=remote_parent_span,
1203                            parent=None,
1204                            end_on_exit=end_on_exit,
1205                            input=input,
1206                            output=output,
1207                            metadata=metadata,
1208                            version=version,
1209                            level=level,
1210                            status_message=status_message,
1211                            completion_start_time=completion_start_time,
1212                            model=model,
1213                            model_parameters=model_parameters,
1214                            usage_details=usage_details,
1215                            cost_details=cost_details,
1216                            prompt=prompt,
1217                        ),
1218                    )
1219
1220            return cast(
1221                Union[
1222                    _AgnosticContextManager[LangfuseGeneration],
1223                    _AgnosticContextManager[LangfuseEmbedding],
1224                ],
1225                self._start_as_current_otel_span_with_processed_media(
1226                    as_type=as_type,
1227                    name=name,
1228                    end_on_exit=end_on_exit,
1229                    input=input,
1230                    output=output,
1231                    metadata=metadata,
1232                    version=version,
1233                    level=level,
1234                    status_message=status_message,
1235                    completion_start_time=completion_start_time,
1236                    model=model,
1237                    model_parameters=model_parameters,
1238                    usage_details=usage_details,
1239                    cost_details=cost_details,
1240                    prompt=prompt,
1241                ),
1242            )
1243
1244        if as_type in get_observation_types_list(ObservationTypeSpanLike):
1245            if trace_context:
1246                trace_id = trace_context.get("trace_id", None)
1247                parent_span_id = trace_context.get("parent_span_id", None)
1248
1249                if trace_id:
1250                    remote_parent_span = self._create_remote_parent_span(
1251                        trace_id=trace_id, parent_span_id=parent_span_id
1252                    )
1253
1254                    return cast(
1255                        Union[
1256                            _AgnosticContextManager[LangfuseSpan],
1257                            _AgnosticContextManager[LangfuseAgent],
1258                            _AgnosticContextManager[LangfuseTool],
1259                            _AgnosticContextManager[LangfuseChain],
1260                            _AgnosticContextManager[LangfuseRetriever],
1261                            _AgnosticContextManager[LangfuseEvaluator],
1262                            _AgnosticContextManager[LangfuseGuardrail],
1263                        ],
1264                        self._create_span_with_parent_context(
1265                            as_type=as_type,
1266                            name=name,
1267                            remote_parent_span=remote_parent_span,
1268                            parent=None,
1269                            end_on_exit=end_on_exit,
1270                            input=input,
1271                            output=output,
1272                            metadata=metadata,
1273                            version=version,
1274                            level=level,
1275                            status_message=status_message,
1276                        ),
1277                    )
1278
1279            return cast(
1280                Union[
1281                    _AgnosticContextManager[LangfuseSpan],
1282                    _AgnosticContextManager[LangfuseAgent],
1283                    _AgnosticContextManager[LangfuseTool],
1284                    _AgnosticContextManager[LangfuseChain],
1285                    _AgnosticContextManager[LangfuseRetriever],
1286                    _AgnosticContextManager[LangfuseEvaluator],
1287                    _AgnosticContextManager[LangfuseGuardrail],
1288                ],
1289                self._start_as_current_otel_span_with_processed_media(
1290                    as_type=as_type,
1291                    name=name,
1292                    end_on_exit=end_on_exit,
1293                    input=input,
1294                    output=output,
1295                    metadata=metadata,
1296                    version=version,
1297                    level=level,
1298                    status_message=status_message,
1299                ),
1300            )
1301
1302        # This should never be reached since all valid types are handled above
1303        langfuse_logger.warning(
1304            f"Unknown observation type: {as_type}, falling back to span"
1305        )
1306        return self._start_as_current_otel_span_with_processed_media(
1307            as_type="span",
1308            name=name,
1309            end_on_exit=end_on_exit,
1310            input=input,
1311            output=output,
1312            metadata=metadata,
1313            version=version,
1314            level=level,
1315            status_message=status_message,
1316        )
1317
1318    def _get_span_class(
1319        self,
1320        as_type: ObservationTypeLiteral,
1321    ) -> Union[
1322        Type[LangfuseAgent],
1323        Type[LangfuseTool],
1324        Type[LangfuseChain],
1325        Type[LangfuseRetriever],
1326        Type[LangfuseEvaluator],
1327        Type[LangfuseEmbedding],
1328        Type[LangfuseGuardrail],
1329        Type[LangfuseGeneration],
1330        Type[LangfuseEvent],
1331        Type[LangfuseSpan],
1332    ]:
1333        """Get the appropriate span class based on as_type."""
1334        normalized_type = as_type.lower()
1335
1336        if normalized_type == "agent":
1337            return LangfuseAgent
1338        elif normalized_type == "tool":
1339            return LangfuseTool
1340        elif normalized_type == "chain":
1341            return LangfuseChain
1342        elif normalized_type == "retriever":
1343            return LangfuseRetriever
1344        elif normalized_type == "evaluator":
1345            return LangfuseEvaluator
1346        elif normalized_type == "embedding":
1347            return LangfuseEmbedding
1348        elif normalized_type == "guardrail":
1349            return LangfuseGuardrail
1350        elif normalized_type == "generation":
1351            return LangfuseGeneration
1352        elif normalized_type == "event":
1353            return LangfuseEvent
1354        elif normalized_type == "span":
1355            return LangfuseSpan
1356        else:
1357            return LangfuseSpan
1358
1359    @_agnosticcontextmanager
1360    def _create_span_with_parent_context(
1361        self,
1362        *,
1363        name: str,
1364        parent: Optional[otel_trace_api.Span] = None,
1365        remote_parent_span: Optional[otel_trace_api.Span] = None,
1366        as_type: ObservationTypeLiteralNoEvent,
1367        end_on_exit: Optional[bool] = None,
1368        input: Optional[Any] = None,
1369        output: Optional[Any] = None,
1370        metadata: Optional[Any] = None,
1371        version: Optional[str] = None,
1372        level: Optional[SpanLevel] = None,
1373        status_message: Optional[str] = None,
1374        completion_start_time: Optional[datetime] = None,
1375        model: Optional[str] = None,
1376        model_parameters: Optional[Dict[str, MapValue]] = None,
1377        usage_details: Optional[Dict[str, int]] = None,
1378        cost_details: Optional[Dict[str, float]] = None,
1379        prompt: Optional[PromptClient] = None,
1380    ) -> Any:
1381        parent_span = parent or cast(otel_trace_api.Span, remote_parent_span)
1382
1383        with otel_trace_api.use_span(parent_span):
1384            with self._start_as_current_otel_span_with_processed_media(
1385                name=name,
1386                as_type=as_type,
1387                end_on_exit=end_on_exit,
1388                input=input,
1389                output=output,
1390                metadata=metadata,
1391                version=version,
1392                level=level,
1393                status_message=status_message,
1394                completion_start_time=completion_start_time,
1395                model=model,
1396                model_parameters=model_parameters,
1397                usage_details=usage_details,
1398                cost_details=cost_details,
1399                prompt=prompt,
1400            ) as langfuse_span:
1401                if remote_parent_span is not None:
1402                    langfuse_span._otel_span.set_attribute(
1403                        LangfuseOtelSpanAttributes.AS_ROOT, True
1404                    )
1405
1406                yield langfuse_span
1407
1408    @_agnosticcontextmanager
1409    def _start_as_current_otel_span_with_processed_media(
1410        self,
1411        *,
1412        name: str,
1413        as_type: Optional[ObservationTypeLiteralNoEvent] = None,
1414        end_on_exit: Optional[bool] = None,
1415        input: Optional[Any] = None,
1416        output: Optional[Any] = None,
1417        metadata: Optional[Any] = None,
1418        version: Optional[str] = None,
1419        level: Optional[SpanLevel] = None,
1420        status_message: Optional[str] = None,
1421        completion_start_time: Optional[datetime] = None,
1422        model: Optional[str] = None,
1423        model_parameters: Optional[Dict[str, MapValue]] = None,
1424        usage_details: Optional[Dict[str, int]] = None,
1425        cost_details: Optional[Dict[str, float]] = None,
1426        prompt: Optional[PromptClient] = None,
1427    ) -> Any:
1428        with self._otel_tracer.start_as_current_span(
1429            name=name,
1430            end_on_exit=end_on_exit if end_on_exit is not None else True,
1431        ) as otel_span:
1432            span_class = self._get_span_class(
1433                as_type or "generation"
1434            )  # default was "generation"
1435            common_args = {
1436                "otel_span": otel_span,
1437                "langfuse_client": self,
1438                "environment": self._environment,
1439                "input": input,
1440                "output": output,
1441                "metadata": metadata,
1442                "version": version,
1443                "level": level,
1444                "status_message": status_message,
1445            }
1446
1447            if span_class in [
1448                LangfuseGeneration,
1449                LangfuseEmbedding,
1450            ]:
1451                common_args.update(
1452                    {
1453                        "completion_start_time": completion_start_time,
1454                        "model": model,
1455                        "model_parameters": model_parameters,
1456                        "usage_details": usage_details,
1457                        "cost_details": cost_details,
1458                        "prompt": prompt,
1459                    }
1460                )
1461            # For span-like types (span, agent, tool, chain, retriever, evaluator, guardrail), no generation properties needed
1462
1463            yield span_class(**common_args)  # type: ignore[arg-type]
1464
1465    def _get_current_otel_span(self) -> Optional[otel_trace_api.Span]:
1466        current_span = otel_trace_api.get_current_span()
1467
1468        if current_span is otel_trace_api.INVALID_SPAN:
1469            langfuse_logger.warning(
1470                "Context error: No active span in current context. Operations that depend on an active span will be skipped. "
1471                "Ensure spans are created with start_as_current_span() or that you're operating within an active span context."
1472            )
1473            return None
1474
1475        return current_span
1476
1477    def update_current_generation(
1478        self,
1479        *,
1480        name: Optional[str] = None,
1481        input: Optional[Any] = None,
1482        output: Optional[Any] = None,
1483        metadata: Optional[Any] = None,
1484        version: Optional[str] = None,
1485        level: Optional[SpanLevel] = None,
1486        status_message: Optional[str] = None,
1487        completion_start_time: Optional[datetime] = None,
1488        model: Optional[str] = None,
1489        model_parameters: Optional[Dict[str, MapValue]] = None,
1490        usage_details: Optional[Dict[str, int]] = None,
1491        cost_details: Optional[Dict[str, float]] = None,
1492        prompt: Optional[PromptClient] = None,
1493    ) -> None:
1494        """Update the current active generation span with new information.
1495
1496        This method updates the current generation span in the active context with
1497        additional information. It's useful for adding output, usage stats, or other
1498        details that become available during or after model generation.
1499
1500        Args:
1501            name: The generation name
1502            input: Updated input data for the model
1503            output: Output from the model (e.g., completions)
1504            metadata: Additional metadata to associate with the generation
1505            version: Version identifier for the model or component
1506            level: Importance level of the generation (info, warning, error)
1507            status_message: Optional status message for the generation
1508            completion_start_time: When the model started generating the response
1509            model: Name/identifier of the AI model used (e.g., "gpt-4")
1510            model_parameters: Parameters used for the model (e.g., temperature, max_tokens)
1511            usage_details: Token usage information (e.g., prompt_tokens, completion_tokens)
1512            cost_details: Cost information for the model call
1513            prompt: Associated prompt template from Langfuse prompt management
1514
1515        Example:
1516            ```python
1517            with langfuse.start_as_current_generation(name="answer-query") as generation:
1518                # Initial setup and API call
1519                response = llm.generate(...)
1520
1521                # Update with results that weren't available at creation time
1522                langfuse.update_current_generation(
1523                    output=response.text,
1524                    usage_details={
1525                        "prompt_tokens": response.usage.prompt_tokens,
1526                        "completion_tokens": response.usage.completion_tokens
1527                    }
1528                )
1529            ```
1530        """
1531        if not self._tracing_enabled:
1532            langfuse_logger.debug(
1533                "Operation skipped: update_current_generation - Tracing is disabled or client is in no-op mode."
1534            )
1535            return
1536
1537        current_otel_span = self._get_current_otel_span()
1538
1539        if current_otel_span is not None:
1540            generation = LangfuseGeneration(
1541                otel_span=current_otel_span, langfuse_client=self
1542            )
1543
1544            if name:
1545                current_otel_span.update_name(name)
1546
1547            generation.update(
1548                input=input,
1549                output=output,
1550                metadata=metadata,
1551                version=version,
1552                level=level,
1553                status_message=status_message,
1554                completion_start_time=completion_start_time,
1555                model=model,
1556                model_parameters=model_parameters,
1557                usage_details=usage_details,
1558                cost_details=cost_details,
1559                prompt=prompt,
1560            )
1561
1562    def update_current_span(
1563        self,
1564        *,
1565        name: Optional[str] = None,
1566        input: Optional[Any] = None,
1567        output: Optional[Any] = None,
1568        metadata: Optional[Any] = None,
1569        version: Optional[str] = None,
1570        level: Optional[SpanLevel] = None,
1571        status_message: Optional[str] = None,
1572    ) -> None:
1573        """Update the current active span with new information.
1574
1575        This method updates the current span in the active context with
1576        additional information. It's useful for adding outputs or metadata
1577        that become available during execution.
1578
1579        Args:
1580            name: The span name
1581            input: Updated input data for the operation
1582            output: Output data from the operation
1583            metadata: Additional metadata to associate with the span
1584            version: Version identifier for the code or component
1585            level: Importance level of the span (info, warning, error)
1586            status_message: Optional status message for the span
1587
1588        Example:
1589            ```python
1590            with langfuse.start_as_current_span(name="process-data") as span:
1591                # Initial processing
1592                result = process_first_part()
1593
1594                # Update with intermediate results
1595                langfuse.update_current_span(metadata={"intermediate_result": result})
1596
1597                # Continue processing
1598                final_result = process_second_part(result)
1599
1600                # Final update
1601                langfuse.update_current_span(output=final_result)
1602            ```
1603        """
1604        if not self._tracing_enabled:
1605            langfuse_logger.debug(
1606                "Operation skipped: update_current_span - Tracing is disabled or client is in no-op mode."
1607            )
1608            return
1609
1610        current_otel_span = self._get_current_otel_span()
1611
1612        if current_otel_span is not None:
1613            span = LangfuseSpan(
1614                otel_span=current_otel_span,
1615                langfuse_client=self,
1616                environment=self._environment,
1617            )
1618
1619            if name:
1620                current_otel_span.update_name(name)
1621
1622            span.update(
1623                input=input,
1624                output=output,
1625                metadata=metadata,
1626                version=version,
1627                level=level,
1628                status_message=status_message,
1629            )
1630
1631    def update_current_trace(
1632        self,
1633        *,
1634        name: Optional[str] = None,
1635        user_id: Optional[str] = None,
1636        session_id: Optional[str] = None,
1637        version: Optional[str] = None,
1638        input: Optional[Any] = None,
1639        output: Optional[Any] = None,
1640        metadata: Optional[Any] = None,
1641        tags: Optional[List[str]] = None,
1642        public: Optional[bool] = None,
1643    ) -> None:
1644        """Update the current trace with additional information.
1645
1646        Args:
1647            name: Updated name for the Langfuse trace
1648            user_id: ID of the user who initiated the Langfuse trace
1649            session_id: Session identifier for grouping related Langfuse traces
1650            version: Version identifier for the application or service
1651            input: Input data for the overall Langfuse trace
1652            output: Output data from the overall Langfuse trace
1653            metadata: Additional metadata to associate with the Langfuse trace
1654            tags: List of tags to categorize the Langfuse trace
1655            public: Whether the Langfuse trace should be publicly accessible
1656
1657        See Also:
1658            :func:`langfuse.propagate_attributes`: Recommended replacement
1659        """
1660        if not self._tracing_enabled:
1661            langfuse_logger.debug(
1662                "Operation skipped: update_current_trace - Tracing is disabled or client is in no-op mode."
1663            )
1664            return
1665
1666        current_otel_span = self._get_current_otel_span()
1667
1668        if current_otel_span is not None:
1669            existing_observation_type = current_otel_span.attributes.get(  # type: ignore[attr-defined]
1670                LangfuseOtelSpanAttributes.OBSERVATION_TYPE, "span"
1671            )
1672            # We need to preserve the class to keep the correct observation type
1673            span_class = self._get_span_class(existing_observation_type)
1674            span = span_class(
1675                otel_span=current_otel_span,
1676                langfuse_client=self,
1677                environment=self._environment,
1678            )
1679
1680            span.update_trace(
1681                name=name,
1682                user_id=user_id,
1683                session_id=session_id,
1684                version=version,
1685                input=input,
1686                output=output,
1687                metadata=metadata,
1688                tags=tags,
1689                public=public,
1690            )
1691
1692    def create_event(
1693        self,
1694        *,
1695        trace_context: Optional[TraceContext] = None,
1696        name: str,
1697        input: Optional[Any] = None,
1698        output: Optional[Any] = None,
1699        metadata: Optional[Any] = None,
1700        version: Optional[str] = None,
1701        level: Optional[SpanLevel] = None,
1702        status_message: Optional[str] = None,
1703    ) -> LangfuseEvent:
1704        """Create a new Langfuse observation of type 'EVENT'.
1705
1706        The created Langfuse Event observation will be the child of the current span in the context.
1707
1708        Args:
1709            trace_context: Optional context for connecting to an existing trace
1710            name: Name of the span (e.g., function or operation name)
1711            input: Input data for the operation (can be any JSON-serializable object)
1712            output: Output data from the operation (can be any JSON-serializable object)
1713            metadata: Additional metadata to associate with the span
1714            version: Version identifier for the code or component
1715            level: Importance level of the span (info, warning, error)
1716            status_message: Optional status message for the span
1717
1718        Returns:
1719            The Langfuse Event object
1720
1721        Example:
1722            ```python
1723            event = langfuse.create_event(name="process-event")
1724            ```
1725        """
1726        timestamp = time_ns()
1727
1728        if trace_context:
1729            trace_id = trace_context.get("trace_id", None)
1730            parent_span_id = trace_context.get("parent_span_id", None)
1731
1732            if trace_id:
1733                remote_parent_span = self._create_remote_parent_span(
1734                    trace_id=trace_id, parent_span_id=parent_span_id
1735                )
1736
1737                with otel_trace_api.use_span(
1738                    cast(otel_trace_api.Span, remote_parent_span)
1739                ):
1740                    otel_span = self._otel_tracer.start_span(
1741                        name=name, start_time=timestamp
1742                    )
1743                    otel_span.set_attribute(LangfuseOtelSpanAttributes.AS_ROOT, True)
1744
1745                    return cast(
1746                        LangfuseEvent,
1747                        LangfuseEvent(
1748                            otel_span=otel_span,
1749                            langfuse_client=self,
1750                            environment=self._environment,
1751                            input=input,
1752                            output=output,
1753                            metadata=metadata,
1754                            version=version,
1755                            level=level,
1756                            status_message=status_message,
1757                        ).end(end_time=timestamp),
1758                    )
1759
1760        otel_span = self._otel_tracer.start_span(name=name, start_time=timestamp)
1761
1762        return cast(
1763            LangfuseEvent,
1764            LangfuseEvent(
1765                otel_span=otel_span,
1766                langfuse_client=self,
1767                environment=self._environment,
1768                input=input,
1769                output=output,
1770                metadata=metadata,
1771                version=version,
1772                level=level,
1773                status_message=status_message,
1774            ).end(end_time=timestamp),
1775        )
1776
1777    def _create_remote_parent_span(
1778        self, *, trace_id: str, parent_span_id: Optional[str]
1779    ) -> Any:
1780        if not self._is_valid_trace_id(trace_id):
1781            langfuse_logger.warning(
1782                f"Passed trace ID '{trace_id}' is not a valid 32 lowercase hex char Langfuse trace id. Ignoring trace ID."
1783            )
1784
1785        if parent_span_id and not self._is_valid_span_id(parent_span_id):
1786            langfuse_logger.warning(
1787                f"Passed span ID '{parent_span_id}' is not a valid 16 lowercase hex char Langfuse span id. Ignoring parent span ID."
1788            )
1789
1790        int_trace_id = int(trace_id, 16)
1791        int_parent_span_id = (
1792            int(parent_span_id, 16)
1793            if parent_span_id
1794            else RandomIdGenerator().generate_span_id()
1795        )
1796
1797        span_context = otel_trace_api.SpanContext(
1798            trace_id=int_trace_id,
1799            span_id=int_parent_span_id,
1800            trace_flags=otel_trace_api.TraceFlags(0x01),  # mark span as sampled
1801            is_remote=False,
1802        )
1803
1804        return otel_trace_api.NonRecordingSpan(span_context)
1805
1806    def _is_valid_trace_id(self, trace_id: str) -> bool:
1807        pattern = r"^[0-9a-f]{32}$"
1808
1809        return bool(re.match(pattern, trace_id))
1810
1811    def _is_valid_span_id(self, span_id: str) -> bool:
1812        pattern = r"^[0-9a-f]{16}$"
1813
1814        return bool(re.match(pattern, span_id))
1815
1816    def _create_observation_id(self, *, seed: Optional[str] = None) -> str:
1817        """Create a unique observation ID for use with Langfuse.
1818
1819        This method generates a unique observation ID (span ID in OpenTelemetry terms)
1820        for use with various Langfuse APIs. It can either generate a random ID or
1821        create a deterministic ID based on a seed string.
1822
1823        Observation IDs must be 16 lowercase hexadecimal characters, representing 8 bytes.
1824        This method ensures the generated ID meets this requirement. If you need to
1825        correlate an external ID with a Langfuse observation ID, use the external ID as
1826        the seed to get a valid, deterministic observation ID.
1827
1828        Args:
1829            seed: Optional string to use as a seed for deterministic ID generation.
1830                 If provided, the same seed will always produce the same ID.
1831                 If not provided, a random ID will be generated.
1832
1833        Returns:
1834            A 16-character lowercase hexadecimal string representing the observation ID.
1835
1836        Example:
1837            ```python
1838            # Generate a random observation ID
1839            obs_id = langfuse.create_observation_id()
1840
1841            # Generate a deterministic ID based on a seed
1842            user_obs_id = langfuse.create_observation_id(seed="user-123-feedback")
1843
1844            # Correlate an external item ID with a Langfuse observation ID
1845            item_id = "item-789012"
1846            correlated_obs_id = langfuse.create_observation_id(seed=item_id)
1847
1848            # Use the ID with Langfuse APIs
1849            langfuse.create_score(
1850                name="relevance",
1851                value=0.95,
1852                trace_id=trace_id,
1853                observation_id=obs_id
1854            )
1855            ```
1856        """
1857        if not seed:
1858            span_id_int = RandomIdGenerator().generate_span_id()
1859
1860            return self._format_otel_span_id(span_id_int)
1861
1862        return sha256(seed.encode("utf-8")).digest()[:8].hex()
1863
1864    @staticmethod
1865    def create_trace_id(*, seed: Optional[str] = None) -> str:
1866        """Create a unique trace ID for use with Langfuse.
1867
1868        This method generates a unique trace ID for use with various Langfuse APIs.
1869        It can either generate a random ID or create a deterministic ID based on
1870        a seed string.
1871
1872        Trace IDs must be 32 lowercase hexadecimal characters, representing 16 bytes.
1873        This method ensures the generated ID meets this requirement. If you need to
1874        correlate an external ID with a Langfuse trace ID, use the external ID as the
1875        seed to get a valid, deterministic Langfuse trace ID.
1876
1877        Args:
1878            seed: Optional string to use as a seed for deterministic ID generation.
1879                 If provided, the same seed will always produce the same ID.
1880                 If not provided, a random ID will be generated.
1881
1882        Returns:
1883            A 32-character lowercase hexadecimal string representing the Langfuse trace ID.
1884
1885        Example:
1886            ```python
1887            # Generate a random trace ID
1888            trace_id = langfuse.create_trace_id()
1889
1890            # Generate a deterministic ID based on a seed
1891            session_trace_id = langfuse.create_trace_id(seed="session-456")
1892
1893            # Correlate an external ID with a Langfuse trace ID
1894            external_id = "external-system-123456"
1895            correlated_trace_id = langfuse.create_trace_id(seed=external_id)
1896
1897            # Use the ID with trace context
1898            with langfuse.start_as_current_span(
1899                name="process-request",
1900                trace_context={"trace_id": trace_id}
1901            ) as span:
1902                # Operation will be part of the specific trace
1903                pass
1904            ```
1905        """
1906        if not seed:
1907            trace_id_int = RandomIdGenerator().generate_trace_id()
1908
1909            return Langfuse._format_otel_trace_id(trace_id_int)
1910
1911        return sha256(seed.encode("utf-8")).digest()[:16].hex()
1912
1913    def _get_otel_trace_id(self, otel_span: otel_trace_api.Span) -> str:
1914        span_context = otel_span.get_span_context()
1915
1916        return self._format_otel_trace_id(span_context.trace_id)
1917
1918    def _get_otel_span_id(self, otel_span: otel_trace_api.Span) -> str:
1919        span_context = otel_span.get_span_context()
1920
1921        return self._format_otel_span_id(span_context.span_id)
1922
1923    @staticmethod
1924    def _format_otel_span_id(span_id_int: int) -> str:
1925        """Format an integer span ID to a 16-character lowercase hex string.
1926
1927        Internal method to convert an OpenTelemetry integer span ID to the standard
1928        W3C Trace Context format (16-character lowercase hex string).
1929
1930        Args:
1931            span_id_int: 64-bit integer representing a span ID
1932
1933        Returns:
1934            A 16-character lowercase hexadecimal string
1935        """
1936        return format(span_id_int, "016x")
1937
1938    @staticmethod
1939    def _format_otel_trace_id(trace_id_int: int) -> str:
1940        """Format an integer trace ID to a 32-character lowercase hex string.
1941
1942        Internal method to convert an OpenTelemetry integer trace ID to the standard
1943        W3C Trace Context format (32-character lowercase hex string).
1944
1945        Args:
1946            trace_id_int: 128-bit integer representing a trace ID
1947
1948        Returns:
1949            A 32-character lowercase hexadecimal string
1950        """
1951        return format(trace_id_int, "032x")
1952
1953    @overload
1954    def create_score(
1955        self,
1956        *,
1957        name: str,
1958        value: float,
1959        session_id: Optional[str] = None,
1960        dataset_run_id: Optional[str] = None,
1961        trace_id: Optional[str] = None,
1962        observation_id: Optional[str] = None,
1963        score_id: Optional[str] = None,
1964        data_type: Optional[Literal["NUMERIC", "BOOLEAN"]] = None,
1965        comment: Optional[str] = None,
1966        config_id: Optional[str] = None,
1967        metadata: Optional[Any] = None,
1968    ) -> None: ...
1969
1970    @overload
1971    def create_score(
1972        self,
1973        *,
1974        name: str,
1975        value: str,
1976        session_id: Optional[str] = None,
1977        dataset_run_id: Optional[str] = None,
1978        trace_id: Optional[str] = None,
1979        score_id: Optional[str] = None,
1980        observation_id: Optional[str] = None,
1981        data_type: Optional[Literal["CATEGORICAL"]] = "CATEGORICAL",
1982        comment: Optional[str] = None,
1983        config_id: Optional[str] = None,
1984        metadata: Optional[Any] = None,
1985    ) -> None: ...
1986
1987    def create_score(
1988        self,
1989        *,
1990        name: str,
1991        value: Union[float, str],
1992        session_id: Optional[str] = None,
1993        dataset_run_id: Optional[str] = None,
1994        trace_id: Optional[str] = None,
1995        observation_id: Optional[str] = None,
1996        score_id: Optional[str] = None,
1997        data_type: Optional[ScoreDataType] = None,
1998        comment: Optional[str] = None,
1999        config_id: Optional[str] = None,
2000        metadata: Optional[Any] = None,
2001    ) -> None:
2002        """Create a score for a specific trace or observation.
2003
2004        This method creates a score for evaluating a Langfuse trace or observation. Scores can be
2005        used to track quality metrics, user feedback, or automated evaluations.
2006
2007        Args:
2008            name: Name of the score (e.g., "relevance", "accuracy")
2009            value: Score value (can be numeric for NUMERIC/BOOLEAN types or string for CATEGORICAL)
2010            session_id: ID of the Langfuse session to associate the score with
2011            dataset_run_id: ID of the Langfuse dataset run to associate the score with
2012            trace_id: ID of the Langfuse trace to associate the score with
2013            observation_id: Optional ID of the specific observation to score. Trace ID must be provided too.
2014            score_id: Optional custom ID for the score (auto-generated if not provided)
2015            data_type: Type of score (NUMERIC, BOOLEAN, or CATEGORICAL)
2016            comment: Optional comment or explanation for the score
2017            config_id: Optional ID of a score config defined in Langfuse
2018            metadata: Optional metadata to be attached to the score
2019
2020        Example:
2021            ```python
2022            # Create a numeric score for accuracy
2023            langfuse.create_score(
2024                name="accuracy",
2025                value=0.92,
2026                trace_id="abcdef1234567890abcdef1234567890",
2027                data_type="NUMERIC",
2028                comment="High accuracy with minor irrelevant details"
2029            )
2030
2031            # Create a categorical score for sentiment
2032            langfuse.create_score(
2033                name="sentiment",
2034                value="positive",
2035                trace_id="abcdef1234567890abcdef1234567890",
2036                observation_id="abcdef1234567890",
2037                data_type="CATEGORICAL"
2038            )
2039            ```
2040        """
2041        if not self._tracing_enabled:
2042            return
2043
2044        score_id = score_id or self._create_observation_id()
2045
2046        try:
2047            new_body = ScoreBody(
2048                id=score_id,
2049                sessionId=session_id,
2050                datasetRunId=dataset_run_id,
2051                traceId=trace_id,
2052                observationId=observation_id,
2053                name=name,
2054                value=value,
2055                dataType=data_type,  # type: ignore
2056                comment=comment,
2057                configId=config_id,
2058                environment=self._environment,
2059                metadata=metadata,
2060            )
2061
2062            event = {
2063                "id": self.create_trace_id(),
2064                "type": "score-create",
2065                "timestamp": _get_timestamp(),
2066                "body": new_body,
2067            }
2068
2069            if self._resources is not None:
2070                # Force the score to be in sample if it was for a legacy trace ID, i.e. non-32 hexchar
2071                force_sample = (
2072                    not self._is_valid_trace_id(trace_id) if trace_id else True
2073                )
2074
2075                self._resources.add_score_task(
2076                    event,
2077                    force_sample=force_sample,
2078                )
2079
2080        except Exception as e:
2081            langfuse_logger.exception(
2082                f"Error creating score: Failed to process score event for trace_id={trace_id}, name={name}. Error: {e}"
2083            )
2084
2085    @overload
2086    def score_current_span(
2087        self,
2088        *,
2089        name: str,
2090        value: float,
2091        score_id: Optional[str] = None,
2092        data_type: Optional[Literal["NUMERIC", "BOOLEAN"]] = None,
2093        comment: Optional[str] = None,
2094        config_id: Optional[str] = None,
2095    ) -> None: ...
2096
2097    @overload
2098    def score_current_span(
2099        self,
2100        *,
2101        name: str,
2102        value: str,
2103        score_id: Optional[str] = None,
2104        data_type: Optional[Literal["CATEGORICAL"]] = "CATEGORICAL",
2105        comment: Optional[str] = None,
2106        config_id: Optional[str] = None,
2107    ) -> None: ...
2108
2109    def score_current_span(
2110        self,
2111        *,
2112        name: str,
2113        value: Union[float, str],
2114        score_id: Optional[str] = None,
2115        data_type: Optional[ScoreDataType] = None,
2116        comment: Optional[str] = None,
2117        config_id: Optional[str] = None,
2118    ) -> None:
2119        """Create a score for the current active span.
2120
2121        This method scores the currently active span in the context. It's a convenient
2122        way to score the current operation without needing to know its trace and span IDs.
2123
2124        Args:
2125            name: Name of the score (e.g., "relevance", "accuracy")
2126            value: Score value (can be numeric for NUMERIC/BOOLEAN types or string for CATEGORICAL)
2127            score_id: Optional custom ID for the score (auto-generated if not provided)
2128            data_type: Type of score (NUMERIC, BOOLEAN, or CATEGORICAL)
2129            comment: Optional comment or explanation for the score
2130            config_id: Optional ID of a score config defined in Langfuse
2131
2132        Example:
2133            ```python
2134            with langfuse.start_as_current_generation(name="answer-query") as generation:
2135                # Generate answer
2136                response = generate_answer(...)
2137                generation.update(output=response)
2138
2139                # Score the generation
2140                langfuse.score_current_span(
2141                    name="relevance",
2142                    value=0.85,
2143                    data_type="NUMERIC",
2144                    comment="Mostly relevant but contains some tangential information"
2145                )
2146            ```
2147        """
2148        current_span = self._get_current_otel_span()
2149
2150        if current_span is not None:
2151            trace_id = self._get_otel_trace_id(current_span)
2152            observation_id = self._get_otel_span_id(current_span)
2153
2154            langfuse_logger.info(
2155                f"Score: Creating score name='{name}' value={value} for current span ({observation_id}) in trace {trace_id}"
2156            )
2157
2158            self.create_score(
2159                trace_id=trace_id,
2160                observation_id=observation_id,
2161                name=name,
2162                value=cast(str, value),
2163                score_id=score_id,
2164                data_type=cast(Literal["CATEGORICAL"], data_type),
2165                comment=comment,
2166                config_id=config_id,
2167            )
2168
2169    @overload
2170    def score_current_trace(
2171        self,
2172        *,
2173        name: str,
2174        value: float,
2175        score_id: Optional[str] = None,
2176        data_type: Optional[Literal["NUMERIC", "BOOLEAN"]] = None,
2177        comment: Optional[str] = None,
2178        config_id: Optional[str] = None,
2179    ) -> None: ...
2180
2181    @overload
2182    def score_current_trace(
2183        self,
2184        *,
2185        name: str,
2186        value: str,
2187        score_id: Optional[str] = None,
2188        data_type: Optional[Literal["CATEGORICAL"]] = "CATEGORICAL",
2189        comment: Optional[str] = None,
2190        config_id: Optional[str] = None,
2191    ) -> None: ...
2192
2193    def score_current_trace(
2194        self,
2195        *,
2196        name: str,
2197        value: Union[float, str],
2198        score_id: Optional[str] = None,
2199        data_type: Optional[ScoreDataType] = None,
2200        comment: Optional[str] = None,
2201        config_id: Optional[str] = None,
2202    ) -> None:
2203        """Create a score for the current trace.
2204
2205        This method scores the trace of the currently active span. Unlike score_current_span,
2206        this method associates the score with the entire trace rather than a specific span.
2207        It's useful for scoring overall performance or quality of the entire operation.
2208
2209        Args:
2210            name: Name of the score (e.g., "user_satisfaction", "overall_quality")
2211            value: Score value (can be numeric for NUMERIC/BOOLEAN types or string for CATEGORICAL)
2212            score_id: Optional custom ID for the score (auto-generated if not provided)
2213            data_type: Type of score (NUMERIC, BOOLEAN, or CATEGORICAL)
2214            comment: Optional comment or explanation for the score
2215            config_id: Optional ID of a score config defined in Langfuse
2216
2217        Example:
2218            ```python
2219            with langfuse.start_as_current_span(name="process-user-request") as span:
2220                # Process request
2221                result = process_complete_request()
2222                span.update(output=result)
2223
2224                # Score the overall trace
2225                langfuse.score_current_trace(
2226                    name="overall_quality",
2227                    value=0.95,
2228                    data_type="NUMERIC",
2229                    comment="High quality end-to-end response"
2230                )
2231            ```
2232        """
2233        current_span = self._get_current_otel_span()
2234
2235        if current_span is not None:
2236            trace_id = self._get_otel_trace_id(current_span)
2237
2238            langfuse_logger.info(
2239                f"Score: Creating score name='{name}' value={value} for entire trace {trace_id}"
2240            )
2241
2242            self.create_score(
2243                trace_id=trace_id,
2244                name=name,
2245                value=cast(str, value),
2246                score_id=score_id,
2247                data_type=cast(Literal["CATEGORICAL"], data_type),
2248                comment=comment,
2249                config_id=config_id,
2250            )
2251
2252    def flush(self) -> None:
2253        """Force flush all pending spans and events to the Langfuse API.
2254
2255        This method manually flushes any pending spans, scores, and other events to the
2256        Langfuse API. It's useful in scenarios where you want to ensure all data is sent
2257        before proceeding, without waiting for the automatic flush interval.
2258
2259        Example:
2260            ```python
2261            # Record some spans and scores
2262            with langfuse.start_as_current_span(name="operation") as span:
2263                # Do work...
2264                pass
2265
2266            # Ensure all data is sent to Langfuse before proceeding
2267            langfuse.flush()
2268
2269            # Continue with other work
2270            ```
2271        """
2272        if self._resources is not None:
2273            self._resources.flush()
2274
2275    def shutdown(self) -> None:
2276        """Shut down the Langfuse client and flush all pending data.
2277
2278        This method cleanly shuts down the Langfuse client, ensuring all pending data
2279        is flushed to the API and all background threads are properly terminated.
2280
2281        It's important to call this method when your application is shutting down to
2282        prevent data loss and resource leaks. For most applications, using the client
2283        as a context manager or relying on the automatic shutdown via atexit is sufficient.
2284
2285        Example:
2286            ```python
2287            # Initialize Langfuse
2288            langfuse = Langfuse(public_key="...", secret_key="...")
2289
2290            # Use Langfuse throughout your application
2291            # ...
2292
2293            # When application is shutting down
2294            langfuse.shutdown()
2295            ```
2296        """
2297        if self._resources is not None:
2298            self._resources.shutdown()
2299
2300    def get_current_trace_id(self) -> Optional[str]:
2301        """Get the trace ID of the current active span.
2302
2303        This method retrieves the trace ID from the currently active span in the context.
2304        It can be used to get the trace ID for referencing in logs, external systems,
2305        or for creating related operations.
2306
2307        Returns:
2308            The current trace ID as a 32-character lowercase hexadecimal string,
2309            or None if there is no active span.
2310
2311        Example:
2312            ```python
2313            with langfuse.start_as_current_span(name="process-request") as span:
2314                # Get the current trace ID for reference
2315                trace_id = langfuse.get_current_trace_id()
2316
2317                # Use it for external correlation
2318                log.info(f"Processing request with trace_id: {trace_id}")
2319
2320                # Or pass to another system
2321                external_system.process(data, trace_id=trace_id)
2322            ```
2323        """
2324        if not self._tracing_enabled:
2325            langfuse_logger.debug(
2326                "Operation skipped: get_current_trace_id - Tracing is disabled or client is in no-op mode."
2327            )
2328            return None
2329
2330        current_otel_span = self._get_current_otel_span()
2331
2332        return self._get_otel_trace_id(current_otel_span) if current_otel_span else None
2333
2334    def get_current_observation_id(self) -> Optional[str]:
2335        """Get the observation ID (span ID) of the current active span.
2336
2337        This method retrieves the observation ID from the currently active span in the context.
2338        It can be used to get the observation ID for referencing in logs, external systems,
2339        or for creating scores or other related operations.
2340
2341        Returns:
2342            The current observation ID as a 16-character lowercase hexadecimal string,
2343            or None if there is no active span.
2344
2345        Example:
2346            ```python
2347            with langfuse.start_as_current_span(name="process-user-query") as span:
2348                # Get the current observation ID
2349                observation_id = langfuse.get_current_observation_id()
2350
2351                # Store it for later reference
2352                cache.set(f"query_{query_id}_observation", observation_id)
2353
2354                # Process the query...
2355            ```
2356        """
2357        if not self._tracing_enabled:
2358            langfuse_logger.debug(
2359                "Operation skipped: get_current_observation_id - Tracing is disabled or client is in no-op mode."
2360            )
2361            return None
2362
2363        current_otel_span = self._get_current_otel_span()
2364
2365        return self._get_otel_span_id(current_otel_span) if current_otel_span else None
2366
2367    def _get_project_id(self) -> Optional[str]:
2368        """Fetch and return the current project id. Persisted across requests. Returns None if no project id is found for api keys."""
2369        if not self._project_id:
2370            proj = self.api.projects.get()
2371            if not proj.data or not proj.data[0].id:
2372                return None
2373
2374            self._project_id = proj.data[0].id
2375
2376        return self._project_id
2377
2378    def get_trace_url(self, *, trace_id: Optional[str] = None) -> Optional[str]:
2379        """Get the URL to view a trace in the Langfuse UI.
2380
2381        This method generates a URL that links directly to a trace in the Langfuse UI.
2382        It's useful for providing links in logs, notifications, or debugging tools.
2383
2384        Args:
2385            trace_id: Optional trace ID to generate a URL for. If not provided,
2386                     the trace ID of the current active span will be used.
2387
2388        Returns:
2389            A URL string pointing to the trace in the Langfuse UI,
2390            or None if the project ID couldn't be retrieved or no trace ID is available.
2391
2392        Example:
2393            ```python
2394            # Get URL for the current trace
2395            with langfuse.start_as_current_span(name="process-request") as span:
2396                trace_url = langfuse.get_trace_url()
2397                log.info(f"Processing trace: {trace_url}")
2398
2399            # Get URL for a specific trace
2400            specific_trace_url = langfuse.get_trace_url(trace_id="1234567890abcdef1234567890abcdef")
2401            send_notification(f"Review needed for trace: {specific_trace_url}")
2402            ```
2403        """
2404        project_id = self._get_project_id()
2405        final_trace_id = trace_id or self.get_current_trace_id()
2406
2407        return (
2408            f"{self._base_url}/project/{project_id}/traces/{final_trace_id}"
2409            if project_id and final_trace_id
2410            else None
2411        )
2412
2413    def get_dataset(
2414        self, name: str, *, fetch_items_page_size: Optional[int] = 50
2415    ) -> "DatasetClient":
2416        """Fetch a dataset by its name.
2417
2418        Args:
2419            name (str): The name of the dataset to fetch.
2420            fetch_items_page_size (Optional[int]): All items of the dataset will be fetched in chunks of this size. Defaults to 50.
2421
2422        Returns:
2423            DatasetClient: The dataset with the given name.
2424        """
2425        try:
2426            langfuse_logger.debug(f"Getting datasets {name}")
2427            dataset = self.api.datasets.get(dataset_name=name)
2428
2429            dataset_items = []
2430            page = 1
2431
2432            while True:
2433                new_items = self.api.dataset_items.list(
2434                    dataset_name=self._url_encode(name, is_url_param=True),
2435                    page=page,
2436                    limit=fetch_items_page_size,
2437                )
2438                dataset_items.extend(new_items.data)
2439
2440                if new_items.meta.total_pages <= page:
2441                    break
2442
2443                page += 1
2444
2445            items = [DatasetItemClient(i, langfuse=self) for i in dataset_items]
2446
2447            return DatasetClient(dataset, items=items)
2448
2449        except Error as e:
2450            handle_fern_exception(e)
2451            raise e
2452
2453    def run_experiment(
2454        self,
2455        *,
2456        name: str,
2457        run_name: Optional[str] = None,
2458        description: Optional[str] = None,
2459        data: ExperimentData,
2460        task: TaskFunction,
2461        evaluators: List[EvaluatorFunction] = [],
2462        run_evaluators: List[RunEvaluatorFunction] = [],
2463        max_concurrency: int = 50,
2464        metadata: Optional[Dict[str, str]] = None,
2465    ) -> ExperimentResult:
2466        """Run an experiment on a dataset with automatic tracing and evaluation.
2467
2468        This method executes a task function on each item in the provided dataset,
2469        automatically traces all executions with Langfuse for observability, runs
2470        item-level and run-level evaluators on the outputs, and returns comprehensive
2471        results with evaluation metrics.
2472
2473        The experiment system provides:
2474        - Automatic tracing of all task executions
2475        - Concurrent processing with configurable limits
2476        - Comprehensive error handling that isolates failures
2477        - Integration with Langfuse datasets for experiment tracking
2478        - Flexible evaluation framework supporting both sync and async evaluators
2479
2480        Args:
2481            name: Human-readable name for the experiment. Used for identification
2482                in the Langfuse UI.
2483            run_name: Optional exact name for the experiment run. If provided, this will be
2484                used as the exact dataset run name if the `data` contains Langfuse dataset items.
2485                If not provided, this will default to the experiment name appended with an ISO timestamp.
2486            description: Optional description explaining the experiment's purpose,
2487                methodology, or expected outcomes.
2488            data: Array of data items to process. Can be either:
2489                - List of dict-like items with 'input', 'expected_output', 'metadata' keys
2490                - List of Langfuse DatasetItem objects from dataset.items
2491            task: Function that processes each data item and returns output.
2492                Must accept 'item' as keyword argument and can return sync or async results.
2493                The task function signature should be: task(*, item, **kwargs) -> Any
2494            evaluators: List of functions to evaluate each item's output individually.
2495                Each evaluator receives input, output, expected_output, and metadata.
2496                Can return single Evaluation dict or list of Evaluation dicts.
2497            run_evaluators: List of functions to evaluate the entire experiment run.
2498                Each run evaluator receives all item_results and can compute aggregate metrics.
2499                Useful for calculating averages, distributions, or cross-item comparisons.
2500            max_concurrency: Maximum number of concurrent task executions (default: 50).
2501                Controls the number of items processed simultaneously. Adjust based on
2502                API rate limits and system resources.
2503            metadata: Optional metadata dictionary to attach to all experiment traces.
2504                This metadata will be included in every trace created during the experiment.
2505                If `data` are Langfuse dataset items, the metadata will be attached to the dataset run, too.
2506
2507        Returns:
2508            ExperimentResult containing:
2509            - run_name: The experiment run name. This is equal to the dataset run name if experiment was on Langfuse dataset.
2510            - item_results: List of results for each processed item with outputs and evaluations
2511            - run_evaluations: List of aggregate evaluation results for the entire run
2512            - dataset_run_id: ID of the dataset run (if using Langfuse datasets)
2513            - dataset_run_url: Direct URL to view results in Langfuse UI (if applicable)
2514
2515        Raises:
2516            ValueError: If required parameters are missing or invalid
2517            Exception: If experiment setup fails (individual item failures are handled gracefully)
2518
2519        Examples:
2520            Basic experiment with local data:
2521            ```python
2522            def summarize_text(*, item, **kwargs):
2523                return f"Summary: {item['input'][:50]}..."
2524
2525            def length_evaluator(*, input, output, expected_output=None, **kwargs):
2526                return {
2527                    "name": "output_length",
2528                    "value": len(output),
2529                    "comment": f"Output contains {len(output)} characters"
2530                }
2531
2532            result = langfuse.run_experiment(
2533                name="Text Summarization Test",
2534                description="Evaluate summarization quality and length",
2535                data=[
2536                    {"input": "Long article text...", "expected_output": "Expected summary"},
2537                    {"input": "Another article...", "expected_output": "Another summary"}
2538                ],
2539                task=summarize_text,
2540                evaluators=[length_evaluator]
2541            )
2542
2543            print(f"Processed {len(result.item_results)} items")
2544            for item_result in result.item_results:
2545                print(f"Input: {item_result.item['input']}")
2546                print(f"Output: {item_result.output}")
2547                print(f"Evaluations: {item_result.evaluations}")
2548            ```
2549
2550            Advanced experiment with async task and multiple evaluators:
2551            ```python
2552            async def llm_task(*, item, **kwargs):
2553                # Simulate async LLM call
2554                response = await openai_client.chat.completions.create(
2555                    model="gpt-4",
2556                    messages=[{"role": "user", "content": item["input"]}]
2557                )
2558                return response.choices[0].message.content
2559
2560            def accuracy_evaluator(*, input, output, expected_output=None, **kwargs):
2561                if expected_output and expected_output.lower() in output.lower():
2562                    return {"name": "accuracy", "value": 1.0, "comment": "Correct answer"}
2563                return {"name": "accuracy", "value": 0.0, "comment": "Incorrect answer"}
2564
2565            def toxicity_evaluator(*, input, output, expected_output=None, **kwargs):
2566                # Simulate toxicity check
2567                toxicity_score = check_toxicity(output)  # Your toxicity checker
2568                return {
2569                    "name": "toxicity",
2570                    "value": toxicity_score,
2571                    "comment": f"Toxicity level: {'high' if toxicity_score > 0.7 else 'low'}"
2572                }
2573
2574            def average_accuracy(*, item_results, **kwargs):
2575                accuracies = [
2576                    eval.value for result in item_results
2577                    for eval in result.evaluations
2578                    if eval.name == "accuracy"
2579                ]
2580                return {
2581                    "name": "average_accuracy",
2582                    "value": sum(accuracies) / len(accuracies) if accuracies else 0,
2583                    "comment": f"Average accuracy across {len(accuracies)} items"
2584                }
2585
2586            result = langfuse.run_experiment(
2587                name="LLM Safety and Accuracy Test",
2588                description="Evaluate model accuracy and safety across diverse prompts",
2589                data=test_dataset,  # Your dataset items
2590                task=llm_task,
2591                evaluators=[accuracy_evaluator, toxicity_evaluator],
2592                run_evaluators=[average_accuracy],
2593                max_concurrency=5,  # Limit concurrent API calls
2594                metadata={"model": "gpt-4", "temperature": 0.7}
2595            )
2596            ```
2597
2598            Using with Langfuse datasets:
2599            ```python
2600            # Get dataset from Langfuse
2601            dataset = langfuse.get_dataset("my-eval-dataset")
2602
2603            result = dataset.run_experiment(
2604                name="Production Model Evaluation",
2605                description="Monthly evaluation of production model performance",
2606                task=my_production_task,
2607                evaluators=[accuracy_evaluator, latency_evaluator]
2608            )
2609
2610            # Results automatically linked to dataset in Langfuse UI
2611            print(f"View results: {result['dataset_run_url']}")
2612            ```
2613
2614        Note:
2615            - Task and evaluator functions can be either synchronous or asynchronous
2616            - Individual item failures are logged but don't stop the experiment
2617            - All executions are automatically traced and visible in Langfuse UI
2618            - When using Langfuse datasets, results are automatically linked for easy comparison
2619            - This method works in both sync and async contexts (Jupyter notebooks, web apps, etc.)
2620            - Async execution is handled automatically with smart event loop detection
2621        """
2622        return cast(
2623            ExperimentResult,
2624            run_async_safely(
2625                self._run_experiment_async(
2626                    name=name,
2627                    run_name=self._create_experiment_run_name(
2628                        name=name, run_name=run_name
2629                    ),
2630                    description=description,
2631                    data=data,
2632                    task=task,
2633                    evaluators=evaluators or [],
2634                    run_evaluators=run_evaluators or [],
2635                    max_concurrency=max_concurrency,
2636                    metadata=metadata,
2637                ),
2638            ),
2639        )
2640
2641    async def _run_experiment_async(
2642        self,
2643        *,
2644        name: str,
2645        run_name: str,
2646        description: Optional[str],
2647        data: ExperimentData,
2648        task: TaskFunction,
2649        evaluators: List[EvaluatorFunction],
2650        run_evaluators: List[RunEvaluatorFunction],
2651        max_concurrency: int,
2652        metadata: Optional[Dict[str, Any]] = None,
2653    ) -> ExperimentResult:
2654        langfuse_logger.debug(
2655            f"Starting experiment '{name}' run '{run_name}' with {len(data)} items"
2656        )
2657
2658        # Set up concurrency control
2659        semaphore = asyncio.Semaphore(max_concurrency)
2660
2661        # Process all items
2662        async def process_item(item: ExperimentItem) -> ExperimentItemResult:
2663            async with semaphore:
2664                return await self._process_experiment_item(
2665                    item, task, evaluators, name, run_name, description, metadata
2666                )
2667
2668        # Run all items concurrently
2669        tasks = [process_item(item) for item in data]
2670        item_results = await asyncio.gather(*tasks, return_exceptions=True)
2671
2672        # Filter out any exceptions and log errors
2673        valid_results: List[ExperimentItemResult] = []
2674        for i, result in enumerate(item_results):
2675            if isinstance(result, Exception):
2676                langfuse_logger.error(f"Item {i} failed: {result}")
2677            elif isinstance(result, ExperimentItemResult):
2678                valid_results.append(result)  # type: ignore
2679
2680        # Run experiment-level evaluators
2681        run_evaluations: List[Evaluation] = []
2682        for run_evaluator in run_evaluators:
2683            try:
2684                evaluations = await _run_evaluator(
2685                    run_evaluator, item_results=valid_results
2686                )
2687                run_evaluations.extend(evaluations)
2688            except Exception as e:
2689                langfuse_logger.error(f"Run evaluator failed: {e}")
2690
2691        # Generate dataset run URL if applicable
2692        dataset_run_id = valid_results[0].dataset_run_id if valid_results else None
2693        dataset_run_url = None
2694        if dataset_run_id and data:
2695            try:
2696                # Check if the first item has dataset_id (for DatasetItem objects)
2697                first_item = data[0]
2698                dataset_id = None
2699
2700                if hasattr(first_item, "dataset_id"):
2701                    dataset_id = getattr(first_item, "dataset_id", None)
2702
2703                if dataset_id:
2704                    project_id = self._get_project_id()
2705
2706                    if project_id:
2707                        dataset_run_url = f"{self._base_url}/project/{project_id}/datasets/{dataset_id}/runs/{dataset_run_id}"
2708
2709            except Exception:
2710                pass  # URL generation is optional
2711
2712        # Store run-level evaluations as scores
2713        for evaluation in run_evaluations:
2714            try:
2715                if dataset_run_id:
2716                    self.create_score(
2717                        dataset_run_id=dataset_run_id,
2718                        name=evaluation.name or "<unknown>",
2719                        value=evaluation.value,  # type: ignore
2720                        comment=evaluation.comment,
2721                        metadata=evaluation.metadata,
2722                        data_type=evaluation.data_type,  # type: ignore
2723                        config_id=evaluation.config_id,
2724                    )
2725
2726            except Exception as e:
2727                langfuse_logger.error(f"Failed to store run evaluation: {e}")
2728
2729        # Flush scores and traces
2730        self.flush()
2731
2732        return ExperimentResult(
2733            name=name,
2734            run_name=run_name,
2735            description=description,
2736            item_results=valid_results,
2737            run_evaluations=run_evaluations,
2738            dataset_run_id=dataset_run_id,
2739            dataset_run_url=dataset_run_url,
2740        )
2741
2742    async def _process_experiment_item(
2743        self,
2744        item: ExperimentItem,
2745        task: Callable,
2746        evaluators: List[Callable],
2747        experiment_name: str,
2748        experiment_run_name: str,
2749        experiment_description: Optional[str],
2750        experiment_metadata: Optional[Dict[str, Any]] = None,
2751    ) -> ExperimentItemResult:
2752        span_name = "experiment-item-run"
2753
2754        with self.start_as_current_span(name=span_name) as span:
2755            try:
2756                input_data = (
2757                    item.get("input")
2758                    if isinstance(item, dict)
2759                    else getattr(item, "input", None)
2760                )
2761
2762                if input_data is None:
2763                    raise ValueError("Experiment Item is missing input. Skipping item.")
2764
2765                expected_output = (
2766                    item.get("expected_output")
2767                    if isinstance(item, dict)
2768                    else getattr(item, "expected_output", None)
2769                )
2770
2771                item_metadata = (
2772                    item.get("metadata")
2773                    if isinstance(item, dict)
2774                    else getattr(item, "metadata", None)
2775                )
2776
2777                final_observation_metadata = {
2778                    "experiment_name": experiment_name,
2779                    "experiment_run_name": experiment_run_name,
2780                    **(experiment_metadata or {}),
2781                }
2782
2783                trace_id = span.trace_id
2784                dataset_id = None
2785                dataset_item_id = None
2786                dataset_run_id = None
2787
2788                # Link to dataset run if this is a dataset item
2789                if hasattr(item, "id") and hasattr(item, "dataset_id"):
2790                    try:
2791                        # Use sync API to avoid event loop issues when run_async_safely
2792                        # creates multiple event loops across different threads
2793                        dataset_run_item = await asyncio.to_thread(
2794                            self.api.dataset_run_items.create,
2795                            request=CreateDatasetRunItemRequest(
2796                                runName=experiment_run_name,
2797                                runDescription=experiment_description,
2798                                metadata=experiment_metadata,
2799                                datasetItemId=item.id,  # type: ignore
2800                                traceId=trace_id,
2801                                observationId=span.id,
2802                            ),
2803                        )
2804
2805                        dataset_run_id = dataset_run_item.dataset_run_id
2806
2807                    except Exception as e:
2808                        langfuse_logger.error(f"Failed to create dataset run item: {e}")
2809
2810                if (
2811                    not isinstance(item, dict)
2812                    and hasattr(item, "dataset_id")
2813                    and hasattr(item, "id")
2814                ):
2815                    dataset_id = item.dataset_id
2816                    dataset_item_id = item.id
2817
2818                    final_observation_metadata.update(
2819                        {"dataset_id": dataset_id, "dataset_item_id": dataset_item_id}
2820                    )
2821
2822                if isinstance(item_metadata, dict):
2823                    final_observation_metadata.update(item_metadata)
2824
2825                experiment_id = dataset_run_id or self._create_observation_id()
2826                experiment_item_id = (
2827                    dataset_item_id or get_sha256_hash_hex(_serialize(input_data))[:16]
2828                )
2829                span._otel_span.set_attributes(
2830                    {
2831                        k: v
2832                        for k, v in {
2833                            LangfuseOtelSpanAttributes.ENVIRONMENT: LANGFUSE_SDK_EXPERIMENT_ENVIRONMENT,
2834                            LangfuseOtelSpanAttributes.EXPERIMENT_DESCRIPTION: experiment_description,
2835                            LangfuseOtelSpanAttributes.EXPERIMENT_ITEM_EXPECTED_OUTPUT: _serialize(
2836                                expected_output
2837                            ),
2838                        }.items()
2839                        if v is not None
2840                    }
2841                )
2842
2843                with _propagate_attributes(
2844                    experiment=PropagatedExperimentAttributes(
2845                        experiment_id=experiment_id,
2846                        experiment_name=experiment_run_name,
2847                        experiment_metadata=_serialize(experiment_metadata),
2848                        experiment_dataset_id=dataset_id,
2849                        experiment_item_id=experiment_item_id,
2850                        experiment_item_metadata=_serialize(item_metadata),
2851                        experiment_item_root_observation_id=span.id,
2852                    )
2853                ):
2854                    output = await _run_task(task, item)
2855
2856                span.update(
2857                    input=input_data,
2858                    output=output,
2859                    metadata=final_observation_metadata,
2860                )
2861
2862                # Run evaluators
2863                evaluations = []
2864
2865                for evaluator in evaluators:
2866                    try:
2867                        eval_metadata: Optional[Dict[str, Any]] = None
2868
2869                        if isinstance(item, dict):
2870                            eval_metadata = item.get("metadata")
2871                        elif hasattr(item, "metadata"):
2872                            eval_metadata = item.metadata
2873
2874                        eval_results = await _run_evaluator(
2875                            evaluator,
2876                            input=input_data,
2877                            output=output,
2878                            expected_output=expected_output,
2879                            metadata=eval_metadata,
2880                        )
2881                        evaluations.extend(eval_results)
2882
2883                        # Store evaluations as scores
2884                        for evaluation in eval_results:
2885                            self.create_score(
2886                                trace_id=trace_id,
2887                                observation_id=span.id,
2888                                name=evaluation.name,
2889                                value=evaluation.value,  # type: ignore
2890                                comment=evaluation.comment,
2891                                metadata=evaluation.metadata,
2892                                config_id=evaluation.config_id,
2893                                data_type=evaluation.data_type,  # type: ignore
2894                            )
2895
2896                    except Exception as e:
2897                        langfuse_logger.error(f"Evaluator failed: {e}")
2898
2899                return ExperimentItemResult(
2900                    item=item,
2901                    output=output,
2902                    evaluations=evaluations,
2903                    trace_id=trace_id,
2904                    dataset_run_id=dataset_run_id,
2905                )
2906
2907            except Exception as e:
2908                span.update(
2909                    output=f"Error: {str(e)}", level="ERROR", status_message=str(e)
2910                )
2911                raise e
2912
2913    def _create_experiment_run_name(
2914        self, *, name: Optional[str] = None, run_name: Optional[str] = None
2915    ) -> str:
2916        if run_name:
2917            return run_name
2918
2919        iso_timestamp = _get_timestamp().isoformat().replace("+00:00", "Z")
2920
2921        return f"{name} - {iso_timestamp}"
2922
2923    def auth_check(self) -> bool:
2924        """Check if the provided credentials (public and secret key) are valid.
2925
2926        Raises:
2927            Exception: If no projects were found for the provided credentials.
2928
2929        Note:
2930            This method is blocking. It is discouraged to use it in production code.
2931        """
2932        try:
2933            projects = self.api.projects.get()
2934            langfuse_logger.debug(
2935                f"Auth check successful, found {len(projects.data)} projects"
2936            )
2937            if len(projects.data) == 0:
2938                raise Exception(
2939                    "Auth check failed, no project found for the keys provided."
2940                )
2941            return True
2942
2943        except AttributeError as e:
2944            langfuse_logger.warning(
2945                f"Auth check failed: Client not properly initialized. Error: {e}"
2946            )
2947            return False
2948
2949        except Error as e:
2950            handle_fern_exception(e)
2951            raise e
2952
2953    def create_dataset(
2954        self,
2955        *,
2956        name: str,
2957        description: Optional[str] = None,
2958        metadata: Optional[Any] = None,
2959    ) -> Dataset:
2960        """Create a dataset with the given name on Langfuse.
2961
2962        Args:
2963            name: Name of the dataset to create.
2964            description: Description of the dataset. Defaults to None.
2965            metadata: Additional metadata. Defaults to None.
2966
2967        Returns:
2968            Dataset: The created dataset as returned by the Langfuse API.
2969        """
2970        try:
2971            body = CreateDatasetRequest(
2972                name=name, description=description, metadata=metadata
2973            )
2974            langfuse_logger.debug(f"Creating datasets {body}")
2975
2976            return self.api.datasets.create(request=body)
2977
2978        except Error as e:
2979            handle_fern_exception(e)
2980            raise e
2981
2982    def create_dataset_item(
2983        self,
2984        *,
2985        dataset_name: str,
2986        input: Optional[Any] = None,
2987        expected_output: Optional[Any] = None,
2988        metadata: Optional[Any] = None,
2989        source_trace_id: Optional[str] = None,
2990        source_observation_id: Optional[str] = None,
2991        status: Optional[DatasetStatus] = None,
2992        id: Optional[str] = None,
2993    ) -> DatasetItem:
2994        """Create a dataset item.
2995
2996        Upserts if an item with id already exists.
2997
2998        Args:
2999            dataset_name: Name of the dataset in which the dataset item should be created.
3000            input: Input data. Defaults to None. Can contain any dict, list or scalar.
3001            expected_output: Expected output data. Defaults to None. Can contain any dict, list or scalar.
3002            metadata: Additional metadata. Defaults to None. Can contain any dict, list or scalar.
3003            source_trace_id: Id of the source trace. Defaults to None.
3004            source_observation_id: Id of the source observation. Defaults to None.
3005            status: Status of the dataset item. Defaults to ACTIVE for newly created items.
3006            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.
3007
3008        Returns:
3009            DatasetItem: The created dataset item as returned by the Langfuse API.
3010
3011        Example:
3012            ```python
3013            from langfuse import Langfuse
3014
3015            langfuse = Langfuse()
3016
3017            # Uploading items to the Langfuse dataset named "capital_cities"
3018            langfuse.create_dataset_item(
3019                dataset_name="capital_cities",
3020                input={"input": {"country": "Italy"}},
3021                expected_output={"expected_output": "Rome"},
3022                metadata={"foo": "bar"}
3023            )
3024            ```
3025        """
3026        try:
3027            body = CreateDatasetItemRequest(
3028                datasetName=dataset_name,
3029                input=input,
3030                expectedOutput=expected_output,
3031                metadata=metadata,
3032                sourceTraceId=source_trace_id,
3033                sourceObservationId=source_observation_id,
3034                status=status,
3035                id=id,
3036            )
3037            langfuse_logger.debug(f"Creating dataset item {body}")
3038            return self.api.dataset_items.create(request=body)
3039        except Error as e:
3040            handle_fern_exception(e)
3041            raise e
3042
3043    def resolve_media_references(
3044        self,
3045        *,
3046        obj: Any,
3047        resolve_with: Literal["base64_data_uri"],
3048        max_depth: int = 10,
3049        content_fetch_timeout_seconds: int = 5,
3050    ) -> Any:
3051        """Replace media reference strings in an object with base64 data URIs.
3052
3053        This method recursively traverses an object (up to max_depth) looking for media reference strings
3054        in the format "@@@langfuseMedia:...@@@". When found, it (synchronously) fetches the actual media content using
3055        the provided Langfuse client and replaces the reference string with a base64 data URI.
3056
3057        If fetching media content fails for a reference string, a warning is logged and the reference
3058        string is left unchanged.
3059
3060        Args:
3061            obj: The object to process. Can be a primitive value, array, or nested object.
3062                If the object has a __dict__ attribute, a dict will be returned instead of the original object type.
3063            resolve_with: The representation of the media content to replace the media reference string with.
3064                Currently only "base64_data_uri" is supported.
3065            max_depth: int: The maximum depth to traverse the object. Default is 10.
3066            content_fetch_timeout_seconds: int: The timeout in seconds for fetching media content. Default is 5.
3067
3068        Returns:
3069            A deep copy of the input object with all media references replaced with base64 data URIs where possible.
3070            If the input object has a __dict__ attribute, a dict will be returned instead of the original object type.
3071
3072        Example:
3073            obj = {
3074                "image": "@@@langfuseMedia:type=image/jpeg|id=123|source=bytes@@@",
3075                "nested": {
3076                    "pdf": "@@@langfuseMedia:type=application/pdf|id=456|source=bytes@@@"
3077                }
3078            }
3079
3080            result = await LangfuseMedia.resolve_media_references(obj, langfuse_client)
3081
3082            # Result:
3083            # {
3084            #     "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
3085            #     "nested": {
3086            #         "pdf": "data:application/pdf;base64,JVBERi0xLjcK..."
3087            #     }
3088            # }
3089        """
3090        return LangfuseMedia.resolve_media_references(
3091            langfuse_client=self,
3092            obj=obj,
3093            resolve_with=resolve_with,
3094            max_depth=max_depth,
3095            content_fetch_timeout_seconds=content_fetch_timeout_seconds,
3096        )
3097
3098    @overload
3099    def get_prompt(
3100        self,
3101        name: str,
3102        *,
3103        version: Optional[int] = None,
3104        label: Optional[str] = None,
3105        type: Literal["chat"],
3106        cache_ttl_seconds: Optional[int] = None,
3107        fallback: Optional[List[ChatMessageDict]] = None,
3108        max_retries: Optional[int] = None,
3109        fetch_timeout_seconds: Optional[int] = None,
3110    ) -> ChatPromptClient: ...
3111
3112    @overload
3113    def get_prompt(
3114        self,
3115        name: str,
3116        *,
3117        version: Optional[int] = None,
3118        label: Optional[str] = None,
3119        type: Literal["text"] = "text",
3120        cache_ttl_seconds: Optional[int] = None,
3121        fallback: Optional[str] = None,
3122        max_retries: Optional[int] = None,
3123        fetch_timeout_seconds: Optional[int] = None,
3124    ) -> TextPromptClient: ...
3125
3126    def get_prompt(
3127        self,
3128        name: str,
3129        *,
3130        version: Optional[int] = None,
3131        label: Optional[str] = None,
3132        type: Literal["chat", "text"] = "text",
3133        cache_ttl_seconds: Optional[int] = None,
3134        fallback: Union[Optional[List[ChatMessageDict]], Optional[str]] = None,
3135        max_retries: Optional[int] = None,
3136        fetch_timeout_seconds: Optional[int] = None,
3137    ) -> PromptClient:
3138        """Get a prompt.
3139
3140        This method attempts to fetch the requested prompt from the local cache. If the prompt is not found
3141        in the cache or if the cached prompt has expired, it will try to fetch the prompt from the server again
3142        and update the cache. If fetching the new prompt fails, and there is an expired prompt in the cache, it will
3143        return the expired prompt as a fallback.
3144
3145        Args:
3146            name (str): The name of the prompt to retrieve.
3147
3148        Keyword Args:
3149            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.
3150            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.
3151            cache_ttl_seconds: Optional[int]: Time-to-live in seconds for caching the prompt. Must be specified as a
3152            keyword argument. If not set, defaults to 60 seconds. Disables caching if set to 0.
3153            type: Literal["chat", "text"]: The type of the prompt to retrieve. Defaults to "text".
3154            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.
3155            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.
3156            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.
3157
3158        Returns:
3159            The prompt object retrieved from the cache or directly fetched if not cached or expired of type
3160            - TextPromptClient, if type argument is 'text'.
3161            - ChatPromptClient, if type argument is 'chat'.
3162
3163        Raises:
3164            Exception: Propagates any exceptions raised during the fetching of a new prompt, unless there is an
3165            expired prompt in the cache, in which case it logs a warning and returns the expired prompt.
3166        """
3167        if self._resources is None:
3168            raise Error(
3169                "SDK is not correctly initialized. Check the init logs for more details."
3170            )
3171        if version is not None and label is not None:
3172            raise ValueError("Cannot specify both version and label at the same time.")
3173
3174        if not name:
3175            raise ValueError("Prompt name cannot be empty.")
3176
3177        cache_key = PromptCache.generate_cache_key(name, version=version, label=label)
3178        bounded_max_retries = self._get_bounded_max_retries(
3179            max_retries, default_max_retries=2, max_retries_upper_bound=4
3180        )
3181
3182        langfuse_logger.debug(f"Getting prompt '{cache_key}'")
3183        cached_prompt = self._resources.prompt_cache.get(cache_key)
3184
3185        if cached_prompt is None or cache_ttl_seconds == 0:
3186            langfuse_logger.debug(
3187                f"Prompt '{cache_key}' not found in cache or caching disabled."
3188            )
3189            try:
3190                return self._fetch_prompt_and_update_cache(
3191                    name,
3192                    version=version,
3193                    label=label,
3194                    ttl_seconds=cache_ttl_seconds,
3195                    max_retries=bounded_max_retries,
3196                    fetch_timeout_seconds=fetch_timeout_seconds,
3197                )
3198            except Exception as e:
3199                if fallback:
3200                    langfuse_logger.warning(
3201                        f"Returning fallback prompt for '{cache_key}' due to fetch error: {e}"
3202                    )
3203
3204                    fallback_client_args: Dict[str, Any] = {
3205                        "name": name,
3206                        "prompt": fallback,
3207                        "type": type,
3208                        "version": version or 0,
3209                        "config": {},
3210                        "labels": [label] if label else [],
3211                        "tags": [],
3212                    }
3213
3214                    if type == "text":
3215                        return TextPromptClient(
3216                            prompt=Prompt_Text(**fallback_client_args),
3217                            is_fallback=True,
3218                        )
3219
3220                    if type == "chat":
3221                        return ChatPromptClient(
3222                            prompt=Prompt_Chat(**fallback_client_args),
3223                            is_fallback=True,
3224                        )
3225
3226                raise e
3227
3228        if cached_prompt.is_expired():
3229            langfuse_logger.debug(f"Stale prompt '{cache_key}' found in cache.")
3230            try:
3231                # refresh prompt in background thread, refresh_prompt deduplicates tasks
3232                langfuse_logger.debug(f"Refreshing prompt '{cache_key}' in background.")
3233
3234                def refresh_task() -> None:
3235                    self._fetch_prompt_and_update_cache(
3236                        name,
3237                        version=version,
3238                        label=label,
3239                        ttl_seconds=cache_ttl_seconds,
3240                        max_retries=bounded_max_retries,
3241                        fetch_timeout_seconds=fetch_timeout_seconds,
3242                    )
3243
3244                self._resources.prompt_cache.add_refresh_prompt_task(
3245                    cache_key,
3246                    refresh_task,
3247                )
3248                langfuse_logger.debug(
3249                    f"Returning stale prompt '{cache_key}' from cache."
3250                )
3251                # return stale prompt
3252                return cached_prompt.value
3253
3254            except Exception as e:
3255                langfuse_logger.warning(
3256                    f"Error when refreshing cached prompt '{cache_key}', returning cached version. Error: {e}"
3257                )
3258                # creation of refresh prompt task failed, return stale prompt
3259                return cached_prompt.value
3260
3261        return cached_prompt.value
3262
3263    def _fetch_prompt_and_update_cache(
3264        self,
3265        name: str,
3266        *,
3267        version: Optional[int] = None,
3268        label: Optional[str] = None,
3269        ttl_seconds: Optional[int] = None,
3270        max_retries: int,
3271        fetch_timeout_seconds: Optional[int],
3272    ) -> PromptClient:
3273        cache_key = PromptCache.generate_cache_key(name, version=version, label=label)
3274        langfuse_logger.debug(f"Fetching prompt '{cache_key}' from server...")
3275
3276        try:
3277
3278            @backoff.on_exception(
3279                backoff.constant, Exception, max_tries=max_retries + 1, logger=None
3280            )
3281            def fetch_prompts() -> Any:
3282                return self.api.prompts.get(
3283                    self._url_encode(name),
3284                    version=version,
3285                    label=label,
3286                    request_options={
3287                        "timeout_in_seconds": fetch_timeout_seconds,
3288                    }
3289                    if fetch_timeout_seconds is not None
3290                    else None,
3291                )
3292
3293            prompt_response = fetch_prompts()
3294
3295            prompt: PromptClient
3296            if prompt_response.type == "chat":
3297                prompt = ChatPromptClient(prompt_response)
3298            else:
3299                prompt = TextPromptClient(prompt_response)
3300
3301            if self._resources is not None:
3302                self._resources.prompt_cache.set(cache_key, prompt, ttl_seconds)
3303
3304            return prompt
3305
3306        except Exception as e:
3307            langfuse_logger.error(
3308                f"Error while fetching prompt '{cache_key}': {str(e)}"
3309            )
3310            raise e
3311
3312    def _get_bounded_max_retries(
3313        self,
3314        max_retries: Optional[int],
3315        *,
3316        default_max_retries: int = 2,
3317        max_retries_upper_bound: int = 4,
3318    ) -> int:
3319        if max_retries is None:
3320            return default_max_retries
3321
3322        bounded_max_retries = min(
3323            max(max_retries, 0),
3324            max_retries_upper_bound,
3325        )
3326
3327        return bounded_max_retries
3328
3329    @overload
3330    def create_prompt(
3331        self,
3332        *,
3333        name: str,
3334        prompt: List[Union[ChatMessageDict, ChatMessageWithPlaceholdersDict]],
3335        labels: List[str] = [],
3336        tags: Optional[List[str]] = None,
3337        type: Optional[Literal["chat"]],
3338        config: Optional[Any] = None,
3339        commit_message: Optional[str] = None,
3340    ) -> ChatPromptClient: ...
3341
3342    @overload
3343    def create_prompt(
3344        self,
3345        *,
3346        name: str,
3347        prompt: str,
3348        labels: List[str] = [],
3349        tags: Optional[List[str]] = None,
3350        type: Optional[Literal["text"]] = "text",
3351        config: Optional[Any] = None,
3352        commit_message: Optional[str] = None,
3353    ) -> TextPromptClient: ...
3354
3355    def create_prompt(
3356        self,
3357        *,
3358        name: str,
3359        prompt: Union[
3360            str, List[Union[ChatMessageDict, ChatMessageWithPlaceholdersDict]]
3361        ],
3362        labels: List[str] = [],
3363        tags: Optional[List[str]] = None,
3364        type: Optional[Literal["chat", "text"]] = "text",
3365        config: Optional[Any] = None,
3366        commit_message: Optional[str] = None,
3367    ) -> PromptClient:
3368        """Create a new prompt in Langfuse.
3369
3370        Keyword Args:
3371            name : The name of the prompt to be created.
3372            prompt : The content of the prompt to be created.
3373            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.
3374            labels: The labels of the prompt. Defaults to None. To create a default-served prompt, add the 'production' label.
3375            tags: The tags of the prompt. Defaults to None. Will be applied to all versions of the prompt.
3376            config: Additional structured data to be saved with the prompt. Defaults to None.
3377            type: The type of the prompt to be created. "chat" vs. "text". Defaults to "text".
3378            commit_message: Optional string describing the change.
3379
3380        Returns:
3381            TextPromptClient: The prompt if type argument is 'text'.
3382            ChatPromptClient: The prompt if type argument is 'chat'.
3383        """
3384        try:
3385            langfuse_logger.debug(f"Creating prompt {name=}, {labels=}")
3386
3387            if type == "chat":
3388                if not isinstance(prompt, list):
3389                    raise ValueError(
3390                        "For 'chat' type, 'prompt' must be a list of chat messages with role and content attributes."
3391                    )
3392                request: Union[CreatePromptRequest_Chat, CreatePromptRequest_Text] = (
3393                    CreatePromptRequest_Chat(
3394                        name=name,
3395                        prompt=cast(Any, prompt),
3396                        labels=labels,
3397                        tags=tags,
3398                        config=config or {},
3399                        commitMessage=commit_message,
3400                        type="chat",
3401                    )
3402                )
3403                server_prompt = self.api.prompts.create(request=request)
3404
3405                if self._resources is not None:
3406                    self._resources.prompt_cache.invalidate(name)
3407
3408                return ChatPromptClient(prompt=cast(Prompt_Chat, server_prompt))
3409
3410            if not isinstance(prompt, str):
3411                raise ValueError("For 'text' type, 'prompt' must be a string.")
3412
3413            request = CreatePromptRequest_Text(
3414                name=name,
3415                prompt=prompt,
3416                labels=labels,
3417                tags=tags,
3418                config=config or {},
3419                commitMessage=commit_message,
3420                type="text",
3421            )
3422
3423            server_prompt = self.api.prompts.create(request=request)
3424
3425            if self._resources is not None:
3426                self._resources.prompt_cache.invalidate(name)
3427
3428            return TextPromptClient(prompt=cast(Prompt_Text, server_prompt))
3429
3430        except Error as e:
3431            handle_fern_exception(e)
3432            raise e
3433
3434    def update_prompt(
3435        self,
3436        *,
3437        name: str,
3438        version: int,
3439        new_labels: List[str] = [],
3440    ) -> Any:
3441        """Update an existing prompt version in Langfuse. The Langfuse SDK prompt cache is invalidated for all prompts witht he specified name.
3442
3443        Args:
3444            name (str): The name of the prompt to update.
3445            version (int): The version number of the prompt to update.
3446            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 [].
3447
3448        Returns:
3449            Prompt: The updated prompt from the Langfuse API.
3450
3451        """
3452        updated_prompt = self.api.prompt_version.update(
3453            name=self._url_encode(name),
3454            version=version,
3455            new_labels=new_labels,
3456        )
3457
3458        if self._resources is not None:
3459            self._resources.prompt_cache.invalidate(name)
3460
3461        return updated_prompt
3462
3463    def _url_encode(self, url: str, *, is_url_param: Optional[bool] = False) -> str:
3464        # httpx ≥ 0.28 does its own WHATWG-compliant quoting (eg. encodes bare
3465        # “%”, “?”, “#”, “|”, … in query/path parts).  Re-quoting here would
3466        # double-encode, so we skip when the value is about to be sent straight
3467        # to httpx (`is_url_param=True`) and the installed version is ≥ 0.28.
3468        if is_url_param and Version(httpx.__version__) >= Version("0.28.0"):
3469            return url
3470
3471        # urllib.parse.quote does not escape slashes "/" by default; we need to add safe="" to force escaping
3472        # we need add safe="" to force escaping of slashes
3473        # This is necessary for prompts in prompt folders
3474        return urllib.parse.quote(url, safe="")
3475
3476    def clear_prompt_cache(self) -> None:
3477        """Clear the entire prompt cache, removing all cached prompts.
3478
3479        This method is useful when you want to force a complete refresh of all
3480        cached prompts, for example after major updates or when you need to
3481        ensure the latest versions are fetched from the server.
3482        """
3483        if self._resources is not None:
3484            self._resources.prompt_cache.clear()

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
Arguments:
  • 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]]): List of instrumentation scope names to block from being exported to Langfuse. Spans from these scopes will be filtered out before being sent to the API. Useful for filtering out spans from specific libraries or frameworks. For exported spans, you can see the instrumentation scope name in the span metadata in Langfuse (metadata.scope.name)
  • additional_headers (Optional[Dict[str, str]]): Additional headers to include in all API requests and 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.
  • 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.
Example:
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_span(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")
Langfuse( *, public_key: Optional[str] = None, secret_key: Optional[str] = None, base_url: Optional[str] = None, host: Optional[str] = None, timeout: Optional[int] = None, httpx_client: Optional[httpx.Client] = None, debug: bool = False, tracing_enabled: Optional[bool] = True, flush_at: Optional[int] = None, flush_interval: Optional[float] = None, environment: Optional[str] = None, release: Optional[str] = None, media_upload_thread_count: Optional[int] = None, sample_rate: Optional[float] = None, mask: Optional[langfuse.types.MaskFunction] = None, blocked_instrumentation_scopes: Optional[List[str]] = None, additional_headers: Optional[Dict[str, str]] = None, tracer_provider: Optional[opentelemetry.sdk.trace.TracerProvider] = None)
200    def __init__(
201        self,
202        *,
203        public_key: Optional[str] = None,
204        secret_key: Optional[str] = None,
205        base_url: Optional[str] = None,
206        host: Optional[str] = None,
207        timeout: Optional[int] = None,
208        httpx_client: Optional[httpx.Client] = None,
209        debug: bool = False,
210        tracing_enabled: Optional[bool] = True,
211        flush_at: Optional[int] = None,
212        flush_interval: Optional[float] = None,
213        environment: Optional[str] = None,
214        release: Optional[str] = None,
215        media_upload_thread_count: Optional[int] = None,
216        sample_rate: Optional[float] = None,
217        mask: Optional[MaskFunction] = None,
218        blocked_instrumentation_scopes: Optional[List[str]] = None,
219        additional_headers: Optional[Dict[str, str]] = None,
220        tracer_provider: Optional[TracerProvider] = None,
221    ):
222        self._base_url = (
223            base_url
224            or os.environ.get(LANGFUSE_BASE_URL)
225            or host
226            or os.environ.get(LANGFUSE_HOST, "https://cloud.langfuse.com")
227        )
228        self._environment = environment or cast(
229            str, os.environ.get(LANGFUSE_TRACING_ENVIRONMENT)
230        )
231        self._project_id: Optional[str] = None
232        sample_rate = sample_rate or float(os.environ.get(LANGFUSE_SAMPLE_RATE, 1.0))
233        if not 0.0 <= sample_rate <= 1.0:
234            raise ValueError(
235                f"Sample rate must be between 0.0 and 1.0, got {sample_rate}"
236            )
237
238        timeout = timeout or int(os.environ.get(LANGFUSE_TIMEOUT, 5))
239
240        self._tracing_enabled = (
241            tracing_enabled
242            and os.environ.get(LANGFUSE_TRACING_ENABLED, "true").lower() != "false"
243        )
244        if not self._tracing_enabled:
245            langfuse_logger.info(
246                "Configuration: Langfuse tracing is explicitly disabled. No data will be sent to the Langfuse API."
247            )
248
249        debug = (
250            debug if debug else (os.getenv(LANGFUSE_DEBUG, "false").lower() == "true")
251        )
252        if debug:
253            logging.basicConfig(
254                format="%(asctime)s - %(name)s - %(levelname)s - %(message)s"
255            )
256            langfuse_logger.setLevel(logging.DEBUG)
257
258        public_key = public_key or os.environ.get(LANGFUSE_PUBLIC_KEY)
259        if public_key is None:
260            langfuse_logger.warning(
261                "Authentication error: Langfuse client initialized without public_key. Client will be disabled. "
262                "Provide a public_key parameter or set LANGFUSE_PUBLIC_KEY environment variable. "
263            )
264            self._otel_tracer = otel_trace_api.NoOpTracer()
265            return
266
267        secret_key = secret_key or os.environ.get(LANGFUSE_SECRET_KEY)
268        if secret_key is None:
269            langfuse_logger.warning(
270                "Authentication error: Langfuse client initialized without secret_key. Client will be disabled. "
271                "Provide a secret_key parameter or set LANGFUSE_SECRET_KEY environment variable. "
272            )
273            self._otel_tracer = otel_trace_api.NoOpTracer()
274            return
275
276        if os.environ.get("OTEL_SDK_DISABLED", "false").lower() == "true":
277            langfuse_logger.warning(
278                "OTEL_SDK_DISABLED is set. Langfuse tracing will be disabled and no traces will appear in the UI."
279            )
280
281        # Initialize api and tracer if requirements are met
282        self._resources = LangfuseResourceManager(
283            public_key=public_key,
284            secret_key=secret_key,
285            base_url=self._base_url,
286            timeout=timeout,
287            environment=self._environment,
288            release=release,
289            flush_at=flush_at,
290            flush_interval=flush_interval,
291            httpx_client=httpx_client,
292            media_upload_thread_count=media_upload_thread_count,
293            sample_rate=sample_rate,
294            mask=mask,
295            tracing_enabled=self._tracing_enabled,
296            blocked_instrumentation_scopes=blocked_instrumentation_scopes,
297            additional_headers=additional_headers,
298            tracer_provider=tracer_provider,
299        )
300        self._mask = self._resources.mask
301
302        self._otel_tracer = (
303            self._resources.tracer
304            if self._tracing_enabled and self._resources.tracer is not None
305            else otel_trace_api.NoOpTracer()
306        )
307        self.api = self._resources.api
308        self.async_api = self._resources.async_api
api
async_api
def start_span( self, *, trace_context: Optional[langfuse.types.TraceContext] = None, name: str, input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, version: Optional[str] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None) -> LangfuseSpan:
310    def start_span(
311        self,
312        *,
313        trace_context: Optional[TraceContext] = None,
314        name: str,
315        input: Optional[Any] = None,
316        output: Optional[Any] = None,
317        metadata: Optional[Any] = None,
318        version: Optional[str] = None,
319        level: Optional[SpanLevel] = None,
320        status_message: Optional[str] = None,
321    ) -> LangfuseSpan:
322        """Create a new span for tracing a unit of work.
323
324        This method creates a new span but does not set it as the current span in the
325        context. To create and use a span within a context, use start_as_current_span().
326
327        The created span will be the child of the current span in the context.
328
329        Args:
330            trace_context: Optional context for connecting to an existing trace
331            name: Name of the span (e.g., function or operation name)
332            input: Input data for the operation (can be any JSON-serializable object)
333            output: Output data from the operation (can be any JSON-serializable object)
334            metadata: Additional metadata to associate with the span
335            version: Version identifier for the code or component
336            level: Importance level of the span (info, warning, error)
337            status_message: Optional status message for the span
338
339        Returns:
340            A LangfuseSpan object that must be ended with .end() when the operation completes
341
342        Example:
343            ```python
344            span = langfuse.start_span(name="process-data")
345            try:
346                # Do work
347                span.update(output="result")
348            finally:
349                span.end()
350            ```
351        """
352        return self.start_observation(
353            trace_context=trace_context,
354            name=name,
355            as_type="span",
356            input=input,
357            output=output,
358            metadata=metadata,
359            version=version,
360            level=level,
361            status_message=status_message,
362        )

Create a new span for tracing a unit of work.

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

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

Arguments:
  • 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:

A LangfuseSpan object that must be ended with .end() when the operation completes

Example:
span = langfuse.start_span(name="process-data")
try:
    # Do work
    span.update(output="result")
finally:
    span.end()
def start_as_current_span( self, *, trace_context: Optional[langfuse.types.TraceContext] = None, name: str, input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, version: Optional[str] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, end_on_exit: Optional[bool] = None) -> opentelemetry.util._decorator._AgnosticContextManager[LangfuseSpan]:
364    def start_as_current_span(
365        self,
366        *,
367        trace_context: Optional[TraceContext] = None,
368        name: str,
369        input: Optional[Any] = None,
370        output: Optional[Any] = None,
371        metadata: Optional[Any] = None,
372        version: Optional[str] = None,
373        level: Optional[SpanLevel] = None,
374        status_message: Optional[str] = None,
375        end_on_exit: Optional[bool] = None,
376    ) -> _AgnosticContextManager[LangfuseSpan]:
377        """Create a new span and set it as the current span in a context manager.
378
379        This method creates a new span and sets it as the current span within a context
380        manager. Use this method with a 'with' statement to automatically handle span
381        lifecycle within a code block.
382
383        The created span will be the child of the current span in the context.
384
385        Args:
386            trace_context: Optional context for connecting to an existing trace
387            name: Name of the span (e.g., function or operation name)
388            input: Input data for the operation (can be any JSON-serializable object)
389            output: Output data from the operation (can be any JSON-serializable object)
390            metadata: Additional metadata to associate with the span
391            version: Version identifier for the code or component
392            level: Importance level of the span (info, warning, error)
393            status_message: Optional status message for the span
394            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.
395
396        Returns:
397            A context manager that yields a LangfuseSpan
398
399        Example:
400            ```python
401            with langfuse.start_as_current_span(name="process-query") as span:
402                # Do work
403                result = process_data()
404                span.update(output=result)
405
406                # Create a child span automatically
407                with span.start_as_current_span(name="sub-operation") as child_span:
408                    # Do sub-operation work
409                    child_span.update(output="sub-result")
410            ```
411        """
412        return self.start_as_current_observation(
413            trace_context=trace_context,
414            name=name,
415            as_type="span",
416            input=input,
417            output=output,
418            metadata=metadata,
419            version=version,
420            level=level,
421            status_message=status_message,
422            end_on_exit=end_on_exit,
423        )

Create a new span and set it as the current span in a context manager.

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

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

Arguments:
  • 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
  • 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.
Returns:

A context manager that yields a LangfuseSpan

Example:
with langfuse.start_as_current_span(name="process-query") as span:
    # Do work
    result = process_data()
    span.update(output=result)

    # Create a child span automatically
    with span.start_as_current_span(name="sub-operation") as child_span:
        # Do sub-operation work
        child_span.update(output="sub-result")
def start_observation( self, *, trace_context: Optional[langfuse.types.TraceContext] = None, name: str, as_type: Union[Literal['generation', 'embedding'], Literal['span', 'agent', 'tool', 'chain', 'retriever', 'evaluator', 'guardrail']] = 'span', input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, version: Optional[str] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, completion_start_time: Optional[datetime.datetime] = None, model: Optional[str] = None, model_parameters: Optional[Dict[str, Union[str, NoneType, int, bool, List[str]]]] = None, usage_details: Optional[Dict[str, int]] = None, cost_details: Optional[Dict[str, float]] = None, prompt: Union[langfuse.model.TextPromptClient, langfuse.model.ChatPromptClient, NoneType] = None) -> Union[LangfuseSpan, LangfuseGeneration, LangfuseAgent, LangfuseTool, LangfuseChain, LangfuseRetriever, LangfuseEvaluator, LangfuseEmbedding, LangfuseGuardrail]:
572    def start_observation(
573        self,
574        *,
575        trace_context: Optional[TraceContext] = None,
576        name: str,
577        as_type: ObservationTypeLiteralNoEvent = "span",
578        input: Optional[Any] = None,
579        output: Optional[Any] = None,
580        metadata: Optional[Any] = None,
581        version: Optional[str] = None,
582        level: Optional[SpanLevel] = None,
583        status_message: Optional[str] = None,
584        completion_start_time: Optional[datetime] = None,
585        model: Optional[str] = None,
586        model_parameters: Optional[Dict[str, MapValue]] = None,
587        usage_details: Optional[Dict[str, int]] = None,
588        cost_details: Optional[Dict[str, float]] = None,
589        prompt: Optional[PromptClient] = None,
590    ) -> Union[
591        LangfuseSpan,
592        LangfuseGeneration,
593        LangfuseAgent,
594        LangfuseTool,
595        LangfuseChain,
596        LangfuseRetriever,
597        LangfuseEvaluator,
598        LangfuseEmbedding,
599        LangfuseGuardrail,
600    ]:
601        """Create a new observation of the specified type.
602
603        This method creates a new observation but does not set it as the current span in the
604        context. To create and use an observation within a context, use start_as_current_observation().
605
606        Args:
607            trace_context: Optional context for connecting to an existing trace
608            name: Name of the observation
609            as_type: Type of observation to create (defaults to "span")
610            input: Input data for the operation
611            output: Output data from the operation
612            metadata: Additional metadata to associate with the observation
613            version: Version identifier for the code or component
614            level: Importance level of the observation
615            status_message: Optional status message for the observation
616            completion_start_time: When the model started generating (for generation types)
617            model: Name/identifier of the AI model used (for generation types)
618            model_parameters: Parameters used for the model (for generation types)
619            usage_details: Token usage information (for generation types)
620            cost_details: Cost information (for generation types)
621            prompt: Associated prompt template (for generation types)
622
623        Returns:
624            An observation object of the appropriate type that must be ended with .end()
625        """
626        if trace_context:
627            trace_id = trace_context.get("trace_id", None)
628            parent_span_id = trace_context.get("parent_span_id", None)
629
630            if trace_id:
631                remote_parent_span = self._create_remote_parent_span(
632                    trace_id=trace_id, parent_span_id=parent_span_id
633                )
634
635                with otel_trace_api.use_span(
636                    cast(otel_trace_api.Span, remote_parent_span)
637                ):
638                    otel_span = self._otel_tracer.start_span(name=name)
639                    otel_span.set_attribute(LangfuseOtelSpanAttributes.AS_ROOT, True)
640
641                    return self._create_observation_from_otel_span(
642                        otel_span=otel_span,
643                        as_type=as_type,
644                        input=input,
645                        output=output,
646                        metadata=metadata,
647                        version=version,
648                        level=level,
649                        status_message=status_message,
650                        completion_start_time=completion_start_time,
651                        model=model,
652                        model_parameters=model_parameters,
653                        usage_details=usage_details,
654                        cost_details=cost_details,
655                        prompt=prompt,
656                    )
657
658        otel_span = self._otel_tracer.start_span(name=name)
659
660        return self._create_observation_from_otel_span(
661            otel_span=otel_span,
662            as_type=as_type,
663            input=input,
664            output=output,
665            metadata=metadata,
666            version=version,
667            level=level,
668            status_message=status_message,
669            completion_start_time=completion_start_time,
670            model=model,
671            model_parameters=model_parameters,
672            usage_details=usage_details,
673            cost_details=cost_details,
674            prompt=prompt,
675        )

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().

Arguments:
  • 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()

def start_generation( self, *, trace_context: Optional[langfuse.types.TraceContext] = None, name: str, input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, version: Optional[str] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, completion_start_time: Optional[datetime.datetime] = None, model: Optional[str] = None, model_parameters: Optional[Dict[str, Union[str, NoneType, int, bool, List[str]]]] = None, usage_details: Optional[Dict[str, int]] = None, cost_details: Optional[Dict[str, float]] = None, prompt: Union[langfuse.model.TextPromptClient, langfuse.model.ChatPromptClient, NoneType] = None) -> LangfuseGeneration:
747    def start_generation(
748        self,
749        *,
750        trace_context: Optional[TraceContext] = None,
751        name: str,
752        input: Optional[Any] = None,
753        output: Optional[Any] = None,
754        metadata: Optional[Any] = None,
755        version: Optional[str] = None,
756        level: Optional[SpanLevel] = None,
757        status_message: Optional[str] = None,
758        completion_start_time: Optional[datetime] = None,
759        model: Optional[str] = None,
760        model_parameters: Optional[Dict[str, MapValue]] = None,
761        usage_details: Optional[Dict[str, int]] = None,
762        cost_details: Optional[Dict[str, float]] = None,
763        prompt: Optional[PromptClient] = None,
764    ) -> LangfuseGeneration:
765        """Create a new generation span for model generations.
766
767        DEPRECATED: This method is deprecated and will be removed in a future version.
768        Use start_observation(as_type='generation') instead.
769
770        This method creates a specialized span for tracking model generations.
771        It includes additional fields specific to model generations such as model name,
772        token usage, and cost details.
773
774        The created generation span will be the child of the current span in the context.
775
776        Args:
777            trace_context: Optional context for connecting to an existing trace
778            name: Name of the generation operation
779            input: Input data for the model (e.g., prompts)
780            output: Output from the model (e.g., completions)
781            metadata: Additional metadata to associate with the generation
782            version: Version identifier for the model or component
783            level: Importance level of the generation (info, warning, error)
784            status_message: Optional status message for the generation
785            completion_start_time: When the model started generating the response
786            model: Name/identifier of the AI model used (e.g., "gpt-4")
787            model_parameters: Parameters used for the model (e.g., temperature, max_tokens)
788            usage_details: Token usage information (e.g., prompt_tokens, completion_tokens)
789            cost_details: Cost information for the model call
790            prompt: Associated prompt template from Langfuse prompt management
791
792        Returns:
793            A LangfuseGeneration object that must be ended with .end() when complete
794
795        Example:
796            ```python
797            generation = langfuse.start_generation(
798                name="answer-generation",
799                model="gpt-4",
800                input={"prompt": "Explain quantum computing"},
801                model_parameters={"temperature": 0.7}
802            )
803            try:
804                # Call model API
805                response = llm.generate(...)
806
807                generation.update(
808                    output=response.text,
809                    usage_details={
810                        "prompt_tokens": response.usage.prompt_tokens,
811                        "completion_tokens": response.usage.completion_tokens
812                    }
813                )
814            finally:
815                generation.end()
816            ```
817        """
818        warnings.warn(
819            "start_generation is deprecated and will be removed in a future version. "
820            "Use start_observation(as_type='generation') instead.",
821            DeprecationWarning,
822            stacklevel=2,
823        )
824        return self.start_observation(
825            trace_context=trace_context,
826            name=name,
827            as_type="generation",
828            input=input,
829            output=output,
830            metadata=metadata,
831            version=version,
832            level=level,
833            status_message=status_message,
834            completion_start_time=completion_start_time,
835            model=model,
836            model_parameters=model_parameters,
837            usage_details=usage_details,
838            cost_details=cost_details,
839            prompt=prompt,
840        )

Create a new generation span for model generations.

DEPRECATED: This method is deprecated and will be removed in a future version. Use start_observation(as_type='generation') instead.

This method creates a specialized span for tracking model generations. It includes additional fields specific to model generations such as model name, token usage, and cost details.

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

Arguments:
  • trace_context: Optional context for connecting to an existing trace
  • name: Name of the generation operation
  • input: Input data for the model (e.g., prompts)
  • 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
Returns:

A LangfuseGeneration object that must be ended with .end() when complete

Example:
generation = langfuse.start_generation(
    name="answer-generation",
    model="gpt-4",
    input={"prompt": "Explain quantum computing"},
    model_parameters={"temperature": 0.7}
)
try:
    # Call model API
    response = llm.generate(...)

    generation.update(
        output=response.text,
        usage_details={
            "prompt_tokens": response.usage.prompt_tokens,
            "completion_tokens": response.usage.completion_tokens
        }
    )
finally:
    generation.end()
def start_as_current_generation( self, *, trace_context: Optional[langfuse.types.TraceContext] = None, name: str, input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, version: Optional[str] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, completion_start_time: Optional[datetime.datetime] = None, model: Optional[str] = None, model_parameters: Optional[Dict[str, Union[str, NoneType, int, bool, List[str]]]] = None, usage_details: Optional[Dict[str, int]] = None, cost_details: Optional[Dict[str, float]] = None, prompt: Union[langfuse.model.TextPromptClient, langfuse.model.ChatPromptClient, NoneType] = None, end_on_exit: Optional[bool] = None) -> opentelemetry.util._decorator._AgnosticContextManager[LangfuseGeneration]:
842    def start_as_current_generation(
843        self,
844        *,
845        trace_context: Optional[TraceContext] = None,
846        name: str,
847        input: Optional[Any] = None,
848        output: Optional[Any] = None,
849        metadata: Optional[Any] = None,
850        version: Optional[str] = None,
851        level: Optional[SpanLevel] = None,
852        status_message: Optional[str] = None,
853        completion_start_time: Optional[datetime] = None,
854        model: Optional[str] = None,
855        model_parameters: Optional[Dict[str, MapValue]] = None,
856        usage_details: Optional[Dict[str, int]] = None,
857        cost_details: Optional[Dict[str, float]] = None,
858        prompt: Optional[PromptClient] = None,
859        end_on_exit: Optional[bool] = None,
860    ) -> _AgnosticContextManager[LangfuseGeneration]:
861        """Create a new generation span and set it as the current span in a context manager.
862
863        DEPRECATED: This method is deprecated and will be removed in a future version.
864        Use start_as_current_observation(as_type='generation') instead.
865
866        This method creates a specialized span for model generations and sets it as the
867        current span within a context manager. Use this method with a 'with' statement to
868        automatically handle the generation span lifecycle within a code block.
869
870        The created generation span will be the child of the current span in the context.
871
872        Args:
873            trace_context: Optional context for connecting to an existing trace
874            name: Name of the generation operation
875            input: Input data for the model (e.g., prompts)
876            output: Output from the model (e.g., completions)
877            metadata: Additional metadata to associate with the generation
878            version: Version identifier for the model or component
879            level: Importance level of the generation (info, warning, error)
880            status_message: Optional status message for the generation
881            completion_start_time: When the model started generating the response
882            model: Name/identifier of the AI model used (e.g., "gpt-4")
883            model_parameters: Parameters used for the model (e.g., temperature, max_tokens)
884            usage_details: Token usage information (e.g., prompt_tokens, completion_tokens)
885            cost_details: Cost information for the model call
886            prompt: Associated prompt template from Langfuse prompt management
887            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.
888
889        Returns:
890            A context manager that yields a LangfuseGeneration
891
892        Example:
893            ```python
894            with langfuse.start_as_current_generation(
895                name="answer-generation",
896                model="gpt-4",
897                input={"prompt": "Explain quantum computing"}
898            ) as generation:
899                # Call model API
900                response = llm.generate(...)
901
902                # Update with results
903                generation.update(
904                    output=response.text,
905                    usage_details={
906                        "prompt_tokens": response.usage.prompt_tokens,
907                        "completion_tokens": response.usage.completion_tokens
908                    }
909                )
910            ```
911        """
912        warnings.warn(
913            "start_as_current_generation is deprecated and will be removed in a future version. "
914            "Use start_as_current_observation(as_type='generation') instead.",
915            DeprecationWarning,
916            stacklevel=2,
917        )
918        return self.start_as_current_observation(
919            trace_context=trace_context,
920            name=name,
921            as_type="generation",
922            input=input,
923            output=output,
924            metadata=metadata,
925            version=version,
926            level=level,
927            status_message=status_message,
928            completion_start_time=completion_start_time,
929            model=model,
930            model_parameters=model_parameters,
931            usage_details=usage_details,
932            cost_details=cost_details,
933            prompt=prompt,
934            end_on_exit=end_on_exit,
935        )

Create a new generation span and set it as the current span in a context manager.

DEPRECATED: This method is deprecated and will be removed in a future version. Use start_as_current_observation(as_type='generation') instead.

This method creates a specialized span for model generations and sets it as the current span within a context manager. Use this method with a 'with' statement to automatically handle the generation span lifecycle within a code block.

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

Arguments:
  • trace_context: Optional context for connecting to an existing trace
  • name: Name of the generation operation
  • input: Input data for the model (e.g., prompts)
  • 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
  • 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.
Returns:

A context manager that yields a LangfuseGeneration

Example:
with langfuse.start_as_current_generation(
    name="answer-generation",
    model="gpt-4",
    input={"prompt": "Explain quantum computing"}
) as generation:
    # Call model API
    response = llm.generate(...)

    # Update with results
    generation.update(
        output=response.text,
        usage_details={
            "prompt_tokens": response.usage.prompt_tokens,
            "completion_tokens": response.usage.completion_tokens
        }
    )
def start_as_current_observation( self, *, trace_context: Optional[langfuse.types.TraceContext] = None, name: str, as_type: Union[Literal['generation', 'embedding'], Literal['span', 'agent', 'tool', 'chain', 'retriever', 'evaluator', 'guardrail']] = 'span', input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, version: Optional[str] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, completion_start_time: Optional[datetime.datetime] = None, model: Optional[str] = None, model_parameters: Optional[Dict[str, Union[str, NoneType, int, bool, List[str]]]] = None, usage_details: Optional[Dict[str, int]] = None, cost_details: Optional[Dict[str, float]] = None, prompt: Union[langfuse.model.TextPromptClient, langfuse.model.ChatPromptClient, NoneType] = None, end_on_exit: Optional[bool] = None) -> Union[opentelemetry.util._decorator._AgnosticContextManager[LangfuseGeneration], opentelemetry.util._decorator._AgnosticContextManager[LangfuseSpan], opentelemetry.util._decorator._AgnosticContextManager[LangfuseAgent], opentelemetry.util._decorator._AgnosticContextManager[LangfuseTool], opentelemetry.util._decorator._AgnosticContextManager[LangfuseChain], opentelemetry.util._decorator._AgnosticContextManager[LangfuseRetriever], opentelemetry.util._decorator._AgnosticContextManager[LangfuseEvaluator], opentelemetry.util._decorator._AgnosticContextManager[LangfuseEmbedding], opentelemetry.util._decorator._AgnosticContextManager[LangfuseGuardrail]]:
1093    def start_as_current_observation(
1094        self,
1095        *,
1096        trace_context: Optional[TraceContext] = None,
1097        name: str,
1098        as_type: ObservationTypeLiteralNoEvent = "span",
1099        input: Optional[Any] = None,
1100        output: Optional[Any] = None,
1101        metadata: Optional[Any] = None,
1102        version: Optional[str] = None,
1103        level: Optional[SpanLevel] = None,
1104        status_message: Optional[str] = None,
1105        completion_start_time: Optional[datetime] = None,
1106        model: Optional[str] = None,
1107        model_parameters: Optional[Dict[str, MapValue]] = None,
1108        usage_details: Optional[Dict[str, int]] = None,
1109        cost_details: Optional[Dict[str, float]] = None,
1110        prompt: Optional[PromptClient] = None,
1111        end_on_exit: Optional[bool] = None,
1112    ) -> Union[
1113        _AgnosticContextManager[LangfuseGeneration],
1114        _AgnosticContextManager[LangfuseSpan],
1115        _AgnosticContextManager[LangfuseAgent],
1116        _AgnosticContextManager[LangfuseTool],
1117        _AgnosticContextManager[LangfuseChain],
1118        _AgnosticContextManager[LangfuseRetriever],
1119        _AgnosticContextManager[LangfuseEvaluator],
1120        _AgnosticContextManager[LangfuseEmbedding],
1121        _AgnosticContextManager[LangfuseGuardrail],
1122    ]:
1123        """Create a new observation and set it as the current span in a context manager.
1124
1125        This method creates a new observation of the specified type and sets it as the
1126        current span within a context manager. Use this method with a 'with' statement to
1127        automatically handle the observation lifecycle within a code block.
1128
1129        The created observation will be the child of the current span in the context.
1130
1131        Args:
1132            trace_context: Optional context for connecting to an existing trace
1133            name: Name of the observation (e.g., function or operation name)
1134            as_type: Type of observation to create (defaults to "span")
1135            input: Input data for the operation (can be any JSON-serializable object)
1136            output: Output data from the operation (can be any JSON-serializable object)
1137            metadata: Additional metadata to associate with the observation
1138            version: Version identifier for the code or component
1139            level: Importance level of the observation (info, warning, error)
1140            status_message: Optional status message for the observation
1141            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.
1142
1143            The following parameters are available when as_type is: "generation" or "embedding".
1144            completion_start_time: When the model started generating the response
1145            model: Name/identifier of the AI model used (e.g., "gpt-4")
1146            model_parameters: Parameters used for the model (e.g., temperature, max_tokens)
1147            usage_details: Token usage information (e.g., prompt_tokens, completion_tokens)
1148            cost_details: Cost information for the model call
1149            prompt: Associated prompt template from Langfuse prompt management
1150
1151        Returns:
1152            A context manager that yields the appropriate observation type based on as_type
1153
1154        Example:
1155            ```python
1156            # Create a span
1157            with langfuse.start_as_current_observation(name="process-query", as_type="span") as span:
1158                # Do work
1159                result = process_data()
1160                span.update(output=result)
1161
1162                # Create a child span automatically
1163                with span.start_as_current_span(name="sub-operation") as child_span:
1164                    # Do sub-operation work
1165                    child_span.update(output="sub-result")
1166
1167            # Create a tool observation
1168            with langfuse.start_as_current_observation(name="web-search", as_type="tool") as tool:
1169                # Do tool work
1170                results = search_web(query)
1171                tool.update(output=results)
1172
1173            # Create a generation observation
1174            with langfuse.start_as_current_observation(
1175                name="answer-generation",
1176                as_type="generation",
1177                model="gpt-4"
1178            ) as generation:
1179                # Generate answer
1180                response = llm.generate(...)
1181                generation.update(output=response)
1182            ```
1183        """
1184        if as_type in get_observation_types_list(ObservationTypeGenerationLike):
1185            if trace_context:
1186                trace_id = trace_context.get("trace_id", None)
1187                parent_span_id = trace_context.get("parent_span_id", None)
1188
1189                if trace_id:
1190                    remote_parent_span = self._create_remote_parent_span(
1191                        trace_id=trace_id, parent_span_id=parent_span_id
1192                    )
1193
1194                    return cast(
1195                        Union[
1196                            _AgnosticContextManager[LangfuseGeneration],
1197                            _AgnosticContextManager[LangfuseEmbedding],
1198                        ],
1199                        self._create_span_with_parent_context(
1200                            as_type=as_type,
1201                            name=name,
1202                            remote_parent_span=remote_parent_span,
1203                            parent=None,
1204                            end_on_exit=end_on_exit,
1205                            input=input,
1206                            output=output,
1207                            metadata=metadata,
1208                            version=version,
1209                            level=level,
1210                            status_message=status_message,
1211                            completion_start_time=completion_start_time,
1212                            model=model,
1213                            model_parameters=model_parameters,
1214                            usage_details=usage_details,
1215                            cost_details=cost_details,
1216                            prompt=prompt,
1217                        ),
1218                    )
1219
1220            return cast(
1221                Union[
1222                    _AgnosticContextManager[LangfuseGeneration],
1223                    _AgnosticContextManager[LangfuseEmbedding],
1224                ],
1225                self._start_as_current_otel_span_with_processed_media(
1226                    as_type=as_type,
1227                    name=name,
1228                    end_on_exit=end_on_exit,
1229                    input=input,
1230                    output=output,
1231                    metadata=metadata,
1232                    version=version,
1233                    level=level,
1234                    status_message=status_message,
1235                    completion_start_time=completion_start_time,
1236                    model=model,
1237                    model_parameters=model_parameters,
1238                    usage_details=usage_details,
1239                    cost_details=cost_details,
1240                    prompt=prompt,
1241                ),
1242            )
1243
1244        if as_type in get_observation_types_list(ObservationTypeSpanLike):
1245            if trace_context:
1246                trace_id = trace_context.get("trace_id", None)
1247                parent_span_id = trace_context.get("parent_span_id", None)
1248
1249                if trace_id:
1250                    remote_parent_span = self._create_remote_parent_span(
1251                        trace_id=trace_id, parent_span_id=parent_span_id
1252                    )
1253
1254                    return cast(
1255                        Union[
1256                            _AgnosticContextManager[LangfuseSpan],
1257                            _AgnosticContextManager[LangfuseAgent],
1258                            _AgnosticContextManager[LangfuseTool],
1259                            _AgnosticContextManager[LangfuseChain],
1260                            _AgnosticContextManager[LangfuseRetriever],
1261                            _AgnosticContextManager[LangfuseEvaluator],
1262                            _AgnosticContextManager[LangfuseGuardrail],
1263                        ],
1264                        self._create_span_with_parent_context(
1265                            as_type=as_type,
1266                            name=name,
1267                            remote_parent_span=remote_parent_span,
1268                            parent=None,
1269                            end_on_exit=end_on_exit,
1270                            input=input,
1271                            output=output,
1272                            metadata=metadata,
1273                            version=version,
1274                            level=level,
1275                            status_message=status_message,
1276                        ),
1277                    )
1278
1279            return cast(
1280                Union[
1281                    _AgnosticContextManager[LangfuseSpan],
1282                    _AgnosticContextManager[LangfuseAgent],
1283                    _AgnosticContextManager[LangfuseTool],
1284                    _AgnosticContextManager[LangfuseChain],
1285                    _AgnosticContextManager[LangfuseRetriever],
1286                    _AgnosticContextManager[LangfuseEvaluator],
1287                    _AgnosticContextManager[LangfuseGuardrail],
1288                ],
1289                self._start_as_current_otel_span_with_processed_media(
1290                    as_type=as_type,
1291                    name=name,
1292                    end_on_exit=end_on_exit,
1293                    input=input,
1294                    output=output,
1295                    metadata=metadata,
1296                    version=version,
1297                    level=level,
1298                    status_message=status_message,
1299                ),
1300            )
1301
1302        # This should never be reached since all valid types are handled above
1303        langfuse_logger.warning(
1304            f"Unknown observation type: {as_type}, falling back to span"
1305        )
1306        return self._start_as_current_otel_span_with_processed_media(
1307            as_type="span",
1308            name=name,
1309            end_on_exit=end_on_exit,
1310            input=input,
1311            output=output,
1312            metadata=metadata,
1313            version=version,
1314            level=level,
1315            status_message=status_message,
1316        )

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.

Arguments:
  • 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:
# 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_span(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)
def update_current_generation( self, *, name: Optional[str] = None, input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, version: Optional[str] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, completion_start_time: Optional[datetime.datetime] = None, model: Optional[str] = None, model_parameters: Optional[Dict[str, Union[str, NoneType, int, bool, List[str]]]] = None, usage_details: Optional[Dict[str, int]] = None, cost_details: Optional[Dict[str, float]] = None, prompt: Union[langfuse.model.TextPromptClient, langfuse.model.ChatPromptClient, NoneType] = None) -> None:
1477    def update_current_generation(
1478        self,
1479        *,
1480        name: Optional[str] = None,
1481        input: Optional[Any] = None,
1482        output: Optional[Any] = None,
1483        metadata: Optional[Any] = None,
1484        version: Optional[str] = None,
1485        level: Optional[SpanLevel] = None,
1486        status_message: Optional[str] = None,
1487        completion_start_time: Optional[datetime] = None,
1488        model: Optional[str] = None,
1489        model_parameters: Optional[Dict[str, MapValue]] = None,
1490        usage_details: Optional[Dict[str, int]] = None,
1491        cost_details: Optional[Dict[str, float]] = None,
1492        prompt: Optional[PromptClient] = None,
1493    ) -> None:
1494        """Update the current active generation span with new information.
1495
1496        This method updates the current generation span in the active context with
1497        additional information. It's useful for adding output, usage stats, or other
1498        details that become available during or after model generation.
1499
1500        Args:
1501            name: The generation name
1502            input: Updated input data for the model
1503            output: Output from the model (e.g., completions)
1504            metadata: Additional metadata to associate with the generation
1505            version: Version identifier for the model or component
1506            level: Importance level of the generation (info, warning, error)
1507            status_message: Optional status message for the generation
1508            completion_start_time: When the model started generating the response
1509            model: Name/identifier of the AI model used (e.g., "gpt-4")
1510            model_parameters: Parameters used for the model (e.g., temperature, max_tokens)
1511            usage_details: Token usage information (e.g., prompt_tokens, completion_tokens)
1512            cost_details: Cost information for the model call
1513            prompt: Associated prompt template from Langfuse prompt management
1514
1515        Example:
1516            ```python
1517            with langfuse.start_as_current_generation(name="answer-query") as generation:
1518                # Initial setup and API call
1519                response = llm.generate(...)
1520
1521                # Update with results that weren't available at creation time
1522                langfuse.update_current_generation(
1523                    output=response.text,
1524                    usage_details={
1525                        "prompt_tokens": response.usage.prompt_tokens,
1526                        "completion_tokens": response.usage.completion_tokens
1527                    }
1528                )
1529            ```
1530        """
1531        if not self._tracing_enabled:
1532            langfuse_logger.debug(
1533                "Operation skipped: update_current_generation - Tracing is disabled or client is in no-op mode."
1534            )
1535            return
1536
1537        current_otel_span = self._get_current_otel_span()
1538
1539        if current_otel_span is not None:
1540            generation = LangfuseGeneration(
1541                otel_span=current_otel_span, langfuse_client=self
1542            )
1543
1544            if name:
1545                current_otel_span.update_name(name)
1546
1547            generation.update(
1548                input=input,
1549                output=output,
1550                metadata=metadata,
1551                version=version,
1552                level=level,
1553                status_message=status_message,
1554                completion_start_time=completion_start_time,
1555                model=model,
1556                model_parameters=model_parameters,
1557                usage_details=usage_details,
1558                cost_details=cost_details,
1559                prompt=prompt,
1560            )

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.

Arguments:
  • 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:
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
        }
    )
def update_current_span( self, *, name: Optional[str] = None, input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, version: Optional[str] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None) -> None:
1562    def update_current_span(
1563        self,
1564        *,
1565        name: Optional[str] = None,
1566        input: Optional[Any] = None,
1567        output: Optional[Any] = None,
1568        metadata: Optional[Any] = None,
1569        version: Optional[str] = None,
1570        level: Optional[SpanLevel] = None,
1571        status_message: Optional[str] = None,
1572    ) -> None:
1573        """Update the current active span with new information.
1574
1575        This method updates the current span in the active context with
1576        additional information. It's useful for adding outputs or metadata
1577        that become available during execution.
1578
1579        Args:
1580            name: The span name
1581            input: Updated input data for the operation
1582            output: Output data from the operation
1583            metadata: Additional metadata to associate with the span
1584            version: Version identifier for the code or component
1585            level: Importance level of the span (info, warning, error)
1586            status_message: Optional status message for the span
1587
1588        Example:
1589            ```python
1590            with langfuse.start_as_current_span(name="process-data") as span:
1591                # Initial processing
1592                result = process_first_part()
1593
1594                # Update with intermediate results
1595                langfuse.update_current_span(metadata={"intermediate_result": result})
1596
1597                # Continue processing
1598                final_result = process_second_part(result)
1599
1600                # Final update
1601                langfuse.update_current_span(output=final_result)
1602            ```
1603        """
1604        if not self._tracing_enabled:
1605            langfuse_logger.debug(
1606                "Operation skipped: update_current_span - Tracing is disabled or client is in no-op mode."
1607            )
1608            return
1609
1610        current_otel_span = self._get_current_otel_span()
1611
1612        if current_otel_span is not None:
1613            span = LangfuseSpan(
1614                otel_span=current_otel_span,
1615                langfuse_client=self,
1616                environment=self._environment,
1617            )
1618
1619            if name:
1620                current_otel_span.update_name(name)
1621
1622            span.update(
1623                input=input,
1624                output=output,
1625                metadata=metadata,
1626                version=version,
1627                level=level,
1628                status_message=status_message,
1629            )

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.

Arguments:
  • 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:
with langfuse.start_as_current_span(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)
def update_current_trace( self, *, name: Optional[str] = None, user_id: Optional[str] = None, session_id: Optional[str] = None, version: Optional[str] = None, input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, tags: Optional[List[str]] = None, public: Optional[bool] = None) -> None:
1631    def update_current_trace(
1632        self,
1633        *,
1634        name: Optional[str] = None,
1635        user_id: Optional[str] = None,
1636        session_id: Optional[str] = None,
1637        version: Optional[str] = None,
1638        input: Optional[Any] = None,
1639        output: Optional[Any] = None,
1640        metadata: Optional[Any] = None,
1641        tags: Optional[List[str]] = None,
1642        public: Optional[bool] = None,
1643    ) -> None:
1644        """Update the current trace with additional information.
1645
1646        Args:
1647            name: Updated name for the Langfuse trace
1648            user_id: ID of the user who initiated the Langfuse trace
1649            session_id: Session identifier for grouping related Langfuse traces
1650            version: Version identifier for the application or service
1651            input: Input data for the overall Langfuse trace
1652            output: Output data from the overall Langfuse trace
1653            metadata: Additional metadata to associate with the Langfuse trace
1654            tags: List of tags to categorize the Langfuse trace
1655            public: Whether the Langfuse trace should be publicly accessible
1656
1657        See Also:
1658            :func:`langfuse.propagate_attributes`: Recommended replacement
1659        """
1660        if not self._tracing_enabled:
1661            langfuse_logger.debug(
1662                "Operation skipped: update_current_trace - Tracing is disabled or client is in no-op mode."
1663            )
1664            return
1665
1666        current_otel_span = self._get_current_otel_span()
1667
1668        if current_otel_span is not None:
1669            existing_observation_type = current_otel_span.attributes.get(  # type: ignore[attr-defined]
1670                LangfuseOtelSpanAttributes.OBSERVATION_TYPE, "span"
1671            )
1672            # We need to preserve the class to keep the correct observation type
1673            span_class = self._get_span_class(existing_observation_type)
1674            span = span_class(
1675                otel_span=current_otel_span,
1676                langfuse_client=self,
1677                environment=self._environment,
1678            )
1679
1680            span.update_trace(
1681                name=name,
1682                user_id=user_id,
1683                session_id=session_id,
1684                version=version,
1685                input=input,
1686                output=output,
1687                metadata=metadata,
1688                tags=tags,
1689                public=public,
1690            )

Update the current trace with additional information.

Arguments:
  • name: Updated name for the Langfuse trace
  • user_id: ID of the user who initiated the Langfuse trace
  • session_id: Session identifier for grouping related Langfuse traces
  • version: Version identifier for the application or service
  • input: Input data for the overall Langfuse trace
  • output: Output data from the overall Langfuse trace
  • metadata: Additional metadata to associate with the Langfuse trace
  • tags: List of tags to categorize the Langfuse trace
  • public: Whether the Langfuse trace should be publicly accessible
See Also:

langfuse.propagate_attributes(): Recommended replacement

def create_event( self, *, trace_context: Optional[langfuse.types.TraceContext] = None, name: str, input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, version: Optional[str] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None) -> LangfuseEvent:
1692    def create_event(
1693        self,
1694        *,
1695        trace_context: Optional[TraceContext] = None,
1696        name: str,
1697        input: Optional[Any] = None,
1698        output: Optional[Any] = None,
1699        metadata: Optional[Any] = None,
1700        version: Optional[str] = None,
1701        level: Optional[SpanLevel] = None,
1702        status_message: Optional[str] = None,
1703    ) -> LangfuseEvent:
1704        """Create a new Langfuse observation of type 'EVENT'.
1705
1706        The created Langfuse Event observation will be the child of the current span in the context.
1707
1708        Args:
1709            trace_context: Optional context for connecting to an existing trace
1710            name: Name of the span (e.g., function or operation name)
1711            input: Input data for the operation (can be any JSON-serializable object)
1712            output: Output data from the operation (can be any JSON-serializable object)
1713            metadata: Additional metadata to associate with the span
1714            version: Version identifier for the code or component
1715            level: Importance level of the span (info, warning, error)
1716            status_message: Optional status message for the span
1717
1718        Returns:
1719            The Langfuse Event object
1720
1721        Example:
1722            ```python
1723            event = langfuse.create_event(name="process-event")
1724            ```
1725        """
1726        timestamp = time_ns()
1727
1728        if trace_context:
1729            trace_id = trace_context.get("trace_id", None)
1730            parent_span_id = trace_context.get("parent_span_id", None)
1731
1732            if trace_id:
1733                remote_parent_span = self._create_remote_parent_span(
1734                    trace_id=trace_id, parent_span_id=parent_span_id
1735                )
1736
1737                with otel_trace_api.use_span(
1738                    cast(otel_trace_api.Span, remote_parent_span)
1739                ):
1740                    otel_span = self._otel_tracer.start_span(
1741                        name=name, start_time=timestamp
1742                    )
1743                    otel_span.set_attribute(LangfuseOtelSpanAttributes.AS_ROOT, True)
1744
1745                    return cast(
1746                        LangfuseEvent,
1747                        LangfuseEvent(
1748                            otel_span=otel_span,
1749                            langfuse_client=self,
1750                            environment=self._environment,
1751                            input=input,
1752                            output=output,
1753                            metadata=metadata,
1754                            version=version,
1755                            level=level,
1756                            status_message=status_message,
1757                        ).end(end_time=timestamp),
1758                    )
1759
1760        otel_span = self._otel_tracer.start_span(name=name, start_time=timestamp)
1761
1762        return cast(
1763            LangfuseEvent,
1764            LangfuseEvent(
1765                otel_span=otel_span,
1766                langfuse_client=self,
1767                environment=self._environment,
1768                input=input,
1769                output=output,
1770                metadata=metadata,
1771                version=version,
1772                level=level,
1773                status_message=status_message,
1774            ).end(end_time=timestamp),
1775        )

Create a new Langfuse observation of type 'EVENT'.

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

Arguments:
  • 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:
event = langfuse.create_event(name="process-event")
@staticmethod
def create_trace_id(*, seed: Optional[str] = None) -> str:
1864    @staticmethod
1865    def create_trace_id(*, seed: Optional[str] = None) -> str:
1866        """Create a unique trace ID for use with Langfuse.
1867
1868        This method generates a unique trace ID for use with various Langfuse APIs.
1869        It can either generate a random ID or create a deterministic ID based on
1870        a seed string.
1871
1872        Trace IDs must be 32 lowercase hexadecimal characters, representing 16 bytes.
1873        This method ensures the generated ID meets this requirement. If you need to
1874        correlate an external ID with a Langfuse trace ID, use the external ID as the
1875        seed to get a valid, deterministic Langfuse trace ID.
1876
1877        Args:
1878            seed: Optional string to use as a seed for deterministic ID generation.
1879                 If provided, the same seed will always produce the same ID.
1880                 If not provided, a random ID will be generated.
1881
1882        Returns:
1883            A 32-character lowercase hexadecimal string representing the Langfuse trace ID.
1884
1885        Example:
1886            ```python
1887            # Generate a random trace ID
1888            trace_id = langfuse.create_trace_id()
1889
1890            # Generate a deterministic ID based on a seed
1891            session_trace_id = langfuse.create_trace_id(seed="session-456")
1892
1893            # Correlate an external ID with a Langfuse trace ID
1894            external_id = "external-system-123456"
1895            correlated_trace_id = langfuse.create_trace_id(seed=external_id)
1896
1897            # Use the ID with trace context
1898            with langfuse.start_as_current_span(
1899                name="process-request",
1900                trace_context={"trace_id": trace_id}
1901            ) as span:
1902                # Operation will be part of the specific trace
1903                pass
1904            ```
1905        """
1906        if not seed:
1907            trace_id_int = RandomIdGenerator().generate_trace_id()
1908
1909            return Langfuse._format_otel_trace_id(trace_id_int)
1910
1911        return sha256(seed.encode("utf-8")).digest()[:16].hex()

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.

Arguments:
  • 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:
# 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_span(
    name="process-request",
    trace_context={"trace_id": trace_id}
) as span:
    # Operation will be part of the specific trace
    pass
def create_score( self, *, name: str, value: Union[float, str], session_id: Optional[str] = None, dataset_run_id: Optional[str] = None, trace_id: Optional[str] = None, observation_id: Optional[str] = None, score_id: Optional[str] = None, data_type: Optional[Literal['NUMERIC', 'CATEGORICAL', 'BOOLEAN']] = None, comment: Optional[str] = None, config_id: Optional[str] = None, metadata: Optional[Any] = None) -> None:
1987    def create_score(
1988        self,
1989        *,
1990        name: str,
1991        value: Union[float, str],
1992        session_id: Optional[str] = None,
1993        dataset_run_id: Optional[str] = None,
1994        trace_id: Optional[str] = None,
1995        observation_id: Optional[str] = None,
1996        score_id: Optional[str] = None,
1997        data_type: Optional[ScoreDataType] = None,
1998        comment: Optional[str] = None,
1999        config_id: Optional[str] = None,
2000        metadata: Optional[Any] = None,
2001    ) -> None:
2002        """Create a score for a specific trace or observation.
2003
2004        This method creates a score for evaluating a Langfuse trace or observation. Scores can be
2005        used to track quality metrics, user feedback, or automated evaluations.
2006
2007        Args:
2008            name: Name of the score (e.g., "relevance", "accuracy")
2009            value: Score value (can be numeric for NUMERIC/BOOLEAN types or string for CATEGORICAL)
2010            session_id: ID of the Langfuse session to associate the score with
2011            dataset_run_id: ID of the Langfuse dataset run to associate the score with
2012            trace_id: ID of the Langfuse trace to associate the score with
2013            observation_id: Optional ID of the specific observation to score. Trace ID must be provided too.
2014            score_id: Optional custom ID for the score (auto-generated if not provided)
2015            data_type: Type of score (NUMERIC, BOOLEAN, or CATEGORICAL)
2016            comment: Optional comment or explanation for the score
2017            config_id: Optional ID of a score config defined in Langfuse
2018            metadata: Optional metadata to be attached to the score
2019
2020        Example:
2021            ```python
2022            # Create a numeric score for accuracy
2023            langfuse.create_score(
2024                name="accuracy",
2025                value=0.92,
2026                trace_id="abcdef1234567890abcdef1234567890",
2027                data_type="NUMERIC",
2028                comment="High accuracy with minor irrelevant details"
2029            )
2030
2031            # Create a categorical score for sentiment
2032            langfuse.create_score(
2033                name="sentiment",
2034                value="positive",
2035                trace_id="abcdef1234567890abcdef1234567890",
2036                observation_id="abcdef1234567890",
2037                data_type="CATEGORICAL"
2038            )
2039            ```
2040        """
2041        if not self._tracing_enabled:
2042            return
2043
2044        score_id = score_id or self._create_observation_id()
2045
2046        try:
2047            new_body = ScoreBody(
2048                id=score_id,
2049                sessionId=session_id,
2050                datasetRunId=dataset_run_id,
2051                traceId=trace_id,
2052                observationId=observation_id,
2053                name=name,
2054                value=value,
2055                dataType=data_type,  # type: ignore
2056                comment=comment,
2057                configId=config_id,
2058                environment=self._environment,
2059                metadata=metadata,
2060            )
2061
2062            event = {
2063                "id": self.create_trace_id(),
2064                "type": "score-create",
2065                "timestamp": _get_timestamp(),
2066                "body": new_body,
2067            }
2068
2069            if self._resources is not None:
2070                # Force the score to be in sample if it was for a legacy trace ID, i.e. non-32 hexchar
2071                force_sample = (
2072                    not self._is_valid_trace_id(trace_id) if trace_id else True
2073                )
2074
2075                self._resources.add_score_task(
2076                    event,
2077                    force_sample=force_sample,
2078                )
2079
2080        except Exception as e:
2081            langfuse_logger.exception(
2082                f"Error creating score: Failed to process score event for trace_id={trace_id}, name={name}. Error: {e}"
2083            )

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.

Arguments:
  • name: Name of the score (e.g., "relevance", "accuracy")
  • value: Score value (can be numeric for NUMERIC/BOOLEAN types or string for CATEGORICAL)
  • 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, or CATEGORICAL)
  • 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:
# 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"
)
def score_current_span( self, *, name: str, value: Union[float, str], score_id: Optional[str] = None, data_type: Optional[Literal['NUMERIC', 'CATEGORICAL', 'BOOLEAN']] = None, comment: Optional[str] = None, config_id: Optional[str] = None) -> None:
2109    def score_current_span(
2110        self,
2111        *,
2112        name: str,
2113        value: Union[float, str],
2114        score_id: Optional[str] = None,
2115        data_type: Optional[ScoreDataType] = None,
2116        comment: Optional[str] = None,
2117        config_id: Optional[str] = None,
2118    ) -> None:
2119        """Create a score for the current active span.
2120
2121        This method scores the currently active span in the context. It's a convenient
2122        way to score the current operation without needing to know its trace and span IDs.
2123
2124        Args:
2125            name: Name of the score (e.g., "relevance", "accuracy")
2126            value: Score value (can be numeric for NUMERIC/BOOLEAN types or string for CATEGORICAL)
2127            score_id: Optional custom ID for the score (auto-generated if not provided)
2128            data_type: Type of score (NUMERIC, BOOLEAN, or CATEGORICAL)
2129            comment: Optional comment or explanation for the score
2130            config_id: Optional ID of a score config defined in Langfuse
2131
2132        Example:
2133            ```python
2134            with langfuse.start_as_current_generation(name="answer-query") as generation:
2135                # Generate answer
2136                response = generate_answer(...)
2137                generation.update(output=response)
2138
2139                # Score the generation
2140                langfuse.score_current_span(
2141                    name="relevance",
2142                    value=0.85,
2143                    data_type="NUMERIC",
2144                    comment="Mostly relevant but contains some tangential information"
2145                )
2146            ```
2147        """
2148        current_span = self._get_current_otel_span()
2149
2150        if current_span is not None:
2151            trace_id = self._get_otel_trace_id(current_span)
2152            observation_id = self._get_otel_span_id(current_span)
2153
2154            langfuse_logger.info(
2155                f"Score: Creating score name='{name}' value={value} for current span ({observation_id}) in trace {trace_id}"
2156            )
2157
2158            self.create_score(
2159                trace_id=trace_id,
2160                observation_id=observation_id,
2161                name=name,
2162                value=cast(str, value),
2163                score_id=score_id,
2164                data_type=cast(Literal["CATEGORICAL"], data_type),
2165                comment=comment,
2166                config_id=config_id,
2167            )

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.

Arguments:
  • name: Name of the score (e.g., "relevance", "accuracy")
  • value: Score value (can be numeric for NUMERIC/BOOLEAN types or string for CATEGORICAL)
  • score_id: Optional custom ID for the score (auto-generated if not provided)
  • data_type: Type of score (NUMERIC, BOOLEAN, or CATEGORICAL)
  • comment: Optional comment or explanation for the score
  • config_id: Optional ID of a score config defined in Langfuse
Example:
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"
    )
def score_current_trace( self, *, name: str, value: Union[float, str], score_id: Optional[str] = None, data_type: Optional[Literal['NUMERIC', 'CATEGORICAL', 'BOOLEAN']] = None, comment: Optional[str] = None, config_id: Optional[str] = None) -> None:
2193    def score_current_trace(
2194        self,
2195        *,
2196        name: str,
2197        value: Union[float, str],
2198        score_id: Optional[str] = None,
2199        data_type: Optional[ScoreDataType] = None,
2200        comment: Optional[str] = None,
2201        config_id: Optional[str] = None,
2202    ) -> None:
2203        """Create a score for the current trace.
2204
2205        This method scores the trace of the currently active span. Unlike score_current_span,
2206        this method associates the score with the entire trace rather than a specific span.
2207        It's useful for scoring overall performance or quality of the entire operation.
2208
2209        Args:
2210            name: Name of the score (e.g., "user_satisfaction", "overall_quality")
2211            value: Score value (can be numeric for NUMERIC/BOOLEAN types or string for CATEGORICAL)
2212            score_id: Optional custom ID for the score (auto-generated if not provided)
2213            data_type: Type of score (NUMERIC, BOOLEAN, or CATEGORICAL)
2214            comment: Optional comment or explanation for the score
2215            config_id: Optional ID of a score config defined in Langfuse
2216
2217        Example:
2218            ```python
2219            with langfuse.start_as_current_span(name="process-user-request") as span:
2220                # Process request
2221                result = process_complete_request()
2222                span.update(output=result)
2223
2224                # Score the overall trace
2225                langfuse.score_current_trace(
2226                    name="overall_quality",
2227                    value=0.95,
2228                    data_type="NUMERIC",
2229                    comment="High quality end-to-end response"
2230                )
2231            ```
2232        """
2233        current_span = self._get_current_otel_span()
2234
2235        if current_span is not None:
2236            trace_id = self._get_otel_trace_id(current_span)
2237
2238            langfuse_logger.info(
2239                f"Score: Creating score name='{name}' value={value} for entire trace {trace_id}"
2240            )
2241
2242            self.create_score(
2243                trace_id=trace_id,
2244                name=name,
2245                value=cast(str, value),
2246                score_id=score_id,
2247                data_type=cast(Literal["CATEGORICAL"], data_type),
2248                comment=comment,
2249                config_id=config_id,
2250            )

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.

Arguments:
  • 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)
  • score_id: Optional custom ID for the score (auto-generated if not provided)
  • data_type: Type of score (NUMERIC, BOOLEAN, or CATEGORICAL)
  • comment: Optional comment or explanation for the score
  • config_id: Optional ID of a score config defined in Langfuse
Example:
with langfuse.start_as_current_span(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"
    )
def flush(self) -> None:
2252    def flush(self) -> None:
2253        """Force flush all pending spans and events to the Langfuse API.
2254
2255        This method manually flushes any pending spans, scores, and other events to the
2256        Langfuse API. It's useful in scenarios where you want to ensure all data is sent
2257        before proceeding, without waiting for the automatic flush interval.
2258
2259        Example:
2260            ```python
2261            # Record some spans and scores
2262            with langfuse.start_as_current_span(name="operation") as span:
2263                # Do work...
2264                pass
2265
2266            # Ensure all data is sent to Langfuse before proceeding
2267            langfuse.flush()
2268
2269            # Continue with other work
2270            ```
2271        """
2272        if self._resources is not None:
2273            self._resources.flush()

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:
# Record some spans and scores
with langfuse.start_as_current_span(name="operation") as span:
    # Do work...
    pass

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

# Continue with other work
def shutdown(self) -> None:
2275    def shutdown(self) -> None:
2276        """Shut down the Langfuse client and flush all pending data.
2277
2278        This method cleanly shuts down the Langfuse client, ensuring all pending data
2279        is flushed to the API and all background threads are properly terminated.
2280
2281        It's important to call this method when your application is shutting down to
2282        prevent data loss and resource leaks. For most applications, using the client
2283        as a context manager or relying on the automatic shutdown via atexit is sufficient.
2284
2285        Example:
2286            ```python
2287            # Initialize Langfuse
2288            langfuse = Langfuse(public_key="...", secret_key="...")
2289
2290            # Use Langfuse throughout your application
2291            # ...
2292
2293            # When application is shutting down
2294            langfuse.shutdown()
2295            ```
2296        """
2297        if self._resources is not None:
2298            self._resources.shutdown()

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:
# Initialize Langfuse
langfuse = Langfuse(public_key="...", secret_key="...")

# Use Langfuse throughout your application
# ...

# When application is shutting down
langfuse.shutdown()
def get_current_trace_id(self) -> Optional[str]:
2300    def get_current_trace_id(self) -> Optional[str]:
2301        """Get the trace ID of the current active span.
2302
2303        This method retrieves the trace ID from the currently active span in the context.
2304        It can be used to get the trace ID for referencing in logs, external systems,
2305        or for creating related operations.
2306
2307        Returns:
2308            The current trace ID as a 32-character lowercase hexadecimal string,
2309            or None if there is no active span.
2310
2311        Example:
2312            ```python
2313            with langfuse.start_as_current_span(name="process-request") as span:
2314                # Get the current trace ID for reference
2315                trace_id = langfuse.get_current_trace_id()
2316
2317                # Use it for external correlation
2318                log.info(f"Processing request with trace_id: {trace_id}")
2319
2320                # Or pass to another system
2321                external_system.process(data, trace_id=trace_id)
2322            ```
2323        """
2324        if not self._tracing_enabled:
2325            langfuse_logger.debug(
2326                "Operation skipped: get_current_trace_id - Tracing is disabled or client is in no-op mode."
2327            )
2328            return None
2329
2330        current_otel_span = self._get_current_otel_span()
2331
2332        return self._get_otel_trace_id(current_otel_span) if current_otel_span else None

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:
with langfuse.start_as_current_span(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)
def get_current_observation_id(self) -> Optional[str]:
2334    def get_current_observation_id(self) -> Optional[str]:
2335        """Get the observation ID (span ID) of the current active span.
2336
2337        This method retrieves the observation ID from the currently active span in the context.
2338        It can be used to get the observation ID for referencing in logs, external systems,
2339        or for creating scores or other related operations.
2340
2341        Returns:
2342            The current observation ID as a 16-character lowercase hexadecimal string,
2343            or None if there is no active span.
2344
2345        Example:
2346            ```python
2347            with langfuse.start_as_current_span(name="process-user-query") as span:
2348                # Get the current observation ID
2349                observation_id = langfuse.get_current_observation_id()
2350
2351                # Store it for later reference
2352                cache.set(f"query_{query_id}_observation", observation_id)
2353
2354                # Process the query...
2355            ```
2356        """
2357        if not self._tracing_enabled:
2358            langfuse_logger.debug(
2359                "Operation skipped: get_current_observation_id - Tracing is disabled or client is in no-op mode."
2360            )
2361            return None
2362
2363        current_otel_span = self._get_current_otel_span()
2364
2365        return self._get_otel_span_id(current_otel_span) if current_otel_span else None

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:
with langfuse.start_as_current_span(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...
def get_trace_url(self, *, trace_id: Optional[str] = None) -> Optional[str]:
2378    def get_trace_url(self, *, trace_id: Optional[str] = None) -> Optional[str]:
2379        """Get the URL to view a trace in the Langfuse UI.
2380
2381        This method generates a URL that links directly to a trace in the Langfuse UI.
2382        It's useful for providing links in logs, notifications, or debugging tools.
2383
2384        Args:
2385            trace_id: Optional trace ID to generate a URL for. If not provided,
2386                     the trace ID of the current active span will be used.
2387
2388        Returns:
2389            A URL string pointing to the trace in the Langfuse UI,
2390            or None if the project ID couldn't be retrieved or no trace ID is available.
2391
2392        Example:
2393            ```python
2394            # Get URL for the current trace
2395            with langfuse.start_as_current_span(name="process-request") as span:
2396                trace_url = langfuse.get_trace_url()
2397                log.info(f"Processing trace: {trace_url}")
2398
2399            # Get URL for a specific trace
2400            specific_trace_url = langfuse.get_trace_url(trace_id="1234567890abcdef1234567890abcdef")
2401            send_notification(f"Review needed for trace: {specific_trace_url}")
2402            ```
2403        """
2404        project_id = self._get_project_id()
2405        final_trace_id = trace_id or self.get_current_trace_id()
2406
2407        return (
2408            f"{self._base_url}/project/{project_id}/traces/{final_trace_id}"
2409            if project_id and final_trace_id
2410            else None
2411        )

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.

Arguments:
  • 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:
# Get URL for the current trace
with langfuse.start_as_current_span(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}")
def get_dataset( self, name: str, *, fetch_items_page_size: Optional[int] = 50) -> langfuse._client.datasets.DatasetClient:
2413    def get_dataset(
2414        self, name: str, *, fetch_items_page_size: Optional[int] = 50
2415    ) -> "DatasetClient":
2416        """Fetch a dataset by its name.
2417
2418        Args:
2419            name (str): The name of the dataset to fetch.
2420            fetch_items_page_size (Optional[int]): All items of the dataset will be fetched in chunks of this size. Defaults to 50.
2421
2422        Returns:
2423            DatasetClient: The dataset with the given name.
2424        """
2425        try:
2426            langfuse_logger.debug(f"Getting datasets {name}")
2427            dataset = self.api.datasets.get(dataset_name=name)
2428
2429            dataset_items = []
2430            page = 1
2431
2432            while True:
2433                new_items = self.api.dataset_items.list(
2434                    dataset_name=self._url_encode(name, is_url_param=True),
2435                    page=page,
2436                    limit=fetch_items_page_size,
2437                )
2438                dataset_items.extend(new_items.data)
2439
2440                if new_items.meta.total_pages <= page:
2441                    break
2442
2443                page += 1
2444
2445            items = [DatasetItemClient(i, langfuse=self) for i in dataset_items]
2446
2447            return DatasetClient(dataset, items=items)
2448
2449        except Error as e:
2450            handle_fern_exception(e)
2451            raise e

Fetch a dataset by its name.

Arguments:
  • 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.
Returns:

DatasetClient: The dataset with the given name.

def run_experiment( self, *, name: str, run_name: Optional[str] = None, description: Optional[str] = None, data: Union[List[langfuse.experiment.LocalExperimentItem], List[langfuse._client.datasets.DatasetItemClient]], task: langfuse.experiment.TaskFunction, evaluators: List[langfuse.experiment.EvaluatorFunction] = [], run_evaluators: List[langfuse.experiment.RunEvaluatorFunction] = [], max_concurrency: int = 50, metadata: Optional[Dict[str, str]] = None) -> langfuse.experiment.ExperimentResult:
2453    def run_experiment(
2454        self,
2455        *,
2456        name: str,
2457        run_name: Optional[str] = None,
2458        description: Optional[str] = None,
2459        data: ExperimentData,
2460        task: TaskFunction,
2461        evaluators: List[EvaluatorFunction] = [],
2462        run_evaluators: List[RunEvaluatorFunction] = [],
2463        max_concurrency: int = 50,
2464        metadata: Optional[Dict[str, str]] = None,
2465    ) -> ExperimentResult:
2466        """Run an experiment on a dataset with automatic tracing and evaluation.
2467
2468        This method executes a task function on each item in the provided dataset,
2469        automatically traces all executions with Langfuse for observability, runs
2470        item-level and run-level evaluators on the outputs, and returns comprehensive
2471        results with evaluation metrics.
2472
2473        The experiment system provides:
2474        - Automatic tracing of all task executions
2475        - Concurrent processing with configurable limits
2476        - Comprehensive error handling that isolates failures
2477        - Integration with Langfuse datasets for experiment tracking
2478        - Flexible evaluation framework supporting both sync and async evaluators
2479
2480        Args:
2481            name: Human-readable name for the experiment. Used for identification
2482                in the Langfuse UI.
2483            run_name: Optional exact name for the experiment run. If provided, this will be
2484                used as the exact dataset run name if the `data` contains Langfuse dataset items.
2485                If not provided, this will default to the experiment name appended with an ISO timestamp.
2486            description: Optional description explaining the experiment's purpose,
2487                methodology, or expected outcomes.
2488            data: Array of data items to process. Can be either:
2489                - List of dict-like items with 'input', 'expected_output', 'metadata' keys
2490                - List of Langfuse DatasetItem objects from dataset.items
2491            task: Function that processes each data item and returns output.
2492                Must accept 'item' as keyword argument and can return sync or async results.
2493                The task function signature should be: task(*, item, **kwargs) -> Any
2494            evaluators: List of functions to evaluate each item's output individually.
2495                Each evaluator receives input, output, expected_output, and metadata.
2496                Can return single Evaluation dict or list of Evaluation dicts.
2497            run_evaluators: List of functions to evaluate the entire experiment run.
2498                Each run evaluator receives all item_results and can compute aggregate metrics.
2499                Useful for calculating averages, distributions, or cross-item comparisons.
2500            max_concurrency: Maximum number of concurrent task executions (default: 50).
2501                Controls the number of items processed simultaneously. Adjust based on
2502                API rate limits and system resources.
2503            metadata: Optional metadata dictionary to attach to all experiment traces.
2504                This metadata will be included in every trace created during the experiment.
2505                If `data` are Langfuse dataset items, the metadata will be attached to the dataset run, too.
2506
2507        Returns:
2508            ExperimentResult containing:
2509            - run_name: The experiment run name. This is equal to the dataset run name if experiment was on Langfuse dataset.
2510            - item_results: List of results for each processed item with outputs and evaluations
2511            - run_evaluations: List of aggregate evaluation results for the entire run
2512            - dataset_run_id: ID of the dataset run (if using Langfuse datasets)
2513            - dataset_run_url: Direct URL to view results in Langfuse UI (if applicable)
2514
2515        Raises:
2516            ValueError: If required parameters are missing or invalid
2517            Exception: If experiment setup fails (individual item failures are handled gracefully)
2518
2519        Examples:
2520            Basic experiment with local data:
2521            ```python
2522            def summarize_text(*, item, **kwargs):
2523                return f"Summary: {item['input'][:50]}..."
2524
2525            def length_evaluator(*, input, output, expected_output=None, **kwargs):
2526                return {
2527                    "name": "output_length",
2528                    "value": len(output),
2529                    "comment": f"Output contains {len(output)} characters"
2530                }
2531
2532            result = langfuse.run_experiment(
2533                name="Text Summarization Test",
2534                description="Evaluate summarization quality and length",
2535                data=[
2536                    {"input": "Long article text...", "expected_output": "Expected summary"},
2537                    {"input": "Another article...", "expected_output": "Another summary"}
2538                ],
2539                task=summarize_text,
2540                evaluators=[length_evaluator]
2541            )
2542
2543            print(f"Processed {len(result.item_results)} items")
2544            for item_result in result.item_results:
2545                print(f"Input: {item_result.item['input']}")
2546                print(f"Output: {item_result.output}")
2547                print(f"Evaluations: {item_result.evaluations}")
2548            ```
2549
2550            Advanced experiment with async task and multiple evaluators:
2551            ```python
2552            async def llm_task(*, item, **kwargs):
2553                # Simulate async LLM call
2554                response = await openai_client.chat.completions.create(
2555                    model="gpt-4",
2556                    messages=[{"role": "user", "content": item["input"]}]
2557                )
2558                return response.choices[0].message.content
2559
2560            def accuracy_evaluator(*, input, output, expected_output=None, **kwargs):
2561                if expected_output and expected_output.lower() in output.lower():
2562                    return {"name": "accuracy", "value": 1.0, "comment": "Correct answer"}
2563                return {"name": "accuracy", "value": 0.0, "comment": "Incorrect answer"}
2564
2565            def toxicity_evaluator(*, input, output, expected_output=None, **kwargs):
2566                # Simulate toxicity check
2567                toxicity_score = check_toxicity(output)  # Your toxicity checker
2568                return {
2569                    "name": "toxicity",
2570                    "value": toxicity_score,
2571                    "comment": f"Toxicity level: {'high' if toxicity_score > 0.7 else 'low'}"
2572                }
2573
2574            def average_accuracy(*, item_results, **kwargs):
2575                accuracies = [
2576                    eval.value for result in item_results
2577                    for eval in result.evaluations
2578                    if eval.name == "accuracy"
2579                ]
2580                return {
2581                    "name": "average_accuracy",
2582                    "value": sum(accuracies) / len(accuracies) if accuracies else 0,
2583                    "comment": f"Average accuracy across {len(accuracies)} items"
2584                }
2585
2586            result = langfuse.run_experiment(
2587                name="LLM Safety and Accuracy Test",
2588                description="Evaluate model accuracy and safety across diverse prompts",
2589                data=test_dataset,  # Your dataset items
2590                task=llm_task,
2591                evaluators=[accuracy_evaluator, toxicity_evaluator],
2592                run_evaluators=[average_accuracy],
2593                max_concurrency=5,  # Limit concurrent API calls
2594                metadata={"model": "gpt-4", "temperature": 0.7}
2595            )
2596            ```
2597
2598            Using with Langfuse datasets:
2599            ```python
2600            # Get dataset from Langfuse
2601            dataset = langfuse.get_dataset("my-eval-dataset")
2602
2603            result = dataset.run_experiment(
2604                name="Production Model Evaluation",
2605                description="Monthly evaluation of production model performance",
2606                task=my_production_task,
2607                evaluators=[accuracy_evaluator, latency_evaluator]
2608            )
2609
2610            # Results automatically linked to dataset in Langfuse UI
2611            print(f"View results: {result['dataset_run_url']}")
2612            ```
2613
2614        Note:
2615            - Task and evaluator functions can be either synchronous or asynchronous
2616            - Individual item failures are logged but don't stop the experiment
2617            - All executions are automatically traced and visible in Langfuse UI
2618            - When using Langfuse datasets, results are automatically linked for easy comparison
2619            - This method works in both sync and async contexts (Jupyter notebooks, web apps, etc.)
2620            - Async execution is handled automatically with smart event loop detection
2621        """
2622        return cast(
2623            ExperimentResult,
2624            run_async_safely(
2625                self._run_experiment_async(
2626                    name=name,
2627                    run_name=self._create_experiment_run_name(
2628                        name=name, run_name=run_name
2629                    ),
2630                    description=description,
2631                    data=data,
2632                    task=task,
2633                    evaluators=evaluators or [],
2634                    run_evaluators=run_evaluators or [],
2635                    max_concurrency=max_concurrency,
2636                    metadata=metadata,
2637                ),
2638            ),
2639        )

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
Arguments:
  • 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.
  • 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
  • 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:

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:

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:

# 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
def auth_check(self) -> bool:
2923    def auth_check(self) -> bool:
2924        """Check if the provided credentials (public and secret key) are valid.
2925
2926        Raises:
2927            Exception: If no projects were found for the provided credentials.
2928
2929        Note:
2930            This method is blocking. It is discouraged to use it in production code.
2931        """
2932        try:
2933            projects = self.api.projects.get()
2934            langfuse_logger.debug(
2935                f"Auth check successful, found {len(projects.data)} projects"
2936            )
2937            if len(projects.data) == 0:
2938                raise Exception(
2939                    "Auth check failed, no project found for the keys provided."
2940                )
2941            return True
2942
2943        except AttributeError as e:
2944            langfuse_logger.warning(
2945                f"Auth check failed: Client not properly initialized. Error: {e}"
2946            )
2947            return False
2948
2949        except Error as e:
2950            handle_fern_exception(e)
2951            raise e

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.

def create_dataset( self, *, name: str, description: Optional[str] = None, metadata: Optional[Any] = None) -> langfuse.api.Dataset:
2953    def create_dataset(
2954        self,
2955        *,
2956        name: str,
2957        description: Optional[str] = None,
2958        metadata: Optional[Any] = None,
2959    ) -> Dataset:
2960        """Create a dataset with the given name on Langfuse.
2961
2962        Args:
2963            name: Name of the dataset to create.
2964            description: Description of the dataset. Defaults to None.
2965            metadata: Additional metadata. Defaults to None.
2966
2967        Returns:
2968            Dataset: The created dataset as returned by the Langfuse API.
2969        """
2970        try:
2971            body = CreateDatasetRequest(
2972                name=name, description=description, metadata=metadata
2973            )
2974            langfuse_logger.debug(f"Creating datasets {body}")
2975
2976            return self.api.datasets.create(request=body)
2977
2978        except Error as e:
2979            handle_fern_exception(e)
2980            raise e

Create a dataset with the given name on Langfuse.

Arguments:
  • name: Name of the dataset to create.
  • description: Description of the dataset. Defaults to None.
  • metadata: Additional metadata. Defaults to None.
Returns:

Dataset: The created dataset as returned by the Langfuse API.

def create_dataset_item( self, *, dataset_name: str, input: Optional[Any] = None, expected_output: Optional[Any] = None, metadata: Optional[Any] = None, source_trace_id: Optional[str] = None, source_observation_id: Optional[str] = None, status: Optional[langfuse.api.DatasetStatus] = None, id: Optional[str] = None) -> langfuse.api.DatasetItem:
2982    def create_dataset_item(
2983        self,
2984        *,
2985        dataset_name: str,
2986        input: Optional[Any] = None,
2987        expected_output: Optional[Any] = None,
2988        metadata: Optional[Any] = None,
2989        source_trace_id: Optional[str] = None,
2990        source_observation_id: Optional[str] = None,
2991        status: Optional[DatasetStatus] = None,
2992        id: Optional[str] = None,
2993    ) -> DatasetItem:
2994        """Create a dataset item.
2995
2996        Upserts if an item with id already exists.
2997
2998        Args:
2999            dataset_name: Name of the dataset in which the dataset item should be created.
3000            input: Input data. Defaults to None. Can contain any dict, list or scalar.
3001            expected_output: Expected output data. Defaults to None. Can contain any dict, list or scalar.
3002            metadata: Additional metadata. Defaults to None. Can contain any dict, list or scalar.
3003            source_trace_id: Id of the source trace. Defaults to None.
3004            source_observation_id: Id of the source observation. Defaults to None.
3005            status: Status of the dataset item. Defaults to ACTIVE for newly created items.
3006            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.
3007
3008        Returns:
3009            DatasetItem: The created dataset item as returned by the Langfuse API.
3010
3011        Example:
3012            ```python
3013            from langfuse import Langfuse
3014
3015            langfuse = Langfuse()
3016
3017            # Uploading items to the Langfuse dataset named "capital_cities"
3018            langfuse.create_dataset_item(
3019                dataset_name="capital_cities",
3020                input={"input": {"country": "Italy"}},
3021                expected_output={"expected_output": "Rome"},
3022                metadata={"foo": "bar"}
3023            )
3024            ```
3025        """
3026        try:
3027            body = CreateDatasetItemRequest(
3028                datasetName=dataset_name,
3029                input=input,
3030                expectedOutput=expected_output,
3031                metadata=metadata,
3032                sourceTraceId=source_trace_id,
3033                sourceObservationId=source_observation_id,
3034                status=status,
3035                id=id,
3036            )
3037            langfuse_logger.debug(f"Creating dataset item {body}")
3038            return self.api.dataset_items.create(request=body)
3039        except Error as e:
3040            handle_fern_exception(e)
3041            raise e

Create a dataset item.

Upserts if an item with id already exists.

Arguments:
  • 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:
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"}
)
def resolve_media_references( self, *, obj: Any, resolve_with: Literal['base64_data_uri'], max_depth: int = 10, content_fetch_timeout_seconds: int = 5) -> Any:
3043    def resolve_media_references(
3044        self,
3045        *,
3046        obj: Any,
3047        resolve_with: Literal["base64_data_uri"],
3048        max_depth: int = 10,
3049        content_fetch_timeout_seconds: int = 5,
3050    ) -> Any:
3051        """Replace media reference strings in an object with base64 data URIs.
3052
3053        This method recursively traverses an object (up to max_depth) looking for media reference strings
3054        in the format "@@@langfuseMedia:...@@@". When found, it (synchronously) fetches the actual media content using
3055        the provided Langfuse client and replaces the reference string with a base64 data URI.
3056
3057        If fetching media content fails for a reference string, a warning is logged and the reference
3058        string is left unchanged.
3059
3060        Args:
3061            obj: The object to process. Can be a primitive value, array, or nested object.
3062                If the object has a __dict__ attribute, a dict will be returned instead of the original object type.
3063            resolve_with: The representation of the media content to replace the media reference string with.
3064                Currently only "base64_data_uri" is supported.
3065            max_depth: int: The maximum depth to traverse the object. Default is 10.
3066            content_fetch_timeout_seconds: int: The timeout in seconds for fetching media content. Default is 5.
3067
3068        Returns:
3069            A deep copy of the input object with all media references replaced with base64 data URIs where possible.
3070            If the input object has a __dict__ attribute, a dict will be returned instead of the original object type.
3071
3072        Example:
3073            obj = {
3074                "image": "@@@langfuseMedia:type=image/jpeg|id=123|source=bytes@@@",
3075                "nested": {
3076                    "pdf": "@@@langfuseMedia:type=application/pdf|id=456|source=bytes@@@"
3077                }
3078            }
3079
3080            result = await LangfuseMedia.resolve_media_references(obj, langfuse_client)
3081
3082            # Result:
3083            # {
3084            #     "image": "data:image/jpeg;base64,/9j/4AAQSkZJRg...",
3085            #     "nested": {
3086            #         "pdf": "data:application/pdf;base64,JVBERi0xLjcK..."
3087            #     }
3088            # }
3089        """
3090        return LangfuseMedia.resolve_media_references(
3091            langfuse_client=self,
3092            obj=obj,
3093            resolve_with=resolve_with,
3094            max_depth=max_depth,
3095            content_fetch_timeout_seconds=content_fetch_timeout_seconds,
3096        )

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.

Arguments:
  • 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..."

}

}

def get_prompt( self, name: str, *, version: Optional[int] = None, label: Optional[str] = None, type: Literal['chat', 'text'] = 'text', cache_ttl_seconds: Optional[int] = None, fallback: Union[List[langfuse.model.ChatMessageDict], NoneType, str] = None, max_retries: Optional[int] = None, fetch_timeout_seconds: Optional[int] = None) -> Union[langfuse.model.TextPromptClient, langfuse.model.ChatPromptClient]:
3126    def get_prompt(
3127        self,
3128        name: str,
3129        *,
3130        version: Optional[int] = None,
3131        label: Optional[str] = None,
3132        type: Literal["chat", "text"] = "text",
3133        cache_ttl_seconds: Optional[int] = None,
3134        fallback: Union[Optional[List[ChatMessageDict]], Optional[str]] = None,
3135        max_retries: Optional[int] = None,
3136        fetch_timeout_seconds: Optional[int] = None,
3137    ) -> PromptClient:
3138        """Get a prompt.
3139
3140        This method attempts to fetch the requested prompt from the local cache. If the prompt is not found
3141        in the cache or if the cached prompt has expired, it will try to fetch the prompt from the server again
3142        and update the cache. If fetching the new prompt fails, and there is an expired prompt in the cache, it will
3143        return the expired prompt as a fallback.
3144
3145        Args:
3146            name (str): The name of the prompt to retrieve.
3147
3148        Keyword Args:
3149            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.
3150            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.
3151            cache_ttl_seconds: Optional[int]: Time-to-live in seconds for caching the prompt. Must be specified as a
3152            keyword argument. If not set, defaults to 60 seconds. Disables caching if set to 0.
3153            type: Literal["chat", "text"]: The type of the prompt to retrieve. Defaults to "text".
3154            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.
3155            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.
3156            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.
3157
3158        Returns:
3159            The prompt object retrieved from the cache or directly fetched if not cached or expired of type
3160            - TextPromptClient, if type argument is 'text'.
3161            - ChatPromptClient, if type argument is 'chat'.
3162
3163        Raises:
3164            Exception: Propagates any exceptions raised during the fetching of a new prompt, unless there is an
3165            expired prompt in the cache, in which case it logs a warning and returns the expired prompt.
3166        """
3167        if self._resources is None:
3168            raise Error(
3169                "SDK is not correctly initialized. Check the init logs for more details."
3170            )
3171        if version is not None and label is not None:
3172            raise ValueError("Cannot specify both version and label at the same time.")
3173
3174        if not name:
3175            raise ValueError("Prompt name cannot be empty.")
3176
3177        cache_key = PromptCache.generate_cache_key(name, version=version, label=label)
3178        bounded_max_retries = self._get_bounded_max_retries(
3179            max_retries, default_max_retries=2, max_retries_upper_bound=4
3180        )
3181
3182        langfuse_logger.debug(f"Getting prompt '{cache_key}'")
3183        cached_prompt = self._resources.prompt_cache.get(cache_key)
3184
3185        if cached_prompt is None or cache_ttl_seconds == 0:
3186            langfuse_logger.debug(
3187                f"Prompt '{cache_key}' not found in cache or caching disabled."
3188            )
3189            try:
3190                return self._fetch_prompt_and_update_cache(
3191                    name,
3192                    version=version,
3193                    label=label,
3194                    ttl_seconds=cache_ttl_seconds,
3195                    max_retries=bounded_max_retries,
3196                    fetch_timeout_seconds=fetch_timeout_seconds,
3197                )
3198            except Exception as e:
3199                if fallback:
3200                    langfuse_logger.warning(
3201                        f"Returning fallback prompt for '{cache_key}' due to fetch error: {e}"
3202                    )
3203
3204                    fallback_client_args: Dict[str, Any] = {
3205                        "name": name,
3206                        "prompt": fallback,
3207                        "type": type,
3208                        "version": version or 0,
3209                        "config": {},
3210                        "labels": [label] if label else [],
3211                        "tags": [],
3212                    }
3213
3214                    if type == "text":
3215                        return TextPromptClient(
3216                            prompt=Prompt_Text(**fallback_client_args),
3217                            is_fallback=True,
3218                        )
3219
3220                    if type == "chat":
3221                        return ChatPromptClient(
3222                            prompt=Prompt_Chat(**fallback_client_args),
3223                            is_fallback=True,
3224                        )
3225
3226                raise e
3227
3228        if cached_prompt.is_expired():
3229            langfuse_logger.debug(f"Stale prompt '{cache_key}' found in cache.")
3230            try:
3231                # refresh prompt in background thread, refresh_prompt deduplicates tasks
3232                langfuse_logger.debug(f"Refreshing prompt '{cache_key}' in background.")
3233
3234                def refresh_task() -> None:
3235                    self._fetch_prompt_and_update_cache(
3236                        name,
3237                        version=version,
3238                        label=label,
3239                        ttl_seconds=cache_ttl_seconds,
3240                        max_retries=bounded_max_retries,
3241                        fetch_timeout_seconds=fetch_timeout_seconds,
3242                    )
3243
3244                self._resources.prompt_cache.add_refresh_prompt_task(
3245                    cache_key,
3246                    refresh_task,
3247                )
3248                langfuse_logger.debug(
3249                    f"Returning stale prompt '{cache_key}' from cache."
3250                )
3251                # return stale prompt
3252                return cached_prompt.value
3253
3254            except Exception as e:
3255                langfuse_logger.warning(
3256                    f"Error when refreshing cached prompt '{cache_key}', returning cached version. Error: {e}"
3257                )
3258                # creation of refresh prompt task failed, return stale prompt
3259                return cached_prompt.value
3260
3261        return cached_prompt.value

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.

Arguments:
  • 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.
def create_prompt( self, *, name: str, prompt: Union[str, List[Union[langfuse.model.ChatMessageDict, langfuse.model.ChatMessageWithPlaceholdersDict_Message, langfuse.model.ChatMessageWithPlaceholdersDict_Placeholder]]], labels: List[str] = [], tags: Optional[List[str]] = None, type: Optional[Literal['chat', 'text']] = 'text', config: Optional[Any] = None, commit_message: Optional[str] = None) -> Union[langfuse.model.TextPromptClient, langfuse.model.ChatPromptClient]:
3355    def create_prompt(
3356        self,
3357        *,
3358        name: str,
3359        prompt: Union[
3360            str, List[Union[ChatMessageDict, ChatMessageWithPlaceholdersDict]]
3361        ],
3362        labels: List[str] = [],
3363        tags: Optional[List[str]] = None,
3364        type: Optional[Literal["chat", "text"]] = "text",
3365        config: Optional[Any] = None,
3366        commit_message: Optional[str] = None,
3367    ) -> PromptClient:
3368        """Create a new prompt in Langfuse.
3369
3370        Keyword Args:
3371            name : The name of the prompt to be created.
3372            prompt : The content of the prompt to be created.
3373            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.
3374            labels: The labels of the prompt. Defaults to None. To create a default-served prompt, add the 'production' label.
3375            tags: The tags of the prompt. Defaults to None. Will be applied to all versions of the prompt.
3376            config: Additional structured data to be saved with the prompt. Defaults to None.
3377            type: The type of the prompt to be created. "chat" vs. "text". Defaults to "text".
3378            commit_message: Optional string describing the change.
3379
3380        Returns:
3381            TextPromptClient: The prompt if type argument is 'text'.
3382            ChatPromptClient: The prompt if type argument is 'chat'.
3383        """
3384        try:
3385            langfuse_logger.debug(f"Creating prompt {name=}, {labels=}")
3386
3387            if type == "chat":
3388                if not isinstance(prompt, list):
3389                    raise ValueError(
3390                        "For 'chat' type, 'prompt' must be a list of chat messages with role and content attributes."
3391                    )
3392                request: Union[CreatePromptRequest_Chat, CreatePromptRequest_Text] = (
3393                    CreatePromptRequest_Chat(
3394                        name=name,
3395                        prompt=cast(Any, prompt),
3396                        labels=labels,
3397                        tags=tags,
3398                        config=config or {},
3399                        commitMessage=commit_message,
3400                        type="chat",
3401                    )
3402                )
3403                server_prompt = self.api.prompts.create(request=request)
3404
3405                if self._resources is not None:
3406                    self._resources.prompt_cache.invalidate(name)
3407
3408                return ChatPromptClient(prompt=cast(Prompt_Chat, server_prompt))
3409
3410            if not isinstance(prompt, str):
3411                raise ValueError("For 'text' type, 'prompt' must be a string.")
3412
3413            request = CreatePromptRequest_Text(
3414                name=name,
3415                prompt=prompt,
3416                labels=labels,
3417                tags=tags,
3418                config=config or {},
3419                commitMessage=commit_message,
3420                type="text",
3421            )
3422
3423            server_prompt = self.api.prompts.create(request=request)
3424
3425            if self._resources is not None:
3426                self._resources.prompt_cache.invalidate(name)
3427
3428            return TextPromptClient(prompt=cast(Prompt_Text, server_prompt))
3429
3430        except Error as e:
3431            handle_fern_exception(e)
3432            raise e

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

def update_prompt(self, *, name: str, version: int, new_labels: List[str] = []) -> Any:
3434    def update_prompt(
3435        self,
3436        *,
3437        name: str,
3438        version: int,
3439        new_labels: List[str] = [],
3440    ) -> Any:
3441        """Update an existing prompt version in Langfuse. The Langfuse SDK prompt cache is invalidated for all prompts witht he specified name.
3442
3443        Args:
3444            name (str): The name of the prompt to update.
3445            version (int): The version number of the prompt to update.
3446            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 [].
3447
3448        Returns:
3449            Prompt: The updated prompt from the Langfuse API.
3450
3451        """
3452        updated_prompt = self.api.prompt_version.update(
3453            name=self._url_encode(name),
3454            version=version,
3455            new_labels=new_labels,
3456        )
3457
3458        if self._resources is not None:
3459            self._resources.prompt_cache.invalidate(name)
3460
3461        return updated_prompt

Update an existing prompt version in Langfuse. The Langfuse SDK prompt cache is invalidated for all prompts witht he specified name.

Arguments:
  • 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.

def clear_prompt_cache(self) -> None:
3476    def clear_prompt_cache(self) -> None:
3477        """Clear the entire prompt cache, removing all cached prompts.
3478
3479        This method is useful when you want to force a complete refresh of all
3480        cached prompts, for example after major updates or when you need to
3481        ensure the latest versions are fetched from the server.
3482        """
3483        if self._resources is not None:
3484            self._resources.prompt_cache.clear()

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.

def get_client(*, public_key: Optional[str] = None) -> Langfuse:
 59def get_client(*, public_key: Optional[str] = None) -> Langfuse:
 60    """Get or create a Langfuse client instance.
 61
 62    Returns an existing Langfuse client or creates a new one if none exists. In multi-project setups,
 63    providing a public_key is required. Multi-project support is experimental - see Langfuse docs.
 64
 65    Behavior:
 66    - Single project: Returns existing client or creates new one
 67    - Multi-project: Requires public_key to return specific client
 68    - No public_key in multi-project: Returns disabled client to prevent data leakage
 69
 70    The function uses a singleton pattern per public_key to conserve resources and maintain state.
 71
 72    Args:
 73        public_key (Optional[str]): Project identifier
 74            - With key: Returns client for that project
 75            - Without key: Returns single client or disabled client if multiple exist
 76
 77    Returns:
 78        Langfuse: Client instance in one of three states:
 79            1. Client for specified public_key
 80            2. Default client for single-project setup
 81            3. Disabled client when multiple projects exist without key
 82
 83    Security:
 84        Disables tracing when multiple projects exist without explicit key to prevent
 85        cross-project data leakage. Multi-project setups are experimental.
 86
 87    Example:
 88        ```python
 89        # Single project
 90        client = get_client()  # Default client
 91
 92        # In multi-project usage:
 93        client_a = get_client(public_key="project_a_key")  # Returns project A's client
 94        client_b = get_client(public_key="project_b_key")  # Returns project B's client
 95
 96        # Without specific key in multi-project setup:
 97        client = get_client()  # Returns disabled client for safety
 98        ```
 99    """
100    with LangfuseResourceManager._lock:
101        active_instances = LangfuseResourceManager._instances
102
103        # If no explicit public_key provided, check execution context
104        if not public_key:
105            public_key = _current_public_key.get(None)
106
107        if not public_key:
108            if len(active_instances) == 0:
109                # No clients initialized yet, create default instance
110                return Langfuse()
111
112            if len(active_instances) == 1:
113                # Only one client exists, safe to use without specifying key
114                instance = list(active_instances.values())[0]
115
116                # Initialize with the credentials bound to the instance
117                # This is important if the original instance was instantiated
118                # via constructor arguments
119                return _create_client_from_instance(instance)
120
121            else:
122                # Multiple clients exist but no key specified - disable tracing
123                # to prevent cross-project data leakage
124                langfuse_logger.warning(
125                    "No 'langfuse_public_key' passed to decorated function, but multiple langfuse clients are instantiated in current process. Skipping tracing for this function to avoid cross-project leakage."
126                )
127                return Langfuse(
128                    tracing_enabled=False, public_key="fake", secret_key="fake"
129                )
130
131        else:
132            # Specific key provided, look up existing instance
133            target_instance: Optional[LangfuseResourceManager] = active_instances.get(
134                public_key, None
135            )
136
137            if target_instance is None:
138                # No instance found with this key - client not initialized properly
139                langfuse_logger.warning(
140                    f"No Langfuse client with public key {public_key} has been initialized. Skipping tracing for decorated function."
141                )
142                return Langfuse(
143                    tracing_enabled=False, public_key="fake", secret_key="fake"
144                )
145
146            # target_instance is guaranteed to be not None at this point
147            return _create_client_from_instance(target_instance, public_key)

Get or create a Langfuse client instance.

Returns an existing Langfuse client or creates a new one if none exists. In multi-project setups, providing a public_key is required. Multi-project support is experimental - see Langfuse docs.

Behavior:

  • Single project: Returns existing client or creates new one
  • Multi-project: Requires public_key to return specific client
  • No public_key in multi-project: Returns disabled client to prevent data leakage

The function uses a singleton pattern per public_key to conserve resources and maintain state.

Arguments:
  • public_key (Optional[str]): Project identifier
    • With key: Returns client for that project
    • Without key: Returns single client or disabled client if multiple exist
Returns:

Langfuse: Client instance in one of three states: 1. Client for specified public_key 2. Default client for single-project setup 3. Disabled client when multiple projects exist without key

Security:

Disables tracing when multiple projects exist without explicit key to prevent cross-project data leakage. Multi-project setups are experimental.

Example:
# Single project
client = get_client()  # Default client

# In multi-project usage:
client_a = get_client(public_key="project_a_key")  # Returns project A's client
client_b = get_client(public_key="project_b_key")  # Returns project B's client

# Without specific key in multi-project setup:
client = get_client()  # Returns disabled client for safety
def observe( func: Optional[~F] = None, *, name: Optional[str] = None, as_type: Union[Literal['generation', 'embedding'], Literal['span', 'agent', 'tool', 'chain', 'retriever', 'evaluator', 'guardrail'], NoneType] = None, capture_input: Optional[bool] = None, capture_output: Optional[bool] = None, transform_to_string: Optional[Callable[[Iterable], str]] = None) -> Union[~F, Callable[[~F], ~F]]:
 90    def observe(
 91        self,
 92        func: Optional[F] = None,
 93        *,
 94        name: Optional[str] = None,
 95        as_type: Optional[ObservationTypeLiteralNoEvent] = None,
 96        capture_input: Optional[bool] = None,
 97        capture_output: Optional[bool] = None,
 98        transform_to_string: Optional[Callable[[Iterable], str]] = None,
 99    ) -> Union[F, Callable[[F], F]]:
100        """Wrap a function to create and manage Langfuse tracing around its execution, supporting both synchronous and asynchronous functions.
101
102        This decorator provides seamless integration of Langfuse observability into your codebase. It automatically creates
103        spans or generations around function execution, capturing timing, inputs/outputs, and error states. The decorator
104        intelligently handles both synchronous and asynchronous functions, preserving function signatures and type hints.
105
106        Using OpenTelemetry's distributed tracing system, it maintains proper trace context propagation throughout your application,
107        enabling you to see hierarchical traces of function calls with detailed performance metrics and function-specific details.
108
109        Args:
110            func (Optional[Callable]): The function to decorate. When used with parentheses @observe(), this will be None.
111            name (Optional[str]): Custom name for the created trace or span. If not provided, the function name is used.
112            as_type (Optional[Literal]): Set the observation type. Supported values:
113                    "generation", "span", "agent", "tool", "chain", "retriever", "embedding", "evaluator", "guardrail".
114                    Observation types are highlighted in the Langfuse UI for filtering and visualization.
115                    The types "generation" and "embedding" create a span on which additional attributes such as model metrics
116                    can be set.
117
118        Returns:
119            Callable: A wrapped version of the original function that automatically creates and manages Langfuse spans.
120
121        Example:
122            For general function tracing with automatic naming:
123            ```python
124            @observe()
125            def process_user_request(user_id, query):
126                # Function is automatically traced with name "process_user_request"
127                return get_response(query)
128            ```
129
130            For language model generation tracking:
131            ```python
132            @observe(name="answer-generation", as_type="generation")
133            async def generate_answer(query):
134                # Creates a generation-type span with extended LLM metrics
135                response = await openai.chat.completions.create(
136                    model="gpt-4",
137                    messages=[{"role": "user", "content": query}]
138                )
139                return response.choices[0].message.content
140            ```
141
142            For trace context propagation between functions:
143            ```python
144            @observe()
145            def main_process():
146                # Parent span is created
147                return sub_process()  # Child span automatically connected to parent
148
149            @observe()
150            def sub_process():
151                # Automatically becomes a child span of main_process
152                return "result"
153            ```
154
155        Raises:
156            Exception: Propagates any exceptions from the wrapped function after logging them in the trace.
157
158        Notes:
159            - The decorator preserves the original function's signature, docstring, and return type.
160            - Proper parent-child relationships between spans are automatically maintained.
161            - Special keyword arguments can be passed to control tracing:
162              - langfuse_trace_id: Explicitly set the trace ID for this function call
163              - langfuse_parent_observation_id: Explicitly set the parent span ID
164              - langfuse_public_key: Use a specific Langfuse project (when multiple clients exist)
165            - For async functions, the decorator returns an async function wrapper.
166            - For sync functions, the decorator returns a synchronous wrapper.
167        """
168        valid_types = set(get_observation_types_list(ObservationTypeLiteralNoEvent))
169        if as_type is not None and as_type not in valid_types:
170            self._log.warning(
171                f"Invalid as_type '{as_type}'. Valid types are: {', '.join(sorted(valid_types))}. Defaulting to 'span'."
172            )
173            as_type = "span"
174
175        function_io_capture_enabled = os.environ.get(
176            LANGFUSE_OBSERVE_DECORATOR_IO_CAPTURE_ENABLED, "True"
177        ).lower() not in ("false", "0")
178
179        should_capture_input = (
180            capture_input if capture_input is not None else function_io_capture_enabled
181        )
182
183        should_capture_output = (
184            capture_output
185            if capture_output is not None
186            else function_io_capture_enabled
187        )
188
189        def decorator(func: F) -> F:
190            return (
191                self._async_observe(
192                    func,
193                    name=name,
194                    as_type=as_type,
195                    capture_input=should_capture_input,
196                    capture_output=should_capture_output,
197                    transform_to_string=transform_to_string,
198                )
199                if asyncio.iscoroutinefunction(func)
200                else self._sync_observe(
201                    func,
202                    name=name,
203                    as_type=as_type,
204                    capture_input=should_capture_input,
205                    capture_output=should_capture_output,
206                    transform_to_string=transform_to_string,
207                )
208            )
209
210        """Handle decorator with or without parentheses.
211
212        This logic enables the decorator to work both with and without parentheses:
213        - @observe - Python passes the function directly to the decorator
214        - @observe() - Python calls the decorator first, which must return a function decorator
215
216        When called without arguments (@observe), the func parameter contains the function to decorate,
217        so we directly apply the decorator to it. When called with parentheses (@observe()),
218        func is None, so we return the decorator function itself for Python to apply in the next step.
219        """
220        if func is None:
221            return decorator
222        else:
223            return decorator(func)

Wrap a function to create and manage Langfuse tracing around its execution, supporting both synchronous and asynchronous functions.

This decorator provides seamless integration of Langfuse observability into your codebase. It automatically creates spans or generations around function execution, capturing timing, inputs/outputs, and error states. The decorator intelligently handles both synchronous and asynchronous functions, preserving function signatures and type hints.

Using OpenTelemetry's distributed tracing system, it maintains proper trace context propagation throughout your application, enabling you to see hierarchical traces of function calls with detailed performance metrics and function-specific details.

Arguments:
  • func (Optional[Callable]): The function to decorate. When used with parentheses @observe(), this will be None.
  • name (Optional[str]): Custom name for the created trace or span. If not provided, the function name is used.
  • as_type (Optional[Literal]): Set the observation type. Supported values: "generation", "span", "agent", "tool", "chain", "retriever", "embedding", "evaluator", "guardrail". Observation types are highlighted in the Langfuse UI for filtering and visualization. The types "generation" and "embedding" create a span on which additional attributes such as model metrics can be set.
Returns:

Callable: A wrapped version of the original function that automatically creates and manages Langfuse spans.

Example:

For general function tracing with automatic naming:

@observe()
def process_user_request(user_id, query):
    # Function is automatically traced with name "process_user_request"
    return get_response(query)

For language model generation tracking:

@observe(name="answer-generation", as_type="generation")
async def generate_answer(query):
    # Creates a generation-type span with extended LLM metrics
    response = await openai.chat.completions.create(
        model="gpt-4",
        messages=[{"role": "user", "content": query}]
    )
    return response.choices[0].message.content

For trace context propagation between functions:

@observe()
def main_process():
    # Parent span is created
    return sub_process()  # Child span automatically connected to parent

@observe()
def sub_process():
    # Automatically becomes a child span of main_process
    return "result"
Raises:
  • Exception: Propagates any exceptions from the wrapped function after logging them in the trace.
Notes:
  • The decorator preserves the original function's signature, docstring, and return type.
  • Proper parent-child relationships between spans are automatically maintained.
  • Special keyword arguments can be passed to control tracing:
    • langfuse_trace_id: Explicitly set the trace ID for this function call
    • langfuse_parent_observation_id: Explicitly set the parent span ID
    • langfuse_public_key: Use a specific Langfuse project (when multiple clients exist)
  • For async functions, the decorator returns an async function wrapper.
  • For sync functions, the decorator returns a synchronous wrapper.
def propagate_attributes( *, user_id: Optional[str] = None, session_id: Optional[str] = None, metadata: Optional[Dict[str, str]] = None, version: Optional[str] = None, tags: Optional[List[str]] = None, as_baggage: bool = False) -> opentelemetry.util._decorator._AgnosticContextManager[typing.Any]:
 74def propagate_attributes(
 75    *,
 76    user_id: Optional[str] = None,
 77    session_id: Optional[str] = None,
 78    metadata: Optional[Dict[str, str]] = None,
 79    version: Optional[str] = None,
 80    tags: Optional[List[str]] = None,
 81    as_baggage: bool = False,
 82) -> _AgnosticContextManager[Any]:
 83    """Propagate trace-level attributes to all spans created within this context.
 84
 85    This context manager sets attributes on the currently active span AND automatically
 86    propagates them to all new child spans created within the context. This is the
 87    recommended way to set trace-level attributes like user_id, session_id, and metadata
 88    dimensions that should be consistently applied across all observations in a trace.
 89
 90    **IMPORTANT**: Call this as early as possible within your trace/workflow. Only the
 91    currently active span and spans created after entering this context will have these
 92    attributes. Pre-existing spans will NOT be retroactively updated.
 93
 94    **Why this matters**: Langfuse aggregation queries (e.g., total cost by user_id,
 95    filtering by session_id) only include observations that have the attribute set.
 96    If you call `propagate_attributes` late in your workflow, earlier spans won't be
 97    included in aggregations for that attribute.
 98
 99    Args:
100        user_id: User identifier to associate with all spans in this context.
101            Must be US-ASCII string, ≤200 characters. Use this to track which user
102            generated each trace and enable e.g. per-user cost/performance analysis.
103        session_id: Session identifier to associate with all spans in this context.
104            Must be US-ASCII string, ≤200 characters. Use this to group related traces
105            within a user session (e.g., a conversation thread, multi-turn interaction).
106        metadata: Additional key-value metadata to propagate to all spans.
107            - Keys and values must be US-ASCII strings
108            - All values must be ≤200 characters
109            - Use for dimensions like internal correlating identifiers
110            - AVOID: large payloads, sensitive data, non-string values (will be dropped with warning)
111        version: Version identfier for parts of your application that are independently versioned, e.g. agents
112        tags: List of tags to categorize the group of observations
113        as_baggage: If True, propagates attributes using OpenTelemetry baggage for
114            cross-process/service propagation. **Security warning**: When enabled,
115            attribute values are added to HTTP headers on ALL outbound requests.
116            Only enable if values are safe to transmit via HTTP headers and you need
117            cross-service tracing. Default: False.
118
119    Returns:
120        Context manager that propagates attributes to all child spans.
121
122    Example:
123        Basic usage with user and session tracking:
124
125        ```python
126        from langfuse import Langfuse
127
128        langfuse = Langfuse()
129
130        # Set attributes early in the trace
131        with langfuse.start_as_current_span(name="user_workflow") as span:
132            with langfuse.propagate_attributes(
133                user_id="user_123",
134                session_id="session_abc",
135                metadata={"experiment": "variant_a", "environment": "production"}
136            ):
137                # All spans created here will have user_id, session_id, and metadata
138                with langfuse.start_span(name="llm_call") as llm_span:
139                    # This span inherits: user_id, session_id, experiment, environment
140                    ...
141
142                with langfuse.start_generation(name="completion") as gen:
143                    # This span also inherits all attributes
144                    ...
145        ```
146
147        Late propagation (anti-pattern):
148
149        ```python
150        with langfuse.start_as_current_span(name="workflow") as span:
151            # These spans WON'T have user_id
152            early_span = langfuse.start_span(name="early_work")
153            early_span.end()
154
155            # Set attributes in the middle
156            with langfuse.propagate_attributes(user_id="user_123"):
157                # Only spans created AFTER this point will have user_id
158                late_span = langfuse.start_span(name="late_work")
159                late_span.end()
160
161            # Result: Aggregations by user_id will miss "early_work" span
162        ```
163
164        Cross-service propagation with baggage (advanced):
165
166        ```python
167        # Service A - originating service
168        with langfuse.start_as_current_span(name="api_request"):
169            with langfuse.propagate_attributes(
170                user_id="user_123",
171                session_id="session_abc",
172                as_baggage=True  # Propagate via HTTP headers
173            ):
174                # Make HTTP request to Service B
175                response = requests.get("https://service-b.example.com/api")
176                # user_id and session_id are now in HTTP headers
177
178        # Service B - downstream service
179        # OpenTelemetry will automatically extract baggage from HTTP headers
180        # and propagate to spans in Service B
181        ```
182
183    Note:
184        - **Validation**: All attribute values (user_id, session_id, metadata values)
185          must be strings ≤200 characters. Invalid values will be dropped with a
186          warning logged. Ensure values meet constraints before calling.
187        - **OpenTelemetry**: This uses OpenTelemetry context propagation under the hood,
188          making it compatible with other OTel-instrumented libraries.
189
190    Raises:
191        No exceptions are raised. Invalid values are logged as warnings and dropped.
192    """
193    return _propagate_attributes(
194        user_id=user_id,
195        session_id=session_id,
196        metadata=metadata,
197        version=version,
198        tags=tags,
199        as_baggage=as_baggage,
200    )

Propagate trace-level attributes to all spans created within this context.

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

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

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

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

Context manager that propagates attributes to all child spans.

Example:

Basic usage with user and session tracking:

from langfuse import Langfuse

langfuse = Langfuse()

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

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

Late propagation (anti-pattern):

with langfuse.start_as_current_span(name="workflow") as span:
    # These spans WON'T have user_id
    early_span = langfuse.start_span(name="early_work")
    early_span.end()

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

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

Cross-service propagation with baggage (advanced):

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

# Service B - downstream service
# OpenTelemetry will automatically extract baggage from HTTP headers
# and propagate to spans in Service B
Note:
  • Validation: All attribute values (user_id, session_id, metadata values) must be strings ≤200 characters. Invalid values will be dropped with a warning logged. Ensure values meet constraints before calling.
  • OpenTelemetry: This uses OpenTelemetry context propagation under the hood, making it compatible with other OTel-instrumented libraries.
Raises:
  • No exceptions are raised. Invalid values are logged as warnings and dropped.
ObservationTypeLiteral = typing.Union[typing.Literal['generation', 'embedding'], typing.Literal['span', 'agent', 'tool', 'chain', 'retriever', 'evaluator', 'guardrail'], typing.Literal['event']]
class LangfuseSpan(langfuse._client.span.LangfuseObservationWrapper):
1146class LangfuseSpan(LangfuseObservationWrapper):
1147    """Standard span implementation for general operations in Langfuse.
1148
1149    This class represents a general-purpose span that can be used to trace
1150    any operation in your application. It extends the base LangfuseObservationWrapper
1151    with specific methods for creating child spans, generations, and updating
1152    span-specific attributes. If possible, use a more specific type for
1153    better observability and insights.
1154    """
1155
1156    def __init__(
1157        self,
1158        *,
1159        otel_span: otel_trace_api.Span,
1160        langfuse_client: "Langfuse",
1161        input: Optional[Any] = None,
1162        output: Optional[Any] = None,
1163        metadata: Optional[Any] = None,
1164        environment: Optional[str] = None,
1165        version: Optional[str] = None,
1166        level: Optional[SpanLevel] = None,
1167        status_message: Optional[str] = None,
1168    ):
1169        """Initialize a new LangfuseSpan.
1170
1171        Args:
1172            otel_span: The OpenTelemetry span to wrap
1173            langfuse_client: Reference to the parent Langfuse client
1174            input: Input data for the span (any JSON-serializable object)
1175            output: Output data from the span (any JSON-serializable object)
1176            metadata: Additional metadata to associate with the span
1177            environment: The tracing environment
1178            version: Version identifier for the code or component
1179            level: Importance level of the span (info, warning, error)
1180            status_message: Optional status message for the span
1181        """
1182        super().__init__(
1183            otel_span=otel_span,
1184            as_type="span",
1185            langfuse_client=langfuse_client,
1186            input=input,
1187            output=output,
1188            metadata=metadata,
1189            environment=environment,
1190            version=version,
1191            level=level,
1192            status_message=status_message,
1193        )
1194
1195    def start_span(
1196        self,
1197        name: str,
1198        input: Optional[Any] = None,
1199        output: Optional[Any] = None,
1200        metadata: Optional[Any] = None,
1201        version: Optional[str] = None,
1202        level: Optional[SpanLevel] = None,
1203        status_message: Optional[str] = None,
1204    ) -> "LangfuseSpan":
1205        """Create a new child span.
1206
1207        This method creates a new child span with this span as the parent.
1208        Unlike start_as_current_span(), this method does not set the new span
1209        as the current span in the context.
1210
1211        Args:
1212            name: Name of the span (e.g., function or operation name)
1213            input: Input data for the operation
1214            output: Output data from the operation
1215            metadata: Additional metadata to associate with the span
1216            version: Version identifier for the code or component
1217            level: Importance level of the span (info, warning, error)
1218            status_message: Optional status message for the span
1219
1220        Returns:
1221            A new LangfuseSpan that must be ended with .end() when complete
1222
1223        Example:
1224            ```python
1225            parent_span = langfuse.start_span(name="process-request")
1226            try:
1227                # Create a child span
1228                child_span = parent_span.start_span(name="validate-input")
1229                try:
1230                    # Do validation work
1231                    validation_result = validate(request_data)
1232                    child_span.update(output=validation_result)
1233                finally:
1234                    child_span.end()
1235
1236                # Continue with parent span
1237                result = process_validated_data(validation_result)
1238                parent_span.update(output=result)
1239            finally:
1240                parent_span.end()
1241            ```
1242        """
1243        return self.start_observation(
1244            name=name,
1245            as_type="span",
1246            input=input,
1247            output=output,
1248            metadata=metadata,
1249            version=version,
1250            level=level,
1251            status_message=status_message,
1252        )
1253
1254    def start_as_current_span(
1255        self,
1256        *,
1257        name: str,
1258        input: Optional[Any] = None,
1259        output: Optional[Any] = None,
1260        metadata: Optional[Any] = None,
1261        version: Optional[str] = None,
1262        level: Optional[SpanLevel] = None,
1263        status_message: Optional[str] = None,
1264    ) -> _AgnosticContextManager["LangfuseSpan"]:
1265        """[DEPRECATED] Create a new child span and set it as the current span in a context manager.
1266
1267        DEPRECATED: This method is deprecated and will be removed in a future version.
1268        Use start_as_current_observation(as_type='span') instead.
1269
1270        This method creates a new child span and sets it as the current span within
1271        a context manager. It should be used with a 'with' statement to automatically
1272        manage the span's lifecycle.
1273
1274        Args:
1275            name: Name of the span (e.g., function or operation name)
1276            input: Input data for the operation
1277            output: Output data from the operation
1278            metadata: Additional metadata to associate with the span
1279            version: Version identifier for the code or component
1280            level: Importance level of the span (info, warning, error)
1281            status_message: Optional status message for the span
1282
1283        Returns:
1284            A context manager that yields a new LangfuseSpan
1285
1286        Example:
1287            ```python
1288            with langfuse.start_as_current_span(name="process-request") as parent_span:
1289                # Parent span is active here
1290
1291                # Create a child span with context management
1292                with parent_span.start_as_current_span(name="validate-input") as child_span:
1293                    # Child span is active here
1294                    validation_result = validate(request_data)
1295                    child_span.update(output=validation_result)
1296
1297                # Back to parent span context
1298                result = process_validated_data(validation_result)
1299                parent_span.update(output=result)
1300            ```
1301        """
1302        warnings.warn(
1303            "start_as_current_span is deprecated and will be removed in a future version. "
1304            "Use start_as_current_observation(as_type='span') instead.",
1305            DeprecationWarning,
1306            stacklevel=2,
1307        )
1308        return self.start_as_current_observation(
1309            name=name,
1310            as_type="span",
1311            input=input,
1312            output=output,
1313            metadata=metadata,
1314            version=version,
1315            level=level,
1316            status_message=status_message,
1317        )
1318
1319    def start_generation(
1320        self,
1321        *,
1322        name: str,
1323        input: Optional[Any] = None,
1324        output: Optional[Any] = None,
1325        metadata: Optional[Any] = None,
1326        version: Optional[str] = None,
1327        level: Optional[SpanLevel] = None,
1328        status_message: Optional[str] = None,
1329        completion_start_time: Optional[datetime] = None,
1330        model: Optional[str] = None,
1331        model_parameters: Optional[Dict[str, MapValue]] = None,
1332        usage_details: Optional[Dict[str, int]] = None,
1333        cost_details: Optional[Dict[str, float]] = None,
1334        prompt: Optional[PromptClient] = None,
1335    ) -> "LangfuseGeneration":
1336        """[DEPRECATED] Create a new child generation span.
1337
1338        DEPRECATED: This method is deprecated and will be removed in a future version.
1339        Use start_observation(as_type='generation') instead.
1340
1341        This method creates a new child generation span with this span as the parent.
1342        Generation spans are specialized for AI/LLM operations and include additional
1343        fields for model information, usage stats, and costs.
1344
1345        Unlike start_as_current_generation(), this method does not set the new span
1346        as the current span in the context.
1347
1348        Args:
1349            name: Name of the generation operation
1350            input: Input data for the model (e.g., prompts)
1351            output: Output from the model (e.g., completions)
1352            metadata: Additional metadata to associate with the generation
1353            version: Version identifier for the model or component
1354            level: Importance level of the generation (info, warning, error)
1355            status_message: Optional status message for the generation
1356            completion_start_time: When the model started generating the response
1357            model: Name/identifier of the AI model used (e.g., "gpt-4")
1358            model_parameters: Parameters used for the model (e.g., temperature, max_tokens)
1359            usage_details: Token usage information (e.g., prompt_tokens, completion_tokens)
1360            cost_details: Cost information for the model call
1361            prompt: Associated prompt template from Langfuse prompt management
1362
1363        Returns:
1364            A new LangfuseGeneration that must be ended with .end() when complete
1365
1366        Example:
1367            ```python
1368            span = langfuse.start_span(name="process-query")
1369            try:
1370                # Create a generation child span
1371                generation = span.start_generation(
1372                    name="generate-answer",
1373                    model="gpt-4",
1374                    input={"prompt": "Explain quantum computing"}
1375                )
1376                try:
1377                    # Call model API
1378                    response = llm.generate(...)
1379
1380                    generation.update(
1381                        output=response.text,
1382                        usage_details={
1383                            "prompt_tokens": response.usage.prompt_tokens,
1384                            "completion_tokens": response.usage.completion_tokens
1385                        }
1386                    )
1387                finally:
1388                    generation.end()
1389
1390                # Continue with parent span
1391                span.update(output={"answer": response.text, "source": "gpt-4"})
1392            finally:
1393                span.end()
1394            ```
1395        """
1396        warnings.warn(
1397            "start_generation is deprecated and will be removed in a future version. "
1398            "Use start_observation(as_type='generation') instead.",
1399            DeprecationWarning,
1400            stacklevel=2,
1401        )
1402        return self.start_observation(
1403            name=name,
1404            as_type="generation",
1405            input=input,
1406            output=output,
1407            metadata=metadata,
1408            version=version,
1409            level=level,
1410            status_message=status_message,
1411            completion_start_time=completion_start_time,
1412            model=model,
1413            model_parameters=model_parameters,
1414            usage_details=usage_details,
1415            cost_details=cost_details,
1416            prompt=prompt,
1417        )
1418
1419    def start_as_current_generation(
1420        self,
1421        *,
1422        name: str,
1423        input: Optional[Any] = None,
1424        output: Optional[Any] = None,
1425        metadata: Optional[Any] = None,
1426        version: Optional[str] = None,
1427        level: Optional[SpanLevel] = None,
1428        status_message: Optional[str] = None,
1429        completion_start_time: Optional[datetime] = None,
1430        model: Optional[str] = None,
1431        model_parameters: Optional[Dict[str, MapValue]] = None,
1432        usage_details: Optional[Dict[str, int]] = None,
1433        cost_details: Optional[Dict[str, float]] = None,
1434        prompt: Optional[PromptClient] = None,
1435    ) -> _AgnosticContextManager["LangfuseGeneration"]:
1436        """[DEPRECATED] Create a new child generation span and set it as the current span in a context manager.
1437
1438        DEPRECATED: This method is deprecated and will be removed in a future version.
1439        Use start_as_current_observation(as_type='generation') instead.
1440
1441        This method creates a new child generation span and sets it as the current span
1442        within a context manager. Generation spans are specialized for AI/LLM operations
1443        and include additional fields for model information, usage stats, and costs.
1444
1445        Args:
1446            name: Name of the generation operation
1447            input: Input data for the model (e.g., prompts)
1448            output: Output from the model (e.g., completions)
1449            metadata: Additional metadata to associate with the generation
1450            version: Version identifier for the model or component
1451            level: Importance level of the generation (info, warning, error)
1452            status_message: Optional status message for the generation
1453            completion_start_time: When the model started generating the response
1454            model: Name/identifier of the AI model used (e.g., "gpt-4")
1455            model_parameters: Parameters used for the model (e.g., temperature, max_tokens)
1456            usage_details: Token usage information (e.g., prompt_tokens, completion_tokens)
1457            cost_details: Cost information for the model call
1458            prompt: Associated prompt template from Langfuse prompt management
1459
1460        Returns:
1461            A context manager that yields a new LangfuseGeneration
1462
1463        Example:
1464            ```python
1465            with langfuse.start_as_current_span(name="process-request") as span:
1466                # Prepare data
1467                query = preprocess_user_query(user_input)
1468
1469                # Create a generation span with context management
1470                with span.start_as_current_generation(
1471                    name="generate-answer",
1472                    model="gpt-4",
1473                    input={"query": query}
1474                ) as generation:
1475                    # Generation span is active here
1476                    response = llm.generate(query)
1477
1478                    # Update with results
1479                    generation.update(
1480                        output=response.text,
1481                        usage_details={
1482                            "prompt_tokens": response.usage.prompt_tokens,
1483                            "completion_tokens": response.usage.completion_tokens
1484                        }
1485                    )
1486
1487                # Back to parent span context
1488                span.update(output={"answer": response.text, "source": "gpt-4"})
1489            ```
1490        """
1491        warnings.warn(
1492            "start_as_current_generation is deprecated and will be removed in a future version. "
1493            "Use start_as_current_observation(as_type='generation') instead.",
1494            DeprecationWarning,
1495            stacklevel=2,
1496        )
1497        return self.start_as_current_observation(
1498            name=name,
1499            as_type="generation",
1500            input=input,
1501            output=output,
1502            metadata=metadata,
1503            version=version,
1504            level=level,
1505            status_message=status_message,
1506            completion_start_time=completion_start_time,
1507            model=model,
1508            model_parameters=model_parameters,
1509            usage_details=usage_details,
1510            cost_details=cost_details,
1511            prompt=prompt,
1512        )
1513
1514    def create_event(
1515        self,
1516        *,
1517        name: str,
1518        input: Optional[Any] = None,
1519        output: Optional[Any] = None,
1520        metadata: Optional[Any] = None,
1521        version: Optional[str] = None,
1522        level: Optional[SpanLevel] = None,
1523        status_message: Optional[str] = None,
1524    ) -> "LangfuseEvent":
1525        """Create a new Langfuse observation of type 'EVENT'.
1526
1527        Args:
1528            name: Name of the span (e.g., function or operation name)
1529            input: Input data for the operation (can be any JSON-serializable object)
1530            output: Output data from the operation (can be any JSON-serializable object)
1531            metadata: Additional metadata to associate with the span
1532            version: Version identifier for the code or component
1533            level: Importance level of the span (info, warning, error)
1534            status_message: Optional status message for the span
1535
1536        Returns:
1537            The LangfuseEvent object
1538
1539        Example:
1540            ```python
1541            event = langfuse.create_event(name="process-event")
1542            ```
1543        """
1544        timestamp = time_ns()
1545
1546        with otel_trace_api.use_span(self._otel_span):
1547            new_otel_span = self._langfuse_client._otel_tracer.start_span(
1548                name=name, start_time=timestamp
1549            )
1550
1551        return cast(
1552            "LangfuseEvent",
1553            LangfuseEvent(
1554                otel_span=new_otel_span,
1555                langfuse_client=self._langfuse_client,
1556                input=input,
1557                output=output,
1558                metadata=metadata,
1559                environment=self._environment,
1560                version=version,
1561                level=level,
1562                status_message=status_message,
1563            ).end(end_time=timestamp),
1564        )

Standard span implementation for general operations in Langfuse.

This class represents a general-purpose span that can be used to trace any operation in your application. It extends the base LangfuseObservationWrapper with specific methods for creating child spans, generations, and updating span-specific attributes. If possible, use a more specific type for better observability and insights.

LangfuseSpan( *, otel_span: opentelemetry.trace.span.Span, langfuse_client: Langfuse, input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, environment: Optional[str] = None, version: Optional[str] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None)
1156    def __init__(
1157        self,
1158        *,
1159        otel_span: otel_trace_api.Span,
1160        langfuse_client: "Langfuse",
1161        input: Optional[Any] = None,
1162        output: Optional[Any] = None,
1163        metadata: Optional[Any] = None,
1164        environment: Optional[str] = None,
1165        version: Optional[str] = None,
1166        level: Optional[SpanLevel] = None,
1167        status_message: Optional[str] = None,
1168    ):
1169        """Initialize a new LangfuseSpan.
1170
1171        Args:
1172            otel_span: The OpenTelemetry span to wrap
1173            langfuse_client: Reference to the parent Langfuse client
1174            input: Input data for the span (any JSON-serializable object)
1175            output: Output data from the span (any JSON-serializable object)
1176            metadata: Additional metadata to associate with the span
1177            environment: The tracing environment
1178            version: Version identifier for the code or component
1179            level: Importance level of the span (info, warning, error)
1180            status_message: Optional status message for the span
1181        """
1182        super().__init__(
1183            otel_span=otel_span,
1184            as_type="span",
1185            langfuse_client=langfuse_client,
1186            input=input,
1187            output=output,
1188            metadata=metadata,
1189            environment=environment,
1190            version=version,
1191            level=level,
1192            status_message=status_message,
1193        )

Initialize a new LangfuseSpan.

Arguments:
  • otel_span: The OpenTelemetry span to wrap
  • langfuse_client: Reference to the parent Langfuse client
  • input: Input data for the span (any JSON-serializable object)
  • output: Output data from the span (any JSON-serializable object)
  • metadata: Additional metadata to associate with the span
  • environment: The tracing environment
  • 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
def start_span( self, name: str, input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, version: Optional[str] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None) -> LangfuseSpan:
1195    def start_span(
1196        self,
1197        name: str,
1198        input: Optional[Any] = None,
1199        output: Optional[Any] = None,
1200        metadata: Optional[Any] = None,
1201        version: Optional[str] = None,
1202        level: Optional[SpanLevel] = None,
1203        status_message: Optional[str] = None,
1204    ) -> "LangfuseSpan":
1205        """Create a new child span.
1206
1207        This method creates a new child span with this span as the parent.
1208        Unlike start_as_current_span(), this method does not set the new span
1209        as the current span in the context.
1210
1211        Args:
1212            name: Name of the span (e.g., function or operation name)
1213            input: Input data for the operation
1214            output: Output data from the operation
1215            metadata: Additional metadata to associate with the span
1216            version: Version identifier for the code or component
1217            level: Importance level of the span (info, warning, error)
1218            status_message: Optional status message for the span
1219
1220        Returns:
1221            A new LangfuseSpan that must be ended with .end() when complete
1222
1223        Example:
1224            ```python
1225            parent_span = langfuse.start_span(name="process-request")
1226            try:
1227                # Create a child span
1228                child_span = parent_span.start_span(name="validate-input")
1229                try:
1230                    # Do validation work
1231                    validation_result = validate(request_data)
1232                    child_span.update(output=validation_result)
1233                finally:
1234                    child_span.end()
1235
1236                # Continue with parent span
1237                result = process_validated_data(validation_result)
1238                parent_span.update(output=result)
1239            finally:
1240                parent_span.end()
1241            ```
1242        """
1243        return self.start_observation(
1244            name=name,
1245            as_type="span",
1246            input=input,
1247            output=output,
1248            metadata=metadata,
1249            version=version,
1250            level=level,
1251            status_message=status_message,
1252        )

Create a new child span.

This method creates a new child span with this span as the parent. Unlike start_as_current_span(), this method does not set the new span as the current span in the context.

Arguments:
  • name: Name of the span (e.g., function or operation name)
  • input: 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
Returns:

A new LangfuseSpan that must be ended with .end() when complete

Example:
parent_span = langfuse.start_span(name="process-request")
try:
    # Create a child span
    child_span = parent_span.start_span(name="validate-input")
    try:
        # Do validation work
        validation_result = validate(request_data)
        child_span.update(output=validation_result)
    finally:
        child_span.end()

    # Continue with parent span
    result = process_validated_data(validation_result)
    parent_span.update(output=result)
finally:
    parent_span.end()
def start_as_current_span( self, *, name: str, input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, version: Optional[str] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None) -> opentelemetry.util._decorator._AgnosticContextManager[LangfuseSpan]:
1254    def start_as_current_span(
1255        self,
1256        *,
1257        name: str,
1258        input: Optional[Any] = None,
1259        output: Optional[Any] = None,
1260        metadata: Optional[Any] = None,
1261        version: Optional[str] = None,
1262        level: Optional[SpanLevel] = None,
1263        status_message: Optional[str] = None,
1264    ) -> _AgnosticContextManager["LangfuseSpan"]:
1265        """[DEPRECATED] Create a new child span and set it as the current span in a context manager.
1266
1267        DEPRECATED: This method is deprecated and will be removed in a future version.
1268        Use start_as_current_observation(as_type='span') instead.
1269
1270        This method creates a new child span and sets it as the current span within
1271        a context manager. It should be used with a 'with' statement to automatically
1272        manage the span's lifecycle.
1273
1274        Args:
1275            name: Name of the span (e.g., function or operation name)
1276            input: Input data for the operation
1277            output: Output data from the operation
1278            metadata: Additional metadata to associate with the span
1279            version: Version identifier for the code or component
1280            level: Importance level of the span (info, warning, error)
1281            status_message: Optional status message for the span
1282
1283        Returns:
1284            A context manager that yields a new LangfuseSpan
1285
1286        Example:
1287            ```python
1288            with langfuse.start_as_current_span(name="process-request") as parent_span:
1289                # Parent span is active here
1290
1291                # Create a child span with context management
1292                with parent_span.start_as_current_span(name="validate-input") as child_span:
1293                    # Child span is active here
1294                    validation_result = validate(request_data)
1295                    child_span.update(output=validation_result)
1296
1297                # Back to parent span context
1298                result = process_validated_data(validation_result)
1299                parent_span.update(output=result)
1300            ```
1301        """
1302        warnings.warn(
1303            "start_as_current_span is deprecated and will be removed in a future version. "
1304            "Use start_as_current_observation(as_type='span') instead.",
1305            DeprecationWarning,
1306            stacklevel=2,
1307        )
1308        return self.start_as_current_observation(
1309            name=name,
1310            as_type="span",
1311            input=input,
1312            output=output,
1313            metadata=metadata,
1314            version=version,
1315            level=level,
1316            status_message=status_message,
1317        )

[DEPRECATED] Create a new child span and set it as the current span in a context manager.

DEPRECATED: This method is deprecated and will be removed in a future version. Use start_as_current_observation(as_type='span') instead.

This method creates a new child span and sets it as the current span within a context manager. It should be used with a 'with' statement to automatically manage the span's lifecycle.

Arguments:
  • name: Name of the span (e.g., function or operation name)
  • input: 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
Returns:

A context manager that yields a new LangfuseSpan

Example:
with langfuse.start_as_current_span(name="process-request") as parent_span:
    # Parent span is active here

    # Create a child span with context management
    with parent_span.start_as_current_span(name="validate-input") as child_span:
        # Child span is active here
        validation_result = validate(request_data)
        child_span.update(output=validation_result)

    # Back to parent span context
    result = process_validated_data(validation_result)
    parent_span.update(output=result)
def start_generation( self, *, name: str, input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, version: Optional[str] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, completion_start_time: Optional[datetime.datetime] = None, model: Optional[str] = None, model_parameters: Optional[Dict[str, Union[str, NoneType, int, bool, List[str]]]] = None, usage_details: Optional[Dict[str, int]] = None, cost_details: Optional[Dict[str, float]] = None, prompt: Union[langfuse.model.TextPromptClient, langfuse.model.ChatPromptClient, NoneType] = None) -> LangfuseGeneration:
1319    def start_generation(
1320        self,
1321        *,
1322        name: str,
1323        input: Optional[Any] = None,
1324        output: Optional[Any] = None,
1325        metadata: Optional[Any] = None,
1326        version: Optional[str] = None,
1327        level: Optional[SpanLevel] = None,
1328        status_message: Optional[str] = None,
1329        completion_start_time: Optional[datetime] = None,
1330        model: Optional[str] = None,
1331        model_parameters: Optional[Dict[str, MapValue]] = None,
1332        usage_details: Optional[Dict[str, int]] = None,
1333        cost_details: Optional[Dict[str, float]] = None,
1334        prompt: Optional[PromptClient] = None,
1335    ) -> "LangfuseGeneration":
1336        """[DEPRECATED] Create a new child generation span.
1337
1338        DEPRECATED: This method is deprecated and will be removed in a future version.
1339        Use start_observation(as_type='generation') instead.
1340
1341        This method creates a new child generation span with this span as the parent.
1342        Generation spans are specialized for AI/LLM operations and include additional
1343        fields for model information, usage stats, and costs.
1344
1345        Unlike start_as_current_generation(), this method does not set the new span
1346        as the current span in the context.
1347
1348        Args:
1349            name: Name of the generation operation
1350            input: Input data for the model (e.g., prompts)
1351            output: Output from the model (e.g., completions)
1352            metadata: Additional metadata to associate with the generation
1353            version: Version identifier for the model or component
1354            level: Importance level of the generation (info, warning, error)
1355            status_message: Optional status message for the generation
1356            completion_start_time: When the model started generating the response
1357            model: Name/identifier of the AI model used (e.g., "gpt-4")
1358            model_parameters: Parameters used for the model (e.g., temperature, max_tokens)
1359            usage_details: Token usage information (e.g., prompt_tokens, completion_tokens)
1360            cost_details: Cost information for the model call
1361            prompt: Associated prompt template from Langfuse prompt management
1362
1363        Returns:
1364            A new LangfuseGeneration that must be ended with .end() when complete
1365
1366        Example:
1367            ```python
1368            span = langfuse.start_span(name="process-query")
1369            try:
1370                # Create a generation child span
1371                generation = span.start_generation(
1372                    name="generate-answer",
1373                    model="gpt-4",
1374                    input={"prompt": "Explain quantum computing"}
1375                )
1376                try:
1377                    # Call model API
1378                    response = llm.generate(...)
1379
1380                    generation.update(
1381                        output=response.text,
1382                        usage_details={
1383                            "prompt_tokens": response.usage.prompt_tokens,
1384                            "completion_tokens": response.usage.completion_tokens
1385                        }
1386                    )
1387                finally:
1388                    generation.end()
1389
1390                # Continue with parent span
1391                span.update(output={"answer": response.text, "source": "gpt-4"})
1392            finally:
1393                span.end()
1394            ```
1395        """
1396        warnings.warn(
1397            "start_generation is deprecated and will be removed in a future version. "
1398            "Use start_observation(as_type='generation') instead.",
1399            DeprecationWarning,
1400            stacklevel=2,
1401        )
1402        return self.start_observation(
1403            name=name,
1404            as_type="generation",
1405            input=input,
1406            output=output,
1407            metadata=metadata,
1408            version=version,
1409            level=level,
1410            status_message=status_message,
1411            completion_start_time=completion_start_time,
1412            model=model,
1413            model_parameters=model_parameters,
1414            usage_details=usage_details,
1415            cost_details=cost_details,
1416            prompt=prompt,
1417        )

[DEPRECATED] Create a new child generation span.

DEPRECATED: This method is deprecated and will be removed in a future version. Use start_observation(as_type='generation') instead.

This method creates a new child generation span with this span as the parent. Generation spans are specialized for AI/LLM operations and include additional fields for model information, usage stats, and costs.

Unlike start_as_current_generation(), this method does not set the new span as the current span in the context.

Arguments:
  • name: Name of the generation operation
  • input: Input data for the model (e.g., prompts)
  • 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
Returns:

A new LangfuseGeneration that must be ended with .end() when complete

Example:
span = langfuse.start_span(name="process-query")
try:
    # Create a generation child span
    generation = span.start_generation(
        name="generate-answer",
        model="gpt-4",
        input={"prompt": "Explain quantum computing"}
    )
    try:
        # Call model API
        response = llm.generate(...)

        generation.update(
            output=response.text,
            usage_details={
                "prompt_tokens": response.usage.prompt_tokens,
                "completion_tokens": response.usage.completion_tokens
            }
        )
    finally:
        generation.end()

    # Continue with parent span
    span.update(output={"answer": response.text, "source": "gpt-4"})
finally:
    span.end()
def start_as_current_generation( self, *, name: str, input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, version: Optional[str] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, completion_start_time: Optional[datetime.datetime] = None, model: Optional[str] = None, model_parameters: Optional[Dict[str, Union[str, NoneType, int, bool, List[str]]]] = None, usage_details: Optional[Dict[str, int]] = None, cost_details: Optional[Dict[str, float]] = None, prompt: Union[langfuse.model.TextPromptClient, langfuse.model.ChatPromptClient, NoneType] = None) -> opentelemetry.util._decorator._AgnosticContextManager[LangfuseGeneration]:
1419    def start_as_current_generation(
1420        self,
1421        *,
1422        name: str,
1423        input: Optional[Any] = None,
1424        output: Optional[Any] = None,
1425        metadata: Optional[Any] = None,
1426        version: Optional[str] = None,
1427        level: Optional[SpanLevel] = None,
1428        status_message: Optional[str] = None,
1429        completion_start_time: Optional[datetime] = None,
1430        model: Optional[str] = None,
1431        model_parameters: Optional[Dict[str, MapValue]] = None,
1432        usage_details: Optional[Dict[str, int]] = None,
1433        cost_details: Optional[Dict[str, float]] = None,
1434        prompt: Optional[PromptClient] = None,
1435    ) -> _AgnosticContextManager["LangfuseGeneration"]:
1436        """[DEPRECATED] Create a new child generation span and set it as the current span in a context manager.
1437
1438        DEPRECATED: This method is deprecated and will be removed in a future version.
1439        Use start_as_current_observation(as_type='generation') instead.
1440
1441        This method creates a new child generation span and sets it as the current span
1442        within a context manager. Generation spans are specialized for AI/LLM operations
1443        and include additional fields for model information, usage stats, and costs.
1444
1445        Args:
1446            name: Name of the generation operation
1447            input: Input data for the model (e.g., prompts)
1448            output: Output from the model (e.g., completions)
1449            metadata: Additional metadata to associate with the generation
1450            version: Version identifier for the model or component
1451            level: Importance level of the generation (info, warning, error)
1452            status_message: Optional status message for the generation
1453            completion_start_time: When the model started generating the response
1454            model: Name/identifier of the AI model used (e.g., "gpt-4")
1455            model_parameters: Parameters used for the model (e.g., temperature, max_tokens)
1456            usage_details: Token usage information (e.g., prompt_tokens, completion_tokens)
1457            cost_details: Cost information for the model call
1458            prompt: Associated prompt template from Langfuse prompt management
1459
1460        Returns:
1461            A context manager that yields a new LangfuseGeneration
1462
1463        Example:
1464            ```python
1465            with langfuse.start_as_current_span(name="process-request") as span:
1466                # Prepare data
1467                query = preprocess_user_query(user_input)
1468
1469                # Create a generation span with context management
1470                with span.start_as_current_generation(
1471                    name="generate-answer",
1472                    model="gpt-4",
1473                    input={"query": query}
1474                ) as generation:
1475                    # Generation span is active here
1476                    response = llm.generate(query)
1477
1478                    # Update with results
1479                    generation.update(
1480                        output=response.text,
1481                        usage_details={
1482                            "prompt_tokens": response.usage.prompt_tokens,
1483                            "completion_tokens": response.usage.completion_tokens
1484                        }
1485                    )
1486
1487                # Back to parent span context
1488                span.update(output={"answer": response.text, "source": "gpt-4"})
1489            ```
1490        """
1491        warnings.warn(
1492            "start_as_current_generation is deprecated and will be removed in a future version. "
1493            "Use start_as_current_observation(as_type='generation') instead.",
1494            DeprecationWarning,
1495            stacklevel=2,
1496        )
1497        return self.start_as_current_observation(
1498            name=name,
1499            as_type="generation",
1500            input=input,
1501            output=output,
1502            metadata=metadata,
1503            version=version,
1504            level=level,
1505            status_message=status_message,
1506            completion_start_time=completion_start_time,
1507            model=model,
1508            model_parameters=model_parameters,
1509            usage_details=usage_details,
1510            cost_details=cost_details,
1511            prompt=prompt,
1512        )

[DEPRECATED] Create a new child generation span and set it as the current span in a context manager.

DEPRECATED: This method is deprecated and will be removed in a future version. Use start_as_current_observation(as_type='generation') instead.

This method creates a new child generation span and sets it as the current span within a context manager. Generation spans are specialized for AI/LLM operations and include additional fields for model information, usage stats, and costs.

Arguments:
  • name: Name of the generation operation
  • input: Input data for the model (e.g., prompts)
  • 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
Returns:

A context manager that yields a new LangfuseGeneration

Example:
with langfuse.start_as_current_span(name="process-request") as span:
    # Prepare data
    query = preprocess_user_query(user_input)

    # Create a generation span with context management
    with span.start_as_current_generation(
        name="generate-answer",
        model="gpt-4",
        input={"query": query}
    ) as generation:
        # Generation span is active here
        response = llm.generate(query)

        # Update with results
        generation.update(
            output=response.text,
            usage_details={
                "prompt_tokens": response.usage.prompt_tokens,
                "completion_tokens": response.usage.completion_tokens
            }
        )

    # Back to parent span context
    span.update(output={"answer": response.text, "source": "gpt-4"})
def create_event( self, *, name: str, input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, version: Optional[str] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None) -> LangfuseEvent:
1514    def create_event(
1515        self,
1516        *,
1517        name: str,
1518        input: Optional[Any] = None,
1519        output: Optional[Any] = None,
1520        metadata: Optional[Any] = None,
1521        version: Optional[str] = None,
1522        level: Optional[SpanLevel] = None,
1523        status_message: Optional[str] = None,
1524    ) -> "LangfuseEvent":
1525        """Create a new Langfuse observation of type 'EVENT'.
1526
1527        Args:
1528            name: Name of the span (e.g., function or operation name)
1529            input: Input data for the operation (can be any JSON-serializable object)
1530            output: Output data from the operation (can be any JSON-serializable object)
1531            metadata: Additional metadata to associate with the span
1532            version: Version identifier for the code or component
1533            level: Importance level of the span (info, warning, error)
1534            status_message: Optional status message for the span
1535
1536        Returns:
1537            The LangfuseEvent object
1538
1539        Example:
1540            ```python
1541            event = langfuse.create_event(name="process-event")
1542            ```
1543        """
1544        timestamp = time_ns()
1545
1546        with otel_trace_api.use_span(self._otel_span):
1547            new_otel_span = self._langfuse_client._otel_tracer.start_span(
1548                name=name, start_time=timestamp
1549            )
1550
1551        return cast(
1552            "LangfuseEvent",
1553            LangfuseEvent(
1554                otel_span=new_otel_span,
1555                langfuse_client=self._langfuse_client,
1556                input=input,
1557                output=output,
1558                metadata=metadata,
1559                environment=self._environment,
1560                version=version,
1561                level=level,
1562                status_message=status_message,
1563            ).end(end_time=timestamp),
1564        )

Create a new Langfuse observation of type 'EVENT'.

Arguments:
  • 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 LangfuseEvent object

Example:
event = langfuse.create_event(name="process-event")
class LangfuseGeneration(langfuse._client.span.LangfuseObservationWrapper):
1567class LangfuseGeneration(LangfuseObservationWrapper):
1568    """Specialized span implementation for AI model generations in Langfuse.
1569
1570    This class represents a generation span specifically designed for tracking
1571    AI/LLM operations. It extends the base LangfuseObservationWrapper with specialized
1572    attributes for model details, token usage, and costs.
1573    """
1574
1575    def __init__(
1576        self,
1577        *,
1578        otel_span: otel_trace_api.Span,
1579        langfuse_client: "Langfuse",
1580        input: Optional[Any] = None,
1581        output: Optional[Any] = None,
1582        metadata: Optional[Any] = None,
1583        environment: Optional[str] = None,
1584        version: Optional[str] = None,
1585        level: Optional[SpanLevel] = None,
1586        status_message: Optional[str] = None,
1587        completion_start_time: Optional[datetime] = None,
1588        model: Optional[str] = None,
1589        model_parameters: Optional[Dict[str, MapValue]] = None,
1590        usage_details: Optional[Dict[str, int]] = None,
1591        cost_details: Optional[Dict[str, float]] = None,
1592        prompt: Optional[PromptClient] = None,
1593    ):
1594        """Initialize a new LangfuseGeneration span.
1595
1596        Args:
1597            otel_span: The OpenTelemetry span to wrap
1598            langfuse_client: Reference to the parent Langfuse client
1599            input: Input data for the generation (e.g., prompts)
1600            output: Output from the generation (e.g., completions)
1601            metadata: Additional metadata to associate with the generation
1602            environment: The tracing environment
1603            version: Version identifier for the model or component
1604            level: Importance level of the generation (info, warning, error)
1605            status_message: Optional status message for the generation
1606            completion_start_time: When the model started generating the response
1607            model: Name/identifier of the AI model used (e.g., "gpt-4")
1608            model_parameters: Parameters used for the model (e.g., temperature, max_tokens)
1609            usage_details: Token usage information (e.g., prompt_tokens, completion_tokens)
1610            cost_details: Cost information for the model call
1611            prompt: Associated prompt template from Langfuse prompt management
1612        """
1613        super().__init__(
1614            as_type="generation",
1615            otel_span=otel_span,
1616            langfuse_client=langfuse_client,
1617            input=input,
1618            output=output,
1619            metadata=metadata,
1620            environment=environment,
1621            version=version,
1622            level=level,
1623            status_message=status_message,
1624            completion_start_time=completion_start_time,
1625            model=model,
1626            model_parameters=model_parameters,
1627            usage_details=usage_details,
1628            cost_details=cost_details,
1629            prompt=prompt,
1630        )

Specialized span implementation for AI model generations in Langfuse.

This class represents a generation span specifically designed for tracking AI/LLM operations. It extends the base LangfuseObservationWrapper with specialized attributes for model details, token usage, and costs.

LangfuseGeneration( *, otel_span: opentelemetry.trace.span.Span, langfuse_client: Langfuse, input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, environment: Optional[str] = None, version: Optional[str] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, completion_start_time: Optional[datetime.datetime] = None, model: Optional[str] = None, model_parameters: Optional[Dict[str, Union[str, NoneType, int, bool, List[str]]]] = None, usage_details: Optional[Dict[str, int]] = None, cost_details: Optional[Dict[str, float]] = None, prompt: Union[langfuse.model.TextPromptClient, langfuse.model.ChatPromptClient, NoneType] = None)
1575    def __init__(
1576        self,
1577        *,
1578        otel_span: otel_trace_api.Span,
1579        langfuse_client: "Langfuse",
1580        input: Optional[Any] = None,
1581        output: Optional[Any] = None,
1582        metadata: Optional[Any] = None,
1583        environment: Optional[str] = None,
1584        version: Optional[str] = None,
1585        level: Optional[SpanLevel] = None,
1586        status_message: Optional[str] = None,
1587        completion_start_time: Optional[datetime] = None,
1588        model: Optional[str] = None,
1589        model_parameters: Optional[Dict[str, MapValue]] = None,
1590        usage_details: Optional[Dict[str, int]] = None,
1591        cost_details: Optional[Dict[str, float]] = None,
1592        prompt: Optional[PromptClient] = None,
1593    ):
1594        """Initialize a new LangfuseGeneration span.
1595
1596        Args:
1597            otel_span: The OpenTelemetry span to wrap
1598            langfuse_client: Reference to the parent Langfuse client
1599            input: Input data for the generation (e.g., prompts)
1600            output: Output from the generation (e.g., completions)
1601            metadata: Additional metadata to associate with the generation
1602            environment: The tracing environment
1603            version: Version identifier for the model or component
1604            level: Importance level of the generation (info, warning, error)
1605            status_message: Optional status message for the generation
1606            completion_start_time: When the model started generating the response
1607            model: Name/identifier of the AI model used (e.g., "gpt-4")
1608            model_parameters: Parameters used for the model (e.g., temperature, max_tokens)
1609            usage_details: Token usage information (e.g., prompt_tokens, completion_tokens)
1610            cost_details: Cost information for the model call
1611            prompt: Associated prompt template from Langfuse prompt management
1612        """
1613        super().__init__(
1614            as_type="generation",
1615            otel_span=otel_span,
1616            langfuse_client=langfuse_client,
1617            input=input,
1618            output=output,
1619            metadata=metadata,
1620            environment=environment,
1621            version=version,
1622            level=level,
1623            status_message=status_message,
1624            completion_start_time=completion_start_time,
1625            model=model,
1626            model_parameters=model_parameters,
1627            usage_details=usage_details,
1628            cost_details=cost_details,
1629            prompt=prompt,
1630        )

Initialize a new LangfuseGeneration span.

Arguments:
  • otel_span: The OpenTelemetry span to wrap
  • langfuse_client: Reference to the parent Langfuse client
  • input: Input data for the generation (e.g., prompts)
  • output: Output from the generation (e.g., completions)
  • metadata: Additional metadata to associate with the generation
  • environment: The tracing environment
  • 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
class LangfuseEvent(langfuse._client.span.LangfuseObservationWrapper):
1633class LangfuseEvent(LangfuseObservationWrapper):
1634    """Specialized span implementation for Langfuse Events."""
1635
1636    def __init__(
1637        self,
1638        *,
1639        otel_span: otel_trace_api.Span,
1640        langfuse_client: "Langfuse",
1641        input: Optional[Any] = None,
1642        output: Optional[Any] = None,
1643        metadata: Optional[Any] = None,
1644        environment: Optional[str] = None,
1645        version: Optional[str] = None,
1646        level: Optional[SpanLevel] = None,
1647        status_message: Optional[str] = None,
1648    ):
1649        """Initialize a new LangfuseEvent span.
1650
1651        Args:
1652            otel_span: The OpenTelemetry span to wrap
1653            langfuse_client: Reference to the parent Langfuse client
1654            input: Input data for the event
1655            output: Output from the event
1656            metadata: Additional metadata to associate with the generation
1657            environment: The tracing environment
1658            version: Version identifier for the model or component
1659            level: Importance level of the generation (info, warning, error)
1660            status_message: Optional status message for the generation
1661        """
1662        super().__init__(
1663            otel_span=otel_span,
1664            as_type="event",
1665            langfuse_client=langfuse_client,
1666            input=input,
1667            output=output,
1668            metadata=metadata,
1669            environment=environment,
1670            version=version,
1671            level=level,
1672            status_message=status_message,
1673        )
1674
1675    def update(
1676        self,
1677        *,
1678        name: Optional[str] = None,
1679        input: Optional[Any] = None,
1680        output: Optional[Any] = None,
1681        metadata: Optional[Any] = None,
1682        version: Optional[str] = None,
1683        level: Optional[SpanLevel] = None,
1684        status_message: Optional[str] = None,
1685        completion_start_time: Optional[datetime] = None,
1686        model: Optional[str] = None,
1687        model_parameters: Optional[Dict[str, MapValue]] = None,
1688        usage_details: Optional[Dict[str, int]] = None,
1689        cost_details: Optional[Dict[str, float]] = None,
1690        prompt: Optional[PromptClient] = None,
1691        **kwargs: Any,
1692    ) -> "LangfuseEvent":
1693        """Update is not allowed for LangfuseEvent because events cannot be updated.
1694
1695        This method logs a warning and returns self without making changes.
1696
1697        Returns:
1698            self: Returns the unchanged LangfuseEvent instance
1699        """
1700        langfuse_logger.warning(
1701            "Attempted to update LangfuseEvent observation. Events cannot be updated after creation."
1702        )
1703        return self

Specialized span implementation for Langfuse Events.

LangfuseEvent( *, otel_span: opentelemetry.trace.span.Span, langfuse_client: Langfuse, input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, environment: Optional[str] = None, version: Optional[str] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None)
1636    def __init__(
1637        self,
1638        *,
1639        otel_span: otel_trace_api.Span,
1640        langfuse_client: "Langfuse",
1641        input: Optional[Any] = None,
1642        output: Optional[Any] = None,
1643        metadata: Optional[Any] = None,
1644        environment: Optional[str] = None,
1645        version: Optional[str] = None,
1646        level: Optional[SpanLevel] = None,
1647        status_message: Optional[str] = None,
1648    ):
1649        """Initialize a new LangfuseEvent span.
1650
1651        Args:
1652            otel_span: The OpenTelemetry span to wrap
1653            langfuse_client: Reference to the parent Langfuse client
1654            input: Input data for the event
1655            output: Output from the event
1656            metadata: Additional metadata to associate with the generation
1657            environment: The tracing environment
1658            version: Version identifier for the model or component
1659            level: Importance level of the generation (info, warning, error)
1660            status_message: Optional status message for the generation
1661        """
1662        super().__init__(
1663            otel_span=otel_span,
1664            as_type="event",
1665            langfuse_client=langfuse_client,
1666            input=input,
1667            output=output,
1668            metadata=metadata,
1669            environment=environment,
1670            version=version,
1671            level=level,
1672            status_message=status_message,
1673        )

Initialize a new LangfuseEvent span.

Arguments:
  • otel_span: The OpenTelemetry span to wrap
  • langfuse_client: Reference to the parent Langfuse client
  • input: Input data for the event
  • output: Output from the event
  • metadata: Additional metadata to associate with the generation
  • environment: The tracing environment
  • 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
def update( self, *, name: Optional[str] = None, input: Optional[Any] = None, output: Optional[Any] = None, metadata: Optional[Any] = None, version: Optional[str] = None, level: Optional[Literal['DEBUG', 'DEFAULT', 'WARNING', 'ERROR']] = None, status_message: Optional[str] = None, completion_start_time: Optional[datetime.datetime] = None, model: Optional[str] = None, model_parameters: Optional[Dict[str, Union[str, NoneType, int, bool, List[str]]]] = None, usage_details: Optional[Dict[str, int]] = None, cost_details: Optional[Dict[str, float]] = None, prompt: Union[langfuse.model.TextPromptClient, langfuse.model.ChatPromptClient, NoneType] = None, **kwargs: Any) -> LangfuseEvent:
1675    def update(
1676        self,
1677        *,
1678        name: Optional[str] = None,
1679        input: Optional[Any] = None,
1680        output: Optional[Any] = None,
1681        metadata: Optional[Any] = None,
1682        version: Optional[str] = None,
1683        level: Optional[SpanLevel] = None,
1684        status_message: Optional[str] = None,
1685        completion_start_time: Optional[datetime] = None,
1686        model: Optional[str] = None,
1687        model_parameters: Optional[Dict[str, MapValue]] = None,
1688        usage_details: Optional[Dict[str, int]] = None,
1689        cost_details: Optional[Dict[str, float]] = None,
1690        prompt: Optional[PromptClient] = None,
1691        **kwargs: Any,
1692    ) -> "LangfuseEvent":
1693        """Update is not allowed for LangfuseEvent because events cannot be updated.
1694
1695        This method logs a warning and returns self without making changes.
1696
1697        Returns:
1698            self: Returns the unchanged LangfuseEvent instance
1699        """
1700        langfuse_logger.warning(
1701            "Attempted to update LangfuseEvent observation. Events cannot be updated after creation."
1702        )
1703        return self

Update is not allowed for LangfuseEvent because events cannot be updated.

This method logs a warning and returns self without making changes.

Returns:

self: Returns the unchanged LangfuseEvent instance

class LangfuseOtelSpanAttributes:
27class LangfuseOtelSpanAttributes:
28    # Langfuse-Trace attributes
29    TRACE_NAME = "langfuse.trace.name"
30    TRACE_USER_ID = "user.id"
31    TRACE_SESSION_ID = "session.id"
32    TRACE_TAGS = "langfuse.trace.tags"
33    TRACE_PUBLIC = "langfuse.trace.public"
34    TRACE_METADATA = "langfuse.trace.metadata"
35    TRACE_INPUT = "langfuse.trace.input"
36    TRACE_OUTPUT = "langfuse.trace.output"
37
38    # Langfuse-observation attributes
39    OBSERVATION_TYPE = "langfuse.observation.type"
40    OBSERVATION_METADATA = "langfuse.observation.metadata"
41    OBSERVATION_LEVEL = "langfuse.observation.level"
42    OBSERVATION_STATUS_MESSAGE = "langfuse.observation.status_message"
43    OBSERVATION_INPUT = "langfuse.observation.input"
44    OBSERVATION_OUTPUT = "langfuse.observation.output"
45
46    # Langfuse-observation of type Generation attributes
47    OBSERVATION_COMPLETION_START_TIME = "langfuse.observation.completion_start_time"
48    OBSERVATION_MODEL = "langfuse.observation.model.name"
49    OBSERVATION_MODEL_PARAMETERS = "langfuse.observation.model.parameters"
50    OBSERVATION_USAGE_DETAILS = "langfuse.observation.usage_details"
51    OBSERVATION_COST_DETAILS = "langfuse.observation.cost_details"
52    OBSERVATION_PROMPT_NAME = "langfuse.observation.prompt.name"
53    OBSERVATION_PROMPT_VERSION = "langfuse.observation.prompt.version"
54
55    # General
56    ENVIRONMENT = "langfuse.environment"
57    RELEASE = "langfuse.release"
58    VERSION = "langfuse.version"
59
60    # Internal
61    AS_ROOT = "langfuse.internal.as_root"
62
63    # Experiments
64    EXPERIMENT_ID = "langfuse.experiment.id"
65    EXPERIMENT_NAME = "langfuse.experiment.name"
66    EXPERIMENT_DESCRIPTION = "langfuse.experiment.description"
67    EXPERIMENT_METADATA = "langfuse.experiment.metadata"
68    EXPERIMENT_DATASET_ID = "langfuse.experiment.dataset.id"
69    EXPERIMENT_ITEM_ID = "langfuse.experiment.item.id"
70    EXPERIMENT_ITEM_EXPECTED_OUTPUT = "langfuse.experiment.item.expected_output"
71    EXPERIMENT_ITEM_METADATA = "langfuse.experiment.item.metadata"
72    EXPERIMENT_ITEM_ROOT_OBSERVATION_ID = "langfuse.experiment.item.root_observation_id"
TRACE_NAME = 'langfuse.trace.name'
TRACE_USER_ID = 'user.id'
TRACE_SESSION_ID = 'session.id'
TRACE_TAGS = 'langfuse.trace.tags'
TRACE_PUBLIC = 'langfuse.trace.public'
TRACE_METADATA = 'langfuse.trace.metadata'
TRACE_INPUT = 'langfuse.trace.input'
TRACE_OUTPUT = 'langfuse.trace.output'
OBSERVATION_TYPE = 'langfuse.observation.type'
OBSERVATION_METADATA = 'langfuse.observation.metadata'
OBSERVATION_LEVEL = 'langfuse.observation.level'
OBSERVATION_STATUS_MESSAGE = 'langfuse.observation.status_message'
OBSERVATION_INPUT = 'langfuse.observation.input'
OBSERVATION_OUTPUT = 'langfuse.observation.output'
OBSERVATION_COMPLETION_START_TIME = 'langfuse.observation.completion_start_time'
OBSERVATION_MODEL = 'langfuse.observation.model.name'
OBSERVATION_MODEL_PARAMETERS = 'langfuse.observation.model.parameters'
OBSERVATION_USAGE_DETAILS = 'langfuse.observation.usage_details'
OBSERVATION_COST_DETAILS = 'langfuse.observation.cost_details'
OBSERVATION_PROMPT_NAME = 'langfuse.observation.prompt.name'
OBSERVATION_PROMPT_VERSION = 'langfuse.observation.prompt.version'
ENVIRONMENT = 'langfuse.environment'
RELEASE = 'langfuse.release'
VERSION = 'langfuse.version'
AS_ROOT = 'langfuse.internal.as_root'
EXPERIMENT_ID = 'langfuse.experiment.id'
EXPERIMENT_NAME = 'langfuse.experiment.name'
EXPERIMENT_DESCRIPTION = 'langfuse.experiment.description'
EXPERIMENT_METADATA = 'langfuse.experiment.metadata'
EXPERIMENT_DATASET_ID = 'langfuse.experiment.dataset.id'
EXPERIMENT_ITEM_ID = 'langfuse.experiment.item.id'
EXPERIMENT_ITEM_EXPECTED_OUTPUT = 'langfuse.experiment.item.expected_output'
EXPERIMENT_ITEM_METADATA = 'langfuse.experiment.item.metadata'
EXPERIMENT_ITEM_ROOT_OBSERVATION_ID = 'langfuse.experiment.item.root_observation_id'
class LangfuseAgent(langfuse._client.span.LangfuseObservationWrapper):
1706class LangfuseAgent(LangfuseObservationWrapper):
1707    """Agent observation for reasoning blocks that act on tools using LLM guidance."""
1708
1709    def __init__(self, **kwargs: Any) -> None:
1710        """Initialize a new LangfuseAgent span."""
1711        kwargs["as_type"] = "agent"
1712        super().__init__(**kwargs)

Agent observation for reasoning blocks that act on tools using LLM guidance.

LangfuseAgent(**kwargs: Any)
1709    def __init__(self, **kwargs: Any) -> None:
1710        """Initialize a new LangfuseAgent span."""
1711        kwargs["as_type"] = "agent"
1712        super().__init__(**kwargs)

Initialize a new LangfuseAgent span.

class LangfuseTool(langfuse._client.span.LangfuseObservationWrapper):
1715class LangfuseTool(LangfuseObservationWrapper):
1716    """Tool observation representing external tool calls, e.g., calling a weather API."""
1717
1718    def __init__(self, **kwargs: Any) -> None:
1719        """Initialize a new LangfuseTool span."""
1720        kwargs["as_type"] = "tool"
1721        super().__init__(**kwargs)

Tool observation representing external tool calls, e.g., calling a weather API.

LangfuseTool(**kwargs: Any)
1718    def __init__(self, **kwargs: Any) -> None:
1719        """Initialize a new LangfuseTool span."""
1720        kwargs["as_type"] = "tool"
1721        super().__init__(**kwargs)

Initialize a new LangfuseTool span.

class LangfuseChain(langfuse._client.span.LangfuseObservationWrapper):
1724class LangfuseChain(LangfuseObservationWrapper):
1725    """Chain observation for connecting LLM application steps, e.g. passing context from retriever to LLM."""
1726
1727    def __init__(self, **kwargs: Any) -> None:
1728        """Initialize a new LangfuseChain span."""
1729        kwargs["as_type"] = "chain"
1730        super().__init__(**kwargs)

Chain observation for connecting LLM application steps, e.g. passing context from retriever to LLM.

LangfuseChain(**kwargs: Any)
1727    def __init__(self, **kwargs: Any) -> None:
1728        """Initialize a new LangfuseChain span."""
1729        kwargs["as_type"] = "chain"
1730        super().__init__(**kwargs)

Initialize a new LangfuseChain span.

class LangfuseEmbedding(langfuse._client.span.LangfuseObservationWrapper):
1742class LangfuseEmbedding(LangfuseObservationWrapper):
1743    """Embedding observation for LLM embedding calls, typically used before retrieval."""
1744
1745    def __init__(self, **kwargs: Any) -> None:
1746        """Initialize a new LangfuseEmbedding span."""
1747        kwargs["as_type"] = "embedding"
1748        super().__init__(**kwargs)

Embedding observation for LLM embedding calls, typically used before retrieval.

LangfuseEmbedding(**kwargs: Any)
1745    def __init__(self, **kwargs: Any) -> None:
1746        """Initialize a new LangfuseEmbedding span."""
1747        kwargs["as_type"] = "embedding"
1748        super().__init__(**kwargs)

Initialize a new LangfuseEmbedding span.

class LangfuseEvaluator(langfuse._client.span.LangfuseObservationWrapper):
1751class LangfuseEvaluator(LangfuseObservationWrapper):
1752    """Evaluator observation for assessing relevance, correctness, or helpfulness of LLM outputs."""
1753
1754    def __init__(self, **kwargs: Any) -> None:
1755        """Initialize a new LangfuseEvaluator span."""
1756        kwargs["as_type"] = "evaluator"
1757        super().__init__(**kwargs)

Evaluator observation for assessing relevance, correctness, or helpfulness of LLM outputs.

LangfuseEvaluator(**kwargs: Any)
1754    def __init__(self, **kwargs: Any) -> None:
1755        """Initialize a new LangfuseEvaluator span."""
1756        kwargs["as_type"] = "evaluator"
1757        super().__init__(**kwargs)

Initialize a new LangfuseEvaluator span.

class LangfuseRetriever(langfuse._client.span.LangfuseObservationWrapper):
1733class LangfuseRetriever(LangfuseObservationWrapper):
1734    """Retriever observation for data retrieval steps, e.g. vector store or database queries."""
1735
1736    def __init__(self, **kwargs: Any) -> None:
1737        """Initialize a new LangfuseRetriever span."""
1738        kwargs["as_type"] = "retriever"
1739        super().__init__(**kwargs)

Retriever observation for data retrieval steps, e.g. vector store or database queries.

LangfuseRetriever(**kwargs: Any)
1736    def __init__(self, **kwargs: Any) -> None:
1737        """Initialize a new LangfuseRetriever span."""
1738        kwargs["as_type"] = "retriever"
1739        super().__init__(**kwargs)

Initialize a new LangfuseRetriever span.

class LangfuseGuardrail(langfuse._client.span.LangfuseObservationWrapper):
1760class LangfuseGuardrail(LangfuseObservationWrapper):
1761    """Guardrail observation for protection e.g. against jailbreaks or offensive content."""
1762
1763    def __init__(self, **kwargs: Any) -> None:
1764        """Initialize a new LangfuseGuardrail span."""
1765        kwargs["as_type"] = "guardrail"
1766        super().__init__(**kwargs)

Guardrail observation for protection e.g. against jailbreaks or offensive content.

LangfuseGuardrail(**kwargs: Any)
1763    def __init__(self, **kwargs: Any) -> None:
1764        """Initialize a new LangfuseGuardrail span."""
1765        kwargs["as_type"] = "guardrail"
1766        super().__init__(**kwargs)

Initialize a new LangfuseGuardrail span.

class Evaluation:
 97class Evaluation:
 98    """Represents an evaluation result for an experiment item or an entire experiment run.
 99
100    This class provides a strongly-typed way to create evaluation results in evaluator functions.
101    Users must use keyword arguments when instantiating this class.
102
103    Attributes:
104        name: Unique identifier for the evaluation metric. Should be descriptive
105            and consistent across runs (e.g., "accuracy", "bleu_score", "toxicity").
106            Used for aggregation and comparison across experiment runs.
107        value: The evaluation score or result. Can be:
108            - Numeric (int/float): For quantitative metrics like accuracy (0.85), BLEU (0.42)
109            - String: For categorical results like "positive", "negative", "neutral"
110            - Boolean: For binary assessments like "passes_safety_check"
111            - None: When evaluation cannot be computed (missing data, API errors, etc.)
112        comment: Optional human-readable explanation of the evaluation result.
113            Useful for providing context, explaining scoring rationale, or noting
114            special conditions. Displayed in Langfuse UI for interpretability.
115        metadata: Optional structured metadata about the evaluation process.
116            Can include confidence scores, intermediate calculations, model versions,
117            or any other relevant technical details.
118        data_type: Optional score data type. Required if value is not NUMERIC.
119            One of NUMERIC, CATEGORICAL, or BOOLEAN. Defaults to NUMERIC.
120        config_id: Optional Langfuse score config ID.
121
122    Examples:
123        Basic accuracy evaluation:
124        ```python
125        from langfuse import Evaluation
126
127        def accuracy_evaluator(*, input, output, expected_output=None, **kwargs):
128            if not expected_output:
129                return Evaluation(name="accuracy", value=None, comment="No expected output")
130
131            is_correct = output.strip().lower() == expected_output.strip().lower()
132            return Evaluation(
133                name="accuracy",
134                value=1.0 if is_correct else 0.0,
135                comment="Correct answer" if is_correct else "Incorrect answer"
136            )
137        ```
138
139        Multi-metric evaluator:
140        ```python
141        def comprehensive_evaluator(*, input, output, expected_output=None, **kwargs):
142            return [
143                Evaluation(name="length", value=len(output), comment=f"Output length: {len(output)} chars"),
144                Evaluation(name="has_greeting", value="hello" in output.lower(), comment="Contains greeting"),
145                Evaluation(
146                    name="quality",
147                    value=0.85,
148                    comment="High quality response",
149                    metadata={"confidence": 0.92, "model": "gpt-4"}
150                )
151            ]
152        ```
153
154        Categorical evaluation:
155        ```python
156        def sentiment_evaluator(*, input, output, **kwargs):
157            sentiment = analyze_sentiment(output)  # Returns "positive", "negative", or "neutral"
158            return Evaluation(
159                name="sentiment",
160                value=sentiment,
161                comment=f"Response expresses {sentiment} sentiment",
162                data_type="CATEGORICAL"
163            )
164        ```
165
166        Failed evaluation with error handling:
167        ```python
168        def external_api_evaluator(*, input, output, **kwargs):
169            try:
170                score = external_api.evaluate(output)
171                return Evaluation(name="external_score", value=score)
172            except Exception as e:
173                return Evaluation(
174                    name="external_score",
175                    value=None,
176                    comment=f"API unavailable: {e}",
177                    metadata={"error": str(e), "retry_count": 3}
178                )
179        ```
180
181    Note:
182        All arguments must be passed as keywords. Positional arguments are not allowed
183        to ensure code clarity and prevent errors from argument reordering.
184    """
185
186    def __init__(
187        self,
188        *,
189        name: str,
190        value: Union[int, float, str, bool, None],
191        comment: Optional[str] = None,
192        metadata: Optional[Dict[str, Any]] = None,
193        data_type: Optional[ScoreDataType] = None,
194        config_id: Optional[str] = None,
195    ):
196        """Initialize an Evaluation with the provided data.
197
198        Args:
199            name: Unique identifier for the evaluation metric.
200            value: The evaluation score or result.
201            comment: Optional human-readable explanation of the result.
202            metadata: Optional structured metadata about the evaluation process.
203            data_type: Optional score data type (NUMERIC, CATEGORICAL, or BOOLEAN).
204            config_id: Optional Langfuse score config ID.
205
206        Note:
207            All arguments must be provided as keywords. Positional arguments will raise a TypeError.
208        """
209        self.name = name
210        self.value = value
211        self.comment = comment
212        self.metadata = metadata
213        self.data_type = data_type
214        self.config_id = config_id

Represents an evaluation result for an experiment item or an entire experiment run.

This class provides a strongly-typed way to create evaluation results in evaluator functions. Users must use keyword arguments when instantiating this class.

Attributes:
  • name: Unique identifier for the evaluation metric. Should be descriptive and consistent across runs (e.g., "accuracy", "bleu_score", "toxicity"). Used for aggregation and comparison across experiment runs.
  • value: The evaluation score or result. Can be:
    • Numeric (int/float): For quantitative metrics like accuracy (0.85), BLEU (0.42)
    • String: For categorical results like "positive", "negative", "neutral"
    • Boolean: For binary assessments like "passes_safety_check"
    • None: When evaluation cannot be computed (missing data, API errors, etc.)
  • comment: Optional human-readable explanation of the evaluation result. Useful for providing context, explaining scoring rationale, or noting special conditions. Displayed in Langfuse UI for interpretability.
  • metadata: Optional structured metadata about the evaluation process. Can include confidence scores, intermediate calculations, model versions, or any other relevant technical details.
  • data_type: Optional score data type. Required if value is not NUMERIC. One of NUMERIC, CATEGORICAL, or BOOLEAN. Defaults to NUMERIC.
  • config_id: Optional Langfuse score config ID.
Examples:

Basic accuracy evaluation:

from langfuse import Evaluation

def accuracy_evaluator(*, input, output, expected_output=None, **kwargs):
    if not expected_output:
        return Evaluation(name="accuracy", value=None, comment="No expected output")

    is_correct = output.strip().lower() == expected_output.strip().lower()
    return Evaluation(
        name="accuracy",
        value=1.0 if is_correct else 0.0,
        comment="Correct answer" if is_correct else "Incorrect answer"
    )

Multi-metric evaluator:

def comprehensive_evaluator(*, input, output, expected_output=None, **kwargs):
    return [
        Evaluation(name="length", value=len(output), comment=f"Output length: {len(output)} chars"),
        Evaluation(name="has_greeting", value="hello" in output.lower(), comment="Contains greeting"),
        Evaluation(
            name="quality",
            value=0.85,
            comment="High quality response",
            metadata={"confidence": 0.92, "model": "gpt-4"}
        )
    ]

Categorical evaluation:

def sentiment_evaluator(*, input, output, **kwargs):
    sentiment = analyze_sentiment(output)  # Returns "positive", "negative", or "neutral"
    return Evaluation(
        name="sentiment",
        value=sentiment,
        comment=f"Response expresses {sentiment} sentiment",
        data_type="CATEGORICAL"
    )

Failed evaluation with error handling:

def external_api_evaluator(*, input, output, **kwargs):
    try:
        score = external_api.evaluate(output)
        return Evaluation(name="external_score", value=score)
    except Exception as e:
        return Evaluation(
            name="external_score",
            value=None,
            comment=f"API unavailable: {e}",
            metadata={"error": str(e), "retry_count": 3}
        )
Note:

All arguments must be passed as keywords. Positional arguments are not allowed to ensure code clarity and prevent errors from argument reordering.

Evaluation( *, name: str, value: Union[int, float, str, bool, NoneType], comment: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None, data_type: Optional[langfuse.api.ScoreDataType] = None, config_id: Optional[str] = None)
186    def __init__(
187        self,
188        *,
189        name: str,
190        value: Union[int, float, str, bool, None],
191        comment: Optional[str] = None,
192        metadata: Optional[Dict[str, Any]] = None,
193        data_type: Optional[ScoreDataType] = None,
194        config_id: Optional[str] = None,
195    ):
196        """Initialize an Evaluation with the provided data.
197
198        Args:
199            name: Unique identifier for the evaluation metric.
200            value: The evaluation score or result.
201            comment: Optional human-readable explanation of the result.
202            metadata: Optional structured metadata about the evaluation process.
203            data_type: Optional score data type (NUMERIC, CATEGORICAL, or BOOLEAN).
204            config_id: Optional Langfuse score config ID.
205
206        Note:
207            All arguments must be provided as keywords. Positional arguments will raise a TypeError.
208        """
209        self.name = name
210        self.value = value
211        self.comment = comment
212        self.metadata = metadata
213        self.data_type = data_type
214        self.config_id = config_id

Initialize an Evaluation with the provided data.

Arguments:
  • name: Unique identifier for the evaluation metric.
  • value: The evaluation score or result.
  • comment: Optional human-readable explanation of the result.
  • metadata: Optional structured metadata about the evaluation process.
  • data_type: Optional score data type (NUMERIC, CATEGORICAL, or BOOLEAN).
  • config_id: Optional Langfuse score config ID.
Note:

All arguments must be provided as keywords. Positional arguments will raise a TypeError.

name
value
comment
metadata
data_type
config_id